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