blob: 95ccdfce36f1622eb01cd03d805334d5c96e3138 [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
Chet Haaseeb32a492012-08-31 13:54:03 -070037#define DEFAULT_TEXT_SMALL_CACHE_WIDTH 1024
38#define DEFAULT_TEXT_SMALL_CACHE_HEIGHT 256
39#define DEFAULT_TEXT_LARGE_CACHE_WIDTH 2048
40#define DEFAULT_TEXT_LARGE_CACHE_HEIGHT 512
Chet Haasee816bae2012-08-09 13:39:02 -070041#define CACHE_BLOCK_ROUNDING_SIZE 4
Chet Haase7de0cb12011-12-05 16:35:38 -080042
Romain Guy97771732012-02-28 18:17:02 -080043#define AUTO_KERN(prev, next) (((next) - (prev) + 32) >> 6 << 16)
44
Chet Haase7de0cb12011-12-05 16:35:38 -080045///////////////////////////////////////////////////////////////////////////////
Chet Haasee816bae2012-08-09 13:39:02 -070046// CacheBlock
47///////////////////////////////////////////////////////////////////////////////
48
49/**
50 * Insert new block into existing linked list of blocks. Blocks are sorted in increasing-width
51 * order, except for the final block (the remainder space at the right, since we fill from the
52 * left).
53 */
54CacheBlock* CacheBlock::insertBlock(CacheBlock* head, CacheBlock *newBlock) {
55#if DEBUG_FONT_RENDERER
56 ALOGD("insertBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
57 newBlock, newBlock->mX, newBlock->mY,
58 newBlock->mWidth, newBlock->mHeight);
59#endif
60 CacheBlock *currBlock = head;
61 CacheBlock *prevBlock = NULL;
62 while (currBlock && currBlock->mY != TEXTURE_BORDER_SIZE) {
63 if (newBlock->mWidth < currBlock->mWidth) {
64 newBlock->mNext = currBlock;
65 newBlock->mPrev = prevBlock;
66 currBlock->mPrev = newBlock;
67 if (prevBlock) {
68 prevBlock->mNext = newBlock;
69 return head;
70 } else {
71 return newBlock;
72 }
73 }
74 prevBlock = currBlock;
75 currBlock = currBlock->mNext;
76 }
77 // new block larger than all others - insert at end (but before the remainder space, if there)
78 newBlock->mNext = currBlock;
79 newBlock->mPrev = prevBlock;
80 if (currBlock) {
81 currBlock->mPrev = newBlock;
82 }
83 if (prevBlock) {
84 prevBlock->mNext = newBlock;
85 return head;
86 } else {
87 return newBlock;
88 }
89}
90
91CacheBlock* CacheBlock::removeBlock(CacheBlock* head, CacheBlock *blockToRemove) {
92#if DEBUG_FONT_RENDERER
93 ALOGD("removeBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
94 blockToRemove, blockToRemove->mX, blockToRemove->mY,
95 blockToRemove->mWidth, blockToRemove->mHeight);
96#endif
97 CacheBlock* newHead = head;
98 CacheBlock* nextBlock = blockToRemove->mNext;
99 CacheBlock* prevBlock = blockToRemove->mPrev;
100 if (prevBlock) {
101 prevBlock->mNext = nextBlock;
102 } else {
103 newHead = nextBlock;
104 }
105 if (nextBlock) {
106 nextBlock->mPrev = prevBlock;
107 }
108 delete blockToRemove;
109 return newHead;
110}
111
112///////////////////////////////////////////////////////////////////////////////
Chet Haase378e9192012-08-15 15:54:54 -0700113// CacheTexture
Chet Haase7de0cb12011-12-05 16:35:38 -0800114///////////////////////////////////////////////////////////////////////////////
115
Chet Haase378e9192012-08-15 15:54:54 -0700116bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
117 if (glyph.fHeight + TEXTURE_BORDER_SIZE > mHeight) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800118 return false;
119 }
120
Chet Haasee816bae2012-08-09 13:39:02 -0700121 uint16_t glyphW = glyph.fWidth + TEXTURE_BORDER_SIZE;
122 uint16_t glyphH = glyph.fHeight + TEXTURE_BORDER_SIZE;
123 // roundedUpW equals glyphW to the next multiple of CACHE_BLOCK_ROUNDING_SIZE.
124 // This columns for glyphs that are close but not necessarily exactly the same size. It trades
125 // off the loss of a few pixels for some glyphs against the ability to store more glyphs
126 // of varying sizes in one block.
127 uint16_t roundedUpW =
128 (glyphW + CACHE_BLOCK_ROUNDING_SIZE - 1) & -CACHE_BLOCK_ROUNDING_SIZE;
129 CacheBlock *cacheBlock = mCacheBlocks;
130 while (cacheBlock) {
131 // Store glyph in this block iff: it fits the block's remaining space and:
132 // it's the remainder space (mY == 0) or there's only enough height for this one glyph
133 // or it's within ROUNDING_SIZE of the block width
134 if (roundedUpW <= cacheBlock->mWidth && glyphH <= cacheBlock->mHeight &&
135 (cacheBlock->mY == TEXTURE_BORDER_SIZE ||
136 (cacheBlock->mWidth - roundedUpW < CACHE_BLOCK_ROUNDING_SIZE))) {
137 if (cacheBlock->mHeight - glyphH < glyphH) {
138 // Only enough space for this glyph - don't bother rounding up the width
139 roundedUpW = glyphW;
140 }
141 *retOriginX = cacheBlock->mX;
Chet Haase378e9192012-08-15 15:54:54 -0700142 *retOriginY = cacheBlock->mY;
Chet Haasee816bae2012-08-09 13:39:02 -0700143 // If this is the remainder space, create a new cache block for this column. Otherwise,
144 // adjust the info about this column.
145 if (cacheBlock->mY == TEXTURE_BORDER_SIZE) {
146 uint16_t oldX = cacheBlock->mX;
147 // Adjust remainder space dimensions
148 cacheBlock->mWidth -= roundedUpW;
149 cacheBlock->mX += roundedUpW;
Chet Haase378e9192012-08-15 15:54:54 -0700150 if (mHeight - glyphH >= glyphH) {
Chet Haasee816bae2012-08-09 13:39:02 -0700151 // There's enough height left over to create a new CacheBlock
Chet Haase5a3ec712012-08-17 15:44:44 -0700152 CacheBlock *newBlock = new CacheBlock(oldX, glyphH + TEXTURE_BORDER_SIZE,
153 roundedUpW, mHeight - glyphH - TEXTURE_BORDER_SIZE);
Chet Haasee816bae2012-08-09 13:39:02 -0700154#if DEBUG_FONT_RENDERER
155 ALOGD("fitBitmap: Created new block: this, x, y, w, h = %p, %d, %d, %d, %d",
156 newBlock, newBlock->mX, newBlock->mY,
157 newBlock->mWidth, newBlock->mHeight);
158#endif
159 mCacheBlocks = CacheBlock::insertBlock(mCacheBlocks, newBlock);
160 }
161 } else {
162 // Insert into current column and adjust column dimensions
163 cacheBlock->mY += glyphH;
164 cacheBlock->mHeight -= glyphH;
165#if DEBUG_FONT_RENDERER
166 ALOGD("fitBitmap: Added to existing block: this, x, y, w, h = %p, %d, %d, %d, %d",
167 cacheBlock, cacheBlock->mX, cacheBlock->mY,
168 cacheBlock->mWidth, cacheBlock->mHeight);
169#endif
170 }
171 if (cacheBlock->mHeight < fmin(glyphH, glyphW)) {
172 // If remaining space in this block is too small to be useful, remove it
173 mCacheBlocks = CacheBlock::removeBlock(mCacheBlocks, cacheBlock);
174 }
175 mDirty = true;
176#if DEBUG_FONT_RENDERER
177 ALOGD("fitBitmap: current block list:");
178 mCacheBlocks->output();
179#endif
180 ++mNumGlyphs;
181 return true;
182 }
183 cacheBlock = cacheBlock->mNext;
Chet Haase7de0cb12011-12-05 16:35:38 -0800184 }
Chet Haasee816bae2012-08-09 13:39:02 -0700185#if DEBUG_FONT_RENDERER
186 ALOGD("fitBitmap: returning false for glyph of size %d, %d", glyphW, glyphH);
187#endif
Chet Haase7de0cb12011-12-05 16:35:38 -0800188 return false;
189}
Chet Haase44984ea2011-05-19 13:50:47 -0700190
Romain Guy51769a62010-07-23 00:28:00 -0700191///////////////////////////////////////////////////////////////////////////////
Romain Guy694b5192010-07-21 21:33:20 -0700192// Font
193///////////////////////////////////////////////////////////////////////////////
194
Romain Guy2577db12011-01-18 13:02:38 -0800195Font::Font(FontRenderer* state, uint32_t fontId, float fontSize,
Romain Guybd496bc2011-08-02 17:32:41 -0700196 int flags, uint32_t italicStyle, uint32_t scaleX,
197 SkPaint::Style style, uint32_t strokeWidth) :
Romain Guy2577db12011-01-18 13:02:38 -0800198 mState(state), mFontId(fontId), mFontSize(fontSize),
Romain Guybd496bc2011-08-02 17:32:41 -0700199 mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX),
200 mStyle(style), mStrokeWidth(mStrokeWidth) {
Romain Guy694b5192010-07-21 21:33:20 -0700201}
202
203
204Font::~Font() {
205 for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
206 if (mState->mActiveFonts[ct] == this) {
207 mState->mActiveFonts.removeAt(ct);
208 break;
209 }
210 }
211
212 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
Romain Guy726aeba2011-06-01 14:52:00 -0700213 delete mCachedGlyphs.valueAt(i);
Romain Guy694b5192010-07-21 21:33:20 -0700214 }
215}
216
Chet Haase378e9192012-08-15 15:54:54 -0700217void Font::invalidateTextureCache(CacheTexture *cacheTexture) {
Romain Guy694b5192010-07-21 21:33:20 -0700218 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
Chet Haase9a824562011-12-16 15:44:59 -0800219 CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i);
Chet Haase378e9192012-08-15 15:54:54 -0700220 if (cacheTexture == NULL || cachedGlyph->mCacheTexture == cacheTexture) {
Chet Haase9a824562011-12-16 15:44:59 -0800221 cachedGlyph->mIsValid = false;
222 }
Romain Guy694b5192010-07-21 21:33:20 -0700223 }
224}
225
Romain Guy671d6cf2012-01-18 12:39:17 -0800226void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
227 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700228 int nPenX = x + glyph->mBitmapLeft;
229 int nPenY = y + glyph->mBitmapTop;
230
231 int width = (int) glyph->mBitmapWidth;
232 int height = (int) glyph->mBitmapHeight;
233
Romain Guy61c8c9c2010-08-09 20:48:09 -0700234 if (bounds->bottom > nPenY) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700235 bounds->bottom = nPenY;
236 }
Romain Guy61c8c9c2010-08-09 20:48:09 -0700237 if (bounds->left > nPenX) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700238 bounds->left = nPenX;
239 }
Romain Guy61c8c9c2010-08-09 20:48:09 -0700240 if (bounds->right < nPenX + width) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700241 bounds->right = nPenX + width;
242 }
Romain Guy61c8c9c2010-08-09 20:48:09 -0700243 if (bounds->top < nPenY + height) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700244 bounds->top = nPenY + height;
245 }
246}
247
Romain Guy671d6cf2012-01-18 12:39:17 -0800248void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
249 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
Romain Guy694b5192010-07-21 21:33:20 -0700250 int nPenX = x + glyph->mBitmapLeft;
251 int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
252
Romain Guy51769a62010-07-23 00:28:00 -0700253 float u1 = glyph->mBitmapMinU;
254 float u2 = glyph->mBitmapMaxU;
255 float v1 = glyph->mBitmapMinV;
256 float v2 = glyph->mBitmapMaxV;
257
258 int width = (int) glyph->mBitmapWidth;
259 int height = (int) glyph->mBitmapHeight;
260
Romain Guyd71dd362011-12-12 19:03:35 -0800261 mState->appendMeshQuad(nPenX, nPenY, u1, v2,
262 nPenX + width, nPenY, u2, v2,
263 nPenX + width, nPenY - height, u2, v1,
Chet Haase378e9192012-08-15 15:54:54 -0700264 nPenX, nPenY - height, u1, v1, glyph->mCacheTexture);
Romain Guy694b5192010-07-21 21:33:20 -0700265}
266
Romain Guy671d6cf2012-01-18 12:39:17 -0800267void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
268 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700269 int nPenX = x + glyph->mBitmapLeft;
270 int nPenY = y + glyph->mBitmapTop;
271
272 uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
273 uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
274
Chet Haase378e9192012-08-15 15:54:54 -0700275 CacheTexture *cacheTexture = glyph->mCacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800276 uint32_t cacheWidth = cacheTexture->mWidth;
277 const uint8_t* cacheBuffer = cacheTexture->mTexture;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700278
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700279 uint32_t cacheX = 0, cacheY = 0;
280 int32_t bX = 0, bY = 0;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700281 for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
282 for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
Romain Guyb6294902012-02-02 15:13:18 -0800283#if DEBUG_FONT_RENDERER
Romain Guyb45c0c92010-08-26 20:35:23 -0700284 if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
Steve Block3762c312012-01-06 19:20:56 +0000285 ALOGE("Skipping invalid index");
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700286 continue;
287 }
Romain Guyb6294902012-02-02 15:13:18 -0800288#endif
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700289 uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
290 bitmap[bY * bitmapW + bX] = tempCol;
291 }
292 }
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700293}
294
Romain Guy97771732012-02-28 18:17:02 -0800295void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
296 SkPathMeasure& measure, SkPoint* position, SkVector* tangent) {
297 const float halfWidth = glyph->mBitmapWidth * 0.5f;
298 const float height = glyph->mBitmapHeight;
299
Romain Guy97771732012-02-28 18:17:02 -0800300 vOffset += glyph->mBitmapTop + height;
301
Romain Guy97771732012-02-28 18:17:02 -0800302 SkPoint destination[4];
Romain Guydd7c8e4c2012-03-01 12:08:38 -0800303 measure.getPosTan(x + hOffset + glyph->mBitmapLeft + halfWidth, position, tangent);
Romain Guy97771732012-02-28 18:17:02 -0800304
305 // Move along the tangent and offset by the normal
306 destination[0].set(-tangent->fX * halfWidth - tangent->fY * vOffset,
307 -tangent->fY * halfWidth + tangent->fX * vOffset);
308 destination[1].set(tangent->fX * halfWidth - tangent->fY * vOffset,
309 tangent->fY * halfWidth + tangent->fX * vOffset);
310 destination[2].set(destination[1].fX + tangent->fY * height,
311 destination[1].fY - tangent->fX * height);
312 destination[3].set(destination[0].fX + tangent->fY * height,
313 destination[0].fY - tangent->fX * height);
314
Romain Guydd7c8e4c2012-03-01 12:08:38 -0800315 const float u1 = glyph->mBitmapMinU;
316 const float u2 = glyph->mBitmapMaxU;
317 const float v1 = glyph->mBitmapMinV;
318 const float v2 = glyph->mBitmapMaxV;
319
Romain Guy97771732012-02-28 18:17:02 -0800320 mState->appendRotatedMeshQuad(
321 position->fX + destination[0].fX,
322 position->fY + destination[0].fY, u1, v2,
323 position->fX + destination[1].fX,
324 position->fY + destination[1].fY, u2, v2,
325 position->fX + destination[2].fX,
326 position->fY + destination[2].fY, u2, v1,
327 position->fX + destination[3].fX,
328 position->fY + destination[3].fY, u1, v1,
Chet Haase378e9192012-08-15 15:54:54 -0700329 glyph->mCacheTexture);
Romain Guy97771732012-02-28 18:17:02 -0800330}
331
Chet Haasef942cf12012-08-30 09:06:46 -0700332CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700333 CachedGlyphInfo* cachedGlyph = NULL;
Romain Guy726aeba2011-06-01 14:52:00 -0700334 ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
Romain Guy1e45aae2010-08-13 19:39:53 -0700335 if (index >= 0) {
336 cachedGlyph = mCachedGlyphs.valueAt(index);
337 } else {
Chet Haasef942cf12012-08-30 09:06:46 -0700338 cachedGlyph = cacheGlyph(paint, textUnit, precaching);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700339 }
340
341 // Is the glyph still in texture cache?
342 if (!cachedGlyph->mIsValid) {
Romain Guy726aeba2011-06-01 14:52:00 -0700343 const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
Chet Haasef942cf12012-08-30 09:06:46 -0700344 updateGlyphCache(paint, skiaGlyph, cachedGlyph, precaching);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700345 }
346
347 return cachedGlyph;
348}
349
Romain Guy726aeba2011-06-01 14:52:00 -0700350void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700351 int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
352 if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
Romain Guy726aeba2011-06-01 14:52:00 -0700353 render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
Romain Guy671d6cf2012-01-18 12:39:17 -0800354 bitmapW, bitmapH, NULL, NULL);
Romain Guy61c8c9c2010-08-09 20:48:09 -0700355 } else {
Romain Guy726aeba2011-06-01 14:52:00 -0700356 render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
Romain Guy671d6cf2012-01-18 12:39:17 -0800357 0, 0, NULL, NULL);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700358 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700359}
360
Romain Guy671d6cf2012-01-18 12:39:17 -0800361void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
362 int numGlyphs, int x, int y, const float* positions) {
363 render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
364 0, 0, NULL, positions);
365}
366
Romain Guy97771732012-02-28 18:17:02 -0800367void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
368 int numGlyphs, SkPath* path, float hOffset, float vOffset) {
369 if (numGlyphs == 0 || text == NULL || len == 0) {
370 return;
371 }
372
373 text += start;
374
375 int glyphsCount = 0;
376 SkFixed prevRsbDelta = 0;
377
378 float penX = 0.0f;
379
380 SkPoint position;
381 SkVector tangent;
382
383 SkPathMeasure measure(*path, false);
384 float pathLength = SkScalarToFloat(measure.getLength());
385
386 if (paint->getTextAlign() != SkPaint::kLeft_Align) {
387 float textWidth = SkScalarToFloat(paint->measureText(text, len));
388 float pathOffset = pathLength;
389 if (paint->getTextAlign() == SkPaint::kCenter_Align) {
390 textWidth *= 0.5f;
391 pathOffset *= 0.5f;
392 }
393 penX += pathOffset - textWidth;
394 }
395
Romain Guydd7c8e4c2012-03-01 12:08:38 -0800396 while (glyphsCount < numGlyphs && penX < pathLength) {
Romain Guy97771732012-02-28 18:17:02 -0800397 glyph_t glyph = GET_GLYPH(text);
398
399 if (IS_END_OF_STRING(glyph)) {
400 break;
401 }
402
403 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
404 penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
405 prevRsbDelta = cachedGlyph->mRsbDelta;
406
407 if (cachedGlyph->mIsValid) {
Romain Guydd7c8e4c2012-03-01 12:08:38 -0800408 drawCachedGlyph(cachedGlyph, penX, hOffset, vOffset, measure, &position, &tangent);
Romain Guy97771732012-02-28 18:17:02 -0800409 }
410
411 penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
412
413 glyphsCount++;
414 }
415}
416
Romain Guy726aeba2011-06-01 14:52:00 -0700417void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Raph Levien416a8472012-07-19 22:48:17 -0700418 int numGlyphs, Rect *bounds, const float* positions) {
Romain Guy61c8c9c2010-08-09 20:48:09 -0700419 if (bounds == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000420 ALOGE("No return rectangle provided to measure text");
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700421 return;
422 }
423 bounds->set(1e6, -1e6, -1e6, 1e6);
Raph Levien416a8472012-07-19 22:48:17 -0700424 render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, positions);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700425}
426
Chet Haasee816bae2012-08-09 13:39:02 -0700427void Font::precache(SkPaint* paint, const char* text, int numGlyphs) {
428
429 if (numGlyphs == 0 || text == NULL) {
430 return;
431 }
432 int glyphsCount = 0;
433
434 while (glyphsCount < numGlyphs) {
435 glyph_t glyph = GET_GLYPH(text);
436
437 // Reached the end of the string
438 if (IS_END_OF_STRING(glyph)) {
439 break;
440 }
441
Chet Haasef942cf12012-08-30 09:06:46 -0700442 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph, true);
Chet Haasee816bae2012-08-09 13:39:02 -0700443
444 glyphsCount++;
445 }
446}
447
Romain Guy726aeba2011-06-01 14:52:00 -0700448void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700449 int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
Romain Guy97771732012-02-28 18:17:02 -0800450 uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) {
Romain Guy694b5192010-07-21 21:33:20 -0700451 if (numGlyphs == 0 || text == NULL || len == 0) {
452 return;
453 }
454
Romain Guy671d6cf2012-01-18 12:39:17 -0800455 static RenderGlyph gRenderGlyph[] = {
456 &android::uirenderer::Font::drawCachedGlyph,
457 &android::uirenderer::Font::drawCachedGlyphBitmap,
458 &android::uirenderer::Font::measureCachedGlyph
459 };
460 RenderGlyph render = gRenderGlyph[mode];
Romain Guy694b5192010-07-21 21:33:20 -0700461
Romain Guy97771732012-02-28 18:17:02 -0800462 text += start;
463 int glyphsCount = 0;
464
Romain Guyb6294902012-02-02 15:13:18 -0800465 if (CC_LIKELY(positions == NULL)) {
Romain Guy671d6cf2012-01-18 12:39:17 -0800466 SkFixed prevRsbDelta = 0;
Romain Guy694b5192010-07-21 21:33:20 -0700467
Romain Guy97771732012-02-28 18:17:02 -0800468 float penX = x + 0.5f;
Romain Guy671d6cf2012-01-18 12:39:17 -0800469 int penY = y;
Romain Guy694b5192010-07-21 21:33:20 -0700470
Romain Guy671d6cf2012-01-18 12:39:17 -0800471 while (glyphsCount < numGlyphs) {
472 glyph_t glyph = GET_GLYPH(text);
473
474 // Reached the end of the string
475 if (IS_END_OF_STRING(glyph)) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700476 break;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700477 }
Romain Guy671d6cf2012-01-18 12:39:17 -0800478
479 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
Romain Guy97771732012-02-28 18:17:02 -0800480 penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
Romain Guy671d6cf2012-01-18 12:39:17 -0800481 prevRsbDelta = cachedGlyph->mRsbDelta;
482
483 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
484 if (cachedGlyph->mIsValid) {
485 (*this.*render)(cachedGlyph, (int) floorf(penX), penY,
486 bitmap, bitmapW, bitmapH, bounds, positions);
487 }
488
489 penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
490
491 glyphsCount++;
Romain Guy694b5192010-07-21 21:33:20 -0700492 }
Romain Guy671d6cf2012-01-18 12:39:17 -0800493 } else {
494 const SkPaint::Align align = paint->getTextAlign();
Romain Guy694b5192010-07-21 21:33:20 -0700495
Romain Guy671d6cf2012-01-18 12:39:17 -0800496 // This is for renderPosText()
497 while (glyphsCount < numGlyphs) {
498 glyph_t glyph = GET_GLYPH(text);
Romain Guy694b5192010-07-21 21:33:20 -0700499
Romain Guy671d6cf2012-01-18 12:39:17 -0800500 // Reached the end of the string
501 if (IS_END_OF_STRING(glyph)) {
502 break;
503 }
504
505 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
506
507 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
508 if (cachedGlyph->mIsValid) {
509 int penX = x + positions[(glyphsCount << 1)];
510 int penY = y + positions[(glyphsCount << 1) + 1];
511
512 switch (align) {
513 case SkPaint::kRight_Align:
514 penX -= SkFixedToFloat(cachedGlyph->mAdvanceX);
515 penY -= SkFixedToFloat(cachedGlyph->mAdvanceY);
516 break;
517 case SkPaint::kCenter_Align:
518 penX -= SkFixedToFloat(cachedGlyph->mAdvanceX >> 1);
519 penY -= SkFixedToFloat(cachedGlyph->mAdvanceY >> 1);
520 default:
521 break;
522 }
523
524 (*this.*render)(cachedGlyph, penX, penY,
525 bitmap, bitmapW, bitmapH, bounds, positions);
526 }
527
528 glyphsCount++;
Romain Guy694b5192010-07-21 21:33:20 -0700529 }
530 }
531}
532
Chet Haasef942cf12012-08-30 09:06:46 -0700533void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph,
534 bool precaching) {
Romain Guy694b5192010-07-21 21:33:20 -0700535 glyph->mAdvanceX = skiaGlyph.fAdvanceX;
536 glyph->mAdvanceY = skiaGlyph.fAdvanceY;
537 glyph->mBitmapLeft = skiaGlyph.fLeft;
538 glyph->mBitmapTop = skiaGlyph.fTop;
Romain Guy2bffd262010-09-12 17:40:02 -0700539 glyph->mLsbDelta = skiaGlyph.fLsbDelta;
540 glyph->mRsbDelta = skiaGlyph.fRsbDelta;
Romain Guy694b5192010-07-21 21:33:20 -0700541
542 uint32_t startX = 0;
543 uint32_t startY = 0;
544
Romain Guy694b5192010-07-21 21:33:20 -0700545 // Get the bitmap for the glyph
546 paint->findImage(skiaGlyph);
Chet Haasef942cf12012-08-30 09:06:46 -0700547 mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY, precaching);
Romain Guy694b5192010-07-21 21:33:20 -0700548
549 if (!glyph->mIsValid) {
550 return;
551 }
552
553 uint32_t endX = startX + skiaGlyph.fWidth;
554 uint32_t endY = startY + skiaGlyph.fHeight;
555
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700556 glyph->mStartX = startX;
557 glyph->mStartY = startY;
Romain Guy694b5192010-07-21 21:33:20 -0700558 glyph->mBitmapWidth = skiaGlyph.fWidth;
559 glyph->mBitmapHeight = skiaGlyph.fHeight;
560
Chet Haase378e9192012-08-15 15:54:54 -0700561 uint32_t cacheWidth = glyph->mCacheTexture->mWidth;
562 uint32_t cacheHeight = glyph->mCacheTexture->mHeight;
Romain Guy694b5192010-07-21 21:33:20 -0700563
Romain Guy33fa1f72012-08-07 19:09:57 -0700564 glyph->mBitmapMinU = startX / (float) cacheWidth;
565 glyph->mBitmapMinV = startY / (float) cacheHeight;
566 glyph->mBitmapMaxU = endX / (float) cacheWidth;
567 glyph->mBitmapMaxV = endY / (float) cacheHeight;
Romain Guy694b5192010-07-21 21:33:20 -0700568
Romain Guy51769a62010-07-23 00:28:00 -0700569 mState->mUploadTexture = true;
Romain Guy694b5192010-07-21 21:33:20 -0700570}
571
Chet Haasef942cf12012-08-30 09:06:46 -0700572CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching) {
Romain Guy51769a62010-07-23 00:28:00 -0700573 CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
Romain Guy694b5192010-07-21 21:33:20 -0700574 mCachedGlyphs.add(glyph, newGlyph);
575
Romain Guy726aeba2011-06-01 14:52:00 -0700576 const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
Romain Guy694b5192010-07-21 21:33:20 -0700577 newGlyph->mGlyphIndex = skiaGlyph.fID;
578 newGlyph->mIsValid = false;
579
Chet Haasef942cf12012-08-30 09:06:46 -0700580 updateGlyphCache(paint, skiaGlyph, newGlyph, precaching);
Romain Guy694b5192010-07-21 21:33:20 -0700581
582 return newGlyph;
583}
584
Romain Guy2577db12011-01-18 13:02:38 -0800585Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
Romain Guybd496bc2011-08-02 17:32:41 -0700586 int flags, uint32_t italicStyle, uint32_t scaleX,
587 SkPaint::Style style, uint32_t strokeWidth) {
Romain Guy694b5192010-07-21 21:33:20 -0700588 Vector<Font*> &activeFonts = state->mActiveFonts;
589
590 for (uint32_t i = 0; i < activeFonts.size(); i++) {
Romain Guy51769a62010-07-23 00:28:00 -0700591 Font* font = activeFonts[i];
Romain Guy2577db12011-01-18 13:02:38 -0800592 if (font->mFontId == fontId && font->mFontSize == fontSize &&
Chet Haase8668f8a2011-03-02 13:51:36 -0800593 font->mFlags == flags && font->mItalicStyle == italicStyle &&
Romain Guybd496bc2011-08-02 17:32:41 -0700594 font->mScaleX == scaleX && font->mStyle == style &&
595 (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
Romain Guy51769a62010-07-23 00:28:00 -0700596 return font;
Romain Guy694b5192010-07-21 21:33:20 -0700597 }
598 }
599
Romain Guybd496bc2011-08-02 17:32:41 -0700600 Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
601 scaleX, style, strokeWidth);
Romain Guy694b5192010-07-21 21:33:20 -0700602 activeFonts.push(newFont);
603 return newFont;
604}
605
606///////////////////////////////////////////////////////////////////////////////
607// FontRenderer
608///////////////////////////////////////////////////////////////////////////////
609
Romain Guy514fb182011-01-19 14:38:29 -0800610static bool sLogFontRendererCreate = true;
611
Romain Guy694b5192010-07-21 21:33:20 -0700612FontRenderer::FontRenderer() {
Romain Guyc9855a52011-01-21 21:14:15 -0800613 if (sLogFontRendererCreate) {
614 INIT_LOGD("Creating FontRenderer");
615 }
Romain Guy51769a62010-07-23 00:28:00 -0700616
Romain Guyb45c0c92010-08-26 20:35:23 -0700617 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700618 mInitialized = false;
619 mMaxNumberOfQuads = 1024;
620 mCurrentQuadIndex = 0;
621
Romain Guy9cccc2b92010-08-07 23:46:15 -0700622 mTextMeshPtr = NULL;
Chet Haase7de0cb12011-12-05 16:35:38 -0800623 mCurrentCacheTexture = NULL;
624 mLastCacheTexture = NULL;
Romain Guy9cccc2b92010-08-07 23:46:15 -0700625
Chet Haase2a47c142011-12-14 15:22:56 -0800626 mLinearFiltering = false;
627
Romain Guy694b5192010-07-21 21:33:20 -0700628 mIndexBufferID = 0;
629
Chet Haaseeb32a492012-08-31 13:54:03 -0700630 mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH;
631 mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT;
632 mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH;
633 mLargeCacheHeight = DEFAULT_TEXT_LARGE_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -0700634
635 char property[PROPERTY_VALUE_MAX];
Chet Haaseeb32a492012-08-31 13:54:03 -0700636 if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, NULL) > 0) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800637 mSmallCacheWidth = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -0700638 }
Chet Haaseeb32a492012-08-31 13:54:03 -0700639 if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, NULL) > 0) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800640 mSmallCacheHeight = atoi(property);
Chet Haaseeb32a492012-08-31 13:54:03 -0700641 }
642 if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, NULL) > 0) {
643 mLargeCacheWidth = atoi(property);
644 }
645 if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, NULL) > 0) {
646 mLargeCacheHeight = atoi(property);
647 }
648 GLint maxTextureSize = Caches::getInstance().maxTextureSize;
649 mSmallCacheWidth = (mSmallCacheWidth > maxTextureSize) ? maxTextureSize : mSmallCacheWidth;
650 mSmallCacheHeight = (mSmallCacheHeight > maxTextureSize) ? maxTextureSize : mSmallCacheHeight;
651 mLargeCacheWidth = (mLargeCacheWidth > maxTextureSize) ? maxTextureSize : mLargeCacheWidth;
652 mLargeCacheHeight = (mLargeCacheHeight > maxTextureSize) ? maxTextureSize : mLargeCacheHeight;
653 if (sLogFontRendererCreate) {
654 INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
655 mSmallCacheWidth, mSmallCacheHeight,
656 mLargeCacheWidth, mLargeCacheHeight >> 1,
657 mLargeCacheWidth, mLargeCacheHeight >> 1,
658 mLargeCacheWidth, mLargeCacheHeight);
Romain Guy51769a62010-07-23 00:28:00 -0700659 }
Romain Guy514fb182011-01-19 14:38:29 -0800660
661 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -0700662}
663
664FontRenderer::~FontRenderer() {
Chet Haase378e9192012-08-15 15:54:54 -0700665 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
666 delete mCacheTextures[i];
Romain Guy694b5192010-07-21 21:33:20 -0700667 }
Chet Haase378e9192012-08-15 15:54:54 -0700668 mCacheTextures.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700669
Romain Guy9cccc2b92010-08-07 23:46:15 -0700670 if (mInitialized) {
Romain Guya9dd8202012-03-26 14:52:00 -0700671 // Unbinding the buffer shouldn't be necessary but it crashes with some drivers
672 Caches::getInstance().unbindIndicesBuffer();
Romain Guyb0317982012-03-21 11:52:52 -0700673 glDeleteBuffers(1, &mIndexBufferID);
674
Romain Guy9cccc2b92010-08-07 23:46:15 -0700675 delete[] mTextMeshPtr;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700676 }
Romain Guy694b5192010-07-21 21:33:20 -0700677
678 Vector<Font*> fontsToDereference = mActiveFonts;
679 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
680 delete fontsToDereference[i];
681 }
682}
683
684void FontRenderer::flushAllAndInvalidate() {
685 if (mCurrentQuadIndex != 0) {
686 issueDrawCommand();
687 mCurrentQuadIndex = 0;
688 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700689
Romain Guy694b5192010-07-21 21:33:20 -0700690 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
691 mActiveFonts[i]->invalidateTextureCache();
692 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700693
Chet Haase378e9192012-08-15 15:54:54 -0700694 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
695 mCacheTextures[i]->init();
Romain Guy694b5192010-07-21 21:33:20 -0700696 }
Chet Haasee816bae2012-08-09 13:39:02 -0700697
Chet Haase378e9192012-08-15 15:54:54 -0700698 #if DEBUG_FONT_RENDERER
699 uint16_t totalGlyphs = 0;
700 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
701 totalGlyphs += mCacheTextures[i]->mNumGlyphs;
702 // Erase caches, just as a debugging facility
703 if (mCacheTextures[i]->mTexture) {
704 memset(mCacheTextures[i]->mTexture, 0,
705 mCacheTextures[i]->mWidth * mCacheTextures[i]->mHeight);
706 }
Chet Haasee816bae2012-08-09 13:39:02 -0700707 }
708 ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
709#endif
Romain Guy694b5192010-07-21 21:33:20 -0700710}
711
Chet Haase9a824562011-12-16 15:44:59 -0800712void FontRenderer::deallocateTextureMemory(CacheTexture *cacheTexture) {
713 if (cacheTexture && cacheTexture->mTexture) {
714 glDeleteTextures(1, &cacheTexture->mTextureId);
Romain Guy9d9758a2012-05-14 15:19:58 -0700715 delete[] cacheTexture->mTexture;
Chet Haase9a824562011-12-16 15:44:59 -0800716 cacheTexture->mTexture = NULL;
Romain Guy99a6ddd2012-05-14 15:32:18 -0700717 cacheTexture->mTextureId = 0;
Chet Haase9a824562011-12-16 15:44:59 -0800718 }
719}
720
721void FontRenderer::flushLargeCaches() {
Chet Haase378e9192012-08-15 15:54:54 -0700722 // Start from 1; don't deallocate smallest/default texture
723 for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
724 CacheTexture* cacheTexture = mCacheTextures[i];
725 if (cacheTexture->mTexture != NULL) {
726 cacheTexture->init();
727 for (uint32_t j = 0; j < mActiveFonts.size(); j++) {
728 mActiveFonts[j]->invalidateTextureCache(cacheTexture);
Chet Haasee816bae2012-08-09 13:39:02 -0700729 }
Chet Haase378e9192012-08-15 15:54:54 -0700730 deallocateTextureMemory(cacheTexture);
Chet Haase9a824562011-12-16 15:44:59 -0800731 }
732 }
Chet Haase9a824562011-12-16 15:44:59 -0800733}
734
Romain Guy9d9758a2012-05-14 15:19:58 -0700735void FontRenderer::allocateTextureMemory(CacheTexture* cacheTexture) {
Chet Haase2a47c142011-12-14 15:22:56 -0800736 int width = cacheTexture->mWidth;
737 int height = cacheTexture->mHeight;
Romain Guy9d9758a2012-05-14 15:19:58 -0700738
Chet Haase2a47c142011-12-14 15:22:56 -0800739 cacheTexture->mTexture = new uint8_t[width * height];
Romain Guy99a6ddd2012-05-14 15:32:18 -0700740
741 if (!cacheTexture->mTextureId) {
742 glGenTextures(1, &cacheTexture->mTextureId);
743 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700744
Romain Guy16c88082012-06-11 16:03:47 -0700745 Caches::getInstance().activeTexture(0);
Chet Haase2a47c142011-12-14 15:22:56 -0800746 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
747 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
748 // Initialize texture dimensions
749 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
750 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800751
Chet Haase2a47c142011-12-14 15:22:56 -0800752 const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST;
753 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
754 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
755
756 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
757 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
Chet Haase7de0cb12011-12-05 16:35:38 -0800758}
759
Chet Haase378e9192012-08-15 15:54:54 -0700760CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
761 uint32_t* startX, uint32_t* startY) {
762 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
763 if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
764 return mCacheTextures[i];
765 }
766 }
767 // Could not fit glyph into current cache textures
768 return NULL;
769}
770
Chet Haase7de0cb12011-12-05 16:35:38 -0800771void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
Chet Haasef942cf12012-08-30 09:06:46 -0700772 uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700773 checkInit();
Chet Haase7de0cb12011-12-05 16:35:38 -0800774 cachedGlyph->mIsValid = false;
Romain Guy694b5192010-07-21 21:33:20 -0700775 // If the glyph is too tall, don't cache it
Chet Haase378e9192012-08-15 15:54:54 -0700776 if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
777 mCacheTextures[mCacheTextures.size() - 1]->mHeight) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700778 ALOGE("Font size too large to fit in cache. width, height = %i, %i",
779 (int) glyph.fWidth, (int) glyph.fHeight);
Chet Haase7de0cb12011-12-05 16:35:38 -0800780 return;
Romain Guy694b5192010-07-21 21:33:20 -0700781 }
782
783 // Now copy the bitmap into the cache texture
784 uint32_t startX = 0;
785 uint32_t startY = 0;
786
Chet Haase378e9192012-08-15 15:54:54 -0700787 CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700788
Chet Haase378e9192012-08-15 15:54:54 -0700789 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700790 if (!precaching) {
791 // If the new glyph didn't fit and we are not just trying to precache it,
792 // clear out the cache and try again
793 flushAllAndInvalidate();
794 cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
795 }
Romain Guy694b5192010-07-21 21:33:20 -0700796
Chet Haase378e9192012-08-15 15:54:54 -0700797 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700798 // either the glyph didn't fit or we're precaching and will cache it when we draw
Chet Haase7de0cb12011-12-05 16:35:38 -0800799 return;
Romain Guy694b5192010-07-21 21:33:20 -0700800 }
801 }
802
Chet Haase378e9192012-08-15 15:54:54 -0700803 cachedGlyph->mCacheTexture = cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800804
Romain Guy694b5192010-07-21 21:33:20 -0700805 *retOriginX = startX;
806 *retOriginY = startY;
807
808 uint32_t endX = startX + glyph.fWidth;
809 uint32_t endY = startY + glyph.fHeight;
810
Chet Haase378e9192012-08-15 15:54:54 -0700811 uint32_t cacheWidth = cacheTexture->mWidth;
Romain Guy694b5192010-07-21 21:33:20 -0700812
Romain Guy9d9758a2012-05-14 15:19:58 -0700813 if (!cacheTexture->mTexture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800814 // Large-glyph texture memory is allocated only as needed
Chet Haase2a47c142011-12-14 15:22:56 -0800815 allocateTextureMemory(cacheTexture);
Chet Haase7de0cb12011-12-05 16:35:38 -0800816 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700817
Chet Haase7de0cb12011-12-05 16:35:38 -0800818 uint8_t* cacheBuffer = cacheTexture->mTexture;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700819 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700820 unsigned int stride = glyph.rowBytes();
821
822 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
Romain Guy33fa1f72012-08-07 19:09:57 -0700823
824 for (cacheX = startX - TEXTURE_BORDER_SIZE; cacheX < endX + TEXTURE_BORDER_SIZE; cacheX++) {
825 cacheBuffer[(startY - TEXTURE_BORDER_SIZE) * cacheWidth + cacheX] = 0;
826 cacheBuffer[(endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + cacheX] = 0;
827 }
828
829 for (cacheY = startY - TEXTURE_BORDER_SIZE + 1;
830 cacheY < endY + TEXTURE_BORDER_SIZE - 1; cacheY++) {
831 cacheBuffer[cacheY * cacheWidth + startX - TEXTURE_BORDER_SIZE] = 0;
832 cacheBuffer[cacheY * cacheWidth + endX + TEXTURE_BORDER_SIZE - 1] = 0;
833 }
834
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700835 if (mGammaTable) {
836 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
837 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
838 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
839 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
840 }
841 }
842 } else {
843 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
844 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
845 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
846 cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
847 }
Romain Guy694b5192010-07-21 21:33:20 -0700848 }
849 }
Romain Guy97771732012-02-28 18:17:02 -0800850
Chet Haase7de0cb12011-12-05 16:35:38 -0800851 cachedGlyph->mIsValid = true;
Romain Guy694b5192010-07-21 21:33:20 -0700852}
853
Chet Haase7de0cb12011-12-05 16:35:38 -0800854CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
Romain Guy0aa87bb2012-07-20 11:14:32 -0700855 CacheTexture* cacheTexture = new CacheTexture(width, height);
Romain Guy9d9758a2012-05-14 15:19:58 -0700856
Chet Haase2a47c142011-12-14 15:22:56 -0800857 if (allocate) {
858 allocateTextureMemory(cacheTexture);
859 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700860
Chet Haase2a47c142011-12-14 15:22:56 -0800861 return cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800862}
863
864void FontRenderer::initTextTexture() {
Chet Haase378e9192012-08-15 15:54:54 -0700865 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
866 delete mCacheTextures[i];
Romain Guy9d9758a2012-05-14 15:19:58 -0700867 }
Chet Haase378e9192012-08-15 15:54:54 -0700868 mCacheTextures.clear();
Romain Guy9d9758a2012-05-14 15:19:58 -0700869
Chet Haase7de0cb12011-12-05 16:35:38 -0800870 mUploadTexture = false;
Chet Haase378e9192012-08-15 15:54:54 -0700871 mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
Chet Haaseeb32a492012-08-31 13:54:03 -0700872 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
873 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
874 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false));
Chet Haase378e9192012-08-15 15:54:54 -0700875 mCurrentCacheTexture = mCacheTextures[0];
Romain Guy694b5192010-07-21 21:33:20 -0700876}
877
878// Avoid having to reallocate memory and render quad by quad
879void FontRenderer::initVertexArrayBuffers() {
Romain Guyd71dd362011-12-12 19:03:35 -0800880 uint32_t numIndices = mMaxNumberOfQuads * 6;
881 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700882 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700883
884 // Four verts, two triangles , six indices per quad
885 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
886 int i6 = i * 6;
887 int i4 = i * 4;
888
889 indexBufferData[i6 + 0] = i4 + 0;
890 indexBufferData[i6 + 1] = i4 + 1;
891 indexBufferData[i6 + 2] = i4 + 2;
892
893 indexBufferData[i6 + 3] = i4 + 0;
894 indexBufferData[i6 + 4] = i4 + 2;
895 indexBufferData[i6 + 5] = i4 + 3;
896 }
897
898 glGenBuffers(1, &mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800899 Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700900 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
Romain Guy694b5192010-07-21 21:33:20 -0700901
902 free(indexBufferData);
903
Romain Guyd71dd362011-12-12 19:03:35 -0800904 uint32_t coordSize = 2;
Romain Guy694b5192010-07-21 21:33:20 -0700905 uint32_t uvSize = 2;
906 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700907 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
908 mTextMeshPtr = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700909}
910
911// We don't want to allocate anything unless we actually draw text
912void FontRenderer::checkInit() {
913 if (mInitialized) {
914 return;
915 }
916
917 initTextTexture();
918 initVertexArrayBuffers();
919
920 mInitialized = true;
921}
922
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700923void FontRenderer::checkTextureUpdate() {
Chet Haase7de0cb12011-12-05 16:35:38 -0800924 if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700925 return;
Romain Guy694b5192010-07-21 21:33:20 -0700926 }
927
Romain Guy2d4fd362011-12-13 22:00:19 -0800928 Caches& caches = Caches::getInstance();
929 GLuint lastTextureId = 0;
Chet Haase378e9192012-08-15 15:54:54 -0700930 // Iterate over all the cache textures and see which ones need to be updated
931 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
932 CacheTexture* cacheTexture = mCacheTextures[i];
933 if (cacheTexture->mDirty && cacheTexture->mTexture != NULL) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700934 uint32_t xOffset = 0;
Chet Haase378e9192012-08-15 15:54:54 -0700935 uint32_t width = cacheTexture->mWidth;
936 uint32_t height = cacheTexture->mHeight;
937 void* textureData = cacheTexture->mTexture;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700938
Romain Guy2d4fd362011-12-13 22:00:19 -0800939 if (cacheTexture->mTextureId != lastTextureId) {
940 caches.activeTexture(0);
941 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
942 lastTextureId = cacheTexture->mTextureId;
943 }
Chet Haasee816bae2012-08-09 13:39:02 -0700944#if DEBUG_FONT_RENDERER
Chet Haase378e9192012-08-15 15:54:54 -0700945 ALOGD("glTextSubimage for cacheTexture %d: xOff, width height = %d, %d, %d",
946 i, xOffset, width, height);
Chet Haasee816bae2012-08-09 13:39:02 -0700947#endif
Chet Haase378e9192012-08-15 15:54:54 -0700948 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, 0, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700949 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700950
Chet Haase378e9192012-08-15 15:54:54 -0700951 cacheTexture->mDirty = false;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700952 }
953 }
954
Romain Guy16c88082012-06-11 16:03:47 -0700955 caches.activeTexture(0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800956 glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
Chet Haase2a47c142011-12-14 15:22:56 -0800957 if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) {
958 const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST;
959 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
960 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
961 mCurrentCacheTexture->mLinearFiltering = mLinearFiltering;
962 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800963 mLastCacheTexture = mCurrentCacheTexture;
964
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700965 mUploadTexture = false;
966}
967
968void FontRenderer::issueDrawCommand() {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700969 checkTextureUpdate();
970
Romain Guy15bc6432011-12-13 13:11:32 -0800971 Caches& caches = Caches::getInstance();
Romain Guy2d4fd362011-12-13 22:00:19 -0800972 caches.bindIndicesBuffer(mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800973 if (!mDrawn) {
974 float* buffer = mTextMeshPtr;
975 int offset = 2;
976
977 bool force = caches.unbindMeshBuffer();
978 caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
979 caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
980 buffer + offset);
981 }
982
Romain Guy694b5192010-07-21 21:33:20 -0700983 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
Romain Guy5b3b3522010-10-27 18:57:51 -0700984
985 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700986}
987
Romain Guy97771732012-02-28 18:17:02 -0800988void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
989 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800990 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800991 if (texture != mCurrentCacheTexture) {
992 if (mCurrentQuadIndex != 0) {
993 // First, draw everything stored already which uses the previous texture
994 issueDrawCommand();
995 mCurrentQuadIndex = 0;
996 }
997 // Now use the new texture id
998 mCurrentCacheTexture = texture;
999 }
Romain Guy09147fb2010-07-22 13:08:20 -07001000
Romain Guy694b5192010-07-21 21:33:20 -07001001 const uint32_t vertsPerQuad = 4;
Romain Guyd71dd362011-12-12 19:03:35 -08001002 const uint32_t floatsPerVert = 4;
Romain Guy51769a62010-07-23 00:28:00 -07001003 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -07001004
Romain Guy694b5192010-07-21 21:33:20 -07001005 (*currentPos++) = x1;
1006 (*currentPos++) = y1;
Romain Guy694b5192010-07-21 21:33:20 -07001007 (*currentPos++) = u1;
1008 (*currentPos++) = v1;
1009
1010 (*currentPos++) = x2;
1011 (*currentPos++) = y2;
Romain Guy694b5192010-07-21 21:33:20 -07001012 (*currentPos++) = u2;
1013 (*currentPos++) = v2;
1014
1015 (*currentPos++) = x3;
1016 (*currentPos++) = y3;
Romain Guy694b5192010-07-21 21:33:20 -07001017 (*currentPos++) = u3;
1018 (*currentPos++) = v3;
1019
1020 (*currentPos++) = x4;
1021 (*currentPos++) = y4;
Romain Guy694b5192010-07-21 21:33:20 -07001022 (*currentPos++) = u4;
1023 (*currentPos++) = v4;
1024
1025 mCurrentQuadIndex++;
Romain Guy97771732012-02-28 18:17:02 -08001026}
1027
1028void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
1029 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
1030 float x4, float y4, float u4, float v4, CacheTexture* texture) {
1031
1032 if (mClip &&
1033 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
1034 return;
1035 }
1036
1037 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 -07001038
Romain Guy5b3b3522010-10-27 18:57:51 -07001039 if (mBounds) {
1040 mBounds->left = fmin(mBounds->left, x1);
1041 mBounds->top = fmin(mBounds->top, y3);
1042 mBounds->right = fmax(mBounds->right, x3);
1043 mBounds->bottom = fmax(mBounds->bottom, y1);
1044 }
1045
Romain Guy694b5192010-07-21 21:33:20 -07001046 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
1047 issueDrawCommand();
1048 mCurrentQuadIndex = 0;
1049 }
1050}
1051
Romain Guy97771732012-02-28 18:17:02 -08001052void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
1053 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
1054 float x4, float y4, float u4, float v4, CacheTexture* texture) {
1055
1056 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
1057
1058 if (mBounds) {
1059 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
1060 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
1061 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
1062 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
1063 }
1064
1065 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
1066 issueDrawCommand();
1067 mCurrentQuadIndex = 0;
1068 }
1069}
1070
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -07001071void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
Romain Guy325a0f92011-01-05 15:26:55 -08001072 int flags = 0;
1073 if (paint->isFakeBoldText()) {
1074 flags |= Font::kFakeBold;
1075 }
Romain Guy2577db12011-01-18 13:02:38 -08001076
1077 const float skewX = paint->getTextSkewX();
1078 uint32_t italicStyle = *(uint32_t*) &skewX;
Chet Haase8668f8a2011-03-02 13:51:36 -08001079 const float scaleXFloat = paint->getTextScaleX();
1080 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
Romain Guybd496bc2011-08-02 17:32:41 -07001081 SkPaint::Style style = paint->getStyle();
1082 const float strokeWidthFloat = paint->getStrokeWidth();
1083 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
1084 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
1085 scaleX, style, strokeWidth);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -07001086
Romain Guy694b5192010-07-21 21:33:20 -07001087}
Romain Guy7975fb62010-10-01 16:36:14 -07001088
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001089FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Raph Levien416a8472012-07-19 22:48:17 -07001090 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
Romain Guy1e45aae2010-08-13 19:39:53 -07001091 checkInit();
1092
1093 if (!mCurrentFont) {
1094 DropShadow image;
1095 image.width = 0;
1096 image.height = 0;
1097 image.image = NULL;
1098 image.penX = 0;
1099 image.penY = 0;
1100 return image;
1101 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001102
Romain Guy2d4fd362011-12-13 22:00:19 -08001103 mDrawn = false;
Romain Guyff98fa52011-11-28 09:35:09 -08001104 mClip = NULL;
1105 mBounds = NULL;
1106
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001107 Rect bounds;
Raph Levien416a8472012-07-19 22:48:17 -07001108 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
Romain Guyff98fa52011-11-28 09:35:09 -08001109
Romain Guy1e45aae2010-08-13 19:39:53 -07001110 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
1111 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001112 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guyff98fa52011-11-28 09:35:09 -08001113
Romain Guy1e45aae2010-08-13 19:39:53 -07001114 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001115 dataBuffer[i] = 0;
1116 }
Romain Guy1e45aae2010-08-13 19:39:53 -07001117
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001118 int penX = radius - bounds.left;
1119 int penY = radius - bounds.bottom;
1120
Romain Guy726aeba2011-06-01 14:52:00 -07001121 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
Raph Levien416a8472012-07-19 22:48:17 -07001122 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001123 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
1124
1125 DropShadow image;
1126 image.width = paddedWidth;
1127 image.height = paddedHeight;
1128 image.image = dataBuffer;
1129 image.penX = penX;
1130 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -08001131
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001132 return image;
1133}
Romain Guy694b5192010-07-21 21:33:20 -07001134
Romain Guy671d6cf2012-01-18 12:39:17 -08001135void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -07001136 checkInit();
1137
Romain Guy5b3b3522010-10-27 18:57:51 -07001138 mDrawn = false;
1139 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -07001140 mClip = clip;
Romain Guy671d6cf2012-01-18 12:39:17 -08001141}
Romain Guyff98fa52011-11-28 09:35:09 -08001142
Romain Guy671d6cf2012-01-18 12:39:17 -08001143void FontRenderer::finishRender() {
Romain Guy5b3b3522010-10-27 18:57:51 -07001144 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -08001145 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -07001146
1147 if (mCurrentQuadIndex != 0) {
1148 issueDrawCommand();
1149 mCurrentQuadIndex = 0;
1150 }
Romain Guy671d6cf2012-01-18 12:39:17 -08001151}
1152
Chet Haasee816bae2012-08-09 13:39:02 -07001153void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs) {
1154 int flags = 0;
1155 if (paint->isFakeBoldText()) {
1156 flags |= Font::kFakeBold;
1157 }
1158 const float skewX = paint->getTextSkewX();
1159 uint32_t italicStyle = *(uint32_t*) &skewX;
1160 const float scaleXFloat = paint->getTextScaleX();
1161 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
1162 SkPaint::Style style = paint->getStyle();
1163 const float strokeWidthFloat = paint->getStrokeWidth();
1164 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
1165 float fontSize = paint->getTextSize();
1166 Font* font = Font::create(this, SkTypeface::UniqueID(paint->getTypeface()),
1167 fontSize, flags, italicStyle, scaleX, style, strokeWidth);
1168
1169 font->precache(paint, text, numGlyphs);
1170}
1171
Romain Guy671d6cf2012-01-18 12:39:17 -08001172bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
1173 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
1174 if (!mCurrentFont) {
1175 ALOGE("No font set");
1176 return false;
1177 }
1178
1179 initRender(clip, bounds);
1180 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
1181 finishRender();
1182
1183 return mDrawn;
1184}
1185
1186bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
1187 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
1188 const float* positions, Rect* bounds) {
1189 if (!mCurrentFont) {
1190 ALOGE("No font set");
1191 return false;
1192 }
1193
1194 initRender(clip, bounds);
1195 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
1196 finishRender();
Romain Guy5b3b3522010-10-27 18:57:51 -07001197
1198 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -07001199}
1200
Romain Guy97771732012-02-28 18:17:02 -08001201bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
1202 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
1203 float hOffset, float vOffset, Rect* bounds) {
1204 if (!mCurrentFont) {
1205 ALOGE("No font set");
1206 return false;
1207 }
1208
1209 initRender(clip, bounds);
1210 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
1211 finishRender();
1212
1213 return mDrawn;
1214}
1215
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001216void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
1217 // Compute gaussian weights for the blur
1218 // e is the euler's number
1219 float e = 2.718281828459045f;
1220 float pi = 3.1415926535897932f;
1221 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
1222 // x is of the form [-radius .. 0 .. radius]
1223 // and sigma varies with radius.
1224 // Based on some experimental radius values and sigma's
1225 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001226 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001227 // The larger the radius gets, the more our gaussian blur
1228 // will resemble a box blur since with large sigma
1229 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -08001230 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001231
1232 // Now compute the coefficints
1233 // We will store some redundant values to save some math during
1234 // the blur calculations
1235 // precompute some values
1236 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
1237 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
1238
1239 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -08001240 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -07001241 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001242 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
1243 normalizeFactor += weights[r + radius];
1244 }
1245
1246 //Now we need to normalize the weights because all our coefficients need to add up to one
1247 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -08001248 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001249 weights[r + radius] *= normalizeFactor;
1250 }
1251}
1252
1253void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -07001254 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001255 float blurredPixel = 0.0f;
1256 float currentPixel = 0.0f;
1257
Romain Guy325a0f92011-01-05 15:26:55 -08001258 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001259
1260 const uint8_t* input = source + y * width;
1261 uint8_t* output = dest + y * width;
1262
Romain Guy325a0f92011-01-05 15:26:55 -08001263 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001264 blurredPixel = 0.0f;
1265 const float* gPtr = weights;
1266 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -08001267 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001268 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -08001269 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -07001270 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001271 blurredPixel += currentPixel * gPtr[0];
1272 gPtr++;
1273 i++;
1274 }
1275 } else {
Romain Guy325a0f92011-01-05 15:26:55 -08001276 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001277 // Stepping left and right away from the pixel
1278 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -08001279 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001280 validW = 0;
1281 }
Romain Guy325a0f92011-01-05 15:26:55 -08001282 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001283 validW = width - 1;
1284 }
1285
Romain Guy325a0f92011-01-05 15:26:55 -08001286 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001287 blurredPixel += currentPixel * gPtr[0];
1288 gPtr++;
1289 }
1290 }
1291 *output = (uint8_t)blurredPixel;
1292 output ++;
1293 }
1294 }
1295}
1296
1297void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -07001298 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001299 float blurredPixel = 0.0f;
1300 float currentPixel = 0.0f;
1301
Romain Guy325a0f92011-01-05 15:26:55 -08001302 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001303
1304 uint8_t* output = dest + y * width;
1305
Romain Guy325a0f92011-01-05 15:26:55 -08001306 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001307 blurredPixel = 0.0f;
1308 const float* gPtr = weights;
1309 const uint8_t* input = source + x;
1310 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -08001311 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001312 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -08001313 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001314 currentPixel = (float)(*i);
1315 blurredPixel += currentPixel * gPtr[0];
1316 gPtr++;
1317 i += width;
1318 }
1319 } else {
Romain Guy325a0f92011-01-05 15:26:55 -08001320 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001321 int validH = y + r;
1322 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -08001323 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001324 validH = 0;
1325 }
Romain Guy325a0f92011-01-05 15:26:55 -08001326 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001327 validH = height - 1;
1328 }
1329
1330 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -08001331 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001332 blurredPixel += currentPixel * gPtr[0];
1333 gPtr++;
1334 }
1335 }
Romain Guy325a0f92011-01-05 15:26:55 -08001336 *output = (uint8_t) blurredPixel;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001337 output ++;
1338 }
1339 }
1340}
1341
1342
1343void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
1344 float *gaussian = new float[2 * radius + 1];
1345 computeGaussianWeights(gaussian, radius);
Romain Guyd71dd362011-12-12 19:03:35 -08001346
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001347 uint8_t* scratch = new uint8_t[width * height];
Romain Guyd71dd362011-12-12 19:03:35 -08001348
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001349 horizontalBlur(gaussian, radius, image, scratch, width, height);
1350 verticalBlur(gaussian, radius, scratch, image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -08001351
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001352 delete[] gaussian;
1353 delete[] scratch;
1354}
1355
Romain Guy694b5192010-07-21 21:33:20 -07001356}; // namespace uirenderer
1357}; // namespace android