blob: 6b08e7fedc637135bb4bb1d1535653eda4617265 [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 Haase378e9192012-08-15 15:54:54 -0700112// CacheTexture
Chet Haase7de0cb12011-12-05 16:35:38 -0800113///////////////////////////////////////////////////////////////////////////////
114
Chet Haase378e9192012-08-15 15:54:54 -0700115bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
116 if (glyph.fHeight + TEXTURE_BORDER_SIZE > mHeight) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800117 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;
Chet Haase378e9192012-08-15 15:54:54 -0700141 *retOriginY = cacheBlock->mY;
Chet Haasee816bae2012-08-09 13:39:02 -0700142 // 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;
Chet Haase378e9192012-08-15 15:54:54 -0700149 if (mHeight - glyphH >= glyphH) {
Chet Haasee816bae2012-08-09 13:39:02 -0700150 // There's enough height left over to create a new CacheBlock
Chet Haase5a3ec712012-08-17 15:44:44 -0700151 CacheBlock *newBlock = new CacheBlock(oldX, glyphH + TEXTURE_BORDER_SIZE,
152 roundedUpW, mHeight - glyphH - TEXTURE_BORDER_SIZE);
Chet Haasee816bae2012-08-09 13:39:02 -0700153#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 Haase378e9192012-08-15 15:54:54 -0700216void Font::invalidateTextureCache(CacheTexture *cacheTexture) {
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);
Chet Haase378e9192012-08-15 15:54:54 -0700219 if (cacheTexture == NULL || cachedGlyph->mCacheTexture == cacheTexture) {
Chet Haase9a824562011-12-16 15:44:59 -0800220 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 Haase378e9192012-08-15 15:54:54 -0700263 nPenX, nPenY - height, u1, v1, glyph->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 Haase378e9192012-08-15 15:54:54 -0700274 CacheTexture *cacheTexture = glyph->mCacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800275 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,
Chet Haase378e9192012-08-15 15:54:54 -0700328 glyph->mCacheTexture);
Romain Guy97771732012-02-28 18:17:02 -0800329}
330
Chet Haasef942cf12012-08-30 09:06:46 -0700331CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching) {
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 {
Chet Haasef942cf12012-08-30 09:06:46 -0700337 cachedGlyph = cacheGlyph(paint, textUnit, precaching);
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);
Chet Haasef942cf12012-08-30 09:06:46 -0700343 updateGlyphCache(paint, skiaGlyph, cachedGlyph, precaching);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700344 }
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
Chet Haasef942cf12012-08-30 09:06:46 -0700441 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph, true);
Chet Haasee816bae2012-08-09 13:39:02 -0700442
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
Chet Haasef942cf12012-08-30 09:06:46 -0700532void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph,
533 bool precaching) {
Romain Guy694b5192010-07-21 21:33:20 -0700534 glyph->mAdvanceX = skiaGlyph.fAdvanceX;
535 glyph->mAdvanceY = skiaGlyph.fAdvanceY;
536 glyph->mBitmapLeft = skiaGlyph.fLeft;
537 glyph->mBitmapTop = skiaGlyph.fTop;
Romain Guy2bffd262010-09-12 17:40:02 -0700538 glyph->mLsbDelta = skiaGlyph.fLsbDelta;
539 glyph->mRsbDelta = skiaGlyph.fRsbDelta;
Romain Guy694b5192010-07-21 21:33:20 -0700540
541 uint32_t startX = 0;
542 uint32_t startY = 0;
543
Romain Guy694b5192010-07-21 21:33:20 -0700544 // Get the bitmap for the glyph
545 paint->findImage(skiaGlyph);
Chet Haasef942cf12012-08-30 09:06:46 -0700546 mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY, precaching);
Romain Guy694b5192010-07-21 21:33:20 -0700547
548 if (!glyph->mIsValid) {
549 return;
550 }
551
552 uint32_t endX = startX + skiaGlyph.fWidth;
553 uint32_t endY = startY + skiaGlyph.fHeight;
554
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700555 glyph->mStartX = startX;
556 glyph->mStartY = startY;
Romain Guy694b5192010-07-21 21:33:20 -0700557 glyph->mBitmapWidth = skiaGlyph.fWidth;
558 glyph->mBitmapHeight = skiaGlyph.fHeight;
559
Chet Haase378e9192012-08-15 15:54:54 -0700560 uint32_t cacheWidth = glyph->mCacheTexture->mWidth;
561 uint32_t cacheHeight = glyph->mCacheTexture->mHeight;
Romain Guy694b5192010-07-21 21:33:20 -0700562
Romain Guy33fa1f72012-08-07 19:09:57 -0700563 glyph->mBitmapMinU = startX / (float) cacheWidth;
564 glyph->mBitmapMinV = startY / (float) cacheHeight;
565 glyph->mBitmapMaxU = endX / (float) cacheWidth;
566 glyph->mBitmapMaxV = endY / (float) cacheHeight;
Romain Guy694b5192010-07-21 21:33:20 -0700567
Romain Guy51769a62010-07-23 00:28:00 -0700568 mState->mUploadTexture = true;
Romain Guy694b5192010-07-21 21:33:20 -0700569}
570
Chet Haasef942cf12012-08-30 09:06:46 -0700571CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching) {
Romain Guy51769a62010-07-23 00:28:00 -0700572 CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
Romain Guy694b5192010-07-21 21:33:20 -0700573 mCachedGlyphs.add(glyph, newGlyph);
574
Romain Guy726aeba2011-06-01 14:52:00 -0700575 const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
Romain Guy694b5192010-07-21 21:33:20 -0700576 newGlyph->mGlyphIndex = skiaGlyph.fID;
577 newGlyph->mIsValid = false;
578
Chet Haasef942cf12012-08-30 09:06:46 -0700579 updateGlyphCache(paint, skiaGlyph, newGlyph, precaching);
Romain Guy694b5192010-07-21 21:33:20 -0700580
581 return newGlyph;
582}
583
Romain Guy2577db12011-01-18 13:02:38 -0800584Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
Romain Guybd496bc2011-08-02 17:32:41 -0700585 int flags, uint32_t italicStyle, uint32_t scaleX,
586 SkPaint::Style style, uint32_t strokeWidth) {
Romain Guy694b5192010-07-21 21:33:20 -0700587 Vector<Font*> &activeFonts = state->mActiveFonts;
588
589 for (uint32_t i = 0; i < activeFonts.size(); i++) {
Romain Guy51769a62010-07-23 00:28:00 -0700590 Font* font = activeFonts[i];
Romain Guy2577db12011-01-18 13:02:38 -0800591 if (font->mFontId == fontId && font->mFontSize == fontSize &&
Chet Haase8668f8a2011-03-02 13:51:36 -0800592 font->mFlags == flags && font->mItalicStyle == italicStyle &&
Romain Guybd496bc2011-08-02 17:32:41 -0700593 font->mScaleX == scaleX && font->mStyle == style &&
594 (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
Romain Guy51769a62010-07-23 00:28:00 -0700595 return font;
Romain Guy694b5192010-07-21 21:33:20 -0700596 }
597 }
598
Romain Guybd496bc2011-08-02 17:32:41 -0700599 Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
600 scaleX, style, strokeWidth);
Romain Guy694b5192010-07-21 21:33:20 -0700601 activeFonts.push(newFont);
602 return newFont;
603}
604
605///////////////////////////////////////////////////////////////////////////////
606// FontRenderer
607///////////////////////////////////////////////////////////////////////////////
608
Romain Guy514fb182011-01-19 14:38:29 -0800609static bool sLogFontRendererCreate = true;
610
Romain Guy694b5192010-07-21 21:33:20 -0700611FontRenderer::FontRenderer() {
Romain Guyc9855a52011-01-21 21:14:15 -0800612 if (sLogFontRendererCreate) {
613 INIT_LOGD("Creating FontRenderer");
614 }
Romain Guy51769a62010-07-23 00:28:00 -0700615
Romain Guyb45c0c92010-08-26 20:35:23 -0700616 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700617 mInitialized = false;
618 mMaxNumberOfQuads = 1024;
619 mCurrentQuadIndex = 0;
620
Romain Guy9cccc2b92010-08-07 23:46:15 -0700621 mTextMeshPtr = NULL;
Chet Haase7de0cb12011-12-05 16:35:38 -0800622 mCurrentCacheTexture = NULL;
623 mLastCacheTexture = NULL;
Romain Guy9cccc2b92010-08-07 23:46:15 -0700624
Chet Haase2a47c142011-12-14 15:22:56 -0800625 mLinearFiltering = false;
626
Romain Guy694b5192010-07-21 21:33:20 -0700627 mIndexBufferID = 0;
628
Chet Haase7de0cb12011-12-05 16:35:38 -0800629 mSmallCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
630 mSmallCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -0700631
632 char property[PROPERTY_VALUE_MAX];
633 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800634 if (sLogFontRendererCreate) {
635 INIT_LOGD(" Setting text cache width to %s pixels", property);
636 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800637 mSmallCacheWidth = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -0700638 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800639 if (sLogFontRendererCreate) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800640 INIT_LOGD(" Using default text cache width of %i pixels", mSmallCacheWidth);
Romain Guy514fb182011-01-19 14:38:29 -0800641 }
Romain Guy51769a62010-07-23 00:28:00 -0700642 }
643
644 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800645 if (sLogFontRendererCreate) {
646 INIT_LOGD(" Setting text cache width to %s pixels", property);
647 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800648 mSmallCacheHeight = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -0700649 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800650 if (sLogFontRendererCreate) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800651 INIT_LOGD(" Using default text cache height of %i pixels", mSmallCacheHeight);
Romain Guy514fb182011-01-19 14:38:29 -0800652 }
Romain Guy51769a62010-07-23 00:28:00 -0700653 }
Romain Guy514fb182011-01-19 14:38:29 -0800654
655 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -0700656}
657
658FontRenderer::~FontRenderer() {
Chet Haase378e9192012-08-15 15:54:54 -0700659 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
660 delete mCacheTextures[i];
Romain Guy694b5192010-07-21 21:33:20 -0700661 }
Chet Haase378e9192012-08-15 15:54:54 -0700662 mCacheTextures.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700663
Romain Guy9cccc2b92010-08-07 23:46:15 -0700664 if (mInitialized) {
Romain Guya9dd8202012-03-26 14:52:00 -0700665 // Unbinding the buffer shouldn't be necessary but it crashes with some drivers
666 Caches::getInstance().unbindIndicesBuffer();
Romain Guyb0317982012-03-21 11:52:52 -0700667 glDeleteBuffers(1, &mIndexBufferID);
668
Romain Guy9cccc2b92010-08-07 23:46:15 -0700669 delete[] mTextMeshPtr;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700670 }
Romain Guy694b5192010-07-21 21:33:20 -0700671
672 Vector<Font*> fontsToDereference = mActiveFonts;
673 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
674 delete fontsToDereference[i];
675 }
676}
677
678void FontRenderer::flushAllAndInvalidate() {
679 if (mCurrentQuadIndex != 0) {
680 issueDrawCommand();
681 mCurrentQuadIndex = 0;
682 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700683
Romain Guy694b5192010-07-21 21:33:20 -0700684 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
685 mActiveFonts[i]->invalidateTextureCache();
686 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700687
Chet Haase378e9192012-08-15 15:54:54 -0700688 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
689 mCacheTextures[i]->init();
Romain Guy694b5192010-07-21 21:33:20 -0700690 }
Chet Haasee816bae2012-08-09 13:39:02 -0700691
Chet Haase378e9192012-08-15 15:54:54 -0700692 #if DEBUG_FONT_RENDERER
693 uint16_t totalGlyphs = 0;
694 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
695 totalGlyphs += mCacheTextures[i]->mNumGlyphs;
696 // Erase caches, just as a debugging facility
697 if (mCacheTextures[i]->mTexture) {
698 memset(mCacheTextures[i]->mTexture, 0,
699 mCacheTextures[i]->mWidth * mCacheTextures[i]->mHeight);
700 }
Chet Haasee816bae2012-08-09 13:39:02 -0700701 }
702 ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
703#endif
Romain Guy694b5192010-07-21 21:33:20 -0700704}
705
Chet Haase9a824562011-12-16 15:44:59 -0800706void FontRenderer::deallocateTextureMemory(CacheTexture *cacheTexture) {
707 if (cacheTexture && cacheTexture->mTexture) {
708 glDeleteTextures(1, &cacheTexture->mTextureId);
Romain Guy9d9758a2012-05-14 15:19:58 -0700709 delete[] cacheTexture->mTexture;
Chet Haase9a824562011-12-16 15:44:59 -0800710 cacheTexture->mTexture = NULL;
Romain Guy99a6ddd2012-05-14 15:32:18 -0700711 cacheTexture->mTextureId = 0;
Chet Haase9a824562011-12-16 15:44:59 -0800712 }
713}
714
715void FontRenderer::flushLargeCaches() {
Chet Haase378e9192012-08-15 15:54:54 -0700716 // Start from 1; don't deallocate smallest/default texture
717 for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
718 CacheTexture* cacheTexture = mCacheTextures[i];
719 if (cacheTexture->mTexture != NULL) {
720 cacheTexture->init();
721 for (uint32_t j = 0; j < mActiveFonts.size(); j++) {
722 mActiveFonts[j]->invalidateTextureCache(cacheTexture);
Chet Haasee816bae2012-08-09 13:39:02 -0700723 }
Chet Haase378e9192012-08-15 15:54:54 -0700724 deallocateTextureMemory(cacheTexture);
Chet Haase9a824562011-12-16 15:44:59 -0800725 }
726 }
Chet Haase9a824562011-12-16 15:44:59 -0800727}
728
Romain Guy9d9758a2012-05-14 15:19:58 -0700729void FontRenderer::allocateTextureMemory(CacheTexture* cacheTexture) {
Chet Haase2a47c142011-12-14 15:22:56 -0800730 int width = cacheTexture->mWidth;
731 int height = cacheTexture->mHeight;
Romain Guy9d9758a2012-05-14 15:19:58 -0700732
Chet Haase2a47c142011-12-14 15:22:56 -0800733 cacheTexture->mTexture = new uint8_t[width * height];
Romain Guy99a6ddd2012-05-14 15:32:18 -0700734
735 if (!cacheTexture->mTextureId) {
736 glGenTextures(1, &cacheTexture->mTextureId);
737 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700738
Romain Guy16c88082012-06-11 16:03:47 -0700739 Caches::getInstance().activeTexture(0);
Chet Haase2a47c142011-12-14 15:22:56 -0800740 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
741 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
742 // Initialize texture dimensions
743 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
744 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800745
Chet Haase2a47c142011-12-14 15:22:56 -0800746 const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST;
747 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
748 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
749
750 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
751 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
Chet Haase7de0cb12011-12-05 16:35:38 -0800752}
753
Chet Haase378e9192012-08-15 15:54:54 -0700754CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
755 uint32_t* startX, uint32_t* startY) {
756 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
757 if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
758 return mCacheTextures[i];
759 }
760 }
761 // Could not fit glyph into current cache textures
762 return NULL;
763}
764
Chet Haase7de0cb12011-12-05 16:35:38 -0800765void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
Chet Haasef942cf12012-08-30 09:06:46 -0700766 uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700767 checkInit();
Chet Haase7de0cb12011-12-05 16:35:38 -0800768 cachedGlyph->mIsValid = false;
Romain Guy694b5192010-07-21 21:33:20 -0700769 // If the glyph is too tall, don't cache it
Chet Haase378e9192012-08-15 15:54:54 -0700770 if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
771 mCacheTextures[mCacheTextures.size() - 1]->mHeight) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700772 ALOGE("Font size too large to fit in cache. width, height = %i, %i",
773 (int) glyph.fWidth, (int) glyph.fHeight);
Chet Haase7de0cb12011-12-05 16:35:38 -0800774 return;
Romain Guy694b5192010-07-21 21:33:20 -0700775 }
776
777 // Now copy the bitmap into the cache texture
778 uint32_t startX = 0;
779 uint32_t startY = 0;
780
Chet Haase378e9192012-08-15 15:54:54 -0700781 CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700782
Chet Haase378e9192012-08-15 15:54:54 -0700783 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700784 if (!precaching) {
785 // If the new glyph didn't fit and we are not just trying to precache it,
786 // clear out the cache and try again
787 flushAllAndInvalidate();
788 cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
789 }
Romain Guy694b5192010-07-21 21:33:20 -0700790
Chet Haase378e9192012-08-15 15:54:54 -0700791 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700792 // either the glyph didn't fit or we're precaching and will cache it when we draw
Chet Haase7de0cb12011-12-05 16:35:38 -0800793 return;
Romain Guy694b5192010-07-21 21:33:20 -0700794 }
795 }
796
Chet Haase378e9192012-08-15 15:54:54 -0700797 cachedGlyph->mCacheTexture = cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800798
Romain Guy694b5192010-07-21 21:33:20 -0700799 *retOriginX = startX;
800 *retOriginY = startY;
801
802 uint32_t endX = startX + glyph.fWidth;
803 uint32_t endY = startY + glyph.fHeight;
804
Chet Haase378e9192012-08-15 15:54:54 -0700805 uint32_t cacheWidth = cacheTexture->mWidth;
Romain Guy694b5192010-07-21 21:33:20 -0700806
Romain Guy9d9758a2012-05-14 15:19:58 -0700807 if (!cacheTexture->mTexture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800808 // Large-glyph texture memory is allocated only as needed
Chet Haase2a47c142011-12-14 15:22:56 -0800809 allocateTextureMemory(cacheTexture);
Chet Haase7de0cb12011-12-05 16:35:38 -0800810 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700811
Chet Haase7de0cb12011-12-05 16:35:38 -0800812 uint8_t* cacheBuffer = cacheTexture->mTexture;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700813 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700814 unsigned int stride = glyph.rowBytes();
815
816 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
Romain Guy33fa1f72012-08-07 19:09:57 -0700817
818 for (cacheX = startX - TEXTURE_BORDER_SIZE; cacheX < endX + TEXTURE_BORDER_SIZE; cacheX++) {
819 cacheBuffer[(startY - TEXTURE_BORDER_SIZE) * cacheWidth + cacheX] = 0;
820 cacheBuffer[(endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + cacheX] = 0;
821 }
822
823 for (cacheY = startY - TEXTURE_BORDER_SIZE + 1;
824 cacheY < endY + TEXTURE_BORDER_SIZE - 1; cacheY++) {
825 cacheBuffer[cacheY * cacheWidth + startX - TEXTURE_BORDER_SIZE] = 0;
826 cacheBuffer[cacheY * cacheWidth + endX + TEXTURE_BORDER_SIZE - 1] = 0;
827 }
828
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700829 if (mGammaTable) {
830 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
831 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
832 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
833 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
834 }
835 }
836 } else {
837 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
838 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
839 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
840 cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
841 }
Romain Guy694b5192010-07-21 21:33:20 -0700842 }
843 }
Romain Guy97771732012-02-28 18:17:02 -0800844
Chet Haase7de0cb12011-12-05 16:35:38 -0800845 cachedGlyph->mIsValid = true;
Romain Guy694b5192010-07-21 21:33:20 -0700846}
847
Chet Haase7de0cb12011-12-05 16:35:38 -0800848CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
Romain Guy0aa87bb2012-07-20 11:14:32 -0700849 CacheTexture* cacheTexture = new CacheTexture(width, height);
Romain Guy9d9758a2012-05-14 15:19:58 -0700850
Chet Haase2a47c142011-12-14 15:22:56 -0800851 if (allocate) {
852 allocateTextureMemory(cacheTexture);
853 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700854
Chet Haase2a47c142011-12-14 15:22:56 -0800855 return cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800856}
857
858void FontRenderer::initTextTexture() {
Chet Haase378e9192012-08-15 15:54:54 -0700859 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
860 delete mCacheTextures[i];
Romain Guy9d9758a2012-05-14 15:19:58 -0700861 }
Chet Haase378e9192012-08-15 15:54:54 -0700862 mCacheTextures.clear();
Romain Guy9d9758a2012-05-14 15:19:58 -0700863
Chet Haase7de0cb12011-12-05 16:35:38 -0800864 // Next, use other, separate caches for large glyphs.
865 uint16_t maxWidth = 0;
866 if (Caches::hasInstance()) {
867 maxWidth = Caches::getInstance().maxTextureSize;
Chet Haase44984ea2011-05-19 13:50:47 -0700868 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700869
Chet Haase7de0cb12011-12-05 16:35:38 -0800870 if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) {
871 maxWidth = MAX_TEXT_CACHE_WIDTH;
872 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700873
Chet Haase7de0cb12011-12-05 16:35:38 -0800874 mUploadTexture = false;
Chet Haase378e9192012-08-15 15:54:54 -0700875 mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
876 mCacheTextures.push(createCacheTexture(maxWidth, 256, false));
877 mCacheTextures.push(createCacheTexture(maxWidth, 256, false));
878 mCacheTextures.push(createCacheTexture(maxWidth, 512, false));
879 mCurrentCacheTexture = mCacheTextures[0];
Romain Guy694b5192010-07-21 21:33:20 -0700880}
881
882// Avoid having to reallocate memory and render quad by quad
883void FontRenderer::initVertexArrayBuffers() {
Romain Guyd71dd362011-12-12 19:03:35 -0800884 uint32_t numIndices = mMaxNumberOfQuads * 6;
885 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700886 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700887
888 // Four verts, two triangles , six indices per quad
889 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
890 int i6 = i * 6;
891 int i4 = i * 4;
892
893 indexBufferData[i6 + 0] = i4 + 0;
894 indexBufferData[i6 + 1] = i4 + 1;
895 indexBufferData[i6 + 2] = i4 + 2;
896
897 indexBufferData[i6 + 3] = i4 + 0;
898 indexBufferData[i6 + 4] = i4 + 2;
899 indexBufferData[i6 + 5] = i4 + 3;
900 }
901
902 glGenBuffers(1, &mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800903 Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700904 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
Romain Guy694b5192010-07-21 21:33:20 -0700905
906 free(indexBufferData);
907
Romain Guyd71dd362011-12-12 19:03:35 -0800908 uint32_t coordSize = 2;
Romain Guy694b5192010-07-21 21:33:20 -0700909 uint32_t uvSize = 2;
910 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700911 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
912 mTextMeshPtr = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700913}
914
915// We don't want to allocate anything unless we actually draw text
916void FontRenderer::checkInit() {
917 if (mInitialized) {
918 return;
919 }
920
921 initTextTexture();
922 initVertexArrayBuffers();
923
924 mInitialized = true;
925}
926
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700927void FontRenderer::checkTextureUpdate() {
Chet Haase7de0cb12011-12-05 16:35:38 -0800928 if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700929 return;
Romain Guy694b5192010-07-21 21:33:20 -0700930 }
931
Romain Guy2d4fd362011-12-13 22:00:19 -0800932 Caches& caches = Caches::getInstance();
933 GLuint lastTextureId = 0;
Chet Haase378e9192012-08-15 15:54:54 -0700934 // Iterate over all the cache textures and see which ones need to be updated
935 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
936 CacheTexture* cacheTexture = mCacheTextures[i];
937 if (cacheTexture->mDirty && cacheTexture->mTexture != NULL) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700938 uint32_t xOffset = 0;
Chet Haase378e9192012-08-15 15:54:54 -0700939 uint32_t width = cacheTexture->mWidth;
940 uint32_t height = cacheTexture->mHeight;
941 void* textureData = cacheTexture->mTexture;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700942
Romain Guy2d4fd362011-12-13 22:00:19 -0800943 if (cacheTexture->mTextureId != lastTextureId) {
944 caches.activeTexture(0);
945 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
946 lastTextureId = cacheTexture->mTextureId;
947 }
Chet Haasee816bae2012-08-09 13:39:02 -0700948#if DEBUG_FONT_RENDERER
Chet Haase378e9192012-08-15 15:54:54 -0700949 ALOGD("glTextSubimage for cacheTexture %d: xOff, width height = %d, %d, %d",
950 i, xOffset, width, height);
Chet Haasee816bae2012-08-09 13:39:02 -0700951#endif
Chet Haase378e9192012-08-15 15:54:54 -0700952 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, 0, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700953 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700954
Chet Haase378e9192012-08-15 15:54:54 -0700955 cacheTexture->mDirty = false;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700956 }
957 }
958
Romain Guy16c88082012-06-11 16:03:47 -0700959 caches.activeTexture(0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800960 glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
Chet Haase2a47c142011-12-14 15:22:56 -0800961 if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) {
962 const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST;
963 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
964 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
965 mCurrentCacheTexture->mLinearFiltering = mLinearFiltering;
966 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800967 mLastCacheTexture = mCurrentCacheTexture;
968
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700969 mUploadTexture = false;
970}
971
972void FontRenderer::issueDrawCommand() {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700973 checkTextureUpdate();
974
Romain Guy15bc6432011-12-13 13:11:32 -0800975 Caches& caches = Caches::getInstance();
Romain Guy2d4fd362011-12-13 22:00:19 -0800976 caches.bindIndicesBuffer(mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800977 if (!mDrawn) {
978 float* buffer = mTextMeshPtr;
979 int offset = 2;
980
981 bool force = caches.unbindMeshBuffer();
982 caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
983 caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
984 buffer + offset);
985 }
986
Romain Guy694b5192010-07-21 21:33:20 -0700987 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
Romain Guy5b3b3522010-10-27 18:57:51 -0700988
989 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700990}
991
Romain Guy97771732012-02-28 18:17:02 -0800992void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
993 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800994 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800995 if (texture != mCurrentCacheTexture) {
996 if (mCurrentQuadIndex != 0) {
997 // First, draw everything stored already which uses the previous texture
998 issueDrawCommand();
999 mCurrentQuadIndex = 0;
1000 }
1001 // Now use the new texture id
1002 mCurrentCacheTexture = texture;
1003 }
Romain Guy09147fb2010-07-22 13:08:20 -07001004
Romain Guy694b5192010-07-21 21:33:20 -07001005 const uint32_t vertsPerQuad = 4;
Romain Guyd71dd362011-12-12 19:03:35 -08001006 const uint32_t floatsPerVert = 4;
Romain Guy51769a62010-07-23 00:28:00 -07001007 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -07001008
Romain Guy694b5192010-07-21 21:33:20 -07001009 (*currentPos++) = x1;
1010 (*currentPos++) = y1;
Romain Guy694b5192010-07-21 21:33:20 -07001011 (*currentPos++) = u1;
1012 (*currentPos++) = v1;
1013
1014 (*currentPos++) = x2;
1015 (*currentPos++) = y2;
Romain Guy694b5192010-07-21 21:33:20 -07001016 (*currentPos++) = u2;
1017 (*currentPos++) = v2;
1018
1019 (*currentPos++) = x3;
1020 (*currentPos++) = y3;
Romain Guy694b5192010-07-21 21:33:20 -07001021 (*currentPos++) = u3;
1022 (*currentPos++) = v3;
1023
1024 (*currentPos++) = x4;
1025 (*currentPos++) = y4;
Romain Guy694b5192010-07-21 21:33:20 -07001026 (*currentPos++) = u4;
1027 (*currentPos++) = v4;
1028
1029 mCurrentQuadIndex++;
Romain Guy97771732012-02-28 18:17:02 -08001030}
1031
1032void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
1033 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
1034 float x4, float y4, float u4, float v4, CacheTexture* texture) {
1035
1036 if (mClip &&
1037 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
1038 return;
1039 }
1040
1041 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 -07001042
Romain Guy5b3b3522010-10-27 18:57:51 -07001043 if (mBounds) {
1044 mBounds->left = fmin(mBounds->left, x1);
1045 mBounds->top = fmin(mBounds->top, y3);
1046 mBounds->right = fmax(mBounds->right, x3);
1047 mBounds->bottom = fmax(mBounds->bottom, y1);
1048 }
1049
Romain Guy694b5192010-07-21 21:33:20 -07001050 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
1051 issueDrawCommand();
1052 mCurrentQuadIndex = 0;
1053 }
1054}
1055
Romain Guy97771732012-02-28 18:17:02 -08001056void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
1057 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
1058 float x4, float y4, float u4, float v4, CacheTexture* texture) {
1059
1060 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
1061
1062 if (mBounds) {
1063 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
1064 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
1065 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
1066 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
1067 }
1068
1069 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
1070 issueDrawCommand();
1071 mCurrentQuadIndex = 0;
1072 }
1073}
1074
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -07001075void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
Romain Guy325a0f92011-01-05 15:26:55 -08001076 int flags = 0;
1077 if (paint->isFakeBoldText()) {
1078 flags |= Font::kFakeBold;
1079 }
Romain Guy2577db12011-01-18 13:02:38 -08001080
1081 const float skewX = paint->getTextSkewX();
1082 uint32_t italicStyle = *(uint32_t*) &skewX;
Chet Haase8668f8a2011-03-02 13:51:36 -08001083 const float scaleXFloat = paint->getTextScaleX();
1084 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
Romain Guybd496bc2011-08-02 17:32:41 -07001085 SkPaint::Style style = paint->getStyle();
1086 const float strokeWidthFloat = paint->getStrokeWidth();
1087 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
1088 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
1089 scaleX, style, strokeWidth);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -07001090
Romain Guy694b5192010-07-21 21:33:20 -07001091}
Romain Guy7975fb62010-10-01 16:36:14 -07001092
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001093FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Raph Levien416a8472012-07-19 22:48:17 -07001094 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
Romain Guy1e45aae2010-08-13 19:39:53 -07001095 checkInit();
1096
1097 if (!mCurrentFont) {
1098 DropShadow image;
1099 image.width = 0;
1100 image.height = 0;
1101 image.image = NULL;
1102 image.penX = 0;
1103 image.penY = 0;
1104 return image;
1105 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001106
Romain Guy2d4fd362011-12-13 22:00:19 -08001107 mDrawn = false;
Romain Guyff98fa52011-11-28 09:35:09 -08001108 mClip = NULL;
1109 mBounds = NULL;
1110
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001111 Rect bounds;
Raph Levien416a8472012-07-19 22:48:17 -07001112 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
Romain Guyff98fa52011-11-28 09:35:09 -08001113
Romain Guy1e45aae2010-08-13 19:39:53 -07001114 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
1115 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001116 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guyff98fa52011-11-28 09:35:09 -08001117
Romain Guy1e45aae2010-08-13 19:39:53 -07001118 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001119 dataBuffer[i] = 0;
1120 }
Romain Guy1e45aae2010-08-13 19:39:53 -07001121
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001122 int penX = radius - bounds.left;
1123 int penY = radius - bounds.bottom;
1124
Romain Guy726aeba2011-06-01 14:52:00 -07001125 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
Raph Levien416a8472012-07-19 22:48:17 -07001126 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001127 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
1128
1129 DropShadow image;
1130 image.width = paddedWidth;
1131 image.height = paddedHeight;
1132 image.image = dataBuffer;
1133 image.penX = penX;
1134 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -08001135
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001136 return image;
1137}
Romain Guy694b5192010-07-21 21:33:20 -07001138
Romain Guy671d6cf2012-01-18 12:39:17 -08001139void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -07001140 checkInit();
1141
Romain Guy5b3b3522010-10-27 18:57:51 -07001142 mDrawn = false;
1143 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -07001144 mClip = clip;
Romain Guy671d6cf2012-01-18 12:39:17 -08001145}
Romain Guyff98fa52011-11-28 09:35:09 -08001146
Romain Guy671d6cf2012-01-18 12:39:17 -08001147void FontRenderer::finishRender() {
Romain Guy5b3b3522010-10-27 18:57:51 -07001148 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -08001149 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -07001150
1151 if (mCurrentQuadIndex != 0) {
1152 issueDrawCommand();
1153 mCurrentQuadIndex = 0;
1154 }
Romain Guy671d6cf2012-01-18 12:39:17 -08001155}
1156
Chet Haasee816bae2012-08-09 13:39:02 -07001157void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs) {
1158 int flags = 0;
1159 if (paint->isFakeBoldText()) {
1160 flags |= Font::kFakeBold;
1161 }
1162 const float skewX = paint->getTextSkewX();
1163 uint32_t italicStyle = *(uint32_t*) &skewX;
1164 const float scaleXFloat = paint->getTextScaleX();
1165 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
1166 SkPaint::Style style = paint->getStyle();
1167 const float strokeWidthFloat = paint->getStrokeWidth();
1168 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
1169 float fontSize = paint->getTextSize();
1170 Font* font = Font::create(this, SkTypeface::UniqueID(paint->getTypeface()),
1171 fontSize, flags, italicStyle, scaleX, style, strokeWidth);
1172
1173 font->precache(paint, text, numGlyphs);
1174}
1175
Romain Guy671d6cf2012-01-18 12:39:17 -08001176bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
1177 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
1178 if (!mCurrentFont) {
1179 ALOGE("No font set");
1180 return false;
1181 }
1182
1183 initRender(clip, bounds);
1184 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
1185 finishRender();
1186
1187 return mDrawn;
1188}
1189
1190bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
1191 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
1192 const float* positions, Rect* bounds) {
1193 if (!mCurrentFont) {
1194 ALOGE("No font set");
1195 return false;
1196 }
1197
1198 initRender(clip, bounds);
1199 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
1200 finishRender();
Romain Guy5b3b3522010-10-27 18:57:51 -07001201
1202 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -07001203}
1204
Romain Guy97771732012-02-28 18:17:02 -08001205bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
1206 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
1207 float hOffset, float vOffset, Rect* bounds) {
1208 if (!mCurrentFont) {
1209 ALOGE("No font set");
1210 return false;
1211 }
1212
1213 initRender(clip, bounds);
1214 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
1215 finishRender();
1216
1217 return mDrawn;
1218}
1219
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001220void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
1221 // Compute gaussian weights for the blur
1222 // e is the euler's number
1223 float e = 2.718281828459045f;
1224 float pi = 3.1415926535897932f;
1225 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
1226 // x is of the form [-radius .. 0 .. radius]
1227 // and sigma varies with radius.
1228 // Based on some experimental radius values and sigma's
1229 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001230 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001231 // The larger the radius gets, the more our gaussian blur
1232 // will resemble a box blur since with large sigma
1233 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -08001234 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001235
1236 // Now compute the coefficints
1237 // We will store some redundant values to save some math during
1238 // the blur calculations
1239 // precompute some values
1240 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
1241 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
1242
1243 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -08001244 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -07001245 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001246 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
1247 normalizeFactor += weights[r + radius];
1248 }
1249
1250 //Now we need to normalize the weights because all our coefficients need to add up to one
1251 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -08001252 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001253 weights[r + radius] *= normalizeFactor;
1254 }
1255}
1256
1257void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -07001258 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001259 float blurredPixel = 0.0f;
1260 float currentPixel = 0.0f;
1261
Romain Guy325a0f92011-01-05 15:26:55 -08001262 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001263
1264 const uint8_t* input = source + y * width;
1265 uint8_t* output = dest + y * width;
1266
Romain Guy325a0f92011-01-05 15:26:55 -08001267 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001268 blurredPixel = 0.0f;
1269 const float* gPtr = weights;
1270 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -08001271 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001272 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -08001273 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -07001274 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001275 blurredPixel += currentPixel * gPtr[0];
1276 gPtr++;
1277 i++;
1278 }
1279 } else {
Romain Guy325a0f92011-01-05 15:26:55 -08001280 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001281 // Stepping left and right away from the pixel
1282 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -08001283 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001284 validW = 0;
1285 }
Romain Guy325a0f92011-01-05 15:26:55 -08001286 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001287 validW = width - 1;
1288 }
1289
Romain Guy325a0f92011-01-05 15:26:55 -08001290 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001291 blurredPixel += currentPixel * gPtr[0];
1292 gPtr++;
1293 }
1294 }
1295 *output = (uint8_t)blurredPixel;
1296 output ++;
1297 }
1298 }
1299}
1300
1301void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -07001302 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001303 float blurredPixel = 0.0f;
1304 float currentPixel = 0.0f;
1305
Romain Guy325a0f92011-01-05 15:26:55 -08001306 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001307
1308 uint8_t* output = dest + y * width;
1309
Romain Guy325a0f92011-01-05 15:26:55 -08001310 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001311 blurredPixel = 0.0f;
1312 const float* gPtr = weights;
1313 const uint8_t* input = source + x;
1314 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -08001315 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001316 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -08001317 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001318 currentPixel = (float)(*i);
1319 blurredPixel += currentPixel * gPtr[0];
1320 gPtr++;
1321 i += width;
1322 }
1323 } else {
Romain Guy325a0f92011-01-05 15:26:55 -08001324 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001325 int validH = y + r;
1326 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -08001327 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001328 validH = 0;
1329 }
Romain Guy325a0f92011-01-05 15:26:55 -08001330 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001331 validH = height - 1;
1332 }
1333
1334 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -08001335 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001336 blurredPixel += currentPixel * gPtr[0];
1337 gPtr++;
1338 }
1339 }
Romain Guy325a0f92011-01-05 15:26:55 -08001340 *output = (uint8_t) blurredPixel;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001341 output ++;
1342 }
1343 }
1344}
1345
1346
1347void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
1348 float *gaussian = new float[2 * radius + 1];
1349 computeGaussianWeights(gaussian, radius);
Romain Guyd71dd362011-12-12 19:03:35 -08001350
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001351 uint8_t* scratch = new uint8_t[width * height];
Romain Guyd71dd362011-12-12 19:03:35 -08001352
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001353 horizontalBlur(gaussian, radius, image, scratch, width, height);
1354 verticalBlur(gaussian, radius, scratch, image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -08001355
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001356 delete[] gaussian;
1357 delete[] scratch;
1358}
1359
Romain Guy694b5192010-07-21 21:33:20 -07001360}; // namespace uirenderer
1361}; // namespace android