blob: caeeb8745243f6c2a42d2a8946d13614e163e351 [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
151 CacheBlock *newBlock = new CacheBlock(oldX, glyphH, roundedUpW,
Chet Haase378e9192012-08-15 15:54:54 -0700152 mHeight - glyphH);
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 Haase7de0cb12011-12-05 16:35:38 -0800331CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700332 CachedGlyphInfo* cachedGlyph = NULL;
Romain Guy726aeba2011-06-01 14:52:00 -0700333 ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
Romain Guy1e45aae2010-08-13 19:39:53 -0700334 if (index >= 0) {
335 cachedGlyph = mCachedGlyphs.valueAt(index);
336 } else {
Romain Guy726aeba2011-06-01 14:52:00 -0700337 cachedGlyph = cacheGlyph(paint, textUnit);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700338 }
339
340 // Is the glyph still in texture cache?
341 if (!cachedGlyph->mIsValid) {
Romain Guy726aeba2011-06-01 14:52:00 -0700342 const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700343 updateGlyphCache(paint, skiaGlyph, cachedGlyph);
344 }
345
346 return cachedGlyph;
347}
348
Romain Guy726aeba2011-06-01 14:52:00 -0700349void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700350 int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
351 if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
Romain Guy726aeba2011-06-01 14:52:00 -0700352 render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
Romain Guy671d6cf2012-01-18 12:39:17 -0800353 bitmapW, bitmapH, NULL, NULL);
Romain Guy61c8c9c2010-08-09 20:48:09 -0700354 } else {
Romain Guy726aeba2011-06-01 14:52:00 -0700355 render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
Romain Guy671d6cf2012-01-18 12:39:17 -0800356 0, 0, NULL, NULL);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700357 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700358}
359
Romain Guy671d6cf2012-01-18 12:39:17 -0800360void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
361 int numGlyphs, int x, int y, const float* positions) {
362 render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
363 0, 0, NULL, positions);
364}
365
Romain Guy97771732012-02-28 18:17:02 -0800366void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
367 int numGlyphs, SkPath* path, float hOffset, float vOffset) {
368 if (numGlyphs == 0 || text == NULL || len == 0) {
369 return;
370 }
371
372 text += start;
373
374 int glyphsCount = 0;
375 SkFixed prevRsbDelta = 0;
376
377 float penX = 0.0f;
378
379 SkPoint position;
380 SkVector tangent;
381
382 SkPathMeasure measure(*path, false);
383 float pathLength = SkScalarToFloat(measure.getLength());
384
385 if (paint->getTextAlign() != SkPaint::kLeft_Align) {
386 float textWidth = SkScalarToFloat(paint->measureText(text, len));
387 float pathOffset = pathLength;
388 if (paint->getTextAlign() == SkPaint::kCenter_Align) {
389 textWidth *= 0.5f;
390 pathOffset *= 0.5f;
391 }
392 penX += pathOffset - textWidth;
393 }
394
Romain Guydd7c8e4c2012-03-01 12:08:38 -0800395 while (glyphsCount < numGlyphs && penX < pathLength) {
Romain Guy97771732012-02-28 18:17:02 -0800396 glyph_t glyph = GET_GLYPH(text);
397
398 if (IS_END_OF_STRING(glyph)) {
399 break;
400 }
401
402 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
403 penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
404 prevRsbDelta = cachedGlyph->mRsbDelta;
405
406 if (cachedGlyph->mIsValid) {
Romain Guydd7c8e4c2012-03-01 12:08:38 -0800407 drawCachedGlyph(cachedGlyph, penX, hOffset, vOffset, measure, &position, &tangent);
Romain Guy97771732012-02-28 18:17:02 -0800408 }
409
410 penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
411
412 glyphsCount++;
413 }
414}
415
Romain Guy726aeba2011-06-01 14:52:00 -0700416void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Raph Levien416a8472012-07-19 22:48:17 -0700417 int numGlyphs, Rect *bounds, const float* positions) {
Romain Guy61c8c9c2010-08-09 20:48:09 -0700418 if (bounds == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000419 ALOGE("No return rectangle provided to measure text");
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700420 return;
421 }
422 bounds->set(1e6, -1e6, -1e6, 1e6);
Raph Levien416a8472012-07-19 22:48:17 -0700423 render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, positions);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700424}
425
Chet Haasee816bae2012-08-09 13:39:02 -0700426void Font::precache(SkPaint* paint, const char* text, int numGlyphs) {
427
428 if (numGlyphs == 0 || text == NULL) {
429 return;
430 }
431 int glyphsCount = 0;
432
433 while (glyphsCount < numGlyphs) {
434 glyph_t glyph = GET_GLYPH(text);
435
436 // Reached the end of the string
437 if (IS_END_OF_STRING(glyph)) {
438 break;
439 }
440
441 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
442
443 glyphsCount++;
444 }
445}
446
Romain Guy726aeba2011-06-01 14:52:00 -0700447void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700448 int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
Romain Guy97771732012-02-28 18:17:02 -0800449 uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) {
Romain Guy694b5192010-07-21 21:33:20 -0700450 if (numGlyphs == 0 || text == NULL || len == 0) {
451 return;
452 }
453
Romain Guy671d6cf2012-01-18 12:39:17 -0800454 static RenderGlyph gRenderGlyph[] = {
455 &android::uirenderer::Font::drawCachedGlyph,
456 &android::uirenderer::Font::drawCachedGlyphBitmap,
457 &android::uirenderer::Font::measureCachedGlyph
458 };
459 RenderGlyph render = gRenderGlyph[mode];
Romain Guy694b5192010-07-21 21:33:20 -0700460
Romain Guy97771732012-02-28 18:17:02 -0800461 text += start;
462 int glyphsCount = 0;
463
Romain Guyb6294902012-02-02 15:13:18 -0800464 if (CC_LIKELY(positions == NULL)) {
Romain Guy671d6cf2012-01-18 12:39:17 -0800465 SkFixed prevRsbDelta = 0;
Romain Guy694b5192010-07-21 21:33:20 -0700466
Romain Guy97771732012-02-28 18:17:02 -0800467 float penX = x + 0.5f;
Romain Guy671d6cf2012-01-18 12:39:17 -0800468 int penY = y;
Romain Guy694b5192010-07-21 21:33:20 -0700469
Romain Guy671d6cf2012-01-18 12:39:17 -0800470 while (glyphsCount < numGlyphs) {
471 glyph_t glyph = GET_GLYPH(text);
472
473 // Reached the end of the string
474 if (IS_END_OF_STRING(glyph)) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700475 break;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700476 }
Romain Guy671d6cf2012-01-18 12:39:17 -0800477
478 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
Romain Guy97771732012-02-28 18:17:02 -0800479 penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
Romain Guy671d6cf2012-01-18 12:39:17 -0800480 prevRsbDelta = cachedGlyph->mRsbDelta;
481
482 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
483 if (cachedGlyph->mIsValid) {
484 (*this.*render)(cachedGlyph, (int) floorf(penX), penY,
485 bitmap, bitmapW, bitmapH, bounds, positions);
486 }
487
488 penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
489
490 glyphsCount++;
Romain Guy694b5192010-07-21 21:33:20 -0700491 }
Romain Guy671d6cf2012-01-18 12:39:17 -0800492 } else {
493 const SkPaint::Align align = paint->getTextAlign();
Romain Guy694b5192010-07-21 21:33:20 -0700494
Romain Guy671d6cf2012-01-18 12:39:17 -0800495 // This is for renderPosText()
496 while (glyphsCount < numGlyphs) {
497 glyph_t glyph = GET_GLYPH(text);
Romain Guy694b5192010-07-21 21:33:20 -0700498
Romain Guy671d6cf2012-01-18 12:39:17 -0800499 // Reached the end of the string
500 if (IS_END_OF_STRING(glyph)) {
501 break;
502 }
503
504 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
505
506 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
507 if (cachedGlyph->mIsValid) {
508 int penX = x + positions[(glyphsCount << 1)];
509 int penY = y + positions[(glyphsCount << 1) + 1];
510
511 switch (align) {
512 case SkPaint::kRight_Align:
513 penX -= SkFixedToFloat(cachedGlyph->mAdvanceX);
514 penY -= SkFixedToFloat(cachedGlyph->mAdvanceY);
515 break;
516 case SkPaint::kCenter_Align:
517 penX -= SkFixedToFloat(cachedGlyph->mAdvanceX >> 1);
518 penY -= SkFixedToFloat(cachedGlyph->mAdvanceY >> 1);
519 default:
520 break;
521 }
522
523 (*this.*render)(cachedGlyph, penX, penY,
524 bitmap, bitmapW, bitmapH, bounds, positions);
525 }
526
527 glyphsCount++;
Romain Guy694b5192010-07-21 21:33:20 -0700528 }
529 }
530}
531
Romain Guy51769a62010-07-23 00:28:00 -0700532void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
Romain Guy694b5192010-07-21 21:33:20 -0700533 glyph->mAdvanceX = skiaGlyph.fAdvanceX;
534 glyph->mAdvanceY = skiaGlyph.fAdvanceY;
535 glyph->mBitmapLeft = skiaGlyph.fLeft;
536 glyph->mBitmapTop = skiaGlyph.fTop;
Romain Guy2bffd262010-09-12 17:40:02 -0700537 glyph->mLsbDelta = skiaGlyph.fLsbDelta;
538 glyph->mRsbDelta = skiaGlyph.fRsbDelta;
Romain Guy694b5192010-07-21 21:33:20 -0700539
540 uint32_t startX = 0;
541 uint32_t startY = 0;
542
Romain Guy694b5192010-07-21 21:33:20 -0700543 // Get the bitmap for the glyph
544 paint->findImage(skiaGlyph);
Chet Haase7de0cb12011-12-05 16:35:38 -0800545 mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700546
547 if (!glyph->mIsValid) {
548 return;
549 }
550
551 uint32_t endX = startX + skiaGlyph.fWidth;
552 uint32_t endY = startY + skiaGlyph.fHeight;
553
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700554 glyph->mStartX = startX;
555 glyph->mStartY = startY;
Romain Guy694b5192010-07-21 21:33:20 -0700556 glyph->mBitmapWidth = skiaGlyph.fWidth;
557 glyph->mBitmapHeight = skiaGlyph.fHeight;
558
Chet Haase378e9192012-08-15 15:54:54 -0700559 uint32_t cacheWidth = glyph->mCacheTexture->mWidth;
560 uint32_t cacheHeight = glyph->mCacheTexture->mHeight;
Romain Guy694b5192010-07-21 21:33:20 -0700561
Romain Guy33fa1f72012-08-07 19:09:57 -0700562 glyph->mBitmapMinU = startX / (float) cacheWidth;
563 glyph->mBitmapMinV = startY / (float) cacheHeight;
564 glyph->mBitmapMaxU = endX / (float) cacheWidth;
565 glyph->mBitmapMaxV = endY / (float) cacheHeight;
Romain Guy694b5192010-07-21 21:33:20 -0700566
Romain Guy51769a62010-07-23 00:28:00 -0700567 mState->mUploadTexture = true;
Romain Guy694b5192010-07-21 21:33:20 -0700568}
569
Chet Haase7de0cb12011-12-05 16:35:38 -0800570CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) {
Romain Guy51769a62010-07-23 00:28:00 -0700571 CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
Romain Guy694b5192010-07-21 21:33:20 -0700572 mCachedGlyphs.add(glyph, newGlyph);
573
Romain Guy726aeba2011-06-01 14:52:00 -0700574 const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
Romain Guy694b5192010-07-21 21:33:20 -0700575 newGlyph->mGlyphIndex = skiaGlyph.fID;
576 newGlyph->mIsValid = false;
577
578 updateGlyphCache(paint, skiaGlyph, newGlyph);
579
580 return newGlyph;
581}
582
Romain Guy2577db12011-01-18 13:02:38 -0800583Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
Romain Guybd496bc2011-08-02 17:32:41 -0700584 int flags, uint32_t italicStyle, uint32_t scaleX,
585 SkPaint::Style style, uint32_t strokeWidth) {
Romain Guy694b5192010-07-21 21:33:20 -0700586 Vector<Font*> &activeFonts = state->mActiveFonts;
587
588 for (uint32_t i = 0; i < activeFonts.size(); i++) {
Romain Guy51769a62010-07-23 00:28:00 -0700589 Font* font = activeFonts[i];
Romain Guy2577db12011-01-18 13:02:38 -0800590 if (font->mFontId == fontId && font->mFontSize == fontSize &&
Chet Haase8668f8a2011-03-02 13:51:36 -0800591 font->mFlags == flags && font->mItalicStyle == italicStyle &&
Romain Guybd496bc2011-08-02 17:32:41 -0700592 font->mScaleX == scaleX && font->mStyle == style &&
593 (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
Romain Guy51769a62010-07-23 00:28:00 -0700594 return font;
Romain Guy694b5192010-07-21 21:33:20 -0700595 }
596 }
597
Romain Guybd496bc2011-08-02 17:32:41 -0700598 Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
599 scaleX, style, strokeWidth);
Romain Guy694b5192010-07-21 21:33:20 -0700600 activeFonts.push(newFont);
601 return newFont;
602}
603
604///////////////////////////////////////////////////////////////////////////////
605// FontRenderer
606///////////////////////////////////////////////////////////////////////////////
607
Romain Guy514fb182011-01-19 14:38:29 -0800608static bool sLogFontRendererCreate = true;
609
Romain Guy694b5192010-07-21 21:33:20 -0700610FontRenderer::FontRenderer() {
Romain Guyc9855a52011-01-21 21:14:15 -0800611 if (sLogFontRendererCreate) {
612 INIT_LOGD("Creating FontRenderer");
613 }
Romain Guy51769a62010-07-23 00:28:00 -0700614
Romain Guyb45c0c92010-08-26 20:35:23 -0700615 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700616 mInitialized = false;
617 mMaxNumberOfQuads = 1024;
618 mCurrentQuadIndex = 0;
619
Romain Guy9cccc2b2010-08-07 23:46:15 -0700620 mTextMeshPtr = NULL;
Chet Haase7de0cb12011-12-05 16:35:38 -0800621 mCurrentCacheTexture = NULL;
622 mLastCacheTexture = NULL;
Romain Guy9cccc2b2010-08-07 23:46:15 -0700623
Chet Haase2a47c142011-12-14 15:22:56 -0800624 mLinearFiltering = false;
625
Romain Guy694b5192010-07-21 21:33:20 -0700626 mIndexBufferID = 0;
627
Chet Haase7de0cb12011-12-05 16:35:38 -0800628 mSmallCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
629 mSmallCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -0700630
631 char property[PROPERTY_VALUE_MAX];
632 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800633 if (sLogFontRendererCreate) {
634 INIT_LOGD(" Setting text cache width to %s pixels", property);
635 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800636 mSmallCacheWidth = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -0700637 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800638 if (sLogFontRendererCreate) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800639 INIT_LOGD(" Using default text cache width of %i pixels", mSmallCacheWidth);
Romain Guy514fb182011-01-19 14:38:29 -0800640 }
Romain Guy51769a62010-07-23 00:28:00 -0700641 }
642
643 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800644 if (sLogFontRendererCreate) {
645 INIT_LOGD(" Setting text cache width to %s pixels", property);
646 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800647 mSmallCacheHeight = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -0700648 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800649 if (sLogFontRendererCreate) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800650 INIT_LOGD(" Using default text cache height of %i pixels", mSmallCacheHeight);
Romain Guy514fb182011-01-19 14:38:29 -0800651 }
Romain Guy51769a62010-07-23 00:28:00 -0700652 }
Romain Guy514fb182011-01-19 14:38:29 -0800653
654 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -0700655}
656
657FontRenderer::~FontRenderer() {
Chet Haase378e9192012-08-15 15:54:54 -0700658 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
659 delete mCacheTextures[i];
Romain Guy694b5192010-07-21 21:33:20 -0700660 }
Chet Haase378e9192012-08-15 15:54:54 -0700661 mCacheTextures.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700662
Romain Guy9cccc2b2010-08-07 23:46:15 -0700663 if (mInitialized) {
Romain Guya9dd8202012-03-26 14:52:00 -0700664 // Unbinding the buffer shouldn't be necessary but it crashes with some drivers
665 Caches::getInstance().unbindIndicesBuffer();
Romain Guyb0317982012-03-21 11:52:52 -0700666 glDeleteBuffers(1, &mIndexBufferID);
667
Romain Guy9cccc2b2010-08-07 23:46:15 -0700668 delete[] mTextMeshPtr;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700669 }
Romain Guy694b5192010-07-21 21:33:20 -0700670
671 Vector<Font*> fontsToDereference = mActiveFonts;
672 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
673 delete fontsToDereference[i];
674 }
675}
676
677void FontRenderer::flushAllAndInvalidate() {
678 if (mCurrentQuadIndex != 0) {
679 issueDrawCommand();
680 mCurrentQuadIndex = 0;
681 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700682
Romain Guy694b5192010-07-21 21:33:20 -0700683 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
684 mActiveFonts[i]->invalidateTextureCache();
685 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700686
Chet Haase378e9192012-08-15 15:54:54 -0700687 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
688 mCacheTextures[i]->init();
Romain Guy694b5192010-07-21 21:33:20 -0700689 }
Chet Haasee816bae2012-08-09 13:39:02 -0700690
Chet Haase378e9192012-08-15 15:54:54 -0700691 #if DEBUG_FONT_RENDERER
692 uint16_t totalGlyphs = 0;
693 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
694 totalGlyphs += mCacheTextures[i]->mNumGlyphs;
695 // Erase caches, just as a debugging facility
696 if (mCacheTextures[i]->mTexture) {
697 memset(mCacheTextures[i]->mTexture, 0,
698 mCacheTextures[i]->mWidth * mCacheTextures[i]->mHeight);
699 }
Chet Haasee816bae2012-08-09 13:39:02 -0700700 }
701 ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
702#endif
Romain Guy694b5192010-07-21 21:33:20 -0700703}
704
Chet Haase9a824562011-12-16 15:44:59 -0800705void FontRenderer::deallocateTextureMemory(CacheTexture *cacheTexture) {
706 if (cacheTexture && cacheTexture->mTexture) {
707 glDeleteTextures(1, &cacheTexture->mTextureId);
Romain Guy9d9758a2012-05-14 15:19:58 -0700708 delete[] cacheTexture->mTexture;
Chet Haase9a824562011-12-16 15:44:59 -0800709 cacheTexture->mTexture = NULL;
Romain Guy99a6ddd2012-05-14 15:32:18 -0700710 cacheTexture->mTextureId = 0;
Chet Haase9a824562011-12-16 15:44:59 -0800711 }
712}
713
714void FontRenderer::flushLargeCaches() {
Chet Haase378e9192012-08-15 15:54:54 -0700715 // Start from 1; don't deallocate smallest/default texture
716 for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
717 CacheTexture* cacheTexture = mCacheTextures[i];
718 if (cacheTexture->mTexture != NULL) {
719 cacheTexture->init();
720 for (uint32_t j = 0; j < mActiveFonts.size(); j++) {
721 mActiveFonts[j]->invalidateTextureCache(cacheTexture);
Chet Haasee816bae2012-08-09 13:39:02 -0700722 }
Chet Haase378e9192012-08-15 15:54:54 -0700723 deallocateTextureMemory(cacheTexture);
Chet Haase9a824562011-12-16 15:44:59 -0800724 }
725 }
Chet Haase9a824562011-12-16 15:44:59 -0800726}
727
Romain Guy9d9758a2012-05-14 15:19:58 -0700728void FontRenderer::allocateTextureMemory(CacheTexture* cacheTexture) {
Chet Haase2a47c142011-12-14 15:22:56 -0800729 int width = cacheTexture->mWidth;
730 int height = cacheTexture->mHeight;
Romain Guy9d9758a2012-05-14 15:19:58 -0700731
Chet Haase2a47c142011-12-14 15:22:56 -0800732 cacheTexture->mTexture = new uint8_t[width * height];
Romain Guy99a6ddd2012-05-14 15:32:18 -0700733
734 if (!cacheTexture->mTextureId) {
735 glGenTextures(1, &cacheTexture->mTextureId);
736 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700737
Romain Guy16c88082012-06-11 16:03:47 -0700738 Caches::getInstance().activeTexture(0);
Chet Haase2a47c142011-12-14 15:22:56 -0800739 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
740 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
741 // Initialize texture dimensions
742 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
743 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800744
Chet Haase2a47c142011-12-14 15:22:56 -0800745 const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST;
746 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
747 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
748
749 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
750 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
Chet Haase7de0cb12011-12-05 16:35:38 -0800751}
752
Chet Haase378e9192012-08-15 15:54:54 -0700753CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
754 uint32_t* startX, uint32_t* startY) {
755 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
756 if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
757 return mCacheTextures[i];
758 }
759 }
760 // Could not fit glyph into current cache textures
761 return NULL;
762}
763
Chet Haase7de0cb12011-12-05 16:35:38 -0800764void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
765 uint32_t* retOriginX, uint32_t* retOriginY) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700766 checkInit();
Chet Haase7de0cb12011-12-05 16:35:38 -0800767 cachedGlyph->mIsValid = false;
Romain Guy694b5192010-07-21 21:33:20 -0700768 // If the glyph is too tall, don't cache it
Chet Haase378e9192012-08-15 15:54:54 -0700769 if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
770 mCacheTextures[mCacheTextures.size() - 1]->mHeight) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700771 ALOGE("Font size too large to fit in cache. width, height = %i, %i",
772 (int) glyph.fWidth, (int) glyph.fHeight);
Chet Haase7de0cb12011-12-05 16:35:38 -0800773 return;
Romain Guy694b5192010-07-21 21:33:20 -0700774 }
775
776 // Now copy the bitmap into the cache texture
777 uint32_t startX = 0;
778 uint32_t startY = 0;
779
Chet Haase378e9192012-08-15 15:54:54 -0700780 CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700781
782 // If the new glyph didn't fit, flush the state so far and invalidate everything
Chet Haase378e9192012-08-15 15:54:54 -0700783 if (!cacheTexture) {
Romain Guy694b5192010-07-21 21:33:20 -0700784 flushAllAndInvalidate();
785
786 // Try to fit it again
Chet Haase378e9192012-08-15 15:54:54 -0700787 cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700788
789 // if we still don't fit, something is wrong and we shouldn't draw
Chet Haase378e9192012-08-15 15:54:54 -0700790 if (!cacheTexture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800791 return;
Romain Guy694b5192010-07-21 21:33:20 -0700792 }
793 }
794
Chet Haase378e9192012-08-15 15:54:54 -0700795 cachedGlyph->mCacheTexture = cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800796
Romain Guy694b5192010-07-21 21:33:20 -0700797 *retOriginX = startX;
798 *retOriginY = startY;
799
800 uint32_t endX = startX + glyph.fWidth;
801 uint32_t endY = startY + glyph.fHeight;
802
Chet Haase378e9192012-08-15 15:54:54 -0700803 uint32_t cacheWidth = cacheTexture->mWidth;
Romain Guy694b5192010-07-21 21:33:20 -0700804
Romain Guy9d9758a2012-05-14 15:19:58 -0700805 if (!cacheTexture->mTexture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800806 // Large-glyph texture memory is allocated only as needed
Chet Haase2a47c142011-12-14 15:22:56 -0800807 allocateTextureMemory(cacheTexture);
Chet Haase7de0cb12011-12-05 16:35:38 -0800808 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700809
Chet Haase7de0cb12011-12-05 16:35:38 -0800810 uint8_t* cacheBuffer = cacheTexture->mTexture;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700811 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700812 unsigned int stride = glyph.rowBytes();
813
814 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
Romain Guy33fa1f72012-08-07 19:09:57 -0700815
816 for (cacheX = startX - TEXTURE_BORDER_SIZE; cacheX < endX + TEXTURE_BORDER_SIZE; cacheX++) {
817 cacheBuffer[(startY - TEXTURE_BORDER_SIZE) * cacheWidth + cacheX] = 0;
818 cacheBuffer[(endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + cacheX] = 0;
819 }
820
821 for (cacheY = startY - TEXTURE_BORDER_SIZE + 1;
822 cacheY < endY + TEXTURE_BORDER_SIZE - 1; cacheY++) {
823 cacheBuffer[cacheY * cacheWidth + startX - TEXTURE_BORDER_SIZE] = 0;
824 cacheBuffer[cacheY * cacheWidth + endX + TEXTURE_BORDER_SIZE - 1] = 0;
825 }
826
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700827 if (mGammaTable) {
828 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
829 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
830 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
831 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
832 }
833 }
834 } else {
835 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
836 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
837 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
838 cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
839 }
Romain Guy694b5192010-07-21 21:33:20 -0700840 }
841 }
Romain Guy97771732012-02-28 18:17:02 -0800842
Chet Haase7de0cb12011-12-05 16:35:38 -0800843 cachedGlyph->mIsValid = true;
Romain Guy694b5192010-07-21 21:33:20 -0700844}
845
Chet Haase7de0cb12011-12-05 16:35:38 -0800846CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
Romain Guy0aa87bb2012-07-20 11:14:32 -0700847 CacheTexture* cacheTexture = new CacheTexture(width, height);
Romain Guy9d9758a2012-05-14 15:19:58 -0700848
Chet Haase2a47c142011-12-14 15:22:56 -0800849 if (allocate) {
850 allocateTextureMemory(cacheTexture);
851 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700852
Chet Haase2a47c142011-12-14 15:22:56 -0800853 return cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800854}
855
856void FontRenderer::initTextTexture() {
Chet Haase378e9192012-08-15 15:54:54 -0700857 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
858 delete mCacheTextures[i];
Romain Guy9d9758a2012-05-14 15:19:58 -0700859 }
Chet Haase378e9192012-08-15 15:54:54 -0700860 mCacheTextures.clear();
Romain Guy9d9758a2012-05-14 15:19:58 -0700861
Chet Haase7de0cb12011-12-05 16:35:38 -0800862 // Next, use other, separate caches for large glyphs.
863 uint16_t maxWidth = 0;
864 if (Caches::hasInstance()) {
865 maxWidth = Caches::getInstance().maxTextureSize;
Chet Haase44984ea2011-05-19 13:50:47 -0700866 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700867
Chet Haase7de0cb12011-12-05 16:35:38 -0800868 if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) {
869 maxWidth = MAX_TEXT_CACHE_WIDTH;
870 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700871
Chet Haase7de0cb12011-12-05 16:35:38 -0800872 mUploadTexture = false;
Chet Haase378e9192012-08-15 15:54:54 -0700873 mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
874 mCacheTextures.push(createCacheTexture(maxWidth, 256, false));
875 mCacheTextures.push(createCacheTexture(maxWidth, 256, false));
876 mCacheTextures.push(createCacheTexture(maxWidth, 512, false));
877 mCurrentCacheTexture = mCacheTextures[0];
Romain Guy694b5192010-07-21 21:33:20 -0700878}
879
880// Avoid having to reallocate memory and render quad by quad
881void FontRenderer::initVertexArrayBuffers() {
Romain Guyd71dd362011-12-12 19:03:35 -0800882 uint32_t numIndices = mMaxNumberOfQuads * 6;
883 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700884 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700885
886 // Four verts, two triangles , six indices per quad
887 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
888 int i6 = i * 6;
889 int i4 = i * 4;
890
891 indexBufferData[i6 + 0] = i4 + 0;
892 indexBufferData[i6 + 1] = i4 + 1;
893 indexBufferData[i6 + 2] = i4 + 2;
894
895 indexBufferData[i6 + 3] = i4 + 0;
896 indexBufferData[i6 + 4] = i4 + 2;
897 indexBufferData[i6 + 5] = i4 + 3;
898 }
899
900 glGenBuffers(1, &mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800901 Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700902 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
Romain Guy694b5192010-07-21 21:33:20 -0700903
904 free(indexBufferData);
905
Romain Guyd71dd362011-12-12 19:03:35 -0800906 uint32_t coordSize = 2;
Romain Guy694b5192010-07-21 21:33:20 -0700907 uint32_t uvSize = 2;
908 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700909 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
910 mTextMeshPtr = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700911}
912
913// We don't want to allocate anything unless we actually draw text
914void FontRenderer::checkInit() {
915 if (mInitialized) {
916 return;
917 }
918
919 initTextTexture();
920 initVertexArrayBuffers();
921
922 mInitialized = true;
923}
924
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700925void FontRenderer::checkTextureUpdate() {
Chet Haase7de0cb12011-12-05 16:35:38 -0800926 if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700927 return;
Romain Guy694b5192010-07-21 21:33:20 -0700928 }
929
Romain Guy2d4fd362011-12-13 22:00:19 -0800930 Caches& caches = Caches::getInstance();
931 GLuint lastTextureId = 0;
Chet Haase378e9192012-08-15 15:54:54 -0700932 // Iterate over all the cache textures and see which ones need to be updated
933 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
934 CacheTexture* cacheTexture = mCacheTextures[i];
935 if (cacheTexture->mDirty && cacheTexture->mTexture != NULL) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700936 uint32_t xOffset = 0;
Chet Haase378e9192012-08-15 15:54:54 -0700937 uint32_t width = cacheTexture->mWidth;
938 uint32_t height = cacheTexture->mHeight;
939 void* textureData = cacheTexture->mTexture;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700940
Romain Guy2d4fd362011-12-13 22:00:19 -0800941 if (cacheTexture->mTextureId != lastTextureId) {
942 caches.activeTexture(0);
943 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
944 lastTextureId = cacheTexture->mTextureId;
945 }
Chet Haasee816bae2012-08-09 13:39:02 -0700946#if DEBUG_FONT_RENDERER
Chet Haase378e9192012-08-15 15:54:54 -0700947 ALOGD("glTextSubimage for cacheTexture %d: xOff, width height = %d, %d, %d",
948 i, xOffset, width, height);
Chet Haasee816bae2012-08-09 13:39:02 -0700949#endif
Chet Haase378e9192012-08-15 15:54:54 -0700950 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, 0, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700951 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700952
Chet Haase378e9192012-08-15 15:54:54 -0700953 cacheTexture->mDirty = false;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700954 }
955 }
956
Romain Guy16c88082012-06-11 16:03:47 -0700957 caches.activeTexture(0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800958 glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
Chet Haase2a47c142011-12-14 15:22:56 -0800959 if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) {
960 const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST;
961 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
962 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
963 mCurrentCacheTexture->mLinearFiltering = mLinearFiltering;
964 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800965 mLastCacheTexture = mCurrentCacheTexture;
966
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700967 mUploadTexture = false;
968}
969
970void FontRenderer::issueDrawCommand() {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700971 checkTextureUpdate();
972
Romain Guy15bc6432011-12-13 13:11:32 -0800973 Caches& caches = Caches::getInstance();
Romain Guy2d4fd362011-12-13 22:00:19 -0800974 caches.bindIndicesBuffer(mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800975 if (!mDrawn) {
976 float* buffer = mTextMeshPtr;
977 int offset = 2;
978
979 bool force = caches.unbindMeshBuffer();
980 caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
981 caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
982 buffer + offset);
983 }
984
Romain Guy694b5192010-07-21 21:33:20 -0700985 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
Romain Guy5b3b3522010-10-27 18:57:51 -0700986
987 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700988}
989
Romain Guy97771732012-02-28 18:17:02 -0800990void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
991 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800992 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800993 if (texture != mCurrentCacheTexture) {
994 if (mCurrentQuadIndex != 0) {
995 // First, draw everything stored already which uses the previous texture
996 issueDrawCommand();
997 mCurrentQuadIndex = 0;
998 }
999 // Now use the new texture id
1000 mCurrentCacheTexture = texture;
1001 }
Romain Guy09147fb2010-07-22 13:08:20 -07001002
Romain Guy694b5192010-07-21 21:33:20 -07001003 const uint32_t vertsPerQuad = 4;
Romain Guyd71dd362011-12-12 19:03:35 -08001004 const uint32_t floatsPerVert = 4;
Romain Guy51769a62010-07-23 00:28:00 -07001005 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -07001006
Romain Guy694b5192010-07-21 21:33:20 -07001007 (*currentPos++) = x1;
1008 (*currentPos++) = y1;
Romain Guy694b5192010-07-21 21:33:20 -07001009 (*currentPos++) = u1;
1010 (*currentPos++) = v1;
1011
1012 (*currentPos++) = x2;
1013 (*currentPos++) = y2;
Romain Guy694b5192010-07-21 21:33:20 -07001014 (*currentPos++) = u2;
1015 (*currentPos++) = v2;
1016
1017 (*currentPos++) = x3;
1018 (*currentPos++) = y3;
Romain Guy694b5192010-07-21 21:33:20 -07001019 (*currentPos++) = u3;
1020 (*currentPos++) = v3;
1021
1022 (*currentPos++) = x4;
1023 (*currentPos++) = y4;
Romain Guy694b5192010-07-21 21:33:20 -07001024 (*currentPos++) = u4;
1025 (*currentPos++) = v4;
1026
1027 mCurrentQuadIndex++;
Romain Guy97771732012-02-28 18:17:02 -08001028}
1029
1030void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
1031 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
1032 float x4, float y4, float u4, float v4, CacheTexture* texture) {
1033
1034 if (mClip &&
1035 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
1036 return;
1037 }
1038
1039 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 -07001040
Romain Guy5b3b3522010-10-27 18:57:51 -07001041 if (mBounds) {
1042 mBounds->left = fmin(mBounds->left, x1);
1043 mBounds->top = fmin(mBounds->top, y3);
1044 mBounds->right = fmax(mBounds->right, x3);
1045 mBounds->bottom = fmax(mBounds->bottom, y1);
1046 }
1047
Romain Guy694b5192010-07-21 21:33:20 -07001048 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
1049 issueDrawCommand();
1050 mCurrentQuadIndex = 0;
1051 }
1052}
1053
Romain Guy97771732012-02-28 18:17:02 -08001054void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
1055 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
1056 float x4, float y4, float u4, float v4, CacheTexture* texture) {
1057
1058 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
1059
1060 if (mBounds) {
1061 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
1062 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
1063 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
1064 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
1065 }
1066
1067 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
1068 issueDrawCommand();
1069 mCurrentQuadIndex = 0;
1070 }
1071}
1072
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -07001073void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
Romain Guy325a0f92011-01-05 15:26:55 -08001074 int flags = 0;
1075 if (paint->isFakeBoldText()) {
1076 flags |= Font::kFakeBold;
1077 }
Romain Guy2577db12011-01-18 13:02:38 -08001078
1079 const float skewX = paint->getTextSkewX();
1080 uint32_t italicStyle = *(uint32_t*) &skewX;
Chet Haase8668f8a2011-03-02 13:51:36 -08001081 const float scaleXFloat = paint->getTextScaleX();
1082 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
Romain Guybd496bc2011-08-02 17:32:41 -07001083 SkPaint::Style style = paint->getStyle();
1084 const float strokeWidthFloat = paint->getStrokeWidth();
1085 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
1086 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
1087 scaleX, style, strokeWidth);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -07001088
Romain Guy694b5192010-07-21 21:33:20 -07001089}
Romain Guy7975fb62010-10-01 16:36:14 -07001090
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001091FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Raph Levien416a8472012-07-19 22:48:17 -07001092 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
Romain Guy1e45aae2010-08-13 19:39:53 -07001093 checkInit();
1094
1095 if (!mCurrentFont) {
1096 DropShadow image;
1097 image.width = 0;
1098 image.height = 0;
1099 image.image = NULL;
1100 image.penX = 0;
1101 image.penY = 0;
1102 return image;
1103 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001104
Romain Guy2d4fd362011-12-13 22:00:19 -08001105 mDrawn = false;
Romain Guyff98fa52011-11-28 09:35:09 -08001106 mClip = NULL;
1107 mBounds = NULL;
1108
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001109 Rect bounds;
Raph Levien416a8472012-07-19 22:48:17 -07001110 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
Romain Guyff98fa52011-11-28 09:35:09 -08001111
Romain Guy1e45aae2010-08-13 19:39:53 -07001112 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
1113 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001114 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guyff98fa52011-11-28 09:35:09 -08001115
Romain Guy1e45aae2010-08-13 19:39:53 -07001116 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001117 dataBuffer[i] = 0;
1118 }
Romain Guy1e45aae2010-08-13 19:39:53 -07001119
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001120 int penX = radius - bounds.left;
1121 int penY = radius - bounds.bottom;
1122
Romain Guy726aeba2011-06-01 14:52:00 -07001123 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
Raph Levien416a8472012-07-19 22:48:17 -07001124 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001125 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
1126
1127 DropShadow image;
1128 image.width = paddedWidth;
1129 image.height = paddedHeight;
1130 image.image = dataBuffer;
1131 image.penX = penX;
1132 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -08001133
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001134 return image;
1135}
Romain Guy694b5192010-07-21 21:33:20 -07001136
Romain Guy671d6cf2012-01-18 12:39:17 -08001137void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -07001138 checkInit();
1139
Romain Guy5b3b3522010-10-27 18:57:51 -07001140 mDrawn = false;
1141 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -07001142 mClip = clip;
Romain Guy671d6cf2012-01-18 12:39:17 -08001143}
Romain Guyff98fa52011-11-28 09:35:09 -08001144
Romain Guy671d6cf2012-01-18 12:39:17 -08001145void FontRenderer::finishRender() {
Romain Guy5b3b3522010-10-27 18:57:51 -07001146 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -08001147 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -07001148
1149 if (mCurrentQuadIndex != 0) {
1150 issueDrawCommand();
1151 mCurrentQuadIndex = 0;
1152 }
Romain Guy671d6cf2012-01-18 12:39:17 -08001153}
1154
Chet Haasee816bae2012-08-09 13:39:02 -07001155void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs) {
1156 int flags = 0;
1157 if (paint->isFakeBoldText()) {
1158 flags |= Font::kFakeBold;
1159 }
1160 const float skewX = paint->getTextSkewX();
1161 uint32_t italicStyle = *(uint32_t*) &skewX;
1162 const float scaleXFloat = paint->getTextScaleX();
1163 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
1164 SkPaint::Style style = paint->getStyle();
1165 const float strokeWidthFloat = paint->getStrokeWidth();
1166 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
1167 float fontSize = paint->getTextSize();
1168 Font* font = Font::create(this, SkTypeface::UniqueID(paint->getTypeface()),
1169 fontSize, flags, italicStyle, scaleX, style, strokeWidth);
1170
1171 font->precache(paint, text, numGlyphs);
1172}
1173
Romain Guy671d6cf2012-01-18 12:39:17 -08001174bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
1175 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
1176 if (!mCurrentFont) {
1177 ALOGE("No font set");
1178 return false;
1179 }
1180
1181 initRender(clip, bounds);
1182 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
1183 finishRender();
1184
1185 return mDrawn;
1186}
1187
1188bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
1189 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
1190 const float* positions, Rect* bounds) {
1191 if (!mCurrentFont) {
1192 ALOGE("No font set");
1193 return false;
1194 }
1195
1196 initRender(clip, bounds);
1197 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
1198 finishRender();
Romain Guy5b3b3522010-10-27 18:57:51 -07001199
1200 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -07001201}
1202
Romain Guy97771732012-02-28 18:17:02 -08001203bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
1204 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
1205 float hOffset, float vOffset, Rect* bounds) {
1206 if (!mCurrentFont) {
1207 ALOGE("No font set");
1208 return false;
1209 }
1210
1211 initRender(clip, bounds);
1212 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
1213 finishRender();
1214
1215 return mDrawn;
1216}
1217
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001218void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
1219 // Compute gaussian weights for the blur
1220 // e is the euler's number
1221 float e = 2.718281828459045f;
1222 float pi = 3.1415926535897932f;
1223 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
1224 // x is of the form [-radius .. 0 .. radius]
1225 // and sigma varies with radius.
1226 // Based on some experimental radius values and sigma's
1227 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001228 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001229 // The larger the radius gets, the more our gaussian blur
1230 // will resemble a box blur since with large sigma
1231 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -08001232 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001233
1234 // Now compute the coefficints
1235 // We will store some redundant values to save some math during
1236 // the blur calculations
1237 // precompute some values
1238 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
1239 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
1240
1241 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -08001242 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -07001243 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001244 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
1245 normalizeFactor += weights[r + radius];
1246 }
1247
1248 //Now we need to normalize the weights because all our coefficients need to add up to one
1249 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -08001250 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001251 weights[r + radius] *= normalizeFactor;
1252 }
1253}
1254
1255void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -07001256 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001257 float blurredPixel = 0.0f;
1258 float currentPixel = 0.0f;
1259
Romain Guy325a0f92011-01-05 15:26:55 -08001260 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001261
1262 const uint8_t* input = source + y * width;
1263 uint8_t* output = dest + y * width;
1264
Romain Guy325a0f92011-01-05 15:26:55 -08001265 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001266 blurredPixel = 0.0f;
1267 const float* gPtr = weights;
1268 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -08001269 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001270 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -08001271 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -07001272 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001273 blurredPixel += currentPixel * gPtr[0];
1274 gPtr++;
1275 i++;
1276 }
1277 } else {
Romain Guy325a0f92011-01-05 15:26:55 -08001278 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001279 // Stepping left and right away from the pixel
1280 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -08001281 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001282 validW = 0;
1283 }
Romain Guy325a0f92011-01-05 15:26:55 -08001284 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001285 validW = width - 1;
1286 }
1287
Romain Guy325a0f92011-01-05 15:26:55 -08001288 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001289 blurredPixel += currentPixel * gPtr[0];
1290 gPtr++;
1291 }
1292 }
1293 *output = (uint8_t)blurredPixel;
1294 output ++;
1295 }
1296 }
1297}
1298
1299void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -07001300 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001301 float blurredPixel = 0.0f;
1302 float currentPixel = 0.0f;
1303
Romain Guy325a0f92011-01-05 15:26:55 -08001304 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001305
1306 uint8_t* output = dest + y * width;
1307
Romain Guy325a0f92011-01-05 15:26:55 -08001308 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001309 blurredPixel = 0.0f;
1310 const float* gPtr = weights;
1311 const uint8_t* input = source + x;
1312 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -08001313 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001314 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -08001315 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001316 currentPixel = (float)(*i);
1317 blurredPixel += currentPixel * gPtr[0];
1318 gPtr++;
1319 i += width;
1320 }
1321 } else {
Romain Guy325a0f92011-01-05 15:26:55 -08001322 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001323 int validH = y + r;
1324 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -08001325 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001326 validH = 0;
1327 }
Romain Guy325a0f92011-01-05 15:26:55 -08001328 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001329 validH = height - 1;
1330 }
1331
1332 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -08001333 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001334 blurredPixel += currentPixel * gPtr[0];
1335 gPtr++;
1336 }
1337 }
Romain Guy325a0f92011-01-05 15:26:55 -08001338 *output = (uint8_t) blurredPixel;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001339 output ++;
1340 }
1341 }
1342}
1343
1344
1345void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
1346 float *gaussian = new float[2 * radius + 1];
1347 computeGaussianWeights(gaussian, radius);
Romain Guyd71dd362011-12-12 19:03:35 -08001348
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001349 uint8_t* scratch = new uint8_t[width * height];
Romain Guyd71dd362011-12-12 19:03:35 -08001350
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001351 horizontalBlur(gaussian, radius, image, scratch, width, height);
1352 verticalBlur(gaussian, radius, scratch, image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -08001353
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001354 delete[] gaussian;
1355 delete[] scratch;
1356}
1357
Romain Guy694b5192010-07-21 21:33:20 -07001358}; // namespace uirenderer
1359}; // namespace android