• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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