• 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 "Debug.h"
26 #include "FontRenderer.h"
27 
28 namespace android {
29 namespace uirenderer {
30 
31 ///////////////////////////////////////////////////////////////////////////////
32 // Defines
33 ///////////////////////////////////////////////////////////////////////////////
34 
35 #define DEFAULT_TEXT_CACHE_WIDTH 1024
36 #define DEFAULT_TEXT_CACHE_HEIGHT 256
37 
38 // We should query these values from the GL context
39 #define MAX_TEXT_CACHE_WIDTH 2048
40 #define MAX_TEXT_CACHE_HEIGHT 2048
41 
42 ///////////////////////////////////////////////////////////////////////////////
43 // Font
44 ///////////////////////////////////////////////////////////////////////////////
45 
Font(FontRenderer * state,uint32_t fontId,float fontSize,int flags,uint32_t italicStyle,uint32_t scaleX,SkPaint::Style style,uint32_t strokeWidth)46 Font::Font(FontRenderer* state, uint32_t fontId, float fontSize,
47         int flags, uint32_t italicStyle, uint32_t scaleX,
48         SkPaint::Style style, uint32_t strokeWidth) :
49         mState(state), mFontId(fontId), mFontSize(fontSize),
50         mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX),
51         mStyle(style), mStrokeWidth(mStrokeWidth) {
52 }
53 
54 
~Font()55 Font::~Font() {
56     for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
57         if (mState->mActiveFonts[ct] == this) {
58             mState->mActiveFonts.removeAt(ct);
59             break;
60         }
61     }
62 
63     for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
64         delete mCachedGlyphs.valueAt(i);
65     }
66 }
67 
invalidateTextureCache()68 void Font::invalidateTextureCache() {
69     for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
70         mCachedGlyphs.valueAt(i)->mIsValid = false;
71     }
72 }
73 
measureCachedGlyph(CachedGlyphInfo * glyph,int x,int y,Rect * bounds)74 void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds) {
75     int nPenX = x + glyph->mBitmapLeft;
76     int nPenY = y + glyph->mBitmapTop;
77 
78     int width = (int) glyph->mBitmapWidth;
79     int height = (int) glyph->mBitmapHeight;
80 
81     if (bounds->bottom > nPenY) {
82         bounds->bottom = nPenY;
83     }
84     if (bounds->left > nPenX) {
85         bounds->left = nPenX;
86     }
87     if (bounds->right < nPenX + width) {
88         bounds->right = nPenX + width;
89     }
90     if (bounds->top < nPenY + height) {
91         bounds->top = nPenY + height;
92     }
93 }
94 
drawCachedGlyph(CachedGlyphInfo * glyph,int x,int y)95 void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
96     int nPenX = x + glyph->mBitmapLeft;
97     int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
98 
99     float u1 = glyph->mBitmapMinU;
100     float u2 = glyph->mBitmapMaxU;
101     float v1 = glyph->mBitmapMinV;
102     float v2 = glyph->mBitmapMaxV;
103 
104     int width = (int) glyph->mBitmapWidth;
105     int height = (int) glyph->mBitmapHeight;
106 
107     mState->appendMeshQuad(nPenX, nPenY, 0, u1, v2,
108             nPenX + width, nPenY, 0, u2, v2,
109             nPenX + width, nPenY - height, 0, u2, v1,
110             nPenX, nPenY - height, 0, u1, v1);
111 }
112 
drawCachedGlyph(CachedGlyphInfo * glyph,int x,int y,uint8_t * bitmap,uint32_t bitmapW,uint32_t bitmapH)113 void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
114         uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) {
115     int nPenX = x + glyph->mBitmapLeft;
116     int nPenY = y + glyph->mBitmapTop;
117 
118     uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
119     uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
120 
121     uint32_t cacheWidth = mState->getCacheWidth();
122     const uint8_t* cacheBuffer = mState->getTextTextureData();
123 
124     uint32_t cacheX = 0, cacheY = 0;
125     int32_t bX = 0, bY = 0;
126     for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
127         for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
128             if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
129                 LOGE("Skipping invalid index");
130                 continue;
131             }
132             uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
133             bitmap[bY * bitmapW + bX] = tempCol;
134         }
135     }
136 
137 }
138 
getCachedGlyph(SkPaint * paint,glyph_t textUnit)139 Font::CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) {
140     CachedGlyphInfo* cachedGlyph = NULL;
141     ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
142     if (index >= 0) {
143         cachedGlyph = mCachedGlyphs.valueAt(index);
144     } else {
145         cachedGlyph = cacheGlyph(paint, textUnit);
146     }
147 
148     // Is the glyph still in texture cache?
149     if (!cachedGlyph->mIsValid) {
150         const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
151         updateGlyphCache(paint, skiaGlyph, cachedGlyph);
152     }
153 
154     return cachedGlyph;
155 }
156 
render(SkPaint * paint,const char * text,uint32_t start,uint32_t len,int numGlyphs,int x,int y,uint8_t * bitmap,uint32_t bitmapW,uint32_t bitmapH)157 void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
158         int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
159     if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
160         render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
161                 bitmapW, bitmapH, NULL);
162     } else {
163         render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
164                 0, 0, NULL);
165     }
166 
167 }
168 
measure(SkPaint * paint,const char * text,uint32_t start,uint32_t len,int numGlyphs,Rect * bounds)169 void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
170         int numGlyphs, Rect *bounds) {
171     if (bounds == NULL) {
172         LOGE("No return rectangle provided to measure text");
173         return;
174     }
175     bounds->set(1e6, -1e6, -1e6, 1e6);
176     render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds);
177 }
178 
179 #define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16)
180 
render(SkPaint * paint,const char * text,uint32_t start,uint32_t len,int numGlyphs,int x,int y,RenderMode mode,uint8_t * bitmap,uint32_t bitmapW,uint32_t bitmapH,Rect * bounds)181 void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
182         int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
183         uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) {
184     if (numGlyphs == 0 || text == NULL || len == 0) {
185         return;
186     }
187 
188     float penX = x;
189     int penY = y;
190     int glyphsLeft = 1;
191     if (numGlyphs > 0) {
192         glyphsLeft = numGlyphs;
193     }
194 
195     SkFixed prevRsbDelta = 0;
196     penX += 0.5f;
197 
198     text += start;
199 
200     while (glyphsLeft > 0) {
201         glyph_t glyph = GET_GLYPH(text);
202 
203         // Reached the end of the string
204         if (IS_END_OF_STRING(glyph)) {
205             break;
206         }
207 
208         CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
209         penX += SkFixedToFloat(SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta));
210         prevRsbDelta = cachedGlyph->mRsbDelta;
211 
212         // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
213         if (cachedGlyph->mIsValid) {
214             switch(mode) {
215             case FRAMEBUFFER:
216                 drawCachedGlyph(cachedGlyph, (int) floorf(penX), penY);
217                 break;
218             case BITMAP:
219                 drawCachedGlyph(cachedGlyph, (int) floorf(penX), penY, bitmap, bitmapW, bitmapH);
220                 break;
221             case MEASURE:
222                 measureCachedGlyph(cachedGlyph, (int) floorf(penX), penY, bounds);
223                 break;
224             }
225         }
226 
227         penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
228 
229         // If we were given a specific number of glyphs, decrement
230         if (numGlyphs > 0) {
231             glyphsLeft--;
232         }
233     }
234 }
235 
updateGlyphCache(SkPaint * paint,const SkGlyph & skiaGlyph,CachedGlyphInfo * glyph)236 void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
237     glyph->mAdvanceX = skiaGlyph.fAdvanceX;
238     glyph->mAdvanceY = skiaGlyph.fAdvanceY;
239     glyph->mBitmapLeft = skiaGlyph.fLeft;
240     glyph->mBitmapTop = skiaGlyph.fTop;
241     glyph->mLsbDelta = skiaGlyph.fLsbDelta;
242     glyph->mRsbDelta = skiaGlyph.fRsbDelta;
243 
244     uint32_t startX = 0;
245     uint32_t startY = 0;
246 
247     // Get the bitmap for the glyph
248     paint->findImage(skiaGlyph);
249     glyph->mIsValid = mState->cacheBitmap(skiaGlyph, &startX, &startY);
250 
251     if (!glyph->mIsValid) {
252         return;
253     }
254 
255     uint32_t endX = startX + skiaGlyph.fWidth;
256     uint32_t endY = startY + skiaGlyph.fHeight;
257 
258     glyph->mStartX = startX;
259     glyph->mStartY = startY;
260     glyph->mBitmapWidth = skiaGlyph.fWidth;
261     glyph->mBitmapHeight = skiaGlyph.fHeight;
262 
263     uint32_t cacheWidth = mState->getCacheWidth();
264     uint32_t cacheHeight = mState->getCacheHeight();
265 
266     glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
267     glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
268     glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
269     glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
270 
271     mState->mUploadTexture = true;
272 }
273 
cacheGlyph(SkPaint * paint,glyph_t glyph)274 Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) {
275     CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
276     mCachedGlyphs.add(glyph, newGlyph);
277 
278     const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
279     newGlyph->mGlyphIndex = skiaGlyph.fID;
280     newGlyph->mIsValid = false;
281 
282     updateGlyphCache(paint, skiaGlyph, newGlyph);
283 
284     return newGlyph;
285 }
286 
create(FontRenderer * state,uint32_t fontId,float fontSize,int flags,uint32_t italicStyle,uint32_t scaleX,SkPaint::Style style,uint32_t strokeWidth)287 Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
288         int flags, uint32_t italicStyle, uint32_t scaleX,
289         SkPaint::Style style, uint32_t strokeWidth) {
290     Vector<Font*> &activeFonts = state->mActiveFonts;
291 
292     for (uint32_t i = 0; i < activeFonts.size(); i++) {
293         Font* font = activeFonts[i];
294         if (font->mFontId == fontId && font->mFontSize == fontSize &&
295                 font->mFlags == flags && font->mItalicStyle == italicStyle &&
296                 font->mScaleX == scaleX && font->mStyle == style &&
297                 (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
298             return font;
299         }
300     }
301 
302     Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
303             scaleX, style, strokeWidth);
304     activeFonts.push(newFont);
305     return newFont;
306 }
307 
308 ///////////////////////////////////////////////////////////////////////////////
309 // FontRenderer
310 ///////////////////////////////////////////////////////////////////////////////
311 
312 static bool sLogFontRendererCreate = true;
313 
FontRenderer()314 FontRenderer::FontRenderer() {
315     if (sLogFontRendererCreate) {
316         INIT_LOGD("Creating FontRenderer");
317     }
318 
319     mGammaTable = NULL;
320     mInitialized = false;
321     mMaxNumberOfQuads = 1024;
322     mCurrentQuadIndex = 0;
323     mTextureId = 0;
324 
325     mTextMeshPtr = NULL;
326     mTextTexture = NULL;
327 
328     mIndexBufferID = 0;
329     mPositionAttrSlot = -1;
330     mTexcoordAttrSlot = -1;
331 
332     mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
333     mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
334 
335     char property[PROPERTY_VALUE_MAX];
336     if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
337         if (sLogFontRendererCreate) {
338             INIT_LOGD("  Setting text cache width to %s pixels", property);
339         }
340         mCacheWidth = atoi(property);
341     } else {
342         if (sLogFontRendererCreate) {
343             INIT_LOGD("  Using default text cache width of %i pixels", mCacheWidth);
344         }
345     }
346 
347     if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
348         if (sLogFontRendererCreate) {
349             INIT_LOGD("  Setting text cache width to %s pixels", property);
350         }
351         mCacheHeight = atoi(property);
352     } else {
353         if (sLogFontRendererCreate) {
354             INIT_LOGD("  Using default text cache height of %i pixels", mCacheHeight);
355         }
356     }
357 
358     sLogFontRendererCreate = false;
359 }
360 
~FontRenderer()361 FontRenderer::~FontRenderer() {
362     for (uint32_t i = 0; i < mCacheLines.size(); i++) {
363         delete mCacheLines[i];
364     }
365     mCacheLines.clear();
366 
367     if (mInitialized) {
368         delete[] mTextMeshPtr;
369         delete[] mTextTexture;
370     }
371 
372     if (mTextureId) {
373         glDeleteTextures(1, &mTextureId);
374     }
375 
376     Vector<Font*> fontsToDereference = mActiveFonts;
377     for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
378         delete fontsToDereference[i];
379     }
380 }
381 
flushAllAndInvalidate()382 void FontRenderer::flushAllAndInvalidate() {
383     if (mCurrentQuadIndex != 0) {
384         issueDrawCommand();
385         mCurrentQuadIndex = 0;
386     }
387     for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
388         mActiveFonts[i]->invalidateTextureCache();
389     }
390     for (uint32_t i = 0; i < mCacheLines.size(); i++) {
391         mCacheLines[i]->mCurrentCol = 0;
392     }
393 }
394 
cacheBitmap(const SkGlyph & glyph,uint32_t * retOriginX,uint32_t * retOriginY)395 bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
396     // If the glyph is too tall, don't cache it
397     if (glyph.fHeight + 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
398         if (mCacheHeight < MAX_TEXT_CACHE_HEIGHT) {
399             // Default cache not large enough for large glyphs - resize cache to
400             // max size and try again
401             flushAllAndInvalidate();
402             initTextTexture(true);
403         }
404         if (glyph.fHeight + 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
405             LOGE("Font size to large to fit in cache. width, height = %i, %i",
406                     (int) glyph.fWidth, (int) glyph.fHeight);
407             return false;
408         }
409     }
410 
411     // Now copy the bitmap into the cache texture
412     uint32_t startX = 0;
413     uint32_t startY = 0;
414 
415     bool bitmapFit = false;
416     for (uint32_t i = 0; i < mCacheLines.size(); i++) {
417         bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
418         if (bitmapFit) {
419             break;
420         }
421     }
422 
423     // If the new glyph didn't fit, flush the state so far and invalidate everything
424     if (!bitmapFit) {
425         flushAllAndInvalidate();
426 
427         // Try to fit it again
428         for (uint32_t i = 0; i < mCacheLines.size(); i++) {
429             bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
430             if (bitmapFit) {
431                 break;
432             }
433         }
434 
435         // if we still don't fit, something is wrong and we shouldn't draw
436         if (!bitmapFit) {
437             LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
438                     (int) glyph.fWidth, (int) glyph.fHeight);
439             return false;
440         }
441     }
442 
443     *retOriginX = startX;
444     *retOriginY = startY;
445 
446     uint32_t endX = startX + glyph.fWidth;
447     uint32_t endY = startY + glyph.fHeight;
448 
449     uint32_t cacheWidth = mCacheWidth;
450 
451     uint8_t* cacheBuffer = mTextTexture;
452     uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
453     unsigned int stride = glyph.rowBytes();
454 
455     uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
456     for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
457         for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
458             uint8_t tempCol = bitmapBuffer[bY * stride + bX];
459             cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
460         }
461     }
462 
463     return true;
464 }
465 
initTextTexture(bool largeFonts)466 void FontRenderer::initTextTexture(bool largeFonts) {
467     mCacheLines.clear();
468     if (largeFonts) {
469         mCacheWidth = MAX_TEXT_CACHE_WIDTH;
470         mCacheHeight = MAX_TEXT_CACHE_HEIGHT;
471     }
472 
473     mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
474     memset(mTextTexture, 0, mCacheWidth * mCacheHeight * sizeof(uint8_t));
475 
476     mUploadTexture = false;
477 
478     if (mTextureId != 0) {
479         glDeleteTextures(1, &mTextureId);
480     }
481     glGenTextures(1, &mTextureId);
482     glBindTexture(GL_TEXTURE_2D, mTextureId);
483     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
484     // Initialize texture dimensions
485     glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
486             GL_ALPHA, GL_UNSIGNED_BYTE, 0);
487 
488     mLinearFiltering = false;
489     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
490     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
491 
492     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
493     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
494 
495     // Split up our cache texture into lines of certain widths
496     int nextLine = 0;
497     mCacheLines.push(new CacheTextureLine(mCacheWidth, 18, nextLine, 0));
498     nextLine += mCacheLines.top()->mMaxHeight;
499     mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
500     nextLine += mCacheLines.top()->mMaxHeight;
501     mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
502     nextLine += mCacheLines.top()->mMaxHeight;
503     mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
504     nextLine += mCacheLines.top()->mMaxHeight;
505     mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
506     nextLine += mCacheLines.top()->mMaxHeight;
507     mCacheLines.push(new CacheTextureLine(mCacheWidth, 42, nextLine, 0));
508     nextLine += mCacheLines.top()->mMaxHeight;
509     if (largeFonts) {
510         int nextSize = 76;
511         // Make several new lines with increasing font sizes
512         while (nextSize < (int)(mCacheHeight - nextLine - (2 * nextSize))) {
513             mCacheLines.push(new CacheTextureLine(mCacheWidth, nextSize, nextLine, 0));
514             nextLine += mCacheLines.top()->mMaxHeight;
515             nextSize += 50;
516         }
517     }
518     mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
519 }
520 
521 // Avoid having to reallocate memory and render quad by quad
initVertexArrayBuffers()522 void FontRenderer::initVertexArrayBuffers() {
523     uint32_t numIndicies = mMaxNumberOfQuads * 6;
524     uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
525     uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
526 
527     // Four verts, two triangles , six indices per quad
528     for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
529         int i6 = i * 6;
530         int i4 = i * 4;
531 
532         indexBufferData[i6 + 0] = i4 + 0;
533         indexBufferData[i6 + 1] = i4 + 1;
534         indexBufferData[i6 + 2] = i4 + 2;
535 
536         indexBufferData[i6 + 3] = i4 + 0;
537         indexBufferData[i6 + 4] = i4 + 2;
538         indexBufferData[i6 + 5] = i4 + 3;
539     }
540 
541     glGenBuffers(1, &mIndexBufferID);
542     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
543     glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
544     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
545 
546     free(indexBufferData);
547 
548     uint32_t coordSize = 3;
549     uint32_t uvSize = 2;
550     uint32_t vertsPerQuad = 4;
551     uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
552     mTextMeshPtr = new float[vertexBufferSize];
553 }
554 
555 // We don't want to allocate anything unless we actually draw text
checkInit()556 void FontRenderer::checkInit() {
557     if (mInitialized) {
558         return;
559     }
560 
561     initTextTexture();
562     initVertexArrayBuffers();
563 
564     // We store a string with letters in a rough frequency of occurrence
565     mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
566     mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
567     mLatinPrecache += String16(",.?!()-+@;:`'");
568     mLatinPrecache += String16("0123456789");
569 
570     mInitialized = true;
571 }
572 
checkTextureUpdate()573 void FontRenderer::checkTextureUpdate() {
574     if (!mUploadTexture) {
575         return;
576     }
577 
578     glBindTexture(GL_TEXTURE_2D, mTextureId);
579 
580     // Iterate over all the cache lines and see which ones need to be updated
581     for (uint32_t i = 0; i < mCacheLines.size(); i++) {
582         CacheTextureLine* cl = mCacheLines[i];
583         if(cl->mDirty) {
584             uint32_t xOffset = 0;
585             uint32_t yOffset = cl->mCurrentRow;
586             uint32_t width   = mCacheWidth;
587             uint32_t height  = cl->mMaxHeight;
588             void* textureData = mTextTexture + yOffset*width;
589 
590             glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
591                     GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
592 
593             cl->mDirty = false;
594         }
595     }
596 
597     mUploadTexture = false;
598 }
599 
issueDrawCommand()600 void FontRenderer::issueDrawCommand() {
601     checkTextureUpdate();
602 
603     float* vtx = mTextMeshPtr;
604     float* tex = vtx + 3;
605 
606     glVertexAttribPointer(mPositionAttrSlot, 3, GL_FLOAT, false, 20, vtx);
607     glVertexAttribPointer(mTexcoordAttrSlot, 2, GL_FLOAT, false, 20, tex);
608 
609     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
610     glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
611 
612     mDrawn = true;
613 }
614 
appendMeshQuad(float x1,float y1,float z1,float u1,float v1,float x2,float y2,float z2,float u2,float v2,float x3,float y3,float z3,float u3,float v3,float x4,float y4,float z4,float u4,float v4)615 void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
616         float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
617         float x4, float y4, float z4, float u4, float v4) {
618     if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) {
619         return;
620     }
621 
622     const uint32_t vertsPerQuad = 4;
623     const uint32_t floatsPerVert = 5;
624     float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
625 
626     (*currentPos++) = x1;
627     (*currentPos++) = y1;
628     (*currentPos++) = z1;
629     (*currentPos++) = u1;
630     (*currentPos++) = v1;
631 
632     (*currentPos++) = x2;
633     (*currentPos++) = y2;
634     (*currentPos++) = z2;
635     (*currentPos++) = u2;
636     (*currentPos++) = v2;
637 
638     (*currentPos++) = x3;
639     (*currentPos++) = y3;
640     (*currentPos++) = z3;
641     (*currentPos++) = u3;
642     (*currentPos++) = v3;
643 
644     (*currentPos++) = x4;
645     (*currentPos++) = y4;
646     (*currentPos++) = z4;
647     (*currentPos++) = u4;
648     (*currentPos++) = v4;
649 
650     mCurrentQuadIndex++;
651 
652     if (mBounds) {
653         mBounds->left = fmin(mBounds->left, x1);
654         mBounds->top = fmin(mBounds->top, y3);
655         mBounds->right = fmax(mBounds->right, x3);
656         mBounds->bottom = fmax(mBounds->bottom, y1);
657     }
658 
659     if (mCurrentQuadIndex == mMaxNumberOfQuads) {
660         issueDrawCommand();
661         mCurrentQuadIndex = 0;
662     }
663 }
664 
getRemainingCacheCapacity()665 uint32_t FontRenderer::getRemainingCacheCapacity() {
666     uint32_t remainingCapacity = 0;
667     float totalPixels = 0;
668     for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
669          remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
670          totalPixels += mCacheLines[i]->mMaxWidth;
671     }
672     remainingCapacity = (remainingCapacity * 100) / totalPixels;
673     return remainingCapacity;
674 }
675 
precacheLatin(SkPaint * paint)676 void FontRenderer::precacheLatin(SkPaint* paint) {
677     // Remaining capacity is measured in %
678     uint32_t remainingCapacity = getRemainingCacheCapacity();
679     uint32_t precacheIdx = 0;
680     while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
681         mCurrentFont->getCachedGlyph(paint, (int32_t) mLatinPrecache[precacheIdx]);
682         remainingCapacity = getRemainingCacheCapacity();
683         precacheIdx ++;
684     }
685 }
686 
setFont(SkPaint * paint,uint32_t fontId,float fontSize)687 void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
688     uint32_t currentNumFonts = mActiveFonts.size();
689     int flags = 0;
690     if (paint->isFakeBoldText()) {
691         flags |= Font::kFakeBold;
692     }
693 
694     const float skewX = paint->getTextSkewX();
695     uint32_t italicStyle = *(uint32_t*) &skewX;
696     const float scaleXFloat = paint->getTextScaleX();
697     uint32_t scaleX = *(uint32_t*) &scaleXFloat;
698     SkPaint::Style style = paint->getStyle();
699     const float strokeWidthFloat = paint->getStrokeWidth();
700     uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
701     mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
702             scaleX, style, strokeWidth);
703 
704     const float maxPrecacheFontSize = 40.0f;
705     bool isNewFont = currentNumFonts != mActiveFonts.size();
706 
707     if (isNewFont && fontSize <= maxPrecacheFontSize) {
708         precacheLatin(paint);
709     }
710 }
711 
renderDropShadow(SkPaint * paint,const char * text,uint32_t startIndex,uint32_t len,int numGlyphs,uint32_t radius)712 FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
713         uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
714     checkInit();
715 
716     if (!mCurrentFont) {
717         DropShadow image;
718         image.width = 0;
719         image.height = 0;
720         image.image = NULL;
721         image.penX = 0;
722         image.penY = 0;
723         return image;
724     }
725 
726     Rect bounds;
727     mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds);
728     uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
729     uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
730     uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
731     for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
732         dataBuffer[i] = 0;
733     }
734 
735     int penX = radius - bounds.left;
736     int penY = radius - bounds.bottom;
737 
738     mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
739             dataBuffer, paddedWidth, paddedHeight);
740     blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
741 
742     DropShadow image;
743     image.width = paddedWidth;
744     image.height = paddedHeight;
745     image.image = dataBuffer;
746     image.penX = penX;
747     image.penY = penY;
748     return image;
749 }
750 
renderText(SkPaint * paint,const Rect * clip,const char * text,uint32_t startIndex,uint32_t len,int numGlyphs,int x,int y,Rect * bounds)751 bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
752         uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
753     checkInit();
754 
755     if (!mCurrentFont) {
756         LOGE("No font set");
757         return false;
758     }
759 
760     if (mPositionAttrSlot < 0 || mTexcoordAttrSlot < 0) {
761         LOGE("Font renderer unable to draw, attribute slots undefined");
762         return false;
763     }
764 
765     mDrawn = false;
766     mBounds = bounds;
767     mClip = clip;
768     mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
769     mBounds = NULL;
770 
771     if (mCurrentQuadIndex != 0) {
772         issueDrawCommand();
773         mCurrentQuadIndex = 0;
774     }
775 
776     return mDrawn;
777 }
778 
computeGaussianWeights(float * weights,int32_t radius)779 void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
780     // Compute gaussian weights for the blur
781     // e is the euler's number
782     float e = 2.718281828459045f;
783     float pi = 3.1415926535897932f;
784     // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
785     // x is of the form [-radius .. 0 .. radius]
786     // and sigma varies with radius.
787     // Based on some experimental radius values and sigma's
788     // we approximately fit sigma = f(radius) as
789     // sigma = radius * 0.3  + 0.6
790     // The larger the radius gets, the more our gaussian blur
791     // will resemble a box blur since with large sigma
792     // the gaussian curve begins to lose its shape
793     float sigma = 0.3f * (float) radius + 0.6f;
794 
795     // Now compute the coefficints
796     // We will store some redundant values to save some math during
797     // the blur calculations
798     // precompute some values
799     float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
800     float coeff2 = - 1.0f / (2.0f * sigma * sigma);
801 
802     float normalizeFactor = 0.0f;
803     for (int32_t r = -radius; r <= radius; r ++) {
804         float floatR = (float) r;
805         weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
806         normalizeFactor += weights[r + radius];
807     }
808 
809     //Now we need to normalize the weights because all our coefficients need to add up to one
810     normalizeFactor = 1.0f / normalizeFactor;
811     for (int32_t r = -radius; r <= radius; r ++) {
812         weights[r + radius] *= normalizeFactor;
813     }
814 }
815 
horizontalBlur(float * weights,int32_t radius,const uint8_t * source,uint8_t * dest,int32_t width,int32_t height)816 void FontRenderer::horizontalBlur(float* weights, int32_t radius,
817         const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
818     float blurredPixel = 0.0f;
819     float currentPixel = 0.0f;
820 
821     for (int32_t y = 0; y < height; y ++) {
822 
823         const uint8_t* input = source + y * width;
824         uint8_t* output = dest + y * width;
825 
826         for (int32_t x = 0; x < width; x ++) {
827             blurredPixel = 0.0f;
828             const float* gPtr = weights;
829             // Optimization for non-border pixels
830             if (x > radius && x < (width - radius)) {
831                 const uint8_t *i = input + (x - radius);
832                 for (int r = -radius; r <= radius; r ++) {
833                     currentPixel = (float) (*i);
834                     blurredPixel += currentPixel * gPtr[0];
835                     gPtr++;
836                     i++;
837                 }
838             } else {
839                 for (int32_t r = -radius; r <= radius; r ++) {
840                     // Stepping left and right away from the pixel
841                     int validW = x + r;
842                     if (validW < 0) {
843                         validW = 0;
844                     }
845                     if (validW > width - 1) {
846                         validW = width - 1;
847                     }
848 
849                     currentPixel = (float) input[validW];
850                     blurredPixel += currentPixel * gPtr[0];
851                     gPtr++;
852                 }
853             }
854             *output = (uint8_t)blurredPixel;
855             output ++;
856         }
857     }
858 }
859 
verticalBlur(float * weights,int32_t radius,const uint8_t * source,uint8_t * dest,int32_t width,int32_t height)860 void FontRenderer::verticalBlur(float* weights, int32_t radius,
861         const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
862     float blurredPixel = 0.0f;
863     float currentPixel = 0.0f;
864 
865     for (int32_t y = 0; y < height; y ++) {
866 
867         uint8_t* output = dest + y * width;
868 
869         for (int32_t x = 0; x < width; x ++) {
870             blurredPixel = 0.0f;
871             const float* gPtr = weights;
872             const uint8_t* input = source + x;
873             // Optimization for non-border pixels
874             if (y > radius && y < (height - radius)) {
875                 const uint8_t *i = input + ((y - radius) * width);
876                 for (int32_t r = -radius; r <= radius; r ++) {
877                     currentPixel = (float)(*i);
878                     blurredPixel += currentPixel * gPtr[0];
879                     gPtr++;
880                     i += width;
881                 }
882             } else {
883                 for (int32_t r = -radius; r <= radius; r ++) {
884                     int validH = y + r;
885                     // Clamp to zero and width
886                     if (validH < 0) {
887                         validH = 0;
888                     }
889                     if (validH > height - 1) {
890                         validH = height - 1;
891                     }
892 
893                     const uint8_t *i = input + validH * width;
894                     currentPixel = (float) (*i);
895                     blurredPixel += currentPixel * gPtr[0];
896                     gPtr++;
897                 }
898             }
899             *output = (uint8_t) blurredPixel;
900             output ++;
901         }
902     }
903 }
904 
905 
blurImage(uint8_t * image,int32_t width,int32_t height,int32_t radius)906 void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
907     float *gaussian = new float[2 * radius + 1];
908     computeGaussianWeights(gaussian, radius);
909     uint8_t* scratch = new uint8_t[width * height];
910     horizontalBlur(gaussian, radius, image, scratch, width, height);
911     verticalBlur(gaussian, radius, scratch, image, width, height);
912     delete[] gaussian;
913     delete[] scratch;
914 }
915 
916 }; // namespace uirenderer
917 }; // namespace android
918