blob: 74efda2576b02ad1d2f70a065ea36c793b475eda [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 Guyb45c0c92010-08-26 20:35:23 -0700154 if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
Steve Block3762c312012-01-06 19:20:56 +0000155 ALOGE("Skipping invalid index");
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700156 continue;
157 }
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700158 uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
159 bitmap[bY * bitmapW + bX] = tempCol;
160 }
161 }
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700162}
163
Chet Haase7de0cb12011-12-05 16:35:38 -0800164CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700165 CachedGlyphInfo* cachedGlyph = NULL;
Romain Guy726aeba2011-06-01 14:52:00 -0700166 ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
Romain Guy1e45aae2010-08-13 19:39:53 -0700167 if (index >= 0) {
168 cachedGlyph = mCachedGlyphs.valueAt(index);
169 } else {
Romain Guy726aeba2011-06-01 14:52:00 -0700170 cachedGlyph = cacheGlyph(paint, textUnit);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700171 }
172
173 // Is the glyph still in texture cache?
174 if (!cachedGlyph->mIsValid) {
Romain Guy726aeba2011-06-01 14:52:00 -0700175 const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700176 updateGlyphCache(paint, skiaGlyph, cachedGlyph);
177 }
178
179 return cachedGlyph;
180}
181
Romain Guy726aeba2011-06-01 14:52:00 -0700182void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700183 int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
184 if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
Romain Guy726aeba2011-06-01 14:52:00 -0700185 render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
Romain Guy671d6cf2012-01-18 12:39:17 -0800186 bitmapW, bitmapH, NULL, NULL);
Romain Guy61c8c9c2010-08-09 20:48:09 -0700187 } else {
Romain Guy726aeba2011-06-01 14:52:00 -0700188 render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
Romain Guy671d6cf2012-01-18 12:39:17 -0800189 0, 0, NULL, NULL);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700190 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700191}
192
Romain Guy671d6cf2012-01-18 12:39:17 -0800193void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
194 int numGlyphs, int x, int y, const float* positions) {
195 render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
196 0, 0, NULL, positions);
197}
198
Romain Guy726aeba2011-06-01 14:52:00 -0700199void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700200 int numGlyphs, Rect *bounds) {
201 if (bounds == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000202 ALOGE("No return rectangle provided to measure text");
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700203 return;
204 }
205 bounds->set(1e6, -1e6, -1e6, 1e6);
Romain Guy671d6cf2012-01-18 12:39:17 -0800206 render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, NULL);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700207}
208
Romain Guy58ef7fb2010-09-13 12:52:37 -0700209#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16)
Romain Guy2bffd262010-09-12 17:40:02 -0700210
Romain Guy726aeba2011-06-01 14:52:00 -0700211void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700212 int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
Romain Guy671d6cf2012-01-18 12:39:17 -0800213 uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions) {
Romain Guy694b5192010-07-21 21:33:20 -0700214 if (numGlyphs == 0 || text == NULL || len == 0) {
215 return;
216 }
217
Romain Guy671d6cf2012-01-18 12:39:17 -0800218 int glyphsCount = 0;
Romain Guy2bffd262010-09-12 17:40:02 -0700219
Romain Guy694b5192010-07-21 21:33:20 -0700220 text += start;
221
Romain Guy671d6cf2012-01-18 12:39:17 -0800222 static RenderGlyph gRenderGlyph[] = {
223 &android::uirenderer::Font::drawCachedGlyph,
224 &android::uirenderer::Font::drawCachedGlyphBitmap,
225 &android::uirenderer::Font::measureCachedGlyph
226 };
227 RenderGlyph render = gRenderGlyph[mode];
Romain Guy694b5192010-07-21 21:33:20 -0700228
Romain Guy671d6cf2012-01-18 12:39:17 -0800229 if (positions == NULL) {
230 SkFixed prevRsbDelta = 0;
Romain Guy694b5192010-07-21 21:33:20 -0700231
Romain Guy671d6cf2012-01-18 12:39:17 -0800232 float penX = x;
233 int penY = y;
Romain Guy694b5192010-07-21 21:33:20 -0700234
Romain Guy671d6cf2012-01-18 12:39:17 -0800235 penX += 0.5f;
236
237 while (glyphsCount < numGlyphs) {
238 glyph_t glyph = GET_GLYPH(text);
239
240 // Reached the end of the string
241 if (IS_END_OF_STRING(glyph)) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700242 break;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700243 }
Romain Guy671d6cf2012-01-18 12:39:17 -0800244
245 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
246 penX += SkFixedToFloat(SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta));
247 prevRsbDelta = cachedGlyph->mRsbDelta;
248
249 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
250 if (cachedGlyph->mIsValid) {
251 (*this.*render)(cachedGlyph, (int) floorf(penX), penY,
252 bitmap, bitmapW, bitmapH, bounds, positions);
253 }
254
255 penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
256
257 glyphsCount++;
Romain Guy694b5192010-07-21 21:33:20 -0700258 }
Romain Guy671d6cf2012-01-18 12:39:17 -0800259 } else {
260 const SkPaint::Align align = paint->getTextAlign();
Romain Guy694b5192010-07-21 21:33:20 -0700261
Romain Guy671d6cf2012-01-18 12:39:17 -0800262 // This is for renderPosText()
263 while (glyphsCount < numGlyphs) {
264 glyph_t glyph = GET_GLYPH(text);
Romain Guy694b5192010-07-21 21:33:20 -0700265
Romain Guy671d6cf2012-01-18 12:39:17 -0800266 // Reached the end of the string
267 if (IS_END_OF_STRING(glyph)) {
268 break;
269 }
270
271 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
272
273 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
274 if (cachedGlyph->mIsValid) {
275 int penX = x + positions[(glyphsCount << 1)];
276 int penY = y + positions[(glyphsCount << 1) + 1];
277
278 switch (align) {
279 case SkPaint::kRight_Align:
280 penX -= SkFixedToFloat(cachedGlyph->mAdvanceX);
281 penY -= SkFixedToFloat(cachedGlyph->mAdvanceY);
282 break;
283 case SkPaint::kCenter_Align:
284 penX -= SkFixedToFloat(cachedGlyph->mAdvanceX >> 1);
285 penY -= SkFixedToFloat(cachedGlyph->mAdvanceY >> 1);
286 default:
287 break;
288 }
289
290 (*this.*render)(cachedGlyph, penX, penY,
291 bitmap, bitmapW, bitmapH, bounds, positions);
292 }
293
294 glyphsCount++;
Romain Guy694b5192010-07-21 21:33:20 -0700295 }
296 }
297}
298
Romain Guy51769a62010-07-23 00:28:00 -0700299void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
Romain Guy694b5192010-07-21 21:33:20 -0700300 glyph->mAdvanceX = skiaGlyph.fAdvanceX;
301 glyph->mAdvanceY = skiaGlyph.fAdvanceY;
302 glyph->mBitmapLeft = skiaGlyph.fLeft;
303 glyph->mBitmapTop = skiaGlyph.fTop;
Romain Guy2bffd262010-09-12 17:40:02 -0700304 glyph->mLsbDelta = skiaGlyph.fLsbDelta;
305 glyph->mRsbDelta = skiaGlyph.fRsbDelta;
Romain Guy694b5192010-07-21 21:33:20 -0700306
307 uint32_t startX = 0;
308 uint32_t startY = 0;
309
Romain Guy694b5192010-07-21 21:33:20 -0700310 // Get the bitmap for the glyph
311 paint->findImage(skiaGlyph);
Chet Haase7de0cb12011-12-05 16:35:38 -0800312 mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700313
314 if (!glyph->mIsValid) {
315 return;
316 }
317
318 uint32_t endX = startX + skiaGlyph.fWidth;
319 uint32_t endY = startY + skiaGlyph.fHeight;
320
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700321 glyph->mStartX = startX;
322 glyph->mStartY = startY;
Romain Guy694b5192010-07-21 21:33:20 -0700323 glyph->mBitmapWidth = skiaGlyph.fWidth;
324 glyph->mBitmapHeight = skiaGlyph.fHeight;
325
Chet Haase7de0cb12011-12-05 16:35:38 -0800326 uint32_t cacheWidth = glyph->mCachedTextureLine->mCacheTexture->mWidth;
327 uint32_t cacheHeight = glyph->mCachedTextureLine->mCacheTexture->mHeight;
Romain Guy694b5192010-07-21 21:33:20 -0700328
329 glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
330 glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
331 glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
332 glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
333
Romain Guy51769a62010-07-23 00:28:00 -0700334 mState->mUploadTexture = true;
Romain Guy694b5192010-07-21 21:33:20 -0700335}
336
Chet Haase7de0cb12011-12-05 16:35:38 -0800337CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) {
Romain Guy51769a62010-07-23 00:28:00 -0700338 CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
Romain Guy694b5192010-07-21 21:33:20 -0700339 mCachedGlyphs.add(glyph, newGlyph);
340
Romain Guy726aeba2011-06-01 14:52:00 -0700341 const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
Romain Guy694b5192010-07-21 21:33:20 -0700342 newGlyph->mGlyphIndex = skiaGlyph.fID;
343 newGlyph->mIsValid = false;
344
345 updateGlyphCache(paint, skiaGlyph, newGlyph);
346
347 return newGlyph;
348}
349
Romain Guy2577db12011-01-18 13:02:38 -0800350Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
Romain Guybd496bc2011-08-02 17:32:41 -0700351 int flags, uint32_t italicStyle, uint32_t scaleX,
352 SkPaint::Style style, uint32_t strokeWidth) {
Romain Guy694b5192010-07-21 21:33:20 -0700353 Vector<Font*> &activeFonts = state->mActiveFonts;
354
355 for (uint32_t i = 0; i < activeFonts.size(); i++) {
Romain Guy51769a62010-07-23 00:28:00 -0700356 Font* font = activeFonts[i];
Romain Guy2577db12011-01-18 13:02:38 -0800357 if (font->mFontId == fontId && font->mFontSize == fontSize &&
Chet Haase8668f8a2011-03-02 13:51:36 -0800358 font->mFlags == flags && font->mItalicStyle == italicStyle &&
Romain Guybd496bc2011-08-02 17:32:41 -0700359 font->mScaleX == scaleX && font->mStyle == style &&
360 (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
Romain Guy51769a62010-07-23 00:28:00 -0700361 return font;
Romain Guy694b5192010-07-21 21:33:20 -0700362 }
363 }
364
Romain Guybd496bc2011-08-02 17:32:41 -0700365 Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
366 scaleX, style, strokeWidth);
Romain Guy694b5192010-07-21 21:33:20 -0700367 activeFonts.push(newFont);
368 return newFont;
369}
370
371///////////////////////////////////////////////////////////////////////////////
372// FontRenderer
373///////////////////////////////////////////////////////////////////////////////
374
Romain Guy514fb182011-01-19 14:38:29 -0800375static bool sLogFontRendererCreate = true;
376
Romain Guy694b5192010-07-21 21:33:20 -0700377FontRenderer::FontRenderer() {
Romain Guyc9855a52011-01-21 21:14:15 -0800378 if (sLogFontRendererCreate) {
379 INIT_LOGD("Creating FontRenderer");
380 }
Romain Guy51769a62010-07-23 00:28:00 -0700381
Romain Guyb45c0c92010-08-26 20:35:23 -0700382 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700383 mInitialized = false;
384 mMaxNumberOfQuads = 1024;
385 mCurrentQuadIndex = 0;
386
Romain Guy9cccc2b92010-08-07 23:46:15 -0700387 mTextMeshPtr = NULL;
Chet Haase7de0cb12011-12-05 16:35:38 -0800388 mCurrentCacheTexture = NULL;
389 mLastCacheTexture = NULL;
390 mCacheTextureSmall = NULL;
391 mCacheTexture128 = NULL;
392 mCacheTexture256 = NULL;
393 mCacheTexture512 = NULL;
Romain Guy9cccc2b92010-08-07 23:46:15 -0700394
Chet Haase2a47c142011-12-14 15:22:56 -0800395 mLinearFiltering = false;
396
Romain Guy694b5192010-07-21 21:33:20 -0700397 mIndexBufferID = 0;
398
Chet Haase7de0cb12011-12-05 16:35:38 -0800399 mSmallCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
400 mSmallCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -0700401
402 char property[PROPERTY_VALUE_MAX];
403 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800404 if (sLogFontRendererCreate) {
405 INIT_LOGD(" Setting text cache width to %s pixels", property);
406 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800407 mSmallCacheWidth = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -0700408 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800409 if (sLogFontRendererCreate) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800410 INIT_LOGD(" Using default text cache width of %i pixels", mSmallCacheWidth);
Romain Guy514fb182011-01-19 14:38:29 -0800411 }
Romain Guy51769a62010-07-23 00:28:00 -0700412 }
413
414 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800415 if (sLogFontRendererCreate) {
416 INIT_LOGD(" Setting text cache width to %s pixels", property);
417 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800418 mSmallCacheHeight = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -0700419 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800420 if (sLogFontRendererCreate) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800421 INIT_LOGD(" Using default text cache height of %i pixels", mSmallCacheHeight);
Romain Guy514fb182011-01-19 14:38:29 -0800422 }
Romain Guy51769a62010-07-23 00:28:00 -0700423 }
Romain Guy514fb182011-01-19 14:38:29 -0800424
425 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -0700426}
427
428FontRenderer::~FontRenderer() {
429 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
430 delete mCacheLines[i];
431 }
432 mCacheLines.clear();
433
Romain Guy9cccc2b92010-08-07 23:46:15 -0700434 if (mInitialized) {
435 delete[] mTextMeshPtr;
Chet Haase7de0cb12011-12-05 16:35:38 -0800436 delete mCacheTextureSmall;
437 delete mCacheTexture128;
438 delete mCacheTexture256;
439 delete mCacheTexture512;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700440 }
Romain Guy694b5192010-07-21 21:33:20 -0700441
442 Vector<Font*> fontsToDereference = mActiveFonts;
443 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
444 delete fontsToDereference[i];
445 }
446}
447
448void FontRenderer::flushAllAndInvalidate() {
449 if (mCurrentQuadIndex != 0) {
450 issueDrawCommand();
451 mCurrentQuadIndex = 0;
452 }
453 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
454 mActiveFonts[i]->invalidateTextureCache();
455 }
456 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
457 mCacheLines[i]->mCurrentCol = 0;
458 }
459}
460
Chet Haase9a824562011-12-16 15:44:59 -0800461void FontRenderer::deallocateTextureMemory(CacheTexture *cacheTexture) {
462 if (cacheTexture && cacheTexture->mTexture) {
463 glDeleteTextures(1, &cacheTexture->mTextureId);
464 delete cacheTexture->mTexture;
465 cacheTexture->mTexture = NULL;
466 }
467}
468
469void FontRenderer::flushLargeCaches() {
470 if ((!mCacheTexture128 || !mCacheTexture128->mTexture) &&
471 (!mCacheTexture256 || !mCacheTexture256->mTexture) &&
472 (!mCacheTexture512 || !mCacheTexture512->mTexture)) {
473 // Typical case; no large glyph caches allocated
474 return;
475 }
476
477 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
478 CacheTextureLine* cacheLine = mCacheLines[i];
479 if ((cacheLine->mCacheTexture == mCacheTexture128 ||
480 cacheLine->mCacheTexture == mCacheTexture256 ||
481 cacheLine->mCacheTexture == mCacheTexture512) &&
482 cacheLine->mCacheTexture->mTexture != NULL) {
483 cacheLine->mCurrentCol = 0;
484 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
485 mActiveFonts[i]->invalidateTextureCache(cacheLine);
486 }
487 }
488 }
489
490 deallocateTextureMemory(mCacheTexture128);
491 deallocateTextureMemory(mCacheTexture256);
492 deallocateTextureMemory(mCacheTexture512);
493}
494
Chet Haase2a47c142011-12-14 15:22:56 -0800495void FontRenderer::allocateTextureMemory(CacheTexture *cacheTexture) {
496 int width = cacheTexture->mWidth;
497 int height = cacheTexture->mHeight;
498 cacheTexture->mTexture = new uint8_t[width * height];
499 memset(cacheTexture->mTexture, 0, width * height * sizeof(uint8_t));
500 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
501 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
502 // Initialize texture dimensions
503 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
504 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800505
Chet Haase2a47c142011-12-14 15:22:56 -0800506 const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST;
507 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
508 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
509
510 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
511 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
Chet Haase7de0cb12011-12-05 16:35:38 -0800512}
513
514void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
515 uint32_t* retOriginX, uint32_t* retOriginY) {
516 cachedGlyph->mIsValid = false;
Romain Guy694b5192010-07-21 21:33:20 -0700517 // If the glyph is too tall, don't cache it
Chet Haase7de0cb12011-12-05 16:35:38 -0800518 if (glyph.fHeight + TEXTURE_BORDER_SIZE > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
Steve Block3762c312012-01-06 19:20:56 +0000519 ALOGE("Font size to large to fit in cache. width, height = %i, %i",
Chet Haase7de0cb12011-12-05 16:35:38 -0800520 (int) glyph.fWidth, (int) glyph.fHeight);
521 return;
Romain Guy694b5192010-07-21 21:33:20 -0700522 }
523
524 // Now copy the bitmap into the cache texture
525 uint32_t startX = 0;
526 uint32_t startY = 0;
527
528 bool bitmapFit = false;
Chet Haase7de0cb12011-12-05 16:35:38 -0800529 CacheTextureLine *cacheLine;
Romain Guy694b5192010-07-21 21:33:20 -0700530 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
531 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
532 if (bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800533 cacheLine = mCacheLines[i];
Romain Guy694b5192010-07-21 21:33:20 -0700534 break;
535 }
536 }
537
538 // If the new glyph didn't fit, flush the state so far and invalidate everything
539 if (!bitmapFit) {
540 flushAllAndInvalidate();
541
542 // Try to fit it again
543 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
544 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
545 if (bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800546 cacheLine = mCacheLines[i];
Romain Guy694b5192010-07-21 21:33:20 -0700547 break;
548 }
549 }
550
551 // if we still don't fit, something is wrong and we shouldn't draw
552 if (!bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800553 return;
Romain Guy694b5192010-07-21 21:33:20 -0700554 }
555 }
556
Chet Haase7de0cb12011-12-05 16:35:38 -0800557 cachedGlyph->mCachedTextureLine = cacheLine;
558
Romain Guy694b5192010-07-21 21:33:20 -0700559 *retOriginX = startX;
560 *retOriginY = startY;
561
562 uint32_t endX = startX + glyph.fWidth;
563 uint32_t endY = startY + glyph.fHeight;
564
Chet Haase7de0cb12011-12-05 16:35:38 -0800565 uint32_t cacheWidth = cacheLine->mMaxWidth;
Romain Guy694b5192010-07-21 21:33:20 -0700566
Chet Haase7de0cb12011-12-05 16:35:38 -0800567 CacheTexture *cacheTexture = cacheLine->mCacheTexture;
568 if (cacheTexture->mTexture == NULL) {
569 // Large-glyph texture memory is allocated only as needed
Chet Haase2a47c142011-12-14 15:22:56 -0800570 allocateTextureMemory(cacheTexture);
Chet Haase7de0cb12011-12-05 16:35:38 -0800571 }
572 uint8_t* cacheBuffer = cacheTexture->mTexture;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700573 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700574 unsigned int stride = glyph.rowBytes();
575
576 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
577 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
578 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700579 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
Romain Guyb45c0c92010-08-26 20:35:23 -0700580 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
Romain Guy694b5192010-07-21 21:33:20 -0700581 }
582 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800583 cachedGlyph->mIsValid = true;
Romain Guy694b5192010-07-21 21:33:20 -0700584}
585
Chet Haase7de0cb12011-12-05 16:35:38 -0800586CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800587 GLuint textureId;
588 glGenTextures(1, &textureId);
Chet Haase2a47c142011-12-14 15:22:56 -0800589 uint8_t* textureMemory = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700590
Chet Haase2a47c142011-12-14 15:22:56 -0800591 CacheTexture* cacheTexture = new CacheTexture(textureMemory, textureId, width, height);
592 if (allocate) {
593 allocateTextureMemory(cacheTexture);
594 }
595 return cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800596}
597
598void FontRenderer::initTextTexture() {
599 mCacheLines.clear();
600
601 // Next, use other, separate caches for large glyphs.
602 uint16_t maxWidth = 0;
603 if (Caches::hasInstance()) {
604 maxWidth = Caches::getInstance().maxTextureSize;
Chet Haase44984ea2011-05-19 13:50:47 -0700605 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800606 if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) {
607 maxWidth = MAX_TEXT_CACHE_WIDTH;
608 }
609 if (mCacheTextureSmall != NULL) {
610 delete mCacheTextureSmall;
611 delete mCacheTexture128;
612 delete mCacheTexture256;
613 delete mCacheTexture512;
614 }
615 mCacheTextureSmall = createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true);
616 mCacheTexture128 = createCacheTexture(maxWidth, 256, false);
617 mCacheTexture256 = createCacheTexture(maxWidth, 256, false);
618 mCacheTexture512 = createCacheTexture(maxWidth, 512, false);
619 mCurrentCacheTexture = mCacheTextureSmall;
620
621 mUploadTexture = false;
622 // Split up our default cache texture into lines of certain widths
623 int nextLine = 0;
624 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 18, nextLine, 0, mCacheTextureSmall));
625 nextLine += mCacheLines.top()->mMaxHeight;
626 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, 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, 34, 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, 42, nextLine, 0, mCacheTextureSmall));
635 nextLine += mCacheLines.top()->mMaxHeight;
636 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, mSmallCacheHeight - nextLine,
637 nextLine, 0, mCacheTextureSmall));
638
639 // The first cache is split into 2 lines of height 128, the rest have just one cache line.
640 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 0, 0, mCacheTexture128));
641 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 128, 0, mCacheTexture128));
642 mCacheLines.push(new CacheTextureLine(maxWidth, 256, 0, 0, mCacheTexture256));
643 mCacheLines.push(new CacheTextureLine(maxWidth, 512, 0, 0, mCacheTexture512));
Romain Guy694b5192010-07-21 21:33:20 -0700644}
645
646// Avoid having to reallocate memory and render quad by quad
647void FontRenderer::initVertexArrayBuffers() {
Romain Guyd71dd362011-12-12 19:03:35 -0800648 uint32_t numIndices = mMaxNumberOfQuads * 6;
649 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700650 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700651
652 // Four verts, two triangles , six indices per quad
653 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
654 int i6 = i * 6;
655 int i4 = i * 4;
656
657 indexBufferData[i6 + 0] = i4 + 0;
658 indexBufferData[i6 + 1] = i4 + 1;
659 indexBufferData[i6 + 2] = i4 + 2;
660
661 indexBufferData[i6 + 3] = i4 + 0;
662 indexBufferData[i6 + 4] = i4 + 2;
663 indexBufferData[i6 + 5] = i4 + 3;
664 }
665
666 glGenBuffers(1, &mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800667 Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700668 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
Romain Guy694b5192010-07-21 21:33:20 -0700669
670 free(indexBufferData);
671
Romain Guyd71dd362011-12-12 19:03:35 -0800672 uint32_t coordSize = 2;
Romain Guy694b5192010-07-21 21:33:20 -0700673 uint32_t uvSize = 2;
674 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700675 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
676 mTextMeshPtr = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700677}
678
679// We don't want to allocate anything unless we actually draw text
680void FontRenderer::checkInit() {
681 if (mInitialized) {
682 return;
683 }
684
685 initTextTexture();
686 initVertexArrayBuffers();
687
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700688 // We store a string with letters in a rough frequency of occurrence
689 mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
690 mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
691 mLatinPrecache += String16(",.?!()-+@;:`'");
692 mLatinPrecache += String16("0123456789");
693
Romain Guy694b5192010-07-21 21:33:20 -0700694 mInitialized = true;
695}
696
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700697void FontRenderer::checkTextureUpdate() {
Chet Haase7de0cb12011-12-05 16:35:38 -0800698 if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700699 return;
Romain Guy694b5192010-07-21 21:33:20 -0700700 }
701
Romain Guy2d4fd362011-12-13 22:00:19 -0800702 Caches& caches = Caches::getInstance();
703 GLuint lastTextureId = 0;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700704 // Iterate over all the cache lines and see which ones need to be updated
705 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
706 CacheTextureLine* cl = mCacheLines[i];
Chet Haase7de0cb12011-12-05 16:35:38 -0800707 if (cl->mDirty && cl->mCacheTexture->mTexture != NULL) {
708 CacheTexture* cacheTexture = cl->mCacheTexture;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700709 uint32_t xOffset = 0;
710 uint32_t yOffset = cl->mCurrentRow;
Chet Haase7de0cb12011-12-05 16:35:38 -0800711 uint32_t width = cl->mMaxWidth;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700712 uint32_t height = cl->mMaxHeight;
Chet Haase7de0cb12011-12-05 16:35:38 -0800713 void* textureData = cacheTexture->mTexture + (yOffset * width);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700714
Romain Guy2d4fd362011-12-13 22:00:19 -0800715 if (cacheTexture->mTextureId != lastTextureId) {
716 caches.activeTexture(0);
717 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
718 lastTextureId = cacheTexture->mTextureId;
719 }
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700720 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700721 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700722
723 cl->mDirty = false;
724 }
725 }
726
Chet Haase7de0cb12011-12-05 16:35:38 -0800727 glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
Chet Haase2a47c142011-12-14 15:22:56 -0800728 if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) {
729 const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST;
730 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
731 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
732 mCurrentCacheTexture->mLinearFiltering = mLinearFiltering;
733 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800734 mLastCacheTexture = mCurrentCacheTexture;
735
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700736 mUploadTexture = false;
737}
738
739void FontRenderer::issueDrawCommand() {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700740 checkTextureUpdate();
741
Romain Guy15bc6432011-12-13 13:11:32 -0800742 Caches& caches = Caches::getInstance();
Romain Guy2d4fd362011-12-13 22:00:19 -0800743 caches.bindIndicesBuffer(mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800744 if (!mDrawn) {
745 float* buffer = mTextMeshPtr;
746 int offset = 2;
747
748 bool force = caches.unbindMeshBuffer();
749 caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
750 caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
751 buffer + offset);
752 }
753
Romain Guy694b5192010-07-21 21:33:20 -0700754 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
Romain Guy5b3b3522010-10-27 18:57:51 -0700755
756 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700757}
758
Romain Guyd71dd362011-12-12 19:03:35 -0800759void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
760 float x2, float y2, float u2, float v2,
761 float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800762 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Romain Guyd71dd362011-12-12 19:03:35 -0800763
Romain Guyff98fa52011-11-28 09:35:09 -0800764 if (mClip &&
765 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
Romain Guy09147fb2010-07-22 13:08:20 -0700766 return;
767 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800768 if (texture != mCurrentCacheTexture) {
769 if (mCurrentQuadIndex != 0) {
770 // First, draw everything stored already which uses the previous texture
771 issueDrawCommand();
772 mCurrentQuadIndex = 0;
773 }
774 // Now use the new texture id
775 mCurrentCacheTexture = texture;
776 }
Romain Guy09147fb2010-07-22 13:08:20 -0700777
Romain Guy694b5192010-07-21 21:33:20 -0700778 const uint32_t vertsPerQuad = 4;
Romain Guyd71dd362011-12-12 19:03:35 -0800779 const uint32_t floatsPerVert = 4;
Romain Guy51769a62010-07-23 00:28:00 -0700780 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700781
Romain Guy694b5192010-07-21 21:33:20 -0700782 (*currentPos++) = x1;
783 (*currentPos++) = y1;
Romain Guy694b5192010-07-21 21:33:20 -0700784 (*currentPos++) = u1;
785 (*currentPos++) = v1;
786
787 (*currentPos++) = x2;
788 (*currentPos++) = y2;
Romain Guy694b5192010-07-21 21:33:20 -0700789 (*currentPos++) = u2;
790 (*currentPos++) = v2;
791
792 (*currentPos++) = x3;
793 (*currentPos++) = y3;
Romain Guy694b5192010-07-21 21:33:20 -0700794 (*currentPos++) = u3;
795 (*currentPos++) = v3;
796
797 (*currentPos++) = x4;
798 (*currentPos++) = y4;
Romain Guy694b5192010-07-21 21:33:20 -0700799 (*currentPos++) = u4;
800 (*currentPos++) = v4;
801
802 mCurrentQuadIndex++;
803
Romain Guy5b3b3522010-10-27 18:57:51 -0700804 if (mBounds) {
805 mBounds->left = fmin(mBounds->left, x1);
806 mBounds->top = fmin(mBounds->top, y3);
807 mBounds->right = fmax(mBounds->right, x3);
808 mBounds->bottom = fmax(mBounds->bottom, y1);
809 }
810
Romain Guy694b5192010-07-21 21:33:20 -0700811 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
812 issueDrawCommand();
813 mCurrentQuadIndex = 0;
814 }
815}
816
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700817uint32_t FontRenderer::getRemainingCacheCapacity() {
818 uint32_t remainingCapacity = 0;
819 float totalPixels = 0;
820 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
821 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
822 totalPixels += mCacheLines[i]->mMaxWidth;
823 }
824 remainingCapacity = (remainingCapacity * 100) / totalPixels;
825 return remainingCapacity;
826}
827
828void FontRenderer::precacheLatin(SkPaint* paint) {
829 // Remaining capacity is measured in %
830 uint32_t remainingCapacity = getRemainingCacheCapacity();
831 uint32_t precacheIdx = 0;
Romain Guy054dc182010-10-15 17:55:25 -0700832 while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
Romain Guy726aeba2011-06-01 14:52:00 -0700833 mCurrentFont->getCachedGlyph(paint, (int32_t) mLatinPrecache[precacheIdx]);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700834 remainingCapacity = getRemainingCacheCapacity();
835 precacheIdx ++;
836 }
837}
838
839void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
840 uint32_t currentNumFonts = mActiveFonts.size();
Romain Guy325a0f92011-01-05 15:26:55 -0800841 int flags = 0;
842 if (paint->isFakeBoldText()) {
843 flags |= Font::kFakeBold;
844 }
Romain Guy2577db12011-01-18 13:02:38 -0800845
846 const float skewX = paint->getTextSkewX();
847 uint32_t italicStyle = *(uint32_t*) &skewX;
Chet Haase8668f8a2011-03-02 13:51:36 -0800848 const float scaleXFloat = paint->getTextScaleX();
849 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
Romain Guybd496bc2011-08-02 17:32:41 -0700850 SkPaint::Style style = paint->getStyle();
851 const float strokeWidthFloat = paint->getStrokeWidth();
852 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
853 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
854 scaleX, style, strokeWidth);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700855
856 const float maxPrecacheFontSize = 40.0f;
857 bool isNewFont = currentNumFonts != mActiveFonts.size();
858
Romain Guy2bffd262010-09-12 17:40:02 -0700859 if (isNewFont && fontSize <= maxPrecacheFontSize) {
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700860 precacheLatin(paint);
861 }
Romain Guy694b5192010-07-21 21:33:20 -0700862}
Romain Guy7975fb62010-10-01 16:36:14 -0700863
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700864FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Romain Guy1e45aae2010-08-13 19:39:53 -0700865 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
866 checkInit();
867
868 if (!mCurrentFont) {
869 DropShadow image;
870 image.width = 0;
871 image.height = 0;
872 image.image = NULL;
873 image.penX = 0;
874 image.penY = 0;
875 return image;
876 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700877
Romain Guy2d4fd362011-12-13 22:00:19 -0800878 mDrawn = false;
Romain Guyff98fa52011-11-28 09:35:09 -0800879 mClip = NULL;
880 mBounds = NULL;
881
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700882 Rect bounds;
Romain Guy726aeba2011-06-01 14:52:00 -0700883 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds);
Romain Guyff98fa52011-11-28 09:35:09 -0800884
Romain Guy1e45aae2010-08-13 19:39:53 -0700885 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
886 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700887 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guyff98fa52011-11-28 09:35:09 -0800888
Romain Guy1e45aae2010-08-13 19:39:53 -0700889 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700890 dataBuffer[i] = 0;
891 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700892
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700893 int penX = radius - bounds.left;
894 int penY = radius - bounds.bottom;
895
Romain Guy726aeba2011-06-01 14:52:00 -0700896 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
Romain Guy1e45aae2010-08-13 19:39:53 -0700897 dataBuffer, paddedWidth, paddedHeight);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700898 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
899
900 DropShadow image;
901 image.width = paddedWidth;
902 image.height = paddedHeight;
903 image.image = dataBuffer;
904 image.penX = penX;
905 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -0800906
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700907 return image;
908}
Romain Guy694b5192010-07-21 21:33:20 -0700909
Romain Guy671d6cf2012-01-18 12:39:17 -0800910void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700911 checkInit();
912
Romain Guy5b3b3522010-10-27 18:57:51 -0700913 mDrawn = false;
914 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -0700915 mClip = clip;
Romain Guy671d6cf2012-01-18 12:39:17 -0800916}
Romain Guyff98fa52011-11-28 09:35:09 -0800917
Romain Guy671d6cf2012-01-18 12:39:17 -0800918void FontRenderer::finishRender() {
Romain Guy5b3b3522010-10-27 18:57:51 -0700919 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -0800920 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700921
922 if (mCurrentQuadIndex != 0) {
923 issueDrawCommand();
924 mCurrentQuadIndex = 0;
925 }
Romain Guy671d6cf2012-01-18 12:39:17 -0800926}
927
928bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
929 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
930 if (!mCurrentFont) {
931 ALOGE("No font set");
932 return false;
933 }
934
935 initRender(clip, bounds);
936 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
937 finishRender();
938
939 return mDrawn;
940}
941
942bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
943 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
944 const float* positions, Rect* bounds) {
945 if (!mCurrentFont) {
946 ALOGE("No font set");
947 return false;
948 }
949
950 initRender(clip, bounds);
951 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
952 finishRender();
Romain Guy5b3b3522010-10-27 18:57:51 -0700953
954 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700955}
956
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700957void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
958 // Compute gaussian weights for the blur
959 // e is the euler's number
960 float e = 2.718281828459045f;
961 float pi = 3.1415926535897932f;
962 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
963 // x is of the form [-radius .. 0 .. radius]
964 // and sigma varies with radius.
965 // Based on some experimental radius values and sigma's
966 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700967 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700968 // The larger the radius gets, the more our gaussian blur
969 // will resemble a box blur since with large sigma
970 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -0800971 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700972
973 // Now compute the coefficints
974 // We will store some redundant values to save some math during
975 // the blur calculations
976 // precompute some values
977 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
978 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
979
980 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -0800981 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700982 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700983 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
984 normalizeFactor += weights[r + radius];
985 }
986
987 //Now we need to normalize the weights because all our coefficients need to add up to one
988 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -0800989 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700990 weights[r + radius] *= normalizeFactor;
991 }
992}
993
994void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700995 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700996 float blurredPixel = 0.0f;
997 float currentPixel = 0.0f;
998
Romain Guy325a0f92011-01-05 15:26:55 -0800999 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001000
1001 const uint8_t* input = source + y * width;
1002 uint8_t* output = dest + y * width;
1003
Romain Guy325a0f92011-01-05 15:26:55 -08001004 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001005 blurredPixel = 0.0f;
1006 const float* gPtr = weights;
1007 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -08001008 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001009 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -08001010 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -07001011 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001012 blurredPixel += currentPixel * gPtr[0];
1013 gPtr++;
1014 i++;
1015 }
1016 } else {
Romain Guy325a0f92011-01-05 15:26:55 -08001017 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001018 // Stepping left and right away from the pixel
1019 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -08001020 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001021 validW = 0;
1022 }
Romain Guy325a0f92011-01-05 15:26:55 -08001023 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001024 validW = width - 1;
1025 }
1026
Romain Guy325a0f92011-01-05 15:26:55 -08001027 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001028 blurredPixel += currentPixel * gPtr[0];
1029 gPtr++;
1030 }
1031 }
1032 *output = (uint8_t)blurredPixel;
1033 output ++;
1034 }
1035 }
1036}
1037
1038void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -07001039 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001040 float blurredPixel = 0.0f;
1041 float currentPixel = 0.0f;
1042
Romain Guy325a0f92011-01-05 15:26:55 -08001043 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001044
1045 uint8_t* output = dest + y * width;
1046
Romain Guy325a0f92011-01-05 15:26:55 -08001047 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001048 blurredPixel = 0.0f;
1049 const float* gPtr = weights;
1050 const uint8_t* input = source + x;
1051 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -08001052 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001053 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -08001054 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001055 currentPixel = (float)(*i);
1056 blurredPixel += currentPixel * gPtr[0];
1057 gPtr++;
1058 i += width;
1059 }
1060 } else {
Romain Guy325a0f92011-01-05 15:26:55 -08001061 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001062 int validH = y + r;
1063 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -08001064 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001065 validH = 0;
1066 }
Romain Guy325a0f92011-01-05 15:26:55 -08001067 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001068 validH = height - 1;
1069 }
1070
1071 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -08001072 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001073 blurredPixel += currentPixel * gPtr[0];
1074 gPtr++;
1075 }
1076 }
Romain Guy325a0f92011-01-05 15:26:55 -08001077 *output = (uint8_t) blurredPixel;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001078 output ++;
1079 }
1080 }
1081}
1082
1083
1084void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
1085 float *gaussian = new float[2 * radius + 1];
1086 computeGaussianWeights(gaussian, radius);
Romain Guyd71dd362011-12-12 19:03:35 -08001087
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001088 uint8_t* scratch = new uint8_t[width * height];
Romain Guyd71dd362011-12-12 19:03:35 -08001089
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001090 horizontalBlur(gaussian, radius, image, scratch, width, height);
1091 verticalBlur(gaussian, radius, scratch, image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -08001092
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001093 delete[] gaussian;
1094 delete[] scratch;
1095}
1096
Romain Guy694b5192010-07-21 21:33:20 -07001097}; // namespace uirenderer
1098}; // namespace android