blob: 3df105be19ea4ba5bdd91c85d258ba729a321a7c [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 Haase7de0cb12011-12-05 16:35:38 -080040#define TEXTURE_BORDER_SIZE 2
41
42///////////////////////////////////////////////////////////////////////////////
43// CacheTextureLine
44///////////////////////////////////////////////////////////////////////////////
45
46bool CacheTextureLine::fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
47 if (glyph.fHeight + TEXTURE_BORDER_SIZE > mMaxHeight) {
48 return false;
49 }
50
51 if (mCurrentCol + glyph.fWidth + TEXTURE_BORDER_SIZE < mMaxWidth) {
52 *retOriginX = mCurrentCol + 1;
53 *retOriginY = mCurrentRow + 1;
54 mCurrentCol += glyph.fWidth + TEXTURE_BORDER_SIZE;
55 mDirty = true;
56 return true;
57 }
58
59 return false;
60}
Chet Haase44984ea2011-05-19 13:50:47 -070061
Romain Guy51769a62010-07-23 00:28:00 -070062///////////////////////////////////////////////////////////////////////////////
Romain Guy694b5192010-07-21 21:33:20 -070063// Font
64///////////////////////////////////////////////////////////////////////////////
65
Romain Guy2577db12011-01-18 13:02:38 -080066Font::Font(FontRenderer* state, uint32_t fontId, float fontSize,
Romain Guybd496bc2011-08-02 17:32:41 -070067 int flags, uint32_t italicStyle, uint32_t scaleX,
68 SkPaint::Style style, uint32_t strokeWidth) :
Romain Guy2577db12011-01-18 13:02:38 -080069 mState(state), mFontId(fontId), mFontSize(fontSize),
Romain Guybd496bc2011-08-02 17:32:41 -070070 mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX),
71 mStyle(style), mStrokeWidth(mStrokeWidth) {
Romain Guy694b5192010-07-21 21:33:20 -070072}
73
74
75Font::~Font() {
76 for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
77 if (mState->mActiveFonts[ct] == this) {
78 mState->mActiveFonts.removeAt(ct);
79 break;
80 }
81 }
82
83 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
Romain Guy726aeba2011-06-01 14:52:00 -070084 delete mCachedGlyphs.valueAt(i);
Romain Guy694b5192010-07-21 21:33:20 -070085 }
86}
87
Chet Haase9a824562011-12-16 15:44:59 -080088void Font::invalidateTextureCache(CacheTextureLine *cacheLine) {
Romain Guy694b5192010-07-21 21:33:20 -070089 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
Chet Haase9a824562011-12-16 15:44:59 -080090 CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i);
91 if (cacheLine == NULL || cachedGlyph->mCachedTextureLine == cacheLine) {
92 cachedGlyph->mIsValid = false;
93 }
Romain Guy694b5192010-07-21 21:33:20 -070094 }
95}
96
Romain Guy671d6cf2012-01-18 12:39:17 -080097void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
98 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070099 int nPenX = x + glyph->mBitmapLeft;
100 int nPenY = y + glyph->mBitmapTop;
101
102 int width = (int) glyph->mBitmapWidth;
103 int height = (int) glyph->mBitmapHeight;
104
Romain Guy61c8c9c2010-08-09 20:48:09 -0700105 if (bounds->bottom > nPenY) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700106 bounds->bottom = nPenY;
107 }
Romain Guy61c8c9c2010-08-09 20:48:09 -0700108 if (bounds->left > nPenX) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700109 bounds->left = nPenX;
110 }
Romain Guy61c8c9c2010-08-09 20:48:09 -0700111 if (bounds->right < nPenX + width) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700112 bounds->right = nPenX + width;
113 }
Romain Guy61c8c9c2010-08-09 20:48:09 -0700114 if (bounds->top < nPenY + height) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700115 bounds->top = nPenY + height;
116 }
117}
118
Romain Guy671d6cf2012-01-18 12:39:17 -0800119void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
120 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
Romain Guy694b5192010-07-21 21:33:20 -0700121 int nPenX = x + glyph->mBitmapLeft;
122 int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
123
Romain Guy51769a62010-07-23 00:28:00 -0700124 float u1 = glyph->mBitmapMinU;
125 float u2 = glyph->mBitmapMaxU;
126 float v1 = glyph->mBitmapMinV;
127 float v2 = glyph->mBitmapMaxV;
128
129 int width = (int) glyph->mBitmapWidth;
130 int height = (int) glyph->mBitmapHeight;
131
Romain Guyd71dd362011-12-12 19:03:35 -0800132 mState->appendMeshQuad(nPenX, nPenY, u1, v2,
133 nPenX + width, nPenY, u2, v2,
134 nPenX + width, nPenY - height, u2, v1,
Chet Haase7de0cb12011-12-05 16:35:38 -0800135 nPenX, nPenY - height, u1, v1, glyph->mCachedTextureLine->mCacheTexture);
Romain Guy694b5192010-07-21 21:33:20 -0700136}
137
Romain Guy671d6cf2012-01-18 12:39:17 -0800138void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
139 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700140 int nPenX = x + glyph->mBitmapLeft;
141 int nPenY = y + glyph->mBitmapTop;
142
143 uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
144 uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
145
Chet Haase7de0cb12011-12-05 16:35:38 -0800146 CacheTexture *cacheTexture = glyph->mCachedTextureLine->mCacheTexture;
147 uint32_t cacheWidth = cacheTexture->mWidth;
148 const uint8_t* cacheBuffer = cacheTexture->mTexture;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700149
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700150 uint32_t cacheX = 0, cacheY = 0;
151 int32_t bX = 0, bY = 0;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700152 for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
153 for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
Romain Guyb6294902012-02-02 15:13:18 -0800154#if DEBUG_FONT_RENDERER
Romain Guyb45c0c92010-08-26 20:35:23 -0700155 if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
Steve Block3762c312012-01-06 19:20:56 +0000156 ALOGE("Skipping invalid index");
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700157 continue;
158 }
Romain Guyb6294902012-02-02 15:13:18 -0800159#endif
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700160 uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
161 bitmap[bY * bitmapW + bX] = tempCol;
162 }
163 }
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700164}
165
Chet Haase7de0cb12011-12-05 16:35:38 -0800166CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700167 CachedGlyphInfo* cachedGlyph = NULL;
Romain Guy726aeba2011-06-01 14:52:00 -0700168 ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
Romain Guy1e45aae2010-08-13 19:39:53 -0700169 if (index >= 0) {
170 cachedGlyph = mCachedGlyphs.valueAt(index);
171 } else {
Romain Guy726aeba2011-06-01 14:52:00 -0700172 cachedGlyph = cacheGlyph(paint, textUnit);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700173 }
174
175 // Is the glyph still in texture cache?
176 if (!cachedGlyph->mIsValid) {
Romain Guy726aeba2011-06-01 14:52:00 -0700177 const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700178 updateGlyphCache(paint, skiaGlyph, cachedGlyph);
179 }
180
181 return cachedGlyph;
182}
183
Romain Guy726aeba2011-06-01 14:52:00 -0700184void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700185 int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
186 if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
Romain Guy726aeba2011-06-01 14:52:00 -0700187 render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
Romain Guy671d6cf2012-01-18 12:39:17 -0800188 bitmapW, bitmapH, NULL, NULL);
Romain Guy61c8c9c2010-08-09 20:48:09 -0700189 } else {
Romain Guy726aeba2011-06-01 14:52:00 -0700190 render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
Romain Guy671d6cf2012-01-18 12:39:17 -0800191 0, 0, NULL, NULL);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700192 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700193}
194
Romain Guy671d6cf2012-01-18 12:39:17 -0800195void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
196 int numGlyphs, int x, int y, const float* positions) {
197 render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
198 0, 0, NULL, positions);
199}
200
Romain Guy726aeba2011-06-01 14:52:00 -0700201void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700202 int numGlyphs, Rect *bounds) {
203 if (bounds == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000204 ALOGE("No return rectangle provided to measure text");
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700205 return;
206 }
207 bounds->set(1e6, -1e6, -1e6, 1e6);
Romain Guy671d6cf2012-01-18 12:39:17 -0800208 render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, NULL);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700209}
210
Romain Guy58ef7fb2010-09-13 12:52:37 -0700211#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16)
Romain Guy2bffd262010-09-12 17:40:02 -0700212
Romain Guy726aeba2011-06-01 14:52:00 -0700213void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700214 int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
Romain Guy671d6cf2012-01-18 12:39:17 -0800215 uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions) {
Romain Guy694b5192010-07-21 21:33:20 -0700216 if (numGlyphs == 0 || text == NULL || len == 0) {
217 return;
218 }
219
Romain Guy671d6cf2012-01-18 12:39:17 -0800220 int glyphsCount = 0;
Romain Guy2bffd262010-09-12 17:40:02 -0700221
Romain Guy694b5192010-07-21 21:33:20 -0700222 text += start;
223
Romain Guy671d6cf2012-01-18 12:39:17 -0800224 static RenderGlyph gRenderGlyph[] = {
225 &android::uirenderer::Font::drawCachedGlyph,
226 &android::uirenderer::Font::drawCachedGlyphBitmap,
227 &android::uirenderer::Font::measureCachedGlyph
228 };
229 RenderGlyph render = gRenderGlyph[mode];
Romain Guy694b5192010-07-21 21:33:20 -0700230
Romain Guyb6294902012-02-02 15:13:18 -0800231 if (CC_LIKELY(positions == NULL)) {
Romain Guy671d6cf2012-01-18 12:39:17 -0800232 SkFixed prevRsbDelta = 0;
Romain Guy694b5192010-07-21 21:33:20 -0700233
Romain Guy671d6cf2012-01-18 12:39:17 -0800234 float penX = x;
235 int penY = y;
Romain Guy694b5192010-07-21 21:33:20 -0700236
Romain Guy671d6cf2012-01-18 12:39:17 -0800237 penX += 0.5f;
238
239 while (glyphsCount < numGlyphs) {
240 glyph_t glyph = GET_GLYPH(text);
241
242 // Reached the end of the string
243 if (IS_END_OF_STRING(glyph)) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700244 break;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700245 }
Romain Guy671d6cf2012-01-18 12:39:17 -0800246
247 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
248 penX += SkFixedToFloat(SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta));
249 prevRsbDelta = cachedGlyph->mRsbDelta;
250
251 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
252 if (cachedGlyph->mIsValid) {
253 (*this.*render)(cachedGlyph, (int) floorf(penX), penY,
254 bitmap, bitmapW, bitmapH, bounds, positions);
255 }
256
257 penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
258
259 glyphsCount++;
Romain Guy694b5192010-07-21 21:33:20 -0700260 }
Romain Guy671d6cf2012-01-18 12:39:17 -0800261 } else {
262 const SkPaint::Align align = paint->getTextAlign();
Romain Guy694b5192010-07-21 21:33:20 -0700263
Romain Guy671d6cf2012-01-18 12:39:17 -0800264 // This is for renderPosText()
265 while (glyphsCount < numGlyphs) {
266 glyph_t glyph = GET_GLYPH(text);
Romain Guy694b5192010-07-21 21:33:20 -0700267
Romain Guy671d6cf2012-01-18 12:39:17 -0800268 // Reached the end of the string
269 if (IS_END_OF_STRING(glyph)) {
270 break;
271 }
272
273 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
274
275 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
276 if (cachedGlyph->mIsValid) {
277 int penX = x + positions[(glyphsCount << 1)];
278 int penY = y + positions[(glyphsCount << 1) + 1];
279
280 switch (align) {
281 case SkPaint::kRight_Align:
282 penX -= SkFixedToFloat(cachedGlyph->mAdvanceX);
283 penY -= SkFixedToFloat(cachedGlyph->mAdvanceY);
284 break;
285 case SkPaint::kCenter_Align:
286 penX -= SkFixedToFloat(cachedGlyph->mAdvanceX >> 1);
287 penY -= SkFixedToFloat(cachedGlyph->mAdvanceY >> 1);
288 default:
289 break;
290 }
291
292 (*this.*render)(cachedGlyph, penX, penY,
293 bitmap, bitmapW, bitmapH, bounds, positions);
294 }
295
296 glyphsCount++;
Romain Guy694b5192010-07-21 21:33:20 -0700297 }
298 }
299}
300
Romain Guy51769a62010-07-23 00:28:00 -0700301void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
Romain Guy694b5192010-07-21 21:33:20 -0700302 glyph->mAdvanceX = skiaGlyph.fAdvanceX;
303 glyph->mAdvanceY = skiaGlyph.fAdvanceY;
304 glyph->mBitmapLeft = skiaGlyph.fLeft;
305 glyph->mBitmapTop = skiaGlyph.fTop;
Romain Guy2bffd262010-09-12 17:40:02 -0700306 glyph->mLsbDelta = skiaGlyph.fLsbDelta;
307 glyph->mRsbDelta = skiaGlyph.fRsbDelta;
Romain Guy694b5192010-07-21 21:33:20 -0700308
309 uint32_t startX = 0;
310 uint32_t startY = 0;
311
Romain Guy694b5192010-07-21 21:33:20 -0700312 // Get the bitmap for the glyph
313 paint->findImage(skiaGlyph);
Chet Haase7de0cb12011-12-05 16:35:38 -0800314 mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700315
316 if (!glyph->mIsValid) {
317 return;
318 }
319
320 uint32_t endX = startX + skiaGlyph.fWidth;
321 uint32_t endY = startY + skiaGlyph.fHeight;
322
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700323 glyph->mStartX = startX;
324 glyph->mStartY = startY;
Romain Guy694b5192010-07-21 21:33:20 -0700325 glyph->mBitmapWidth = skiaGlyph.fWidth;
326 glyph->mBitmapHeight = skiaGlyph.fHeight;
327
Chet Haase7de0cb12011-12-05 16:35:38 -0800328 uint32_t cacheWidth = glyph->mCachedTextureLine->mCacheTexture->mWidth;
329 uint32_t cacheHeight = glyph->mCachedTextureLine->mCacheTexture->mHeight;
Romain Guy694b5192010-07-21 21:33:20 -0700330
331 glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
332 glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
333 glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
334 glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
335
Romain Guy51769a62010-07-23 00:28:00 -0700336 mState->mUploadTexture = true;
Romain Guy694b5192010-07-21 21:33:20 -0700337}
338
Chet Haase7de0cb12011-12-05 16:35:38 -0800339CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) {
Romain Guy51769a62010-07-23 00:28:00 -0700340 CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
Romain Guy694b5192010-07-21 21:33:20 -0700341 mCachedGlyphs.add(glyph, newGlyph);
342
Romain Guy726aeba2011-06-01 14:52:00 -0700343 const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
Romain Guy694b5192010-07-21 21:33:20 -0700344 newGlyph->mGlyphIndex = skiaGlyph.fID;
345 newGlyph->mIsValid = false;
346
347 updateGlyphCache(paint, skiaGlyph, newGlyph);
348
349 return newGlyph;
350}
351
Romain Guy2577db12011-01-18 13:02:38 -0800352Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
Romain Guybd496bc2011-08-02 17:32:41 -0700353 int flags, uint32_t italicStyle, uint32_t scaleX,
354 SkPaint::Style style, uint32_t strokeWidth) {
Romain Guy694b5192010-07-21 21:33:20 -0700355 Vector<Font*> &activeFonts = state->mActiveFonts;
356
357 for (uint32_t i = 0; i < activeFonts.size(); i++) {
Romain Guy51769a62010-07-23 00:28:00 -0700358 Font* font = activeFonts[i];
Romain Guy2577db12011-01-18 13:02:38 -0800359 if (font->mFontId == fontId && font->mFontSize == fontSize &&
Chet Haase8668f8a2011-03-02 13:51:36 -0800360 font->mFlags == flags && font->mItalicStyle == italicStyle &&
Romain Guybd496bc2011-08-02 17:32:41 -0700361 font->mScaleX == scaleX && font->mStyle == style &&
362 (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
Romain Guy51769a62010-07-23 00:28:00 -0700363 return font;
Romain Guy694b5192010-07-21 21:33:20 -0700364 }
365 }
366
Romain Guybd496bc2011-08-02 17:32:41 -0700367 Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
368 scaleX, style, strokeWidth);
Romain Guy694b5192010-07-21 21:33:20 -0700369 activeFonts.push(newFont);
370 return newFont;
371}
372
373///////////////////////////////////////////////////////////////////////////////
374// FontRenderer
375///////////////////////////////////////////////////////////////////////////////
376
Romain Guy514fb182011-01-19 14:38:29 -0800377static bool sLogFontRendererCreate = true;
378
Romain Guy694b5192010-07-21 21:33:20 -0700379FontRenderer::FontRenderer() {
Romain Guyc9855a52011-01-21 21:14:15 -0800380 if (sLogFontRendererCreate) {
381 INIT_LOGD("Creating FontRenderer");
382 }
Romain Guy51769a62010-07-23 00:28:00 -0700383
Romain Guyb45c0c92010-08-26 20:35:23 -0700384 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700385 mInitialized = false;
386 mMaxNumberOfQuads = 1024;
387 mCurrentQuadIndex = 0;
388
Romain Guy9cccc2b92010-08-07 23:46:15 -0700389 mTextMeshPtr = NULL;
Chet Haase7de0cb12011-12-05 16:35:38 -0800390 mCurrentCacheTexture = NULL;
391 mLastCacheTexture = NULL;
392 mCacheTextureSmall = NULL;
393 mCacheTexture128 = NULL;
394 mCacheTexture256 = NULL;
395 mCacheTexture512 = NULL;
Romain Guy9cccc2b92010-08-07 23:46:15 -0700396
Chet Haase2a47c142011-12-14 15:22:56 -0800397 mLinearFiltering = false;
398
Romain Guy694b5192010-07-21 21:33:20 -0700399 mIndexBufferID = 0;
400
Chet Haase7de0cb12011-12-05 16:35:38 -0800401 mSmallCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
402 mSmallCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -0700403
404 char property[PROPERTY_VALUE_MAX];
405 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800406 if (sLogFontRendererCreate) {
407 INIT_LOGD(" Setting text cache width to %s pixels", property);
408 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800409 mSmallCacheWidth = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -0700410 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800411 if (sLogFontRendererCreate) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800412 INIT_LOGD(" Using default text cache width of %i pixels", mSmallCacheWidth);
Romain Guy514fb182011-01-19 14:38:29 -0800413 }
Romain Guy51769a62010-07-23 00:28:00 -0700414 }
415
416 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800417 if (sLogFontRendererCreate) {
418 INIT_LOGD(" Setting text cache width to %s pixels", property);
419 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800420 mSmallCacheHeight = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -0700421 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800422 if (sLogFontRendererCreate) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800423 INIT_LOGD(" Using default text cache height of %i pixels", mSmallCacheHeight);
Romain Guy514fb182011-01-19 14:38:29 -0800424 }
Romain Guy51769a62010-07-23 00:28:00 -0700425 }
Romain Guy514fb182011-01-19 14:38:29 -0800426
427 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -0700428}
429
430FontRenderer::~FontRenderer() {
431 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
432 delete mCacheLines[i];
433 }
434 mCacheLines.clear();
435
Romain Guy9cccc2b92010-08-07 23:46:15 -0700436 if (mInitialized) {
437 delete[] mTextMeshPtr;
Chet Haase7de0cb12011-12-05 16:35:38 -0800438 delete mCacheTextureSmall;
439 delete mCacheTexture128;
440 delete mCacheTexture256;
441 delete mCacheTexture512;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700442 }
Romain Guy694b5192010-07-21 21:33:20 -0700443
444 Vector<Font*> fontsToDereference = mActiveFonts;
445 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
446 delete fontsToDereference[i];
447 }
448}
449
450void FontRenderer::flushAllAndInvalidate() {
451 if (mCurrentQuadIndex != 0) {
452 issueDrawCommand();
453 mCurrentQuadIndex = 0;
454 }
455 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
456 mActiveFonts[i]->invalidateTextureCache();
457 }
458 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
459 mCacheLines[i]->mCurrentCol = 0;
460 }
461}
462
Chet Haase9a824562011-12-16 15:44:59 -0800463void FontRenderer::deallocateTextureMemory(CacheTexture *cacheTexture) {
464 if (cacheTexture && cacheTexture->mTexture) {
465 glDeleteTextures(1, &cacheTexture->mTextureId);
466 delete cacheTexture->mTexture;
467 cacheTexture->mTexture = NULL;
468 }
469}
470
471void FontRenderer::flushLargeCaches() {
472 if ((!mCacheTexture128 || !mCacheTexture128->mTexture) &&
473 (!mCacheTexture256 || !mCacheTexture256->mTexture) &&
474 (!mCacheTexture512 || !mCacheTexture512->mTexture)) {
475 // Typical case; no large glyph caches allocated
476 return;
477 }
478
479 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
480 CacheTextureLine* cacheLine = mCacheLines[i];
481 if ((cacheLine->mCacheTexture == mCacheTexture128 ||
482 cacheLine->mCacheTexture == mCacheTexture256 ||
483 cacheLine->mCacheTexture == mCacheTexture512) &&
484 cacheLine->mCacheTexture->mTexture != NULL) {
485 cacheLine->mCurrentCol = 0;
486 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
487 mActiveFonts[i]->invalidateTextureCache(cacheLine);
488 }
489 }
490 }
491
492 deallocateTextureMemory(mCacheTexture128);
493 deallocateTextureMemory(mCacheTexture256);
494 deallocateTextureMemory(mCacheTexture512);
495}
496
Chet Haase2a47c142011-12-14 15:22:56 -0800497void FontRenderer::allocateTextureMemory(CacheTexture *cacheTexture) {
498 int width = cacheTexture->mWidth;
499 int height = cacheTexture->mHeight;
500 cacheTexture->mTexture = new uint8_t[width * height];
501 memset(cacheTexture->mTexture, 0, width * height * sizeof(uint8_t));
502 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
503 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
504 // Initialize texture dimensions
505 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
506 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800507
Chet Haase2a47c142011-12-14 15:22:56 -0800508 const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST;
509 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
510 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
511
512 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
513 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
Chet Haase7de0cb12011-12-05 16:35:38 -0800514}
515
516void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
517 uint32_t* retOriginX, uint32_t* retOriginY) {
518 cachedGlyph->mIsValid = false;
Romain Guy694b5192010-07-21 21:33:20 -0700519 // If the glyph is too tall, don't cache it
Chet Haase7de0cb12011-12-05 16:35:38 -0800520 if (glyph.fHeight + TEXTURE_BORDER_SIZE > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
Steve Block3762c312012-01-06 19:20:56 +0000521 ALOGE("Font size to large to fit in cache. width, height = %i, %i",
Chet Haase7de0cb12011-12-05 16:35:38 -0800522 (int) glyph.fWidth, (int) glyph.fHeight);
523 return;
Romain Guy694b5192010-07-21 21:33:20 -0700524 }
525
526 // Now copy the bitmap into the cache texture
527 uint32_t startX = 0;
528 uint32_t startY = 0;
529
530 bool bitmapFit = false;
Chet Haase7de0cb12011-12-05 16:35:38 -0800531 CacheTextureLine *cacheLine;
Romain Guy694b5192010-07-21 21:33:20 -0700532 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
533 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
534 if (bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800535 cacheLine = mCacheLines[i];
Romain Guy694b5192010-07-21 21:33:20 -0700536 break;
537 }
538 }
539
540 // If the new glyph didn't fit, flush the state so far and invalidate everything
541 if (!bitmapFit) {
542 flushAllAndInvalidate();
543
544 // Try to fit it again
545 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
546 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
547 if (bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800548 cacheLine = mCacheLines[i];
Romain Guy694b5192010-07-21 21:33:20 -0700549 break;
550 }
551 }
552
553 // if we still don't fit, something is wrong and we shouldn't draw
554 if (!bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800555 return;
Romain Guy694b5192010-07-21 21:33:20 -0700556 }
557 }
558
Chet Haase7de0cb12011-12-05 16:35:38 -0800559 cachedGlyph->mCachedTextureLine = cacheLine;
560
Romain Guy694b5192010-07-21 21:33:20 -0700561 *retOriginX = startX;
562 *retOriginY = startY;
563
564 uint32_t endX = startX + glyph.fWidth;
565 uint32_t endY = startY + glyph.fHeight;
566
Chet Haase7de0cb12011-12-05 16:35:38 -0800567 uint32_t cacheWidth = cacheLine->mMaxWidth;
Romain Guy694b5192010-07-21 21:33:20 -0700568
Chet Haase7de0cb12011-12-05 16:35:38 -0800569 CacheTexture *cacheTexture = cacheLine->mCacheTexture;
570 if (cacheTexture->mTexture == NULL) {
571 // Large-glyph texture memory is allocated only as needed
Chet Haase2a47c142011-12-14 15:22:56 -0800572 allocateTextureMemory(cacheTexture);
Chet Haase7de0cb12011-12-05 16:35:38 -0800573 }
574 uint8_t* cacheBuffer = cacheTexture->mTexture;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700575 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700576 unsigned int stride = glyph.rowBytes();
577
578 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
579 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
580 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700581 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
Romain Guyb45c0c92010-08-26 20:35:23 -0700582 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
Romain Guy694b5192010-07-21 21:33:20 -0700583 }
584 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800585 cachedGlyph->mIsValid = true;
Romain Guy694b5192010-07-21 21:33:20 -0700586}
587
Chet Haase7de0cb12011-12-05 16:35:38 -0800588CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800589 GLuint textureId;
590 glGenTextures(1, &textureId);
Chet Haase2a47c142011-12-14 15:22:56 -0800591 uint8_t* textureMemory = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700592
Chet Haase2a47c142011-12-14 15:22:56 -0800593 CacheTexture* cacheTexture = new CacheTexture(textureMemory, textureId, width, height);
594 if (allocate) {
595 allocateTextureMemory(cacheTexture);
596 }
597 return cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800598}
599
600void FontRenderer::initTextTexture() {
601 mCacheLines.clear();
602
603 // Next, use other, separate caches for large glyphs.
604 uint16_t maxWidth = 0;
605 if (Caches::hasInstance()) {
606 maxWidth = Caches::getInstance().maxTextureSize;
Chet Haase44984ea2011-05-19 13:50:47 -0700607 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800608 if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) {
609 maxWidth = MAX_TEXT_CACHE_WIDTH;
610 }
611 if (mCacheTextureSmall != NULL) {
612 delete mCacheTextureSmall;
613 delete mCacheTexture128;
614 delete mCacheTexture256;
615 delete mCacheTexture512;
616 }
617 mCacheTextureSmall = createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true);
618 mCacheTexture128 = createCacheTexture(maxWidth, 256, false);
619 mCacheTexture256 = createCacheTexture(maxWidth, 256, false);
620 mCacheTexture512 = createCacheTexture(maxWidth, 512, false);
621 mCurrentCacheTexture = mCacheTextureSmall;
622
623 mUploadTexture = false;
624 // Split up our default cache texture into lines of certain widths
625 int nextLine = 0;
626 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 18, nextLine, 0, mCacheTextureSmall));
627 nextLine += mCacheLines.top()->mMaxHeight;
628 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
629 nextLine += mCacheLines.top()->mMaxHeight;
630 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
631 nextLine += mCacheLines.top()->mMaxHeight;
632 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
633 nextLine += mCacheLines.top()->mMaxHeight;
634 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
635 nextLine += mCacheLines.top()->mMaxHeight;
636 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 42, nextLine, 0, mCacheTextureSmall));
637 nextLine += mCacheLines.top()->mMaxHeight;
638 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, mSmallCacheHeight - nextLine,
639 nextLine, 0, mCacheTextureSmall));
640
641 // The first cache is split into 2 lines of height 128, the rest have just one cache line.
642 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 0, 0, mCacheTexture128));
643 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 128, 0, mCacheTexture128));
644 mCacheLines.push(new CacheTextureLine(maxWidth, 256, 0, 0, mCacheTexture256));
645 mCacheLines.push(new CacheTextureLine(maxWidth, 512, 0, 0, mCacheTexture512));
Romain Guy694b5192010-07-21 21:33:20 -0700646}
647
648// Avoid having to reallocate memory and render quad by quad
649void FontRenderer::initVertexArrayBuffers() {
Romain Guyd71dd362011-12-12 19:03:35 -0800650 uint32_t numIndices = mMaxNumberOfQuads * 6;
651 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700652 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700653
654 // Four verts, two triangles , six indices per quad
655 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
656 int i6 = i * 6;
657 int i4 = i * 4;
658
659 indexBufferData[i6 + 0] = i4 + 0;
660 indexBufferData[i6 + 1] = i4 + 1;
661 indexBufferData[i6 + 2] = i4 + 2;
662
663 indexBufferData[i6 + 3] = i4 + 0;
664 indexBufferData[i6 + 4] = i4 + 2;
665 indexBufferData[i6 + 5] = i4 + 3;
666 }
667
668 glGenBuffers(1, &mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800669 Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700670 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
Romain Guy694b5192010-07-21 21:33:20 -0700671
672 free(indexBufferData);
673
Romain Guyd71dd362011-12-12 19:03:35 -0800674 uint32_t coordSize = 2;
Romain Guy694b5192010-07-21 21:33:20 -0700675 uint32_t uvSize = 2;
676 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700677 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
678 mTextMeshPtr = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700679}
680
681// We don't want to allocate anything unless we actually draw text
682void FontRenderer::checkInit() {
683 if (mInitialized) {
684 return;
685 }
686
687 initTextTexture();
688 initVertexArrayBuffers();
689
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700690 // We store a string with letters in a rough frequency of occurrence
691 mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
692 mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
693 mLatinPrecache += String16(",.?!()-+@;:`'");
694 mLatinPrecache += String16("0123456789");
695
Romain Guy694b5192010-07-21 21:33:20 -0700696 mInitialized = true;
697}
698
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700699void FontRenderer::checkTextureUpdate() {
Chet Haase7de0cb12011-12-05 16:35:38 -0800700 if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700701 return;
Romain Guy694b5192010-07-21 21:33:20 -0700702 }
703
Romain Guy2d4fd362011-12-13 22:00:19 -0800704 Caches& caches = Caches::getInstance();
705 GLuint lastTextureId = 0;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700706 // Iterate over all the cache lines and see which ones need to be updated
707 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
708 CacheTextureLine* cl = mCacheLines[i];
Chet Haase7de0cb12011-12-05 16:35:38 -0800709 if (cl->mDirty && cl->mCacheTexture->mTexture != NULL) {
710 CacheTexture* cacheTexture = cl->mCacheTexture;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700711 uint32_t xOffset = 0;
712 uint32_t yOffset = cl->mCurrentRow;
Chet Haase7de0cb12011-12-05 16:35:38 -0800713 uint32_t width = cl->mMaxWidth;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700714 uint32_t height = cl->mMaxHeight;
Chet Haase7de0cb12011-12-05 16:35:38 -0800715 void* textureData = cacheTexture->mTexture + (yOffset * width);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700716
Romain Guy2d4fd362011-12-13 22:00:19 -0800717 if (cacheTexture->mTextureId != lastTextureId) {
718 caches.activeTexture(0);
719 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
720 lastTextureId = cacheTexture->mTextureId;
721 }
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700722 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700723 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700724
725 cl->mDirty = false;
726 }
727 }
728
Chet Haase7de0cb12011-12-05 16:35:38 -0800729 glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
Chet Haase2a47c142011-12-14 15:22:56 -0800730 if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) {
731 const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST;
732 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
733 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
734 mCurrentCacheTexture->mLinearFiltering = mLinearFiltering;
735 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800736 mLastCacheTexture = mCurrentCacheTexture;
737
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700738 mUploadTexture = false;
739}
740
741void FontRenderer::issueDrawCommand() {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700742 checkTextureUpdate();
743
Romain Guy15bc6432011-12-13 13:11:32 -0800744 Caches& caches = Caches::getInstance();
Romain Guy2d4fd362011-12-13 22:00:19 -0800745 caches.bindIndicesBuffer(mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800746 if (!mDrawn) {
747 float* buffer = mTextMeshPtr;
748 int offset = 2;
749
750 bool force = caches.unbindMeshBuffer();
751 caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
752 caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
753 buffer + offset);
754 }
755
Romain Guy694b5192010-07-21 21:33:20 -0700756 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
Romain Guy5b3b3522010-10-27 18:57:51 -0700757
758 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700759}
760
Romain Guyd71dd362011-12-12 19:03:35 -0800761void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
762 float x2, float y2, float u2, float v2,
763 float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800764 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Romain Guyd71dd362011-12-12 19:03:35 -0800765
Romain Guyff98fa52011-11-28 09:35:09 -0800766 if (mClip &&
767 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
Romain Guy09147fb2010-07-22 13:08:20 -0700768 return;
769 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800770 if (texture != mCurrentCacheTexture) {
771 if (mCurrentQuadIndex != 0) {
772 // First, draw everything stored already which uses the previous texture
773 issueDrawCommand();
774 mCurrentQuadIndex = 0;
775 }
776 // Now use the new texture id
777 mCurrentCacheTexture = texture;
778 }
Romain Guy09147fb2010-07-22 13:08:20 -0700779
Romain Guy694b5192010-07-21 21:33:20 -0700780 const uint32_t vertsPerQuad = 4;
Romain Guyd71dd362011-12-12 19:03:35 -0800781 const uint32_t floatsPerVert = 4;
Romain Guy51769a62010-07-23 00:28:00 -0700782 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700783
Romain Guy694b5192010-07-21 21:33:20 -0700784 (*currentPos++) = x1;
785 (*currentPos++) = y1;
Romain Guy694b5192010-07-21 21:33:20 -0700786 (*currentPos++) = u1;
787 (*currentPos++) = v1;
788
789 (*currentPos++) = x2;
790 (*currentPos++) = y2;
Romain Guy694b5192010-07-21 21:33:20 -0700791 (*currentPos++) = u2;
792 (*currentPos++) = v2;
793
794 (*currentPos++) = x3;
795 (*currentPos++) = y3;
Romain Guy694b5192010-07-21 21:33:20 -0700796 (*currentPos++) = u3;
797 (*currentPos++) = v3;
798
799 (*currentPos++) = x4;
800 (*currentPos++) = y4;
Romain Guy694b5192010-07-21 21:33:20 -0700801 (*currentPos++) = u4;
802 (*currentPos++) = v4;
803
804 mCurrentQuadIndex++;
805
Romain Guy5b3b3522010-10-27 18:57:51 -0700806 if (mBounds) {
807 mBounds->left = fmin(mBounds->left, x1);
808 mBounds->top = fmin(mBounds->top, y3);
809 mBounds->right = fmax(mBounds->right, x3);
810 mBounds->bottom = fmax(mBounds->bottom, y1);
811 }
812
Romain Guy694b5192010-07-21 21:33:20 -0700813 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
814 issueDrawCommand();
815 mCurrentQuadIndex = 0;
816 }
817}
818
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700819uint32_t FontRenderer::getRemainingCacheCapacity() {
820 uint32_t remainingCapacity = 0;
821 float totalPixels = 0;
822 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
823 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
824 totalPixels += mCacheLines[i]->mMaxWidth;
825 }
826 remainingCapacity = (remainingCapacity * 100) / totalPixels;
827 return remainingCapacity;
828}
829
830void FontRenderer::precacheLatin(SkPaint* paint) {
831 // Remaining capacity is measured in %
832 uint32_t remainingCapacity = getRemainingCacheCapacity();
833 uint32_t precacheIdx = 0;
Romain Guy054dc182010-10-15 17:55:25 -0700834 while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
Romain Guy726aeba2011-06-01 14:52:00 -0700835 mCurrentFont->getCachedGlyph(paint, (int32_t) mLatinPrecache[precacheIdx]);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700836 remainingCapacity = getRemainingCacheCapacity();
837 precacheIdx ++;
838 }
839}
840
841void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
842 uint32_t currentNumFonts = mActiveFonts.size();
Romain Guy325a0f92011-01-05 15:26:55 -0800843 int flags = 0;
844 if (paint->isFakeBoldText()) {
845 flags |= Font::kFakeBold;
846 }
Romain Guy2577db12011-01-18 13:02:38 -0800847
848 const float skewX = paint->getTextSkewX();
849 uint32_t italicStyle = *(uint32_t*) &skewX;
Chet Haase8668f8a2011-03-02 13:51:36 -0800850 const float scaleXFloat = paint->getTextScaleX();
851 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
Romain Guybd496bc2011-08-02 17:32:41 -0700852 SkPaint::Style style = paint->getStyle();
853 const float strokeWidthFloat = paint->getStrokeWidth();
854 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
855 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
856 scaleX, style, strokeWidth);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700857
858 const float maxPrecacheFontSize = 40.0f;
859 bool isNewFont = currentNumFonts != mActiveFonts.size();
860
Romain Guy2bffd262010-09-12 17:40:02 -0700861 if (isNewFont && fontSize <= maxPrecacheFontSize) {
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700862 precacheLatin(paint);
863 }
Romain Guy694b5192010-07-21 21:33:20 -0700864}
Romain Guy7975fb62010-10-01 16:36:14 -0700865
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700866FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Romain Guy1e45aae2010-08-13 19:39:53 -0700867 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
868 checkInit();
869
870 if (!mCurrentFont) {
871 DropShadow image;
872 image.width = 0;
873 image.height = 0;
874 image.image = NULL;
875 image.penX = 0;
876 image.penY = 0;
877 return image;
878 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700879
Romain Guy2d4fd362011-12-13 22:00:19 -0800880 mDrawn = false;
Romain Guyff98fa52011-11-28 09:35:09 -0800881 mClip = NULL;
882 mBounds = NULL;
883
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700884 Rect bounds;
Romain Guy726aeba2011-06-01 14:52:00 -0700885 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds);
Romain Guyff98fa52011-11-28 09:35:09 -0800886
Romain Guy1e45aae2010-08-13 19:39:53 -0700887 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
888 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700889 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guyff98fa52011-11-28 09:35:09 -0800890
Romain Guy1e45aae2010-08-13 19:39:53 -0700891 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700892 dataBuffer[i] = 0;
893 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700894
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700895 int penX = radius - bounds.left;
896 int penY = radius - bounds.bottom;
897
Romain Guy726aeba2011-06-01 14:52:00 -0700898 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
Romain Guy1e45aae2010-08-13 19:39:53 -0700899 dataBuffer, paddedWidth, paddedHeight);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700900 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
901
902 DropShadow image;
903 image.width = paddedWidth;
904 image.height = paddedHeight;
905 image.image = dataBuffer;
906 image.penX = penX;
907 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -0800908
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700909 return image;
910}
Romain Guy694b5192010-07-21 21:33:20 -0700911
Romain Guy671d6cf2012-01-18 12:39:17 -0800912void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700913 checkInit();
914
Romain Guy5b3b3522010-10-27 18:57:51 -0700915 mDrawn = false;
916 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -0700917 mClip = clip;
Romain Guy671d6cf2012-01-18 12:39:17 -0800918}
Romain Guyff98fa52011-11-28 09:35:09 -0800919
Romain Guy671d6cf2012-01-18 12:39:17 -0800920void FontRenderer::finishRender() {
Romain Guy5b3b3522010-10-27 18:57:51 -0700921 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -0800922 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700923
924 if (mCurrentQuadIndex != 0) {
925 issueDrawCommand();
926 mCurrentQuadIndex = 0;
927 }
Romain Guy671d6cf2012-01-18 12:39:17 -0800928}
929
930bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
931 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
932 if (!mCurrentFont) {
933 ALOGE("No font set");
934 return false;
935 }
936
937 initRender(clip, bounds);
938 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
939 finishRender();
940
941 return mDrawn;
942}
943
944bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
945 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
946 const float* positions, Rect* bounds) {
947 if (!mCurrentFont) {
948 ALOGE("No font set");
949 return false;
950 }
951
952 initRender(clip, bounds);
953 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
954 finishRender();
Romain Guy5b3b3522010-10-27 18:57:51 -0700955
956 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700957}
958
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700959void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
960 // Compute gaussian weights for the blur
961 // e is the euler's number
962 float e = 2.718281828459045f;
963 float pi = 3.1415926535897932f;
964 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
965 // x is of the form [-radius .. 0 .. radius]
966 // and sigma varies with radius.
967 // Based on some experimental radius values and sigma's
968 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700969 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700970 // The larger the radius gets, the more our gaussian blur
971 // will resemble a box blur since with large sigma
972 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -0800973 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700974
975 // Now compute the coefficints
976 // We will store some redundant values to save some math during
977 // the blur calculations
978 // precompute some values
979 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
980 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
981
982 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -0800983 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700984 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700985 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
986 normalizeFactor += weights[r + radius];
987 }
988
989 //Now we need to normalize the weights because all our coefficients need to add up to one
990 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -0800991 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700992 weights[r + radius] *= normalizeFactor;
993 }
994}
995
996void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700997 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700998 float blurredPixel = 0.0f;
999 float currentPixel = 0.0f;
1000
Romain Guy325a0f92011-01-05 15:26:55 -08001001 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001002
1003 const uint8_t* input = source + y * width;
1004 uint8_t* output = dest + y * width;
1005
Romain Guy325a0f92011-01-05 15:26:55 -08001006 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001007 blurredPixel = 0.0f;
1008 const float* gPtr = weights;
1009 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -08001010 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001011 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -08001012 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -07001013 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001014 blurredPixel += currentPixel * gPtr[0];
1015 gPtr++;
1016 i++;
1017 }
1018 } else {
Romain Guy325a0f92011-01-05 15:26:55 -08001019 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001020 // Stepping left and right away from the pixel
1021 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -08001022 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001023 validW = 0;
1024 }
Romain Guy325a0f92011-01-05 15:26:55 -08001025 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001026 validW = width - 1;
1027 }
1028
Romain Guy325a0f92011-01-05 15:26:55 -08001029 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001030 blurredPixel += currentPixel * gPtr[0];
1031 gPtr++;
1032 }
1033 }
1034 *output = (uint8_t)blurredPixel;
1035 output ++;
1036 }
1037 }
1038}
1039
1040void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -07001041 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001042 float blurredPixel = 0.0f;
1043 float currentPixel = 0.0f;
1044
Romain Guy325a0f92011-01-05 15:26:55 -08001045 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001046
1047 uint8_t* output = dest + y * width;
1048
Romain Guy325a0f92011-01-05 15:26:55 -08001049 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001050 blurredPixel = 0.0f;
1051 const float* gPtr = weights;
1052 const uint8_t* input = source + x;
1053 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -08001054 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001055 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -08001056 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001057 currentPixel = (float)(*i);
1058 blurredPixel += currentPixel * gPtr[0];
1059 gPtr++;
1060 i += width;
1061 }
1062 } else {
Romain Guy325a0f92011-01-05 15:26:55 -08001063 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001064 int validH = y + r;
1065 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -08001066 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001067 validH = 0;
1068 }
Romain Guy325a0f92011-01-05 15:26:55 -08001069 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001070 validH = height - 1;
1071 }
1072
1073 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -08001074 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001075 blurredPixel += currentPixel * gPtr[0];
1076 gPtr++;
1077 }
1078 }
Romain Guy325a0f92011-01-05 15:26:55 -08001079 *output = (uint8_t) blurredPixel;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001080 output ++;
1081 }
1082 }
1083}
1084
1085
1086void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
1087 float *gaussian = new float[2 * radius + 1];
1088 computeGaussianWeights(gaussian, radius);
Romain Guyd71dd362011-12-12 19:03:35 -08001089
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001090 uint8_t* scratch = new uint8_t[width * height];
Romain Guyd71dd362011-12-12 19:03:35 -08001091
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001092 horizontalBlur(gaussian, radius, image, scratch, width, height);
1093 verticalBlur(gaussian, radius, scratch, image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -08001094
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001095 delete[] gaussian;
1096 delete[] scratch;
1097}
1098
Romain Guy694b5192010-07-21 21:33:20 -07001099}; // namespace uirenderer
1100}; // namespace android