1 /*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "OpenGLRenderer"
18
19 #include <SkUtils.h>
20
21 #include <cutils/properties.h>
22
23 #include <utils/Log.h>
24
25 #include "Caches.h"
26 #include "Debug.h"
27 #include "FontRenderer.h"
28 #include "Rect.h"
29
30 namespace android {
31 namespace uirenderer {
32
33 ///////////////////////////////////////////////////////////////////////////////
34 // FontRenderer
35 ///////////////////////////////////////////////////////////////////////////////
36
37 static bool sLogFontRendererCreate = true;
38
FontRenderer()39 FontRenderer::FontRenderer() {
40 if (sLogFontRendererCreate) {
41 INIT_LOGD("Creating FontRenderer");
42 }
43
44 mGammaTable = NULL;
45 mInitialized = false;
46 mMaxNumberOfQuads = 1024;
47 mCurrentQuadIndex = 0;
48
49 mTextMesh = NULL;
50 mCurrentCacheTexture = NULL;
51 mLastCacheTexture = NULL;
52
53 mLinearFiltering = false;
54
55 mIndexBufferID = 0;
56
57 mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH;
58 mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT;
59 mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH;
60 mLargeCacheHeight = DEFAULT_TEXT_LARGE_CACHE_HEIGHT;
61
62 char property[PROPERTY_VALUE_MAX];
63 if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, NULL) > 0) {
64 mSmallCacheWidth = atoi(property);
65 }
66
67 if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, NULL) > 0) {
68 mSmallCacheHeight = atoi(property);
69 }
70
71 if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, NULL) > 0) {
72 mLargeCacheWidth = atoi(property);
73 }
74
75 if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, NULL) > 0) {
76 mLargeCacheHeight = atoi(property);
77 }
78
79 uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize;
80 mSmallCacheWidth = mSmallCacheWidth > maxTextureSize ? maxTextureSize : mSmallCacheWidth;
81 mSmallCacheHeight = mSmallCacheHeight > maxTextureSize ? maxTextureSize : mSmallCacheHeight;
82 mLargeCacheWidth = mLargeCacheWidth > maxTextureSize ? maxTextureSize : mLargeCacheWidth;
83 mLargeCacheHeight = mLargeCacheHeight > maxTextureSize ? maxTextureSize : mLargeCacheHeight;
84
85 if (sLogFontRendererCreate) {
86 INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
87 mSmallCacheWidth, mSmallCacheHeight,
88 mLargeCacheWidth, mLargeCacheHeight >> 1,
89 mLargeCacheWidth, mLargeCacheHeight >> 1,
90 mLargeCacheWidth, mLargeCacheHeight);
91 }
92
93 sLogFontRendererCreate = false;
94 }
95
~FontRenderer()96 FontRenderer::~FontRenderer() {
97 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
98 delete mCacheTextures[i];
99 }
100 mCacheTextures.clear();
101
102 if (mInitialized) {
103 // Unbinding the buffer shouldn't be necessary but it crashes with some drivers
104 Caches::getInstance().unbindIndicesBuffer();
105 glDeleteBuffers(1, &mIndexBufferID);
106
107 delete[] mTextMesh;
108 }
109
110 Vector<Font*> fontsToDereference = mActiveFonts;
111 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
112 delete fontsToDereference[i];
113 }
114 }
115
flushAllAndInvalidate()116 void FontRenderer::flushAllAndInvalidate() {
117 if (mCurrentQuadIndex != 0) {
118 issueDrawCommand();
119 mCurrentQuadIndex = 0;
120 }
121
122 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
123 mActiveFonts[i]->invalidateTextureCache();
124 }
125
126 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
127 mCacheTextures[i]->init();
128 }
129
130 #if DEBUG_FONT_RENDERER
131 uint16_t totalGlyphs = 0;
132 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
133 totalGlyphs += mCacheTextures[i]->getGlyphCount();
134 // Erase caches, just as a debugging facility
135 if (mCacheTextures[i]->getTexture()) {
136 memset(mCacheTextures[i]->getTexture(), 0,
137 mCacheTextures[i]->getWidth() * mCacheTextures[i]->getHeight());
138 }
139 }
140 ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
141 #endif
142 }
143
flushLargeCaches()144 void FontRenderer::flushLargeCaches() {
145 // Start from 1; don't deallocate smallest/default texture
146 for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
147 CacheTexture* cacheTexture = mCacheTextures[i];
148 if (cacheTexture->getTexture()) {
149 cacheTexture->init();
150 for (uint32_t j = 0; j < mActiveFonts.size(); j++) {
151 mActiveFonts[j]->invalidateTextureCache(cacheTexture);
152 }
153 cacheTexture->releaseTexture();
154 }
155 }
156 }
157
cacheBitmapInTexture(const SkGlyph & glyph,uint32_t * startX,uint32_t * startY)158 CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
159 uint32_t* startX, uint32_t* startY) {
160 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
161 if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
162 return mCacheTextures[i];
163 }
164 }
165 // Could not fit glyph into current cache textures
166 return NULL;
167 }
168
cacheBitmap(const SkGlyph & glyph,CachedGlyphInfo * cachedGlyph,uint32_t * retOriginX,uint32_t * retOriginY,bool precaching)169 void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
170 uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
171 checkInit();
172 cachedGlyph->mIsValid = false;
173 // If the glyph is too tall, don't cache it
174 if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
175 mCacheTextures[mCacheTextures.size() - 1]->getHeight()) {
176 ALOGE("Font size too large to fit in cache. width, height = %i, %i",
177 (int) glyph.fWidth, (int) glyph.fHeight);
178 return;
179 }
180
181 // Now copy the bitmap into the cache texture
182 uint32_t startX = 0;
183 uint32_t startY = 0;
184
185 CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
186
187 if (!cacheTexture) {
188 if (!precaching) {
189 // If the new glyph didn't fit and we are not just trying to precache it,
190 // clear out the cache and try again
191 flushAllAndInvalidate();
192 cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
193 }
194
195 if (!cacheTexture) {
196 // either the glyph didn't fit or we're precaching and will cache it when we draw
197 return;
198 }
199 }
200
201 cachedGlyph->mCacheTexture = cacheTexture;
202
203 *retOriginX = startX;
204 *retOriginY = startY;
205
206 uint32_t endX = startX + glyph.fWidth;
207 uint32_t endY = startY + glyph.fHeight;
208
209 uint32_t cacheWidth = cacheTexture->getWidth();
210
211 if (!cacheTexture->getTexture()) {
212 Caches::getInstance().activeTexture(0);
213 // Large-glyph texture memory is allocated only as needed
214 cacheTexture->allocateTexture();
215 }
216
217 uint8_t* cacheBuffer = cacheTexture->getTexture();
218 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
219 unsigned int stride = glyph.rowBytes();
220
221 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
222
223 for (cacheX = startX - TEXTURE_BORDER_SIZE; cacheX < endX + TEXTURE_BORDER_SIZE; cacheX++) {
224 cacheBuffer[(startY - TEXTURE_BORDER_SIZE) * cacheWidth + cacheX] = 0;
225 cacheBuffer[(endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + cacheX] = 0;
226 }
227
228 for (cacheY = startY - TEXTURE_BORDER_SIZE + 1;
229 cacheY < endY + TEXTURE_BORDER_SIZE - 1; cacheY++) {
230 cacheBuffer[cacheY * cacheWidth + startX - TEXTURE_BORDER_SIZE] = 0;
231 cacheBuffer[cacheY * cacheWidth + endX + TEXTURE_BORDER_SIZE - 1] = 0;
232 }
233
234 if (mGammaTable) {
235 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
236 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
237 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
238 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
239 }
240 }
241 } else {
242 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
243 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
244 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
245 cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
246 }
247 }
248 }
249
250 cachedGlyph->mIsValid = true;
251 }
252
createCacheTexture(int width,int height,bool allocate)253 CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
254 CacheTexture* cacheTexture = new CacheTexture(width, height);
255
256 if (allocate) {
257 Caches::getInstance().activeTexture(0);
258 cacheTexture->allocateTexture();
259 }
260
261 return cacheTexture;
262 }
263
initTextTexture()264 void FontRenderer::initTextTexture() {
265 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
266 delete mCacheTextures[i];
267 }
268 mCacheTextures.clear();
269
270 mUploadTexture = false;
271 mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
272 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
273 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
274 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false));
275 mCurrentCacheTexture = mCacheTextures[0];
276 }
277
278 // Avoid having to reallocate memory and render quad by quad
initVertexArrayBuffers()279 void FontRenderer::initVertexArrayBuffers() {
280 uint32_t numIndices = mMaxNumberOfQuads * 6;
281 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
282 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
283
284 // Four verts, two triangles , six indices per quad
285 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
286 int i6 = i * 6;
287 int i4 = i * 4;
288
289 indexBufferData[i6 + 0] = i4 + 0;
290 indexBufferData[i6 + 1] = i4 + 1;
291 indexBufferData[i6 + 2] = i4 + 2;
292
293 indexBufferData[i6 + 3] = i4 + 0;
294 indexBufferData[i6 + 4] = i4 + 2;
295 indexBufferData[i6 + 5] = i4 + 3;
296 }
297
298 glGenBuffers(1, &mIndexBufferID);
299 Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
300 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
301
302 free(indexBufferData);
303
304 uint32_t coordSize = 2;
305 uint32_t uvSize = 2;
306 uint32_t vertsPerQuad = 4;
307 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
308 mTextMesh = new float[vertexBufferSize];
309 }
310
311 // We don't want to allocate anything unless we actually draw text
checkInit()312 void FontRenderer::checkInit() {
313 if (mInitialized) {
314 return;
315 }
316
317 initTextTexture();
318 initVertexArrayBuffers();
319
320 mInitialized = true;
321 }
322
checkTextureUpdate()323 void FontRenderer::checkTextureUpdate() {
324 if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) {
325 return;
326 }
327
328 Caches& caches = Caches::getInstance();
329 GLuint lastTextureId = 0;
330 // Iterate over all the cache textures and see which ones need to be updated
331 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
332 CacheTexture* cacheTexture = mCacheTextures[i];
333 if (cacheTexture->isDirty() && cacheTexture->getTexture()) {
334 // Can't copy inner rect; glTexSubimage expects pointer to deal with entire buffer
335 // of data. So expand the dirty rect to the encompassing horizontal stripe.
336 const Rect* dirtyRect = cacheTexture->getDirtyRect();
337 uint32_t x = 0;
338 uint32_t y = dirtyRect->top;
339 uint32_t width = cacheTexture->getWidth();
340 uint32_t height = dirtyRect->getHeight();
341 void* textureData = cacheTexture->getTexture() + y * width;
342
343 if (cacheTexture->getTextureId() != lastTextureId) {
344 lastTextureId = cacheTexture->getTextureId();
345 caches.activeTexture(0);
346 glBindTexture(GL_TEXTURE_2D, lastTextureId);
347 }
348 #if DEBUG_FONT_RENDERER
349 ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d",
350 i, x, y, width, height);
351 #endif
352 glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height,
353 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
354 cacheTexture->setDirty(false);
355 }
356 }
357
358 caches.activeTexture(0);
359 glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->getTextureId());
360
361 mCurrentCacheTexture->setLinearFiltering(mLinearFiltering, false);
362 mLastCacheTexture = mCurrentCacheTexture;
363
364 mUploadTexture = false;
365 }
366
issueDrawCommand()367 void FontRenderer::issueDrawCommand() {
368 checkTextureUpdate();
369
370 Caches& caches = Caches::getInstance();
371 caches.bindIndicesBuffer(mIndexBufferID);
372 if (!mDrawn) {
373 float* buffer = mTextMesh;
374 int offset = 2;
375
376 bool force = caches.unbindMeshBuffer();
377 caches.bindPositionVertexPointer(force, buffer);
378 caches.bindTexCoordsVertexPointer(force, buffer + offset);
379 }
380
381 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
382
383 mDrawn = true;
384 }
385
appendMeshQuadNoClip(float x1,float y1,float u1,float v1,float x2,float y2,float u2,float v2,float x3,float y3,float u3,float v3,float x4,float y4,float u4,float v4,CacheTexture * texture)386 void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
387 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
388 float x4, float y4, float u4, float v4, CacheTexture* texture) {
389 if (texture != mCurrentCacheTexture) {
390 if (mCurrentQuadIndex != 0) {
391 // First, draw everything stored already which uses the previous texture
392 issueDrawCommand();
393 mCurrentQuadIndex = 0;
394 }
395 // Now use the new texture id
396 mCurrentCacheTexture = texture;
397 }
398
399 const uint32_t vertsPerQuad = 4;
400 const uint32_t floatsPerVert = 4;
401 float* currentPos = mTextMesh + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
402
403 (*currentPos++) = x1;
404 (*currentPos++) = y1;
405 (*currentPos++) = u1;
406 (*currentPos++) = v1;
407
408 (*currentPos++) = x2;
409 (*currentPos++) = y2;
410 (*currentPos++) = u2;
411 (*currentPos++) = v2;
412
413 (*currentPos++) = x3;
414 (*currentPos++) = y3;
415 (*currentPos++) = u3;
416 (*currentPos++) = v3;
417
418 (*currentPos++) = x4;
419 (*currentPos++) = y4;
420 (*currentPos++) = u4;
421 (*currentPos++) = v4;
422
423 mCurrentQuadIndex++;
424 }
425
appendMeshQuad(float x1,float y1,float u1,float v1,float x2,float y2,float u2,float v2,float x3,float y3,float u3,float v3,float x4,float y4,float u4,float v4,CacheTexture * texture)426 void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
427 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
428 float x4, float y4, float u4, float v4, CacheTexture* texture) {
429
430 if (mClip &&
431 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
432 return;
433 }
434
435 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
436
437 if (mBounds) {
438 mBounds->left = fmin(mBounds->left, x1);
439 mBounds->top = fmin(mBounds->top, y3);
440 mBounds->right = fmax(mBounds->right, x3);
441 mBounds->bottom = fmax(mBounds->bottom, y1);
442 }
443
444 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
445 issueDrawCommand();
446 mCurrentQuadIndex = 0;
447 }
448 }
449
appendRotatedMeshQuad(float x1,float y1,float u1,float v1,float x2,float y2,float u2,float v2,float x3,float y3,float u3,float v3,float x4,float y4,float u4,float v4,CacheTexture * texture)450 void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
451 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
452 float x4, float y4, float u4, float v4, CacheTexture* texture) {
453
454 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
455
456 if (mBounds) {
457 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
458 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
459 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
460 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
461 }
462
463 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
464 issueDrawCommand();
465 mCurrentQuadIndex = 0;
466 }
467 }
468
setFont(SkPaint * paint,uint32_t fontId,float fontSize)469 void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
470 int flags = 0;
471 if (paint->isFakeBoldText()) {
472 flags |= Font::kFakeBold;
473 }
474
475 const float skewX = paint->getTextSkewX();
476 uint32_t italicStyle = *(uint32_t*) &skewX;
477 const float scaleXFloat = paint->getTextScaleX();
478 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
479 SkPaint::Style style = paint->getStyle();
480 const float strokeWidthFloat = paint->getStrokeWidth();
481 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
482 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
483 scaleX, style, strokeWidth);
484
485 }
486
renderDropShadow(SkPaint * paint,const char * text,uint32_t startIndex,uint32_t len,int numGlyphs,uint32_t radius,const float * positions)487 FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
488 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
489 checkInit();
490
491 if (!mCurrentFont) {
492 DropShadow image;
493 image.width = 0;
494 image.height = 0;
495 image.image = NULL;
496 image.penX = 0;
497 image.penY = 0;
498 return image;
499 }
500
501 mDrawn = false;
502 mClip = NULL;
503 mBounds = NULL;
504
505 Rect bounds;
506 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
507
508 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
509 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
510 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
511
512 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
513 dataBuffer[i] = 0;
514 }
515
516 int penX = radius - bounds.left;
517 int penY = radius - bounds.bottom;
518
519 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
520 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
521 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
522
523 DropShadow image;
524 image.width = paddedWidth;
525 image.height = paddedHeight;
526 image.image = dataBuffer;
527 image.penX = penX;
528 image.penY = penY;
529
530 return image;
531 }
532
initRender(const Rect * clip,Rect * bounds)533 void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
534 checkInit();
535
536 mDrawn = false;
537 mBounds = bounds;
538 mClip = clip;
539 }
540
finishRender()541 void FontRenderer::finishRender() {
542 mBounds = NULL;
543 mClip = NULL;
544
545 if (mCurrentQuadIndex != 0) {
546 issueDrawCommand();
547 mCurrentQuadIndex = 0;
548 }
549 }
550
precache(SkPaint * paint,const char * text,int numGlyphs)551 void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs) {
552 int flags = 0;
553 if (paint->isFakeBoldText()) {
554 flags |= Font::kFakeBold;
555 }
556 const float skewX = paint->getTextSkewX();
557 uint32_t italicStyle = *(uint32_t*) &skewX;
558 const float scaleXFloat = paint->getTextScaleX();
559 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
560 SkPaint::Style style = paint->getStyle();
561 const float strokeWidthFloat = paint->getStrokeWidth();
562 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
563 float fontSize = paint->getTextSize();
564 Font* font = Font::create(this, SkTypeface::UniqueID(paint->getTypeface()),
565 fontSize, flags, italicStyle, scaleX, style, strokeWidth);
566
567 font->precache(paint, text, numGlyphs);
568 }
569
renderText(SkPaint * paint,const Rect * clip,const char * text,uint32_t startIndex,uint32_t len,int numGlyphs,int x,int y,Rect * bounds)570 bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
571 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
572 if (!mCurrentFont) {
573 ALOGE("No font set");
574 return false;
575 }
576
577 initRender(clip, bounds);
578 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
579 finishRender();
580
581 return mDrawn;
582 }
583
renderPosText(SkPaint * paint,const Rect * clip,const char * text,uint32_t startIndex,uint32_t len,int numGlyphs,int x,int y,const float * positions,Rect * bounds)584 bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
585 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
586 const float* positions, Rect* bounds) {
587 if (!mCurrentFont) {
588 ALOGE("No font set");
589 return false;
590 }
591
592 initRender(clip, bounds);
593 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
594 finishRender();
595
596 return mDrawn;
597 }
598
renderTextOnPath(SkPaint * paint,const Rect * clip,const char * text,uint32_t startIndex,uint32_t len,int numGlyphs,SkPath * path,float hOffset,float vOffset,Rect * bounds)599 bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
600 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
601 float hOffset, float vOffset, Rect* bounds) {
602 if (!mCurrentFont) {
603 ALOGE("No font set");
604 return false;
605 }
606
607 initRender(clip, bounds);
608 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
609 finishRender();
610
611 return mDrawn;
612 }
613
removeFont(const Font * font)614 void FontRenderer::removeFont(const Font* font) {
615 for (uint32_t ct = 0; ct < mActiveFonts.size(); ct++) {
616 if (mActiveFonts[ct] == font) {
617 mActiveFonts.removeAt(ct);
618 break;
619 }
620 }
621
622 if (mCurrentFont == font) {
623 mCurrentFont = NULL;
624 }
625 }
626
computeGaussianWeights(float * weights,int32_t radius)627 void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
628 // Compute gaussian weights for the blur
629 // e is the euler's number
630 float e = 2.718281828459045f;
631 float pi = 3.1415926535897932f;
632 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
633 // x is of the form [-radius .. 0 .. radius]
634 // and sigma varies with radius.
635 // Based on some experimental radius values and sigma's
636 // we approximately fit sigma = f(radius) as
637 // sigma = radius * 0.3 + 0.6
638 // The larger the radius gets, the more our gaussian blur
639 // will resemble a box blur since with large sigma
640 // the gaussian curve begins to lose its shape
641 float sigma = 0.3f * (float) radius + 0.6f;
642
643 // Now compute the coefficints
644 // We will store some redundant values to save some math during
645 // the blur calculations
646 // precompute some values
647 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
648 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
649
650 float normalizeFactor = 0.0f;
651 for (int32_t r = -radius; r <= radius; r ++) {
652 float floatR = (float) r;
653 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
654 normalizeFactor += weights[r + radius];
655 }
656
657 //Now we need to normalize the weights because all our coefficients need to add up to one
658 normalizeFactor = 1.0f / normalizeFactor;
659 for (int32_t r = -radius; r <= radius; r ++) {
660 weights[r + radius] *= normalizeFactor;
661 }
662 }
663
horizontalBlur(float * weights,int32_t radius,const uint8_t * source,uint8_t * dest,int32_t width,int32_t height)664 void FontRenderer::horizontalBlur(float* weights, int32_t radius,
665 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
666 float blurredPixel = 0.0f;
667 float currentPixel = 0.0f;
668
669 for (int32_t y = 0; y < height; y ++) {
670
671 const uint8_t* input = source + y * width;
672 uint8_t* output = dest + y * width;
673
674 for (int32_t x = 0; x < width; x ++) {
675 blurredPixel = 0.0f;
676 const float* gPtr = weights;
677 // Optimization for non-border pixels
678 if (x > radius && x < (width - radius)) {
679 const uint8_t *i = input + (x - radius);
680 for (int r = -radius; r <= radius; r ++) {
681 currentPixel = (float) (*i);
682 blurredPixel += currentPixel * gPtr[0];
683 gPtr++;
684 i++;
685 }
686 } else {
687 for (int32_t r = -radius; r <= radius; r ++) {
688 // Stepping left and right away from the pixel
689 int validW = x + r;
690 if (validW < 0) {
691 validW = 0;
692 }
693 if (validW > width - 1) {
694 validW = width - 1;
695 }
696
697 currentPixel = (float) input[validW];
698 blurredPixel += currentPixel * gPtr[0];
699 gPtr++;
700 }
701 }
702 *output = (uint8_t)blurredPixel;
703 output ++;
704 }
705 }
706 }
707
verticalBlur(float * weights,int32_t radius,const uint8_t * source,uint8_t * dest,int32_t width,int32_t height)708 void FontRenderer::verticalBlur(float* weights, int32_t radius,
709 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
710 float blurredPixel = 0.0f;
711 float currentPixel = 0.0f;
712
713 for (int32_t y = 0; y < height; y ++) {
714 uint8_t* output = dest + y * width;
715
716 for (int32_t x = 0; x < width; x ++) {
717 blurredPixel = 0.0f;
718 const float* gPtr = weights;
719 const uint8_t* input = source + x;
720 // Optimization for non-border pixels
721 if (y > radius && y < (height - radius)) {
722 const uint8_t *i = input + ((y - radius) * width);
723 for (int32_t r = -radius; r <= radius; r ++) {
724 currentPixel = (float)(*i);
725 blurredPixel += currentPixel * gPtr[0];
726 gPtr++;
727 i += width;
728 }
729 } else {
730 for (int32_t r = -radius; r <= radius; r ++) {
731 int validH = y + r;
732 // Clamp to zero and width
733 if (validH < 0) {
734 validH = 0;
735 }
736 if (validH > height - 1) {
737 validH = height - 1;
738 }
739
740 const uint8_t *i = input + validH * width;
741 currentPixel = (float) (*i);
742 blurredPixel += currentPixel * gPtr[0];
743 gPtr++;
744 }
745 }
746 *output = (uint8_t) blurredPixel;
747 output++;
748 }
749 }
750 }
751
752
blurImage(uint8_t * image,int32_t width,int32_t height,int32_t radius)753 void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
754 float *gaussian = new float[2 * radius + 1];
755 computeGaussianWeights(gaussian, radius);
756
757 uint8_t* scratch = new uint8_t[width * height];
758
759 horizontalBlur(gaussian, radius, image, scratch, width, height);
760 verticalBlur(gaussian, radius, scratch, image, width, height);
761
762 delete[] gaussian;
763 delete[] scratch;
764 }
765
766 }; // namespace uirenderer
767 }; // namespace android
768