blob: b352ffc538ae519ec39d4783db2e4fcfe231c7db [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
Chet Haasee816bae2012-08-09 13:39:02 -0700702 // Erase caches, just as a debugging facility
703 if (mCacheTextureSmall && mCacheTextureSmall->mTexture) {
704 memset(mCacheTextureSmall->mTexture, 0,
705 mCacheTextureSmall->mWidth * mCacheTextureSmall->mHeight);
706 }
707 if (mCacheTexture128 && mCacheTexture128->mTexture) {
708 memset(mCacheTexture128->mTexture, 0,
709 mCacheTexture128->mWidth * mCacheTexture128->mHeight);
710 }
711 if (mCacheTexture256 && mCacheTexture256->mTexture) {
712 memset(mCacheTexture256->mTexture, 0,
713 mCacheTexture256->mWidth * mCacheTexture256->mHeight);
714 }
715 if (mCacheTexture512 && mCacheTexture512->mTexture) {
716 memset(mCacheTexture512->mTexture, 0,
717 mCacheTexture512->mWidth * mCacheTexture512->mHeight);
718 }
719 ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
720#endif
Romain Guy694b5192010-07-21 21:33:20 -0700721}
722
Chet Haase9a824562011-12-16 15:44:59 -0800723void FontRenderer::deallocateTextureMemory(CacheTexture *cacheTexture) {
724 if (cacheTexture && cacheTexture->mTexture) {
725 glDeleteTextures(1, &cacheTexture->mTextureId);
Romain Guy9d9758a2012-05-14 15:19:58 -0700726 delete[] cacheTexture->mTexture;
Chet Haase9a824562011-12-16 15:44:59 -0800727 cacheTexture->mTexture = NULL;
Romain Guy99a6ddd2012-05-14 15:32:18 -0700728 cacheTexture->mTextureId = 0;
Chet Haase9a824562011-12-16 15:44:59 -0800729 }
730}
731
732void FontRenderer::flushLargeCaches() {
733 if ((!mCacheTexture128 || !mCacheTexture128->mTexture) &&
734 (!mCacheTexture256 || !mCacheTexture256->mTexture) &&
735 (!mCacheTexture512 || !mCacheTexture512->mTexture)) {
736 // Typical case; no large glyph caches allocated
737 return;
738 }
739
740 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
741 CacheTextureLine* cacheLine = mCacheLines[i];
742 if ((cacheLine->mCacheTexture == mCacheTexture128 ||
743 cacheLine->mCacheTexture == mCacheTexture256 ||
744 cacheLine->mCacheTexture == mCacheTexture512) &&
745 cacheLine->mCacheTexture->mTexture != NULL) {
Chet Haasee816bae2012-08-09 13:39:02 -0700746#if DEBUG_FONT_RENDERER
747 if (cacheLine->mCacheTexture == mCacheTexture128) {
748 ALOGD("flushing cacheTexture128");
749 } else if (cacheLine->mCacheTexture == mCacheTexture256) {
750 ALOGD("flushing cacheTexture256");
751 } else {
752 ALOGD("flushing cacheTexture512");
753 }
754#endif
755 cacheLine->init();
Chet Haase9a824562011-12-16 15:44:59 -0800756 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
757 mActiveFonts[i]->invalidateTextureCache(cacheLine);
758 }
759 }
760 }
761
762 deallocateTextureMemory(mCacheTexture128);
763 deallocateTextureMemory(mCacheTexture256);
764 deallocateTextureMemory(mCacheTexture512);
765}
766
Romain Guy9d9758a2012-05-14 15:19:58 -0700767void FontRenderer::allocateTextureMemory(CacheTexture* cacheTexture) {
Chet Haase2a47c142011-12-14 15:22:56 -0800768 int width = cacheTexture->mWidth;
769 int height = cacheTexture->mHeight;
Romain Guy9d9758a2012-05-14 15:19:58 -0700770
Chet Haase2a47c142011-12-14 15:22:56 -0800771 cacheTexture->mTexture = new uint8_t[width * height];
Romain Guy99a6ddd2012-05-14 15:32:18 -0700772
773 if (!cacheTexture->mTextureId) {
774 glGenTextures(1, &cacheTexture->mTextureId);
775 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700776
Romain Guy16c88082012-06-11 16:03:47 -0700777 Caches::getInstance().activeTexture(0);
Chet Haase2a47c142011-12-14 15:22:56 -0800778 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
779 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
780 // Initialize texture dimensions
781 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
782 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800783
Chet Haase2a47c142011-12-14 15:22:56 -0800784 const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST;
785 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
786 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
787
788 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
789 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
Chet Haase7de0cb12011-12-05 16:35:38 -0800790}
791
792void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
793 uint32_t* retOriginX, uint32_t* retOriginY) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700794 checkInit();
Chet Haase7de0cb12011-12-05 16:35:38 -0800795 cachedGlyph->mIsValid = false;
Romain Guy694b5192010-07-21 21:33:20 -0700796 // If the glyph is too tall, don't cache it
Chet Haase2efd5c52012-08-15 13:15:16 -0700797 if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
798 ALOGE("Font size too large to fit in cache. width, height = %i, %i",
799 (int) glyph.fWidth, (int) glyph.fHeight);
Chet Haase7de0cb12011-12-05 16:35:38 -0800800 return;
Romain Guy694b5192010-07-21 21:33:20 -0700801 }
802
803 // Now copy the bitmap into the cache texture
804 uint32_t startX = 0;
805 uint32_t startY = 0;
806
807 bool bitmapFit = false;
Chet Haase7de0cb12011-12-05 16:35:38 -0800808 CacheTextureLine *cacheLine;
Romain Guy694b5192010-07-21 21:33:20 -0700809 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
810 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
811 if (bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800812 cacheLine = mCacheLines[i];
Romain Guy694b5192010-07-21 21:33:20 -0700813 break;
814 }
815 }
816
817 // If the new glyph didn't fit, flush the state so far and invalidate everything
818 if (!bitmapFit) {
819 flushAllAndInvalidate();
820
821 // Try to fit it again
822 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
823 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
824 if (bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800825 cacheLine = mCacheLines[i];
Romain Guy694b5192010-07-21 21:33:20 -0700826 break;
827 }
828 }
829
830 // if we still don't fit, something is wrong and we shouldn't draw
831 if (!bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800832 return;
Romain Guy694b5192010-07-21 21:33:20 -0700833 }
834 }
835
Chet Haase7de0cb12011-12-05 16:35:38 -0800836 cachedGlyph->mCachedTextureLine = cacheLine;
837
Romain Guy694b5192010-07-21 21:33:20 -0700838 *retOriginX = startX;
839 *retOriginY = startY;
840
841 uint32_t endX = startX + glyph.fWidth;
842 uint32_t endY = startY + glyph.fHeight;
843
Chet Haase7de0cb12011-12-05 16:35:38 -0800844 uint32_t cacheWidth = cacheLine->mMaxWidth;
Romain Guy694b5192010-07-21 21:33:20 -0700845
Romain Guy9d9758a2012-05-14 15:19:58 -0700846 CacheTexture* cacheTexture = cacheLine->mCacheTexture;
847 if (!cacheTexture->mTexture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800848 // Large-glyph texture memory is allocated only as needed
Chet Haase2a47c142011-12-14 15:22:56 -0800849 allocateTextureMemory(cacheTexture);
Chet Haase7de0cb12011-12-05 16:35:38 -0800850 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700851
Chet Haase7de0cb12011-12-05 16:35:38 -0800852 uint8_t* cacheBuffer = cacheTexture->mTexture;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700853 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700854 unsigned int stride = glyph.rowBytes();
855
856 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
Romain Guy33fa1f72012-08-07 19:09:57 -0700857
858 for (cacheX = startX - TEXTURE_BORDER_SIZE; cacheX < endX + TEXTURE_BORDER_SIZE; cacheX++) {
859 cacheBuffer[(startY - TEXTURE_BORDER_SIZE) * cacheWidth + cacheX] = 0;
860 cacheBuffer[(endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + cacheX] = 0;
861 }
862
863 for (cacheY = startY - TEXTURE_BORDER_SIZE + 1;
864 cacheY < endY + TEXTURE_BORDER_SIZE - 1; cacheY++) {
865 cacheBuffer[cacheY * cacheWidth + startX - TEXTURE_BORDER_SIZE] = 0;
866 cacheBuffer[cacheY * cacheWidth + endX + TEXTURE_BORDER_SIZE - 1] = 0;
867 }
868
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700869 if (mGammaTable) {
870 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
871 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
872 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
873 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
874 }
875 }
876 } else {
877 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
878 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
879 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
880 cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
881 }
Romain Guy694b5192010-07-21 21:33:20 -0700882 }
883 }
Romain Guy97771732012-02-28 18:17:02 -0800884
Chet Haase7de0cb12011-12-05 16:35:38 -0800885 cachedGlyph->mIsValid = true;
Romain Guy694b5192010-07-21 21:33:20 -0700886}
887
Chet Haase7de0cb12011-12-05 16:35:38 -0800888CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
Romain Guy0aa87bb2012-07-20 11:14:32 -0700889 CacheTexture* cacheTexture = new CacheTexture(width, height);
Romain Guy9d9758a2012-05-14 15:19:58 -0700890
Chet Haase2a47c142011-12-14 15:22:56 -0800891 if (allocate) {
892 allocateTextureMemory(cacheTexture);
893 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700894
Chet Haase2a47c142011-12-14 15:22:56 -0800895 return cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800896}
897
898void FontRenderer::initTextTexture() {
Romain Guy9d9758a2012-05-14 15:19:58 -0700899 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
900 delete mCacheLines[i];
901 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800902 mCacheLines.clear();
903
Romain Guy9d9758a2012-05-14 15:19:58 -0700904 if (mCacheTextureSmall) {
905 delete mCacheTextureSmall;
906 delete mCacheTexture128;
907 delete mCacheTexture256;
908 delete mCacheTexture512;
909 }
910
Chet Haase7de0cb12011-12-05 16:35:38 -0800911 // Next, use other, separate caches for large glyphs.
912 uint16_t maxWidth = 0;
913 if (Caches::hasInstance()) {
914 maxWidth = Caches::getInstance().maxTextureSize;
Chet Haase44984ea2011-05-19 13:50:47 -0700915 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700916
Chet Haase7de0cb12011-12-05 16:35:38 -0800917 if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) {
918 maxWidth = MAX_TEXT_CACHE_WIDTH;
919 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700920
Chet Haase7de0cb12011-12-05 16:35:38 -0800921 mCacheTextureSmall = createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true);
922 mCacheTexture128 = createCacheTexture(maxWidth, 256, false);
923 mCacheTexture256 = createCacheTexture(maxWidth, 256, false);
924 mCacheTexture512 = createCacheTexture(maxWidth, 512, false);
925 mCurrentCacheTexture = mCacheTextureSmall;
926
927 mUploadTexture = false;
928 // Split up our default cache texture into lines of certain widths
929 int nextLine = 0;
Chet Haasee816bae2012-08-09 13:39:02 -0700930 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 18, nextLine, mCacheTextureSmall));
Chet Haase7de0cb12011-12-05 16:35:38 -0800931 nextLine += mCacheLines.top()->mMaxHeight;
Chet Haasee816bae2012-08-09 13:39:02 -0700932 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, mCacheTextureSmall));
Chet Haase7de0cb12011-12-05 16:35:38 -0800933 nextLine += mCacheLines.top()->mMaxHeight;
Chet Haasee816bae2012-08-09 13:39:02 -0700934 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, mCacheTextureSmall));
Chet Haase7de0cb12011-12-05 16:35:38 -0800935 nextLine += mCacheLines.top()->mMaxHeight;
Chet Haasee816bae2012-08-09 13:39:02 -0700936 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, mCacheTextureSmall));
Chet Haase7de0cb12011-12-05 16:35:38 -0800937 nextLine += mCacheLines.top()->mMaxHeight;
Chet Haasee816bae2012-08-09 13:39:02 -0700938 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, mCacheTextureSmall));
Chet Haase7de0cb12011-12-05 16:35:38 -0800939 nextLine += mCacheLines.top()->mMaxHeight;
Chet Haasee816bae2012-08-09 13:39:02 -0700940 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 42, nextLine, mCacheTextureSmall));
Chet Haase7de0cb12011-12-05 16:35:38 -0800941 nextLine += mCacheLines.top()->mMaxHeight;
942 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, mSmallCacheHeight - nextLine,
Chet Haasee816bae2012-08-09 13:39:02 -0700943 nextLine, mCacheTextureSmall));
Chet Haase7de0cb12011-12-05 16:35:38 -0800944
945 // 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 -0700946 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 0, mCacheTexture128));
947 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 128, mCacheTexture128));
948 mCacheLines.push(new CacheTextureLine(maxWidth, 256, 0, mCacheTexture256));
949 mCacheLines.push(new CacheTextureLine(maxWidth, 512, 0, mCacheTexture512));
Romain Guy694b5192010-07-21 21:33:20 -0700950}
951
952// Avoid having to reallocate memory and render quad by quad
953void FontRenderer::initVertexArrayBuffers() {
Romain Guyd71dd362011-12-12 19:03:35 -0800954 uint32_t numIndices = mMaxNumberOfQuads * 6;
955 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700956 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700957
958 // Four verts, two triangles , six indices per quad
959 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
960 int i6 = i * 6;
961 int i4 = i * 4;
962
963 indexBufferData[i6 + 0] = i4 + 0;
964 indexBufferData[i6 + 1] = i4 + 1;
965 indexBufferData[i6 + 2] = i4 + 2;
966
967 indexBufferData[i6 + 3] = i4 + 0;
968 indexBufferData[i6 + 4] = i4 + 2;
969 indexBufferData[i6 + 5] = i4 + 3;
970 }
971
972 glGenBuffers(1, &mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800973 Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700974 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
Romain Guy694b5192010-07-21 21:33:20 -0700975
976 free(indexBufferData);
977
Romain Guyd71dd362011-12-12 19:03:35 -0800978 uint32_t coordSize = 2;
Romain Guy694b5192010-07-21 21:33:20 -0700979 uint32_t uvSize = 2;
980 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700981 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
982 mTextMeshPtr = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700983}
984
985// We don't want to allocate anything unless we actually draw text
986void FontRenderer::checkInit() {
987 if (mInitialized) {
988 return;
989 }
990
991 initTextTexture();
992 initVertexArrayBuffers();
993
994 mInitialized = true;
995}
996
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700997void FontRenderer::checkTextureUpdate() {
Chet Haase7de0cb12011-12-05 16:35:38 -0800998 if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700999 return;
Romain Guy694b5192010-07-21 21:33:20 -07001000 }
1001
Romain Guy2d4fd362011-12-13 22:00:19 -08001002 Caches& caches = Caches::getInstance();
1003 GLuint lastTextureId = 0;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -07001004 // Iterate over all the cache lines and see which ones need to be updated
1005 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
1006 CacheTextureLine* cl = mCacheLines[i];
Chet Haase7de0cb12011-12-05 16:35:38 -08001007 if (cl->mDirty && cl->mCacheTexture->mTexture != NULL) {
1008 CacheTexture* cacheTexture = cl->mCacheTexture;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -07001009 uint32_t xOffset = 0;
1010 uint32_t yOffset = cl->mCurrentRow;
Chet Haase7de0cb12011-12-05 16:35:38 -08001011 uint32_t width = cl->mMaxWidth;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -07001012 uint32_t height = cl->mMaxHeight;
Chet Haase7de0cb12011-12-05 16:35:38 -08001013 void* textureData = cacheTexture->mTexture + (yOffset * width);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -07001014
Romain Guy2d4fd362011-12-13 22:00:19 -08001015 if (cacheTexture->mTextureId != lastTextureId) {
1016 caches.activeTexture(0);
1017 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
1018 lastTextureId = cacheTexture->mTextureId;
1019 }
Chet Haasee816bae2012-08-09 13:39:02 -07001020#if DEBUG_FONT_RENDERER
1021 ALOGD("glTextSubimage for cacheLine %d: xOff, yOff, width height = %d, %d, %d, %d", i,
1022 xOffset, yOffset, width, height);
1023#endif
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -07001024 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -07001025 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -07001026
1027 cl->mDirty = false;
1028 }
1029 }
1030
Romain Guy16c88082012-06-11 16:03:47 -07001031 caches.activeTexture(0);
Chet Haase7de0cb12011-12-05 16:35:38 -08001032 glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
Chet Haase2a47c142011-12-14 15:22:56 -08001033 if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) {
1034 const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST;
1035 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
1036 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
1037 mCurrentCacheTexture->mLinearFiltering = mLinearFiltering;
1038 }
Chet Haase7de0cb12011-12-05 16:35:38 -08001039 mLastCacheTexture = mCurrentCacheTexture;
1040
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -07001041 mUploadTexture = false;
1042}
1043
1044void FontRenderer::issueDrawCommand() {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -07001045 checkTextureUpdate();
1046
Romain Guy15bc6432011-12-13 13:11:32 -08001047 Caches& caches = Caches::getInstance();
Romain Guy2d4fd362011-12-13 22:00:19 -08001048 caches.bindIndicesBuffer(mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -08001049 if (!mDrawn) {
1050 float* buffer = mTextMeshPtr;
1051 int offset = 2;
1052
1053 bool force = caches.unbindMeshBuffer();
1054 caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
1055 caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
1056 buffer + offset);
1057 }
1058
Romain Guy694b5192010-07-21 21:33:20 -07001059 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
Romain Guy5b3b3522010-10-27 18:57:51 -07001060
1061 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -07001062}
1063
Romain Guy97771732012-02-28 18:17:02 -08001064void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
1065 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -08001066 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Chet Haase7de0cb12011-12-05 16:35:38 -08001067 if (texture != mCurrentCacheTexture) {
1068 if (mCurrentQuadIndex != 0) {
1069 // First, draw everything stored already which uses the previous texture
1070 issueDrawCommand();
1071 mCurrentQuadIndex = 0;
1072 }
1073 // Now use the new texture id
1074 mCurrentCacheTexture = texture;
1075 }
Romain Guy09147fb2010-07-22 13:08:20 -07001076
Romain Guy694b5192010-07-21 21:33:20 -07001077 const uint32_t vertsPerQuad = 4;
Romain Guyd71dd362011-12-12 19:03:35 -08001078 const uint32_t floatsPerVert = 4;
Romain Guy51769a62010-07-23 00:28:00 -07001079 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -07001080
Romain Guy694b5192010-07-21 21:33:20 -07001081 (*currentPos++) = x1;
1082 (*currentPos++) = y1;
Romain Guy694b5192010-07-21 21:33:20 -07001083 (*currentPos++) = u1;
1084 (*currentPos++) = v1;
1085
1086 (*currentPos++) = x2;
1087 (*currentPos++) = y2;
Romain Guy694b5192010-07-21 21:33:20 -07001088 (*currentPos++) = u2;
1089 (*currentPos++) = v2;
1090
1091 (*currentPos++) = x3;
1092 (*currentPos++) = y3;
Romain Guy694b5192010-07-21 21:33:20 -07001093 (*currentPos++) = u3;
1094 (*currentPos++) = v3;
1095
1096 (*currentPos++) = x4;
1097 (*currentPos++) = y4;
Romain Guy694b5192010-07-21 21:33:20 -07001098 (*currentPos++) = u4;
1099 (*currentPos++) = v4;
1100
1101 mCurrentQuadIndex++;
Romain Guy97771732012-02-28 18:17:02 -08001102}
1103
1104void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
1105 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
1106 float x4, float y4, float u4, float v4, CacheTexture* texture) {
1107
1108 if (mClip &&
1109 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
1110 return;
1111 }
1112
1113 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 -07001114
Romain Guy5b3b3522010-10-27 18:57:51 -07001115 if (mBounds) {
1116 mBounds->left = fmin(mBounds->left, x1);
1117 mBounds->top = fmin(mBounds->top, y3);
1118 mBounds->right = fmax(mBounds->right, x3);
1119 mBounds->bottom = fmax(mBounds->bottom, y1);
1120 }
1121
Romain Guy694b5192010-07-21 21:33:20 -07001122 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
1123 issueDrawCommand();
1124 mCurrentQuadIndex = 0;
1125 }
1126}
1127
Romain Guy97771732012-02-28 18:17:02 -08001128void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
1129 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
1130 float x4, float y4, float u4, float v4, CacheTexture* texture) {
1131
1132 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
1133
1134 if (mBounds) {
1135 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
1136 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
1137 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
1138 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
1139 }
1140
1141 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
1142 issueDrawCommand();
1143 mCurrentQuadIndex = 0;
1144 }
1145}
1146
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -07001147void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
Romain Guy325a0f92011-01-05 15:26:55 -08001148 int flags = 0;
1149 if (paint->isFakeBoldText()) {
1150 flags |= Font::kFakeBold;
1151 }
Romain Guy2577db12011-01-18 13:02:38 -08001152
1153 const float skewX = paint->getTextSkewX();
1154 uint32_t italicStyle = *(uint32_t*) &skewX;
Chet Haase8668f8a2011-03-02 13:51:36 -08001155 const float scaleXFloat = paint->getTextScaleX();
1156 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
Romain Guybd496bc2011-08-02 17:32:41 -07001157 SkPaint::Style style = paint->getStyle();
1158 const float strokeWidthFloat = paint->getStrokeWidth();
1159 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
1160 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
1161 scaleX, style, strokeWidth);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -07001162
Romain Guy694b5192010-07-21 21:33:20 -07001163}
Romain Guy7975fb62010-10-01 16:36:14 -07001164
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001165FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Raph Levien416a8472012-07-19 22:48:17 -07001166 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
Romain Guy1e45aae2010-08-13 19:39:53 -07001167 checkInit();
1168
1169 if (!mCurrentFont) {
1170 DropShadow image;
1171 image.width = 0;
1172 image.height = 0;
1173 image.image = NULL;
1174 image.penX = 0;
1175 image.penY = 0;
1176 return image;
1177 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001178
Romain Guy2d4fd362011-12-13 22:00:19 -08001179 mDrawn = false;
Romain Guyff98fa52011-11-28 09:35:09 -08001180 mClip = NULL;
1181 mBounds = NULL;
1182
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001183 Rect bounds;
Raph Levien416a8472012-07-19 22:48:17 -07001184 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
Romain Guyff98fa52011-11-28 09:35:09 -08001185
Romain Guy1e45aae2010-08-13 19:39:53 -07001186 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
1187 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001188 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guyff98fa52011-11-28 09:35:09 -08001189
Romain Guy1e45aae2010-08-13 19:39:53 -07001190 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001191 dataBuffer[i] = 0;
1192 }
Romain Guy1e45aae2010-08-13 19:39:53 -07001193
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001194 int penX = radius - bounds.left;
1195 int penY = radius - bounds.bottom;
1196
Romain Guy726aeba2011-06-01 14:52:00 -07001197 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
Raph Levien416a8472012-07-19 22:48:17 -07001198 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001199 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
1200
1201 DropShadow image;
1202 image.width = paddedWidth;
1203 image.height = paddedHeight;
1204 image.image = dataBuffer;
1205 image.penX = penX;
1206 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -08001207
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001208 return image;
1209}
Romain Guy694b5192010-07-21 21:33:20 -07001210
Romain Guy671d6cf2012-01-18 12:39:17 -08001211void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -07001212 checkInit();
1213
Romain Guy5b3b3522010-10-27 18:57:51 -07001214 mDrawn = false;
1215 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -07001216 mClip = clip;
Romain Guy671d6cf2012-01-18 12:39:17 -08001217}
Romain Guyff98fa52011-11-28 09:35:09 -08001218
Romain Guy671d6cf2012-01-18 12:39:17 -08001219void FontRenderer::finishRender() {
Romain Guy5b3b3522010-10-27 18:57:51 -07001220 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -08001221 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -07001222
1223 if (mCurrentQuadIndex != 0) {
1224 issueDrawCommand();
1225 mCurrentQuadIndex = 0;
1226 }
Romain Guy671d6cf2012-01-18 12:39:17 -08001227}
1228
Chet Haasee816bae2012-08-09 13:39:02 -07001229void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs) {
1230 int flags = 0;
1231 if (paint->isFakeBoldText()) {
1232 flags |= Font::kFakeBold;
1233 }
1234 const float skewX = paint->getTextSkewX();
1235 uint32_t italicStyle = *(uint32_t*) &skewX;
1236 const float scaleXFloat = paint->getTextScaleX();
1237 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
1238 SkPaint::Style style = paint->getStyle();
1239 const float strokeWidthFloat = paint->getStrokeWidth();
1240 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
1241 float fontSize = paint->getTextSize();
1242 Font* font = Font::create(this, SkTypeface::UniqueID(paint->getTypeface()),
1243 fontSize, flags, italicStyle, scaleX, style, strokeWidth);
1244
1245 font->precache(paint, text, numGlyphs);
1246}
1247
Romain Guy671d6cf2012-01-18 12:39:17 -08001248bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
1249 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
1250 if (!mCurrentFont) {
1251 ALOGE("No font set");
1252 return false;
1253 }
1254
1255 initRender(clip, bounds);
1256 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
1257 finishRender();
1258
1259 return mDrawn;
1260}
1261
1262bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
1263 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
1264 const float* positions, Rect* bounds) {
1265 if (!mCurrentFont) {
1266 ALOGE("No font set");
1267 return false;
1268 }
1269
1270 initRender(clip, bounds);
1271 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
1272 finishRender();
Romain Guy5b3b3522010-10-27 18:57:51 -07001273
1274 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -07001275}
1276
Romain Guy97771732012-02-28 18:17:02 -08001277bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
1278 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
1279 float hOffset, float vOffset, Rect* bounds) {
1280 if (!mCurrentFont) {
1281 ALOGE("No font set");
1282 return false;
1283 }
1284
1285 initRender(clip, bounds);
1286 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
1287 finishRender();
1288
1289 return mDrawn;
1290}
1291
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001292void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
1293 // Compute gaussian weights for the blur
1294 // e is the euler's number
1295 float e = 2.718281828459045f;
1296 float pi = 3.1415926535897932f;
1297 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
1298 // x is of the form [-radius .. 0 .. radius]
1299 // and sigma varies with radius.
1300 // Based on some experimental radius values and sigma's
1301 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001302 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001303 // The larger the radius gets, the more our gaussian blur
1304 // will resemble a box blur since with large sigma
1305 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -08001306 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001307
1308 // Now compute the coefficints
1309 // We will store some redundant values to save some math during
1310 // the blur calculations
1311 // precompute some values
1312 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
1313 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
1314
1315 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -08001316 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -07001317 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001318 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
1319 normalizeFactor += weights[r + radius];
1320 }
1321
1322 //Now we need to normalize the weights because all our coefficients need to add up to one
1323 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -08001324 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001325 weights[r + radius] *= normalizeFactor;
1326 }
1327}
1328
1329void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -07001330 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001331 float blurredPixel = 0.0f;
1332 float currentPixel = 0.0f;
1333
Romain Guy325a0f92011-01-05 15:26:55 -08001334 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001335
1336 const uint8_t* input = source + y * width;
1337 uint8_t* output = dest + y * width;
1338
Romain Guy325a0f92011-01-05 15:26:55 -08001339 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001340 blurredPixel = 0.0f;
1341 const float* gPtr = weights;
1342 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -08001343 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001344 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -08001345 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -07001346 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001347 blurredPixel += currentPixel * gPtr[0];
1348 gPtr++;
1349 i++;
1350 }
1351 } else {
Romain Guy325a0f92011-01-05 15:26:55 -08001352 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001353 // Stepping left and right away from the pixel
1354 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -08001355 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001356 validW = 0;
1357 }
Romain Guy325a0f92011-01-05 15:26:55 -08001358 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001359 validW = width - 1;
1360 }
1361
Romain Guy325a0f92011-01-05 15:26:55 -08001362 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001363 blurredPixel += currentPixel * gPtr[0];
1364 gPtr++;
1365 }
1366 }
1367 *output = (uint8_t)blurredPixel;
1368 output ++;
1369 }
1370 }
1371}
1372
1373void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -07001374 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001375 float blurredPixel = 0.0f;
1376 float currentPixel = 0.0f;
1377
Romain Guy325a0f92011-01-05 15:26:55 -08001378 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001379
1380 uint8_t* output = dest + y * width;
1381
Romain Guy325a0f92011-01-05 15:26:55 -08001382 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001383 blurredPixel = 0.0f;
1384 const float* gPtr = weights;
1385 const uint8_t* input = source + x;
1386 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -08001387 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001388 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -08001389 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001390 currentPixel = (float)(*i);
1391 blurredPixel += currentPixel * gPtr[0];
1392 gPtr++;
1393 i += width;
1394 }
1395 } else {
Romain Guy325a0f92011-01-05 15:26:55 -08001396 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001397 int validH = y + r;
1398 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -08001399 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001400 validH = 0;
1401 }
Romain Guy325a0f92011-01-05 15:26:55 -08001402 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001403 validH = height - 1;
1404 }
1405
1406 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -08001407 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001408 blurredPixel += currentPixel * gPtr[0];
1409 gPtr++;
1410 }
1411 }
Romain Guy325a0f92011-01-05 15:26:55 -08001412 *output = (uint8_t) blurredPixel;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001413 output ++;
1414 }
1415 }
1416}
1417
1418
1419void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
1420 float *gaussian = new float[2 * radius + 1];
1421 computeGaussianWeights(gaussian, radius);
Romain Guyd71dd362011-12-12 19:03:35 -08001422
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001423 uint8_t* scratch = new uint8_t[width * height];
Romain Guyd71dd362011-12-12 19:03:35 -08001424
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001425 horizontalBlur(gaussian, radius, image, scratch, width, height);
1426 verticalBlur(gaussian, radius, scratch, image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -08001427
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001428 delete[] gaussian;
1429 delete[] scratch;
1430}
1431
Romain Guy694b5192010-07-21 21:33:20 -07001432}; // namespace uirenderer
1433}; // namespace android