blob: a596fa9464675c2ce3099f6d9a86980dcbdd6360 [file] [log] [blame]
Romain Guy694b5192010-07-21 21:33:20 -07001/*
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
Romain Guy694b5192010-07-21 21:33:20 -070019#include <SkUtils.h>
20
Romain Guy51769a62010-07-23 00:28:00 -070021#include <cutils/properties.h>
Romain Guye2d345e2010-09-24 18:39:22 -070022
Romain Guy51769a62010-07-23 00:28:00 -070023#include <utils/Log.h>
24
Romain Guy15bc6432011-12-13 13:11:32 -080025#include "Caches.h"
Romain Guyc9855a52011-01-21 21:14:15 -080026#include "Debug.h"
Romain Guy51769a62010-07-23 00:28:00 -070027#include "FontRenderer.h"
Chet Haase7de0cb12011-12-05 16:35:38 -080028#include "Caches.h"
Romain Guy51769a62010-07-23 00:28:00 -070029
Romain Guy694b5192010-07-21 21:33:20 -070030namespace android {
31namespace uirenderer {
32
33///////////////////////////////////////////////////////////////////////////////
Romain Guy51769a62010-07-23 00:28:00 -070034// Defines
35///////////////////////////////////////////////////////////////////////////////
36
37#define DEFAULT_TEXT_CACHE_WIDTH 1024
38#define DEFAULT_TEXT_CACHE_HEIGHT 256
Chet Haase44984ea2011-05-19 13:50:47 -070039#define MAX_TEXT_CACHE_WIDTH 2048
Chet Haasee816bae2012-08-09 13:39:02 -070040#define CACHE_BLOCK_ROUNDING_SIZE 4
Chet Haase7de0cb12011-12-05 16:35:38 -080041
Romain Guy97771732012-02-28 18:17:02 -080042#define AUTO_KERN(prev, next) (((next) - (prev) + 32) >> 6 << 16)
43
Chet Haase7de0cb12011-12-05 16:35:38 -080044///////////////////////////////////////////////////////////////////////////////
Chet Haasee816bae2012-08-09 13:39:02 -070045// CacheBlock
46///////////////////////////////////////////////////////////////////////////////
47
48/**
49 * Insert new block into existing linked list of blocks. Blocks are sorted in increasing-width
50 * order, except for the final block (the remainder space at the right, since we fill from the
51 * left).
52 */
53CacheBlock* CacheBlock::insertBlock(CacheBlock* head, CacheBlock *newBlock) {
54#if DEBUG_FONT_RENDERER
55 ALOGD("insertBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
56 newBlock, newBlock->mX, newBlock->mY,
57 newBlock->mWidth, newBlock->mHeight);
58#endif
59 CacheBlock *currBlock = head;
60 CacheBlock *prevBlock = NULL;
61 while (currBlock && currBlock->mY != TEXTURE_BORDER_SIZE) {
62 if (newBlock->mWidth < currBlock->mWidth) {
63 newBlock->mNext = currBlock;
64 newBlock->mPrev = prevBlock;
65 currBlock->mPrev = newBlock;
66 if (prevBlock) {
67 prevBlock->mNext = newBlock;
68 return head;
69 } else {
70 return newBlock;
71 }
72 }
73 prevBlock = currBlock;
74 currBlock = currBlock->mNext;
75 }
76 // new block larger than all others - insert at end (but before the remainder space, if there)
77 newBlock->mNext = currBlock;
78 newBlock->mPrev = prevBlock;
79 if (currBlock) {
80 currBlock->mPrev = newBlock;
81 }
82 if (prevBlock) {
83 prevBlock->mNext = newBlock;
84 return head;
85 } else {
86 return newBlock;
87 }
88}
89
90CacheBlock* CacheBlock::removeBlock(CacheBlock* head, CacheBlock *blockToRemove) {
91#if DEBUG_FONT_RENDERER
92 ALOGD("removeBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
93 blockToRemove, blockToRemove->mX, blockToRemove->mY,
94 blockToRemove->mWidth, blockToRemove->mHeight);
95#endif
96 CacheBlock* newHead = head;
97 CacheBlock* nextBlock = blockToRemove->mNext;
98 CacheBlock* prevBlock = blockToRemove->mPrev;
99 if (prevBlock) {
100 prevBlock->mNext = nextBlock;
101 } else {
102 newHead = nextBlock;
103 }
104 if (nextBlock) {
105 nextBlock->mPrev = prevBlock;
106 }
107 delete blockToRemove;
108 return newHead;
109}
110
111///////////////////////////////////////////////////////////////////////////////
Chet Haase7de0cb12011-12-05 16:35:38 -0800112// CacheTextureLine
113///////////////////////////////////////////////////////////////////////////////
114
115bool CacheTextureLine::fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
116 if (glyph.fHeight + TEXTURE_BORDER_SIZE > mMaxHeight) {
117 return false;
118 }
119
Chet Haasee816bae2012-08-09 13:39:02 -0700120 uint16_t glyphW = glyph.fWidth + TEXTURE_BORDER_SIZE;
121 uint16_t glyphH = glyph.fHeight + TEXTURE_BORDER_SIZE;
122 // roundedUpW equals glyphW to the next multiple of CACHE_BLOCK_ROUNDING_SIZE.
123 // This columns for glyphs that are close but not necessarily exactly the same size. It trades
124 // off the loss of a few pixels for some glyphs against the ability to store more glyphs
125 // of varying sizes in one block.
126 uint16_t roundedUpW =
127 (glyphW + CACHE_BLOCK_ROUNDING_SIZE - 1) & -CACHE_BLOCK_ROUNDING_SIZE;
128 CacheBlock *cacheBlock = mCacheBlocks;
129 while (cacheBlock) {
130 // Store glyph in this block iff: it fits the block's remaining space and:
131 // it's the remainder space (mY == 0) or there's only enough height for this one glyph
132 // or it's within ROUNDING_SIZE of the block width
133 if (roundedUpW <= cacheBlock->mWidth && glyphH <= cacheBlock->mHeight &&
134 (cacheBlock->mY == TEXTURE_BORDER_SIZE ||
135 (cacheBlock->mWidth - roundedUpW < CACHE_BLOCK_ROUNDING_SIZE))) {
136 if (cacheBlock->mHeight - glyphH < glyphH) {
137 // Only enough space for this glyph - don't bother rounding up the width
138 roundedUpW = glyphW;
139 }
140 *retOriginX = cacheBlock->mX;
141 *retOriginY = mCurrentRow + cacheBlock->mY;
142 // If this is the remainder space, create a new cache block for this column. Otherwise,
143 // adjust the info about this column.
144 if (cacheBlock->mY == TEXTURE_BORDER_SIZE) {
145 uint16_t oldX = cacheBlock->mX;
146 // Adjust remainder space dimensions
147 cacheBlock->mWidth -= roundedUpW;
148 cacheBlock->mX += roundedUpW;
149 if (mMaxHeight - glyphH >= glyphH) {
150 // There's enough height left over to create a new CacheBlock
151 CacheBlock *newBlock = new CacheBlock(oldX, glyphH, roundedUpW,
152 mMaxHeight - glyphH);
153#if DEBUG_FONT_RENDERER
154 ALOGD("fitBitmap: Created new block: this, x, y, w, h = %p, %d, %d, %d, %d",
155 newBlock, newBlock->mX, newBlock->mY,
156 newBlock->mWidth, newBlock->mHeight);
157#endif
158 mCacheBlocks = CacheBlock::insertBlock(mCacheBlocks, newBlock);
159 }
160 } else {
161 // Insert into current column and adjust column dimensions
162 cacheBlock->mY += glyphH;
163 cacheBlock->mHeight -= glyphH;
164#if DEBUG_FONT_RENDERER
165 ALOGD("fitBitmap: Added to existing block: this, x, y, w, h = %p, %d, %d, %d, %d",
166 cacheBlock, cacheBlock->mX, cacheBlock->mY,
167 cacheBlock->mWidth, cacheBlock->mHeight);
168#endif
169 }
170 if (cacheBlock->mHeight < fmin(glyphH, glyphW)) {
171 // If remaining space in this block is too small to be useful, remove it
172 mCacheBlocks = CacheBlock::removeBlock(mCacheBlocks, cacheBlock);
173 }
174 mDirty = true;
175#if DEBUG_FONT_RENDERER
176 ALOGD("fitBitmap: current block list:");
177 mCacheBlocks->output();
178#endif
179 ++mNumGlyphs;
180 return true;
181 }
182 cacheBlock = cacheBlock->mNext;
Chet Haase7de0cb12011-12-05 16:35:38 -0800183 }
Chet Haasee816bae2012-08-09 13:39:02 -0700184#if DEBUG_FONT_RENDERER
185 ALOGD("fitBitmap: returning false for glyph of size %d, %d", glyphW, glyphH);
186#endif
Chet Haase7de0cb12011-12-05 16:35:38 -0800187 return false;
188}
Chet Haase44984ea2011-05-19 13:50:47 -0700189
Romain Guy51769a62010-07-23 00:28:00 -0700190///////////////////////////////////////////////////////////////////////////////
Romain Guy694b5192010-07-21 21:33:20 -0700191// Font
192///////////////////////////////////////////////////////////////////////////////
193
Romain Guy2577db12011-01-18 13:02:38 -0800194Font::Font(FontRenderer* state, uint32_t fontId, float fontSize,
Romain Guybd496bc2011-08-02 17:32:41 -0700195 int flags, uint32_t italicStyle, uint32_t scaleX,
196 SkPaint::Style style, uint32_t strokeWidth) :
Romain Guy2577db12011-01-18 13:02:38 -0800197 mState(state), mFontId(fontId), mFontSize(fontSize),
Romain Guybd496bc2011-08-02 17:32:41 -0700198 mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX),
199 mStyle(style), mStrokeWidth(mStrokeWidth) {
Romain Guy694b5192010-07-21 21:33:20 -0700200}
201
202
203Font::~Font() {
204 for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
205 if (mState->mActiveFonts[ct] == this) {
206 mState->mActiveFonts.removeAt(ct);
207 break;
208 }
209 }
210
211 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
Romain Guy726aeba2011-06-01 14:52:00 -0700212 delete mCachedGlyphs.valueAt(i);
Romain Guy694b5192010-07-21 21:33:20 -0700213 }
214}
215
Chet Haase9a824562011-12-16 15:44:59 -0800216void Font::invalidateTextureCache(CacheTextureLine *cacheLine) {
Romain Guy694b5192010-07-21 21:33:20 -0700217 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
Chet Haase9a824562011-12-16 15:44:59 -0800218 CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i);
219 if (cacheLine == NULL || cachedGlyph->mCachedTextureLine == cacheLine) {
220 cachedGlyph->mIsValid = false;
221 }
Romain Guy694b5192010-07-21 21:33:20 -0700222 }
223}
224
Romain Guy671d6cf2012-01-18 12:39:17 -0800225void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
226 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700227 int nPenX = x + glyph->mBitmapLeft;
228 int nPenY = y + glyph->mBitmapTop;
229
230 int width = (int) glyph->mBitmapWidth;
231 int height = (int) glyph->mBitmapHeight;
232
Romain Guy61c8c9c2010-08-09 20:48:09 -0700233 if (bounds->bottom > nPenY) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700234 bounds->bottom = nPenY;
235 }
Romain Guy61c8c9c2010-08-09 20:48:09 -0700236 if (bounds->left > nPenX) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700237 bounds->left = nPenX;
238 }
Romain Guy61c8c9c2010-08-09 20:48:09 -0700239 if (bounds->right < nPenX + width) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700240 bounds->right = nPenX + width;
241 }
Romain Guy61c8c9c2010-08-09 20:48:09 -0700242 if (bounds->top < nPenY + height) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700243 bounds->top = nPenY + height;
244 }
245}
246
Romain Guy671d6cf2012-01-18 12:39:17 -0800247void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
248 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
Romain Guy694b5192010-07-21 21:33:20 -0700249 int nPenX = x + glyph->mBitmapLeft;
250 int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
251
Romain Guy51769a62010-07-23 00:28:00 -0700252 float u1 = glyph->mBitmapMinU;
253 float u2 = glyph->mBitmapMaxU;
254 float v1 = glyph->mBitmapMinV;
255 float v2 = glyph->mBitmapMaxV;
256
257 int width = (int) glyph->mBitmapWidth;
258 int height = (int) glyph->mBitmapHeight;
259
Romain Guyd71dd362011-12-12 19:03:35 -0800260 mState->appendMeshQuad(nPenX, nPenY, u1, v2,
261 nPenX + width, nPenY, u2, v2,
262 nPenX + width, nPenY - height, u2, v1,
Chet Haase7de0cb12011-12-05 16:35:38 -0800263 nPenX, nPenY - height, u1, v1, glyph->mCachedTextureLine->mCacheTexture);
Romain Guy694b5192010-07-21 21:33:20 -0700264}
265
Romain Guy671d6cf2012-01-18 12:39:17 -0800266void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
267 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700268 int nPenX = x + glyph->mBitmapLeft;
269 int nPenY = y + glyph->mBitmapTop;
270
271 uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
272 uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
273
Chet Haase7de0cb12011-12-05 16:35:38 -0800274 CacheTexture *cacheTexture = glyph->mCachedTextureLine->mCacheTexture;
275 uint32_t cacheWidth = cacheTexture->mWidth;
276 const uint8_t* cacheBuffer = cacheTexture->mTexture;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700277
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700278 uint32_t cacheX = 0, cacheY = 0;
279 int32_t bX = 0, bY = 0;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700280 for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
281 for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
Romain Guyb6294902012-02-02 15:13:18 -0800282#if DEBUG_FONT_RENDERER
Romain Guyb45c0c92010-08-26 20:35:23 -0700283 if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
Steve Block3762c312012-01-06 19:20:56 +0000284 ALOGE("Skipping invalid index");
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700285 continue;
286 }
Romain Guyb6294902012-02-02 15:13:18 -0800287#endif
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700288 uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
289 bitmap[bY * bitmapW + bX] = tempCol;
290 }
291 }
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700292}
293
Romain Guy97771732012-02-28 18:17:02 -0800294void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
295 SkPathMeasure& measure, SkPoint* position, SkVector* tangent) {
296 const float halfWidth = glyph->mBitmapWidth * 0.5f;
297 const float height = glyph->mBitmapHeight;
298
Romain Guy97771732012-02-28 18:17:02 -0800299 vOffset += glyph->mBitmapTop + height;
300
Romain Guy97771732012-02-28 18:17:02 -0800301 SkPoint destination[4];
Romain Guydd7c8e4c2012-03-01 12:08:38 -0800302 measure.getPosTan(x + hOffset + glyph->mBitmapLeft + halfWidth, position, tangent);
Romain Guy97771732012-02-28 18:17:02 -0800303
304 // Move along the tangent and offset by the normal
305 destination[0].set(-tangent->fX * halfWidth - tangent->fY * vOffset,
306 -tangent->fY * halfWidth + tangent->fX * vOffset);
307 destination[1].set(tangent->fX * halfWidth - tangent->fY * vOffset,
308 tangent->fY * halfWidth + tangent->fX * vOffset);
309 destination[2].set(destination[1].fX + tangent->fY * height,
310 destination[1].fY - tangent->fX * height);
311 destination[3].set(destination[0].fX + tangent->fY * height,
312 destination[0].fY - tangent->fX * height);
313
Romain Guydd7c8e4c2012-03-01 12:08:38 -0800314 const float u1 = glyph->mBitmapMinU;
315 const float u2 = glyph->mBitmapMaxU;
316 const float v1 = glyph->mBitmapMinV;
317 const float v2 = glyph->mBitmapMaxV;
318
Romain Guy97771732012-02-28 18:17:02 -0800319 mState->appendRotatedMeshQuad(
320 position->fX + destination[0].fX,
321 position->fY + destination[0].fY, u1, v2,
322 position->fX + destination[1].fX,
323 position->fY + destination[1].fY, u2, v2,
324 position->fX + destination[2].fX,
325 position->fY + destination[2].fY, u2, v1,
326 position->fX + destination[3].fX,
327 position->fY + destination[3].fY, u1, v1,
328 glyph->mCachedTextureLine->mCacheTexture);
329}
330
Chet Haase7de0cb12011-12-05 16:35:38 -0800331CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700332 CachedGlyphInfo* cachedGlyph = NULL;
Romain Guy726aeba2011-06-01 14:52:00 -0700333 ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
Romain Guy1e45aae2010-08-13 19:39:53 -0700334 if (index >= 0) {
335 cachedGlyph = mCachedGlyphs.valueAt(index);
336 } else {
Romain Guy726aeba2011-06-01 14:52:00 -0700337 cachedGlyph = cacheGlyph(paint, textUnit);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700338 }
339
340 // Is the glyph still in texture cache?
341 if (!cachedGlyph->mIsValid) {
Romain Guy726aeba2011-06-01 14:52:00 -0700342 const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700343 updateGlyphCache(paint, skiaGlyph, cachedGlyph);
344 }
345
346 return cachedGlyph;
347}
348
Romain Guy726aeba2011-06-01 14:52:00 -0700349void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700350 int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
351 if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
Romain Guy726aeba2011-06-01 14:52:00 -0700352 render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
Romain Guy671d6cf2012-01-18 12:39:17 -0800353 bitmapW, bitmapH, NULL, NULL);
Romain Guy61c8c9c2010-08-09 20:48:09 -0700354 } else {
Romain Guy726aeba2011-06-01 14:52:00 -0700355 render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
Romain Guy671d6cf2012-01-18 12:39:17 -0800356 0, 0, NULL, NULL);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700357 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700358}
359
Romain Guy671d6cf2012-01-18 12:39:17 -0800360void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
361 int numGlyphs, int x, int y, const float* positions) {
362 render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
363 0, 0, NULL, positions);
364}
365
Romain Guy97771732012-02-28 18:17:02 -0800366void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
367 int numGlyphs, SkPath* path, float hOffset, float vOffset) {
368 if (numGlyphs == 0 || text == NULL || len == 0) {
369 return;
370 }
371
372 text += start;
373
374 int glyphsCount = 0;
375 SkFixed prevRsbDelta = 0;
376
377 float penX = 0.0f;
378
379 SkPoint position;
380 SkVector tangent;
381
382 SkPathMeasure measure(*path, false);
383 float pathLength = SkScalarToFloat(measure.getLength());
384
385 if (paint->getTextAlign() != SkPaint::kLeft_Align) {
386 float textWidth = SkScalarToFloat(paint->measureText(text, len));
387 float pathOffset = pathLength;
388 if (paint->getTextAlign() == SkPaint::kCenter_Align) {
389 textWidth *= 0.5f;
390 pathOffset *= 0.5f;
391 }
392 penX += pathOffset - textWidth;
393 }
394
Romain Guydd7c8e4c2012-03-01 12:08:38 -0800395 while (glyphsCount < numGlyphs && penX < pathLength) {
Romain Guy97771732012-02-28 18:17:02 -0800396 glyph_t glyph = GET_GLYPH(text);
397
398 if (IS_END_OF_STRING(glyph)) {
399 break;
400 }
401
402 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
403 penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
404 prevRsbDelta = cachedGlyph->mRsbDelta;
405
406 if (cachedGlyph->mIsValid) {
Romain Guydd7c8e4c2012-03-01 12:08:38 -0800407 drawCachedGlyph(cachedGlyph, penX, hOffset, vOffset, measure, &position, &tangent);
Romain Guy97771732012-02-28 18:17:02 -0800408 }
409
410 penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
411
412 glyphsCount++;
413 }
414}
415
Romain Guy726aeba2011-06-01 14:52:00 -0700416void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Raph Levien416a8472012-07-19 22:48:17 -0700417 int numGlyphs, Rect *bounds, const float* positions) {
Romain Guy61c8c9c2010-08-09 20:48:09 -0700418 if (bounds == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000419 ALOGE("No return rectangle provided to measure text");
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700420 return;
421 }
422 bounds->set(1e6, -1e6, -1e6, 1e6);
Raph Levien416a8472012-07-19 22:48:17 -0700423 render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, positions);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700424}
425
Chet Haasee816bae2012-08-09 13:39:02 -0700426void Font::precache(SkPaint* paint, const char* text, int numGlyphs) {
427
428 if (numGlyphs == 0 || text == NULL) {
429 return;
430 }
431 int glyphsCount = 0;
432
433 while (glyphsCount < numGlyphs) {
434 glyph_t glyph = GET_GLYPH(text);
435
436 // Reached the end of the string
437 if (IS_END_OF_STRING(glyph)) {
438 break;
439 }
440
441 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
442
443 glyphsCount++;
444 }
445}
446
Romain Guy726aeba2011-06-01 14:52:00 -0700447void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700448 int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
Romain Guy97771732012-02-28 18:17:02 -0800449 uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) {
Romain Guy694b5192010-07-21 21:33:20 -0700450 if (numGlyphs == 0 || text == NULL || len == 0) {
451 return;
452 }
453
Romain Guy671d6cf2012-01-18 12:39:17 -0800454 static RenderGlyph gRenderGlyph[] = {
455 &android::uirenderer::Font::drawCachedGlyph,
456 &android::uirenderer::Font::drawCachedGlyphBitmap,
457 &android::uirenderer::Font::measureCachedGlyph
458 };
459 RenderGlyph render = gRenderGlyph[mode];
Romain Guy694b5192010-07-21 21:33:20 -0700460
Romain Guy97771732012-02-28 18:17:02 -0800461 text += start;
462 int glyphsCount = 0;
463
Romain Guyb6294902012-02-02 15:13:18 -0800464 if (CC_LIKELY(positions == NULL)) {
Romain Guy671d6cf2012-01-18 12:39:17 -0800465 SkFixed prevRsbDelta = 0;
Romain Guy694b5192010-07-21 21:33:20 -0700466
Romain Guy97771732012-02-28 18:17:02 -0800467 float penX = x + 0.5f;
Romain Guy671d6cf2012-01-18 12:39:17 -0800468 int penY = y;
Romain Guy694b5192010-07-21 21:33:20 -0700469
Romain Guy671d6cf2012-01-18 12:39:17 -0800470 while (glyphsCount < numGlyphs) {
471 glyph_t glyph = GET_GLYPH(text);
472
473 // Reached the end of the string
474 if (IS_END_OF_STRING(glyph)) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700475 break;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700476 }
Romain Guy671d6cf2012-01-18 12:39:17 -0800477
478 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
Romain Guy97771732012-02-28 18:17:02 -0800479 penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
Romain Guy671d6cf2012-01-18 12:39:17 -0800480 prevRsbDelta = cachedGlyph->mRsbDelta;
481
482 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
483 if (cachedGlyph->mIsValid) {
484 (*this.*render)(cachedGlyph, (int) floorf(penX), penY,
485 bitmap, bitmapW, bitmapH, bounds, positions);
486 }
487
488 penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
489
490 glyphsCount++;
Romain Guy694b5192010-07-21 21:33:20 -0700491 }
Romain Guy671d6cf2012-01-18 12:39:17 -0800492 } else {
493 const SkPaint::Align align = paint->getTextAlign();
Romain Guy694b5192010-07-21 21:33:20 -0700494
Romain Guy671d6cf2012-01-18 12:39:17 -0800495 // This is for renderPosText()
496 while (glyphsCount < numGlyphs) {
497 glyph_t glyph = GET_GLYPH(text);
Romain Guy694b5192010-07-21 21:33:20 -0700498
Romain Guy671d6cf2012-01-18 12:39:17 -0800499 // Reached the end of the string
500 if (IS_END_OF_STRING(glyph)) {
501 break;
502 }
503
504 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
505
506 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
507 if (cachedGlyph->mIsValid) {
508 int penX = x + positions[(glyphsCount << 1)];
509 int penY = y + positions[(glyphsCount << 1) + 1];
510
511 switch (align) {
512 case SkPaint::kRight_Align:
513 penX -= SkFixedToFloat(cachedGlyph->mAdvanceX);
514 penY -= SkFixedToFloat(cachedGlyph->mAdvanceY);
515 break;
516 case SkPaint::kCenter_Align:
517 penX -= SkFixedToFloat(cachedGlyph->mAdvanceX >> 1);
518 penY -= SkFixedToFloat(cachedGlyph->mAdvanceY >> 1);
519 default:
520 break;
521 }
522
523 (*this.*render)(cachedGlyph, penX, penY,
524 bitmap, bitmapW, bitmapH, bounds, positions);
525 }
526
527 glyphsCount++;
Romain Guy694b5192010-07-21 21:33:20 -0700528 }
529 }
530}
531
Romain Guy51769a62010-07-23 00:28:00 -0700532void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
Romain Guy694b5192010-07-21 21:33:20 -0700533 glyph->mAdvanceX = skiaGlyph.fAdvanceX;
534 glyph->mAdvanceY = skiaGlyph.fAdvanceY;
535 glyph->mBitmapLeft = skiaGlyph.fLeft;
536 glyph->mBitmapTop = skiaGlyph.fTop;
Romain Guy2bffd262010-09-12 17:40:02 -0700537 glyph->mLsbDelta = skiaGlyph.fLsbDelta;
538 glyph->mRsbDelta = skiaGlyph.fRsbDelta;
Romain Guy694b5192010-07-21 21:33:20 -0700539
540 uint32_t startX = 0;
541 uint32_t startY = 0;
542
Romain Guy694b5192010-07-21 21:33:20 -0700543 // Get the bitmap for the glyph
544 paint->findImage(skiaGlyph);
Chet Haase7de0cb12011-12-05 16:35:38 -0800545 mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700546
547 if (!glyph->mIsValid) {
548 return;
549 }
550
551 uint32_t endX = startX + skiaGlyph.fWidth;
552 uint32_t endY = startY + skiaGlyph.fHeight;
553
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700554 glyph->mStartX = startX;
555 glyph->mStartY = startY;
Romain Guy694b5192010-07-21 21:33:20 -0700556 glyph->mBitmapWidth = skiaGlyph.fWidth;
557 glyph->mBitmapHeight = skiaGlyph.fHeight;
558
Chet Haase7de0cb12011-12-05 16:35:38 -0800559 uint32_t cacheWidth = glyph->mCachedTextureLine->mCacheTexture->mWidth;
560 uint32_t cacheHeight = glyph->mCachedTextureLine->mCacheTexture->mHeight;
Romain Guy694b5192010-07-21 21:33:20 -0700561
Romain Guy33fa1f72012-08-07 19:09:57 -0700562 glyph->mBitmapMinU = startX / (float) cacheWidth;
563 glyph->mBitmapMinV = startY / (float) cacheHeight;
564 glyph->mBitmapMaxU = endX / (float) cacheWidth;
565 glyph->mBitmapMaxV = endY / (float) cacheHeight;
Romain Guy694b5192010-07-21 21:33:20 -0700566
Romain Guy51769a62010-07-23 00:28:00 -0700567 mState->mUploadTexture = true;
Romain Guy694b5192010-07-21 21:33:20 -0700568}
569
Chet Haase7de0cb12011-12-05 16:35:38 -0800570CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) {
Romain Guy51769a62010-07-23 00:28:00 -0700571 CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
Romain Guy694b5192010-07-21 21:33:20 -0700572 mCachedGlyphs.add(glyph, newGlyph);
573
Romain Guy726aeba2011-06-01 14:52:00 -0700574 const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
Romain Guy694b5192010-07-21 21:33:20 -0700575 newGlyph->mGlyphIndex = skiaGlyph.fID;
576 newGlyph->mIsValid = false;
577
578 updateGlyphCache(paint, skiaGlyph, newGlyph);
579
580 return newGlyph;
581}
582
Romain Guy2577db12011-01-18 13:02:38 -0800583Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
Romain Guybd496bc2011-08-02 17:32:41 -0700584 int flags, uint32_t italicStyle, uint32_t scaleX,
585 SkPaint::Style style, uint32_t strokeWidth) {
Romain Guy694b5192010-07-21 21:33:20 -0700586 Vector<Font*> &activeFonts = state->mActiveFonts;
587
588 for (uint32_t i = 0; i < activeFonts.size(); i++) {
Romain Guy51769a62010-07-23 00:28:00 -0700589 Font* font = activeFonts[i];
Romain Guy2577db12011-01-18 13:02:38 -0800590 if (font->mFontId == fontId && font->mFontSize == fontSize &&
Chet Haase8668f8a2011-03-02 13:51:36 -0800591 font->mFlags == flags && font->mItalicStyle == italicStyle &&
Romain Guybd496bc2011-08-02 17:32:41 -0700592 font->mScaleX == scaleX && font->mStyle == style &&
593 (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
Romain Guy51769a62010-07-23 00:28:00 -0700594 return font;
Romain Guy694b5192010-07-21 21:33:20 -0700595 }
596 }
597
Romain Guybd496bc2011-08-02 17:32:41 -0700598 Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
599 scaleX, style, strokeWidth);
Romain Guy694b5192010-07-21 21:33:20 -0700600 activeFonts.push(newFont);
601 return newFont;
602}
603
604///////////////////////////////////////////////////////////////////////////////
605// FontRenderer
606///////////////////////////////////////////////////////////////////////////////
607
Romain Guy514fb182011-01-19 14:38:29 -0800608static bool sLogFontRendererCreate = true;
609
Romain Guy694b5192010-07-21 21:33:20 -0700610FontRenderer::FontRenderer() {
Romain Guyc9855a52011-01-21 21:14:15 -0800611 if (sLogFontRendererCreate) {
612 INIT_LOGD("Creating FontRenderer");
613 }
Romain Guy51769a62010-07-23 00:28:00 -0700614
Romain Guyb45c0c92010-08-26 20:35:23 -0700615 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700616 mInitialized = false;
617 mMaxNumberOfQuads = 1024;
618 mCurrentQuadIndex = 0;
619
Romain Guy9cccc2b92010-08-07 23:46:15 -0700620 mTextMeshPtr = NULL;
Chet Haase7de0cb12011-12-05 16:35:38 -0800621 mCurrentCacheTexture = NULL;
622 mLastCacheTexture = NULL;
623 mCacheTextureSmall = NULL;
624 mCacheTexture128 = NULL;
625 mCacheTexture256 = NULL;
626 mCacheTexture512 = NULL;
Romain Guy9cccc2b92010-08-07 23:46:15 -0700627
Chet Haase2a47c142011-12-14 15:22:56 -0800628 mLinearFiltering = false;
629
Romain Guy694b5192010-07-21 21:33:20 -0700630 mIndexBufferID = 0;
631
Chet Haase7de0cb12011-12-05 16:35:38 -0800632 mSmallCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
633 mSmallCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -0700634
635 char property[PROPERTY_VALUE_MAX];
636 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800637 if (sLogFontRendererCreate) {
638 INIT_LOGD(" Setting text cache width to %s pixels", property);
639 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800640 mSmallCacheWidth = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -0700641 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800642 if (sLogFontRendererCreate) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800643 INIT_LOGD(" Using default text cache width of %i pixels", mSmallCacheWidth);
Romain Guy514fb182011-01-19 14:38:29 -0800644 }
Romain Guy51769a62010-07-23 00:28:00 -0700645 }
646
647 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800648 if (sLogFontRendererCreate) {
649 INIT_LOGD(" Setting text cache width to %s pixels", property);
650 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800651 mSmallCacheHeight = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -0700652 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800653 if (sLogFontRendererCreate) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800654 INIT_LOGD(" Using default text cache height of %i pixels", mSmallCacheHeight);
Romain Guy514fb182011-01-19 14:38:29 -0800655 }
Romain Guy51769a62010-07-23 00:28:00 -0700656 }
Romain Guy514fb182011-01-19 14:38:29 -0800657
658 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -0700659}
660
661FontRenderer::~FontRenderer() {
662 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
663 delete mCacheLines[i];
664 }
665 mCacheLines.clear();
666
Romain Guy9cccc2b92010-08-07 23:46:15 -0700667 if (mInitialized) {
Romain Guya9dd8202012-03-26 14:52:00 -0700668 // Unbinding the buffer shouldn't be necessary but it crashes with some drivers
669 Caches::getInstance().unbindIndicesBuffer();
Romain Guyb0317982012-03-21 11:52:52 -0700670 glDeleteBuffers(1, &mIndexBufferID);
671
Romain Guy9cccc2b92010-08-07 23:46:15 -0700672 delete[] mTextMeshPtr;
Chet Haase7de0cb12011-12-05 16:35:38 -0800673 delete mCacheTextureSmall;
674 delete mCacheTexture128;
675 delete mCacheTexture256;
676 delete mCacheTexture512;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700677 }
Romain Guy694b5192010-07-21 21:33:20 -0700678
679 Vector<Font*> fontsToDereference = mActiveFonts;
680 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
681 delete fontsToDereference[i];
682 }
683}
684
685void FontRenderer::flushAllAndInvalidate() {
686 if (mCurrentQuadIndex != 0) {
687 issueDrawCommand();
688 mCurrentQuadIndex = 0;
689 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700690
Romain Guy694b5192010-07-21 21:33:20 -0700691 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
692 mActiveFonts[i]->invalidateTextureCache();
693 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700694
Chet Haasee816bae2012-08-09 13:39:02 -0700695 uint16_t totalGlyphs = 0;
Romain Guy694b5192010-07-21 21:33:20 -0700696 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
Chet Haasee816bae2012-08-09 13:39:02 -0700697 totalGlyphs += mCacheLines[i]->mNumGlyphs;
698 mCacheLines[i]->init();
Romain Guy694b5192010-07-21 21:33:20 -0700699 }
Chet Haasee816bae2012-08-09 13:39:02 -0700700
701#if DEBUG_FONT_RENDERER
702 ALOGD("FontRenderer: flushAllAndInvalidatel");
703 // Erase caches, just as a debugging facility
704 if (mCacheTextureSmall && mCacheTextureSmall->mTexture) {
705 memset(mCacheTextureSmall->mTexture, 0,
706 mCacheTextureSmall->mWidth * mCacheTextureSmall->mHeight);
707 }
708 if (mCacheTexture128 && mCacheTexture128->mTexture) {
709 memset(mCacheTexture128->mTexture, 0,
710 mCacheTexture128->mWidth * mCacheTexture128->mHeight);
711 }
712 if (mCacheTexture256 && mCacheTexture256->mTexture) {
713 memset(mCacheTexture256->mTexture, 0,
714 mCacheTexture256->mWidth * mCacheTexture256->mHeight);
715 }
716 if (mCacheTexture512 && mCacheTexture512->mTexture) {
717 memset(mCacheTexture512->mTexture, 0,
718 mCacheTexture512->mWidth * mCacheTexture512->mHeight);
719 }
720 ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
721#endif
Romain Guy694b5192010-07-21 21:33:20 -0700722}
723
Chet Haase9a824562011-12-16 15:44:59 -0800724void FontRenderer::deallocateTextureMemory(CacheTexture *cacheTexture) {
725 if (cacheTexture && cacheTexture->mTexture) {
726 glDeleteTextures(1, &cacheTexture->mTextureId);
Romain Guy9d9758a2012-05-14 15:19:58 -0700727 delete[] cacheTexture->mTexture;
Chet Haase9a824562011-12-16 15:44:59 -0800728 cacheTexture->mTexture = NULL;
Romain Guy99a6ddd2012-05-14 15:32:18 -0700729 cacheTexture->mTextureId = 0;
Chet Haase9a824562011-12-16 15:44:59 -0800730 }
731}
732
733void FontRenderer::flushLargeCaches() {
734 if ((!mCacheTexture128 || !mCacheTexture128->mTexture) &&
735 (!mCacheTexture256 || !mCacheTexture256->mTexture) &&
736 (!mCacheTexture512 || !mCacheTexture512->mTexture)) {
737 // Typical case; no large glyph caches allocated
738 return;
739 }
740
741 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
742 CacheTextureLine* cacheLine = mCacheLines[i];
743 if ((cacheLine->mCacheTexture == mCacheTexture128 ||
744 cacheLine->mCacheTexture == mCacheTexture256 ||
745 cacheLine->mCacheTexture == mCacheTexture512) &&
746 cacheLine->mCacheTexture->mTexture != NULL) {
Chet Haasee816bae2012-08-09 13:39:02 -0700747#if DEBUG_FONT_RENDERER
748 if (cacheLine->mCacheTexture == mCacheTexture128) {
749 ALOGD("flushing cacheTexture128");
750 } else if (cacheLine->mCacheTexture == mCacheTexture256) {
751 ALOGD("flushing cacheTexture256");
752 } else {
753 ALOGD("flushing cacheTexture512");
754 }
755#endif
756 cacheLine->init();
Chet Haase9a824562011-12-16 15:44:59 -0800757 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
758 mActiveFonts[i]->invalidateTextureCache(cacheLine);
759 }
760 }
761 }
762
763 deallocateTextureMemory(mCacheTexture128);
764 deallocateTextureMemory(mCacheTexture256);
765 deallocateTextureMemory(mCacheTexture512);
766}
767
Romain Guy9d9758a2012-05-14 15:19:58 -0700768void FontRenderer::allocateTextureMemory(CacheTexture* cacheTexture) {
Chet Haase2a47c142011-12-14 15:22:56 -0800769 int width = cacheTexture->mWidth;
770 int height = cacheTexture->mHeight;
Romain Guy9d9758a2012-05-14 15:19:58 -0700771
Chet Haase2a47c142011-12-14 15:22:56 -0800772 cacheTexture->mTexture = new uint8_t[width * height];
Romain Guy99a6ddd2012-05-14 15:32:18 -0700773
774 if (!cacheTexture->mTextureId) {
775 glGenTextures(1, &cacheTexture->mTextureId);
776 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700777
Romain Guy16c88082012-06-11 16:03:47 -0700778 Caches::getInstance().activeTexture(0);
Chet Haase2a47c142011-12-14 15:22:56 -0800779 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
780 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
781 // Initialize texture dimensions
782 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
783 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800784
Chet Haase2a47c142011-12-14 15:22:56 -0800785 const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST;
786 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
787 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
788
789 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
790 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
Chet Haase7de0cb12011-12-05 16:35:38 -0800791}
792
793void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
794 uint32_t* retOriginX, uint32_t* retOriginY) {
795 cachedGlyph->mIsValid = false;
Romain Guy694b5192010-07-21 21:33:20 -0700796 // If the glyph is too tall, don't cache it
Chet Haasee816bae2012-08-09 13:39:02 -0700797 if (mCacheLines.size() == 0 ||
798 glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
799 if (mCacheLines.size() != 0) {
800 ALOGE("Font size too large to fit in cache. width, height = %i, %i",
801 (int) glyph.fWidth, (int) glyph.fHeight);
802 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800803 return;
Romain Guy694b5192010-07-21 21:33:20 -0700804 }
805
806 // Now copy the bitmap into the cache texture
807 uint32_t startX = 0;
808 uint32_t startY = 0;
809
810 bool bitmapFit = false;
Chet Haase7de0cb12011-12-05 16:35:38 -0800811 CacheTextureLine *cacheLine;
Romain Guy694b5192010-07-21 21:33:20 -0700812 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
813 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
814 if (bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800815 cacheLine = mCacheLines[i];
Romain Guy694b5192010-07-21 21:33:20 -0700816 break;
817 }
818 }
819
820 // If the new glyph didn't fit, flush the state so far and invalidate everything
821 if (!bitmapFit) {
822 flushAllAndInvalidate();
823
824 // Try to fit it again
825 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
826 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
827 if (bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800828 cacheLine = mCacheLines[i];
Romain Guy694b5192010-07-21 21:33:20 -0700829 break;
830 }
831 }
832
833 // if we still don't fit, something is wrong and we shouldn't draw
834 if (!bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800835 return;
Romain Guy694b5192010-07-21 21:33:20 -0700836 }
837 }
838
Chet Haase7de0cb12011-12-05 16:35:38 -0800839 cachedGlyph->mCachedTextureLine = cacheLine;
840
Romain Guy694b5192010-07-21 21:33:20 -0700841 *retOriginX = startX;
842 *retOriginY = startY;
843
844 uint32_t endX = startX + glyph.fWidth;
845 uint32_t endY = startY + glyph.fHeight;
846
Chet Haase7de0cb12011-12-05 16:35:38 -0800847 uint32_t cacheWidth = cacheLine->mMaxWidth;
Romain Guy694b5192010-07-21 21:33:20 -0700848
Romain Guy9d9758a2012-05-14 15:19:58 -0700849 CacheTexture* cacheTexture = cacheLine->mCacheTexture;
850 if (!cacheTexture->mTexture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800851 // Large-glyph texture memory is allocated only as needed
Chet Haase2a47c142011-12-14 15:22:56 -0800852 allocateTextureMemory(cacheTexture);
Chet Haase7de0cb12011-12-05 16:35:38 -0800853 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700854
Chet Haase7de0cb12011-12-05 16:35:38 -0800855 uint8_t* cacheBuffer = cacheTexture->mTexture;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700856 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700857 unsigned int stride = glyph.rowBytes();
858
859 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
Romain Guy33fa1f72012-08-07 19:09:57 -0700860
861 for (cacheX = startX - TEXTURE_BORDER_SIZE; cacheX < endX + TEXTURE_BORDER_SIZE; cacheX++) {
862 cacheBuffer[(startY - TEXTURE_BORDER_SIZE) * cacheWidth + cacheX] = 0;
863 cacheBuffer[(endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + cacheX] = 0;
864 }
865
866 for (cacheY = startY - TEXTURE_BORDER_SIZE + 1;
867 cacheY < endY + TEXTURE_BORDER_SIZE - 1; cacheY++) {
868 cacheBuffer[cacheY * cacheWidth + startX - TEXTURE_BORDER_SIZE] = 0;
869 cacheBuffer[cacheY * cacheWidth + endX + TEXTURE_BORDER_SIZE - 1] = 0;
870 }
871
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700872 if (mGammaTable) {
873 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
874 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
875 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
876 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
877 }
878 }
879 } else {
880 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
881 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
882 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
883 cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
884 }
Romain Guy694b5192010-07-21 21:33:20 -0700885 }
886 }
Romain Guy97771732012-02-28 18:17:02 -0800887
Chet Haase7de0cb12011-12-05 16:35:38 -0800888 cachedGlyph->mIsValid = true;
Romain Guy694b5192010-07-21 21:33:20 -0700889}
890
Chet Haase7de0cb12011-12-05 16:35:38 -0800891CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
Romain Guy0aa87bb2012-07-20 11:14:32 -0700892 CacheTexture* cacheTexture = new CacheTexture(width, height);
Romain Guy9d9758a2012-05-14 15:19:58 -0700893
Chet Haase2a47c142011-12-14 15:22:56 -0800894 if (allocate) {
895 allocateTextureMemory(cacheTexture);
896 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700897
Chet Haase2a47c142011-12-14 15:22:56 -0800898 return cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800899}
900
901void FontRenderer::initTextTexture() {
Romain Guy9d9758a2012-05-14 15:19:58 -0700902 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
903 delete mCacheLines[i];
904 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800905 mCacheLines.clear();
906
Romain Guy9d9758a2012-05-14 15:19:58 -0700907 if (mCacheTextureSmall) {
908 delete mCacheTextureSmall;
909 delete mCacheTexture128;
910 delete mCacheTexture256;
911 delete mCacheTexture512;
912 }
913
Chet Haase7de0cb12011-12-05 16:35:38 -0800914 // Next, use other, separate caches for large glyphs.
915 uint16_t maxWidth = 0;
916 if (Caches::hasInstance()) {
917 maxWidth = Caches::getInstance().maxTextureSize;
Chet Haase44984ea2011-05-19 13:50:47 -0700918 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700919
Chet Haase7de0cb12011-12-05 16:35:38 -0800920 if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) {
921 maxWidth = MAX_TEXT_CACHE_WIDTH;
922 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700923
Chet Haase7de0cb12011-12-05 16:35:38 -0800924 mCacheTextureSmall = createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true);
925 mCacheTexture128 = createCacheTexture(maxWidth, 256, false);
926 mCacheTexture256 = createCacheTexture(maxWidth, 256, false);
927 mCacheTexture512 = createCacheTexture(maxWidth, 512, false);
928 mCurrentCacheTexture = mCacheTextureSmall;
929
930 mUploadTexture = false;
931 // Split up our default cache texture into lines of certain widths
932 int nextLine = 0;
Chet Haasee816bae2012-08-09 13:39:02 -0700933 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 18, nextLine, mCacheTextureSmall));
Chet Haase7de0cb12011-12-05 16:35:38 -0800934 nextLine += mCacheLines.top()->mMaxHeight;
Chet Haasee816bae2012-08-09 13:39:02 -0700935 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, mCacheTextureSmall));
Chet Haase7de0cb12011-12-05 16:35:38 -0800936 nextLine += mCacheLines.top()->mMaxHeight;
Chet Haasee816bae2012-08-09 13:39:02 -0700937 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, mCacheTextureSmall));
Chet Haase7de0cb12011-12-05 16:35:38 -0800938 nextLine += mCacheLines.top()->mMaxHeight;
Chet Haasee816bae2012-08-09 13:39:02 -0700939 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, mCacheTextureSmall));
Chet Haase7de0cb12011-12-05 16:35:38 -0800940 nextLine += mCacheLines.top()->mMaxHeight;
Chet Haasee816bae2012-08-09 13:39:02 -0700941 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, mCacheTextureSmall));
Chet Haase7de0cb12011-12-05 16:35:38 -0800942 nextLine += mCacheLines.top()->mMaxHeight;
Chet Haasee816bae2012-08-09 13:39:02 -0700943 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 42, nextLine, mCacheTextureSmall));
Chet Haase7de0cb12011-12-05 16:35:38 -0800944 nextLine += mCacheLines.top()->mMaxHeight;
945 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, mSmallCacheHeight - nextLine,
Chet Haasee816bae2012-08-09 13:39:02 -0700946 nextLine, mCacheTextureSmall));
Chet Haase7de0cb12011-12-05 16:35:38 -0800947
948 // The first cache is split into 2 lines of height 128, the rest have just one cache line.
Chet Haasee816bae2012-08-09 13:39:02 -0700949 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 0, mCacheTexture128));
950 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 128, mCacheTexture128));
951 mCacheLines.push(new CacheTextureLine(maxWidth, 256, 0, mCacheTexture256));
952 mCacheLines.push(new CacheTextureLine(maxWidth, 512, 0, mCacheTexture512));
Romain Guy694b5192010-07-21 21:33:20 -0700953}
954
955// Avoid having to reallocate memory and render quad by quad
956void FontRenderer::initVertexArrayBuffers() {
Romain Guyd71dd362011-12-12 19:03:35 -0800957 uint32_t numIndices = mMaxNumberOfQuads * 6;
958 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700959 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700960
961 // Four verts, two triangles , six indices per quad
962 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
963 int i6 = i * 6;
964 int i4 = i * 4;
965
966 indexBufferData[i6 + 0] = i4 + 0;
967 indexBufferData[i6 + 1] = i4 + 1;
968 indexBufferData[i6 + 2] = i4 + 2;
969
970 indexBufferData[i6 + 3] = i4 + 0;
971 indexBufferData[i6 + 4] = i4 + 2;
972 indexBufferData[i6 + 5] = i4 + 3;
973 }
974
975 glGenBuffers(1, &mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800976 Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700977 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
Romain Guy694b5192010-07-21 21:33:20 -0700978
979 free(indexBufferData);
980
Romain Guyd71dd362011-12-12 19:03:35 -0800981 uint32_t coordSize = 2;
Romain Guy694b5192010-07-21 21:33:20 -0700982 uint32_t uvSize = 2;
983 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700984 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
985 mTextMeshPtr = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700986}
987
988// We don't want to allocate anything unless we actually draw text
989void FontRenderer::checkInit() {
990 if (mInitialized) {
991 return;
992 }
993
994 initTextTexture();
995 initVertexArrayBuffers();
996
997 mInitialized = true;
998}
999
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -07001000void FontRenderer::checkTextureUpdate() {
Chet Haase7de0cb12011-12-05 16:35:38 -08001001 if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -07001002 return;
Romain Guy694b5192010-07-21 21:33:20 -07001003 }
1004
Romain Guy2d4fd362011-12-13 22:00:19 -08001005 Caches& caches = Caches::getInstance();
1006 GLuint lastTextureId = 0;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -07001007 // Iterate over all the cache lines and see which ones need to be updated
1008 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
1009 CacheTextureLine* cl = mCacheLines[i];
Chet Haase7de0cb12011-12-05 16:35:38 -08001010 if (cl->mDirty && cl->mCacheTexture->mTexture != NULL) {
1011 CacheTexture* cacheTexture = cl->mCacheTexture;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -07001012 uint32_t xOffset = 0;
1013 uint32_t yOffset = cl->mCurrentRow;
Chet Haase7de0cb12011-12-05 16:35:38 -08001014 uint32_t width = cl->mMaxWidth;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -07001015 uint32_t height = cl->mMaxHeight;
Chet Haase7de0cb12011-12-05 16:35:38 -08001016 void* textureData = cacheTexture->mTexture + (yOffset * width);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -07001017
Romain Guy2d4fd362011-12-13 22:00:19 -08001018 if (cacheTexture->mTextureId != lastTextureId) {
1019 caches.activeTexture(0);
1020 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
1021 lastTextureId = cacheTexture->mTextureId;
1022 }
Chet Haasee816bae2012-08-09 13:39:02 -07001023#if DEBUG_FONT_RENDERER
1024 ALOGD("glTextSubimage for cacheLine %d: xOff, yOff, width height = %d, %d, %d, %d", i,
1025 xOffset, yOffset, width, height);
1026#endif
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -07001027 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -07001028 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -07001029
1030 cl->mDirty = false;
1031 }
1032 }
1033
Romain Guy16c88082012-06-11 16:03:47 -07001034 caches.activeTexture(0);
Chet Haase7de0cb12011-12-05 16:35:38 -08001035 glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
Chet Haase2a47c142011-12-14 15:22:56 -08001036 if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) {
1037 const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST;
1038 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
1039 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
1040 mCurrentCacheTexture->mLinearFiltering = mLinearFiltering;
1041 }
Chet Haase7de0cb12011-12-05 16:35:38 -08001042 mLastCacheTexture = mCurrentCacheTexture;
1043
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -07001044 mUploadTexture = false;
1045}
1046
1047void FontRenderer::issueDrawCommand() {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -07001048 checkTextureUpdate();
1049
Romain Guy15bc6432011-12-13 13:11:32 -08001050 Caches& caches = Caches::getInstance();
Romain Guy2d4fd362011-12-13 22:00:19 -08001051 caches.bindIndicesBuffer(mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -08001052 if (!mDrawn) {
1053 float* buffer = mTextMeshPtr;
1054 int offset = 2;
1055
1056 bool force = caches.unbindMeshBuffer();
1057 caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
1058 caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
1059 buffer + offset);
1060 }
1061
Romain Guy694b5192010-07-21 21:33:20 -07001062 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
Romain Guy5b3b3522010-10-27 18:57:51 -07001063
1064 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -07001065}
1066
Romain Guy97771732012-02-28 18:17:02 -08001067void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
1068 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -08001069 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Chet Haase7de0cb12011-12-05 16:35:38 -08001070 if (texture != mCurrentCacheTexture) {
1071 if (mCurrentQuadIndex != 0) {
1072 // First, draw everything stored already which uses the previous texture
1073 issueDrawCommand();
1074 mCurrentQuadIndex = 0;
1075 }
1076 // Now use the new texture id
1077 mCurrentCacheTexture = texture;
1078 }
Romain Guy09147fb2010-07-22 13:08:20 -07001079
Romain Guy694b5192010-07-21 21:33:20 -07001080 const uint32_t vertsPerQuad = 4;
Romain Guyd71dd362011-12-12 19:03:35 -08001081 const uint32_t floatsPerVert = 4;
Romain Guy51769a62010-07-23 00:28:00 -07001082 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -07001083
Romain Guy694b5192010-07-21 21:33:20 -07001084 (*currentPos++) = x1;
1085 (*currentPos++) = y1;
Romain Guy694b5192010-07-21 21:33:20 -07001086 (*currentPos++) = u1;
1087 (*currentPos++) = v1;
1088
1089 (*currentPos++) = x2;
1090 (*currentPos++) = y2;
Romain Guy694b5192010-07-21 21:33:20 -07001091 (*currentPos++) = u2;
1092 (*currentPos++) = v2;
1093
1094 (*currentPos++) = x3;
1095 (*currentPos++) = y3;
Romain Guy694b5192010-07-21 21:33:20 -07001096 (*currentPos++) = u3;
1097 (*currentPos++) = v3;
1098
1099 (*currentPos++) = x4;
1100 (*currentPos++) = y4;
Romain Guy694b5192010-07-21 21:33:20 -07001101 (*currentPos++) = u4;
1102 (*currentPos++) = v4;
1103
1104 mCurrentQuadIndex++;
Romain Guy97771732012-02-28 18:17:02 -08001105}
1106
1107void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
1108 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
1109 float x4, float y4, float u4, float v4, CacheTexture* texture) {
1110
1111 if (mClip &&
1112 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
1113 return;
1114 }
1115
1116 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
Romain Guy694b5192010-07-21 21:33:20 -07001117
Romain Guy5b3b3522010-10-27 18:57:51 -07001118 if (mBounds) {
1119 mBounds->left = fmin(mBounds->left, x1);
1120 mBounds->top = fmin(mBounds->top, y3);
1121 mBounds->right = fmax(mBounds->right, x3);
1122 mBounds->bottom = fmax(mBounds->bottom, y1);
1123 }
1124
Romain Guy694b5192010-07-21 21:33:20 -07001125 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
1126 issueDrawCommand();
1127 mCurrentQuadIndex = 0;
1128 }
1129}
1130
Romain Guy97771732012-02-28 18:17:02 -08001131void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
1132 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
1133 float x4, float y4, float u4, float v4, CacheTexture* texture) {
1134
1135 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
1136
1137 if (mBounds) {
1138 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
1139 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
1140 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
1141 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
1142 }
1143
1144 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
1145 issueDrawCommand();
1146 mCurrentQuadIndex = 0;
1147 }
1148}
1149
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -07001150void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
Romain Guy325a0f92011-01-05 15:26:55 -08001151 int flags = 0;
1152 if (paint->isFakeBoldText()) {
1153 flags |= Font::kFakeBold;
1154 }
Romain Guy2577db12011-01-18 13:02:38 -08001155
1156 const float skewX = paint->getTextSkewX();
1157 uint32_t italicStyle = *(uint32_t*) &skewX;
Chet Haase8668f8a2011-03-02 13:51:36 -08001158 const float scaleXFloat = paint->getTextScaleX();
1159 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
Romain Guybd496bc2011-08-02 17:32:41 -07001160 SkPaint::Style style = paint->getStyle();
1161 const float strokeWidthFloat = paint->getStrokeWidth();
1162 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
1163 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
1164 scaleX, style, strokeWidth);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -07001165
Romain Guy694b5192010-07-21 21:33:20 -07001166}
Romain Guy7975fb62010-10-01 16:36:14 -07001167
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001168FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Raph Levien416a8472012-07-19 22:48:17 -07001169 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
Romain Guy1e45aae2010-08-13 19:39:53 -07001170 checkInit();
1171
1172 if (!mCurrentFont) {
1173 DropShadow image;
1174 image.width = 0;
1175 image.height = 0;
1176 image.image = NULL;
1177 image.penX = 0;
1178 image.penY = 0;
1179 return image;
1180 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001181
Romain Guy2d4fd362011-12-13 22:00:19 -08001182 mDrawn = false;
Romain Guyff98fa52011-11-28 09:35:09 -08001183 mClip = NULL;
1184 mBounds = NULL;
1185
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001186 Rect bounds;
Raph Levien416a8472012-07-19 22:48:17 -07001187 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
Romain Guyff98fa52011-11-28 09:35:09 -08001188
Romain Guy1e45aae2010-08-13 19:39:53 -07001189 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
1190 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001191 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guyff98fa52011-11-28 09:35:09 -08001192
Romain Guy1e45aae2010-08-13 19:39:53 -07001193 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001194 dataBuffer[i] = 0;
1195 }
Romain Guy1e45aae2010-08-13 19:39:53 -07001196
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001197 int penX = radius - bounds.left;
1198 int penY = radius - bounds.bottom;
1199
Romain Guy726aeba2011-06-01 14:52:00 -07001200 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
Raph Levien416a8472012-07-19 22:48:17 -07001201 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001202 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
1203
1204 DropShadow image;
1205 image.width = paddedWidth;
1206 image.height = paddedHeight;
1207 image.image = dataBuffer;
1208 image.penX = penX;
1209 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -08001210
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001211 return image;
1212}
Romain Guy694b5192010-07-21 21:33:20 -07001213
Romain Guy671d6cf2012-01-18 12:39:17 -08001214void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -07001215 checkInit();
1216
Romain Guy5b3b3522010-10-27 18:57:51 -07001217 mDrawn = false;
1218 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -07001219 mClip = clip;
Romain Guy671d6cf2012-01-18 12:39:17 -08001220}
Romain Guyff98fa52011-11-28 09:35:09 -08001221
Romain Guy671d6cf2012-01-18 12:39:17 -08001222void FontRenderer::finishRender() {
Romain Guy5b3b3522010-10-27 18:57:51 -07001223 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -08001224 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -07001225
1226 if (mCurrentQuadIndex != 0) {
1227 issueDrawCommand();
1228 mCurrentQuadIndex = 0;
1229 }
Romain Guy671d6cf2012-01-18 12:39:17 -08001230}
1231
Chet Haasee816bae2012-08-09 13:39:02 -07001232void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs) {
1233 int flags = 0;
1234 if (paint->isFakeBoldText()) {
1235 flags |= Font::kFakeBold;
1236 }
1237 const float skewX = paint->getTextSkewX();
1238 uint32_t italicStyle = *(uint32_t*) &skewX;
1239 const float scaleXFloat = paint->getTextScaleX();
1240 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
1241 SkPaint::Style style = paint->getStyle();
1242 const float strokeWidthFloat = paint->getStrokeWidth();
1243 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
1244 float fontSize = paint->getTextSize();
1245 Font* font = Font::create(this, SkTypeface::UniqueID(paint->getTypeface()),
1246 fontSize, flags, italicStyle, scaleX, style, strokeWidth);
1247
1248 font->precache(paint, text, numGlyphs);
1249}
1250
Romain Guy671d6cf2012-01-18 12:39:17 -08001251bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
1252 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
1253 if (!mCurrentFont) {
1254 ALOGE("No font set");
1255 return false;
1256 }
1257
1258 initRender(clip, bounds);
1259 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
1260 finishRender();
1261
1262 return mDrawn;
1263}
1264
1265bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
1266 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
1267 const float* positions, Rect* bounds) {
1268 if (!mCurrentFont) {
1269 ALOGE("No font set");
1270 return false;
1271 }
1272
1273 initRender(clip, bounds);
1274 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
1275 finishRender();
Romain Guy5b3b3522010-10-27 18:57:51 -07001276
1277 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -07001278}
1279
Romain Guy97771732012-02-28 18:17:02 -08001280bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
1281 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
1282 float hOffset, float vOffset, Rect* bounds) {
1283 if (!mCurrentFont) {
1284 ALOGE("No font set");
1285 return false;
1286 }
1287
1288 initRender(clip, bounds);
1289 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
1290 finishRender();
1291
1292 return mDrawn;
1293}
1294
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001295void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
1296 // Compute gaussian weights for the blur
1297 // e is the euler's number
1298 float e = 2.718281828459045f;
1299 float pi = 3.1415926535897932f;
1300 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
1301 // x is of the form [-radius .. 0 .. radius]
1302 // and sigma varies with radius.
1303 // Based on some experimental radius values and sigma's
1304 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001305 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001306 // The larger the radius gets, the more our gaussian blur
1307 // will resemble a box blur since with large sigma
1308 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -08001309 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001310
1311 // Now compute the coefficints
1312 // We will store some redundant values to save some math during
1313 // the blur calculations
1314 // precompute some values
1315 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
1316 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
1317
1318 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -08001319 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -07001320 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001321 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
1322 normalizeFactor += weights[r + radius];
1323 }
1324
1325 //Now we need to normalize the weights because all our coefficients need to add up to one
1326 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -08001327 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001328 weights[r + radius] *= normalizeFactor;
1329 }
1330}
1331
1332void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -07001333 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001334 float blurredPixel = 0.0f;
1335 float currentPixel = 0.0f;
1336
Romain Guy325a0f92011-01-05 15:26:55 -08001337 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001338
1339 const uint8_t* input = source + y * width;
1340 uint8_t* output = dest + y * width;
1341
Romain Guy325a0f92011-01-05 15:26:55 -08001342 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001343 blurredPixel = 0.0f;
1344 const float* gPtr = weights;
1345 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -08001346 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001347 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -08001348 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -07001349 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001350 blurredPixel += currentPixel * gPtr[0];
1351 gPtr++;
1352 i++;
1353 }
1354 } else {
Romain Guy325a0f92011-01-05 15:26:55 -08001355 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001356 // Stepping left and right away from the pixel
1357 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -08001358 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001359 validW = 0;
1360 }
Romain Guy325a0f92011-01-05 15:26:55 -08001361 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001362 validW = width - 1;
1363 }
1364
Romain Guy325a0f92011-01-05 15:26:55 -08001365 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001366 blurredPixel += currentPixel * gPtr[0];
1367 gPtr++;
1368 }
1369 }
1370 *output = (uint8_t)blurredPixel;
1371 output ++;
1372 }
1373 }
1374}
1375
1376void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -07001377 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001378 float blurredPixel = 0.0f;
1379 float currentPixel = 0.0f;
1380
Romain Guy325a0f92011-01-05 15:26:55 -08001381 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001382
1383 uint8_t* output = dest + y * width;
1384
Romain Guy325a0f92011-01-05 15:26:55 -08001385 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001386 blurredPixel = 0.0f;
1387 const float* gPtr = weights;
1388 const uint8_t* input = source + x;
1389 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -08001390 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001391 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -08001392 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001393 currentPixel = (float)(*i);
1394 blurredPixel += currentPixel * gPtr[0];
1395 gPtr++;
1396 i += width;
1397 }
1398 } else {
Romain Guy325a0f92011-01-05 15:26:55 -08001399 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001400 int validH = y + r;
1401 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -08001402 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001403 validH = 0;
1404 }
Romain Guy325a0f92011-01-05 15:26:55 -08001405 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001406 validH = height - 1;
1407 }
1408
1409 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -08001410 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001411 blurredPixel += currentPixel * gPtr[0];
1412 gPtr++;
1413 }
1414 }
Romain Guy325a0f92011-01-05 15:26:55 -08001415 *output = (uint8_t) blurredPixel;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001416 output ++;
1417 }
1418 }
1419}
1420
1421
1422void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
1423 float *gaussian = new float[2 * radius + 1];
1424 computeGaussianWeights(gaussian, radius);
Romain Guyd71dd362011-12-12 19:03:35 -08001425
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001426 uint8_t* scratch = new uint8_t[width * height];
Romain Guyd71dd362011-12-12 19:03:35 -08001427
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001428 horizontalBlur(gaussian, radius, image, scratch, width, height);
1429 verticalBlur(gaussian, radius, scratch, image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -08001430
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001431 delete[] gaussian;
1432 delete[] scratch;
1433}
1434
Romain Guy694b5192010-07-21 21:33:20 -07001435}; // namespace uirenderer
1436}; // namespace android