blob: 158f785036891fba0555df0e2b3f4d5d76b73389 [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 Guyc9855a52011-01-21 21:14:15 -080025#include "Debug.h"
Romain Guy51769a62010-07-23 00:28:00 -070026#include "FontRenderer.h"
27
Romain Guy694b5192010-07-21 21:33:20 -070028namespace android {
29namespace uirenderer {
30
31///////////////////////////////////////////////////////////////////////////////
Romain Guy51769a62010-07-23 00:28:00 -070032// Defines
33///////////////////////////////////////////////////////////////////////////////
34
35#define DEFAULT_TEXT_CACHE_WIDTH 1024
36#define DEFAULT_TEXT_CACHE_HEIGHT 256
37
Romain Guy726aeba2011-06-01 14:52:00 -070038// We should query these values from the GL context
Chet Haase44984ea2011-05-19 13:50:47 -070039#define MAX_TEXT_CACHE_WIDTH 2048
40#define MAX_TEXT_CACHE_HEIGHT 2048
41
Romain Guy51769a62010-07-23 00:28:00 -070042///////////////////////////////////////////////////////////////////////////////
Romain Guy694b5192010-07-21 21:33:20 -070043// Font
44///////////////////////////////////////////////////////////////////////////////
45
Romain Guy2577db12011-01-18 13:02:38 -080046Font::Font(FontRenderer* state, uint32_t fontId, float fontSize,
Romain Guybd496bc2011-08-02 17:32:41 -070047 int flags, uint32_t italicStyle, uint32_t scaleX,
48 SkPaint::Style style, uint32_t strokeWidth) :
Romain Guy2577db12011-01-18 13:02:38 -080049 mState(state), mFontId(fontId), mFontSize(fontSize),
Romain Guybd496bc2011-08-02 17:32:41 -070050 mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX),
51 mStyle(style), mStrokeWidth(mStrokeWidth) {
Romain Guy694b5192010-07-21 21:33:20 -070052}
53
54
55Font::~Font() {
56 for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
57 if (mState->mActiveFonts[ct] == this) {
58 mState->mActiveFonts.removeAt(ct);
59 break;
60 }
61 }
62
63 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
Romain Guy726aeba2011-06-01 14:52:00 -070064 delete mCachedGlyphs.valueAt(i);
Romain Guy694b5192010-07-21 21:33:20 -070065 }
66}
67
68void Font::invalidateTextureCache() {
69 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
70 mCachedGlyphs.valueAt(i)->mIsValid = false;
71 }
72}
73
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070074void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds) {
75 int nPenX = x + glyph->mBitmapLeft;
76 int nPenY = y + glyph->mBitmapTop;
77
78 int width = (int) glyph->mBitmapWidth;
79 int height = (int) glyph->mBitmapHeight;
80
Romain Guy61c8c9c2010-08-09 20:48:09 -070081 if (bounds->bottom > nPenY) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070082 bounds->bottom = nPenY;
83 }
Romain Guy61c8c9c2010-08-09 20:48:09 -070084 if (bounds->left > nPenX) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070085 bounds->left = nPenX;
86 }
Romain Guy61c8c9c2010-08-09 20:48:09 -070087 if (bounds->right < nPenX + width) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070088 bounds->right = nPenX + width;
89 }
Romain Guy61c8c9c2010-08-09 20:48:09 -070090 if (bounds->top < nPenY + height) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070091 bounds->top = nPenY + height;
92 }
93}
94
Romain Guy694b5192010-07-21 21:33:20 -070095void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
Romain Guy694b5192010-07-21 21:33:20 -070096 int nPenX = x + glyph->mBitmapLeft;
97 int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
98
Romain Guy51769a62010-07-23 00:28:00 -070099 float u1 = glyph->mBitmapMinU;
100 float u2 = glyph->mBitmapMaxU;
101 float v1 = glyph->mBitmapMinV;
102 float v2 = glyph->mBitmapMaxV;
103
104 int width = (int) glyph->mBitmapWidth;
105 int height = (int) glyph->mBitmapHeight;
106
107 mState->appendMeshQuad(nPenX, nPenY, 0, u1, v2,
108 nPenX + width, nPenY, 0, u2, v2,
109 nPenX + width, nPenY - height, 0, u2, v1,
110 nPenX, nPenY - height, 0, u1, v1);
Romain Guy694b5192010-07-21 21:33:20 -0700111}
112
Romain Guyb45c0c92010-08-26 20:35:23 -0700113void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
114 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700115 int nPenX = x + glyph->mBitmapLeft;
116 int nPenY = y + glyph->mBitmapTop;
117
118 uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
119 uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
120
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700121 uint32_t cacheWidth = mState->getCacheWidth();
122 const uint8_t* cacheBuffer = mState->getTextTextureData();
123
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700124 uint32_t cacheX = 0, cacheY = 0;
125 int32_t bX = 0, bY = 0;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700126 for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
127 for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
Romain Guyb45c0c92010-08-26 20:35:23 -0700128 if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700129 LOGE("Skipping invalid index");
130 continue;
131 }
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700132 uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
133 bitmap[bY * bitmapW + bX] = tempCol;
134 }
135 }
136
137}
138
Romain Guy726aeba2011-06-01 14:52:00 -0700139Font::CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700140 CachedGlyphInfo* cachedGlyph = NULL;
Romain Guy726aeba2011-06-01 14:52:00 -0700141 ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
Romain Guy1e45aae2010-08-13 19:39:53 -0700142 if (index >= 0) {
143 cachedGlyph = mCachedGlyphs.valueAt(index);
144 } else {
Romain Guy726aeba2011-06-01 14:52:00 -0700145 cachedGlyph = cacheGlyph(paint, textUnit);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700146 }
147
148 // Is the glyph still in texture cache?
149 if (!cachedGlyph->mIsValid) {
Romain Guy726aeba2011-06-01 14:52:00 -0700150 const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700151 updateGlyphCache(paint, skiaGlyph, cachedGlyph);
152 }
153
154 return cachedGlyph;
155}
156
Romain Guy726aeba2011-06-01 14:52:00 -0700157void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700158 int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
159 if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
Romain Guy726aeba2011-06-01 14:52:00 -0700160 render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700161 bitmapW, bitmapH, NULL);
162 } else {
Romain Guy726aeba2011-06-01 14:52:00 -0700163 render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
164 0, 0, NULL);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700165 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700166}
167
Romain Guy726aeba2011-06-01 14:52:00 -0700168void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700169 int numGlyphs, Rect *bounds) {
170 if (bounds == NULL) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700171 LOGE("No return rectangle provided to measure text");
172 return;
173 }
174 bounds->set(1e6, -1e6, -1e6, 1e6);
Romain Guy726aeba2011-06-01 14:52:00 -0700175 render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700176}
177
Romain Guy58ef7fb2010-09-13 12:52:37 -0700178#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16)
Romain Guy2bffd262010-09-12 17:40:02 -0700179
Romain Guy726aeba2011-06-01 14:52:00 -0700180void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700181 int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
182 uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700183 if (numGlyphs == 0 || text == NULL || len == 0) {
184 return;
185 }
186
Romain Guy5a6d3a42011-10-07 15:03:24 -0700187 float penX = x;
Romain Guy2bffd262010-09-12 17:40:02 -0700188 int penY = y;
Romain Guy694b5192010-07-21 21:33:20 -0700189 int glyphsLeft = 1;
190 if (numGlyphs > 0) {
191 glyphsLeft = numGlyphs;
192 }
193
Romain Guy2bffd262010-09-12 17:40:02 -0700194 SkFixed prevRsbDelta = 0;
Romain Guy5a6d3a42011-10-07 15:03:24 -0700195 penX += 0.5f;
Romain Guy2bffd262010-09-12 17:40:02 -0700196
Romain Guy694b5192010-07-21 21:33:20 -0700197 text += start;
198
199 while (glyphsLeft > 0) {
Romain Guy726aeba2011-06-01 14:52:00 -0700200 glyph_t glyph = GET_GLYPH(text);
Romain Guy694b5192010-07-21 21:33:20 -0700201
Romain Guy61c8c9c2010-08-09 20:48:09 -0700202 // Reached the end of the string
Romain Guy726aeba2011-06-01 14:52:00 -0700203 if (IS_END_OF_STRING(glyph)) {
Romain Guy694b5192010-07-21 21:33:20 -0700204 break;
205 }
206
Romain Guy726aeba2011-06-01 14:52:00 -0700207 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
Romain Guy5a6d3a42011-10-07 15:03:24 -0700208 penX += SkFixedToFloat(SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta));
Romain Guy2bffd262010-09-12 17:40:02 -0700209 prevRsbDelta = cachedGlyph->mRsbDelta;
Romain Guy694b5192010-07-21 21:33:20 -0700210
211 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
212 if (cachedGlyph->mIsValid) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700213 switch(mode) {
214 case FRAMEBUFFER:
Romain Guy5a6d3a42011-10-07 15:03:24 -0700215 drawCachedGlyph(cachedGlyph, (int) floorf(penX), penY);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700216 break;
217 case BITMAP:
Romain Guy5a6d3a42011-10-07 15:03:24 -0700218 drawCachedGlyph(cachedGlyph, (int) floorf(penX), penY, bitmap, bitmapW, bitmapH);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700219 break;
220 case MEASURE:
Romain Guy5a6d3a42011-10-07 15:03:24 -0700221 measureCachedGlyph(cachedGlyph, (int) floorf(penX), penY, bounds);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700222 break;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700223 }
Romain Guy694b5192010-07-21 21:33:20 -0700224 }
225
Romain Guy5a6d3a42011-10-07 15:03:24 -0700226 penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
Romain Guy694b5192010-07-21 21:33:20 -0700227
228 // If we were given a specific number of glyphs, decrement
229 if (numGlyphs > 0) {
230 glyphsLeft--;
231 }
232 }
233}
234
Romain Guy51769a62010-07-23 00:28:00 -0700235void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
Romain Guy694b5192010-07-21 21:33:20 -0700236 glyph->mAdvanceX = skiaGlyph.fAdvanceX;
237 glyph->mAdvanceY = skiaGlyph.fAdvanceY;
238 glyph->mBitmapLeft = skiaGlyph.fLeft;
239 glyph->mBitmapTop = skiaGlyph.fTop;
Romain Guy2bffd262010-09-12 17:40:02 -0700240 glyph->mLsbDelta = skiaGlyph.fLsbDelta;
241 glyph->mRsbDelta = skiaGlyph.fRsbDelta;
Romain Guy694b5192010-07-21 21:33:20 -0700242
243 uint32_t startX = 0;
244 uint32_t startY = 0;
245
Romain Guy694b5192010-07-21 21:33:20 -0700246 // Get the bitmap for the glyph
247 paint->findImage(skiaGlyph);
Romain Guy51769a62010-07-23 00:28:00 -0700248 glyph->mIsValid = mState->cacheBitmap(skiaGlyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700249
250 if (!glyph->mIsValid) {
251 return;
252 }
253
254 uint32_t endX = startX + skiaGlyph.fWidth;
255 uint32_t endY = startY + skiaGlyph.fHeight;
256
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700257 glyph->mStartX = startX;
258 glyph->mStartY = startY;
Romain Guy694b5192010-07-21 21:33:20 -0700259 glyph->mBitmapWidth = skiaGlyph.fWidth;
260 glyph->mBitmapHeight = skiaGlyph.fHeight;
261
Romain Guy51769a62010-07-23 00:28:00 -0700262 uint32_t cacheWidth = mState->getCacheWidth();
263 uint32_t cacheHeight = mState->getCacheHeight();
Romain Guy694b5192010-07-21 21:33:20 -0700264
265 glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
266 glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
267 glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
268 glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
269
Romain Guy51769a62010-07-23 00:28:00 -0700270 mState->mUploadTexture = true;
Romain Guy694b5192010-07-21 21:33:20 -0700271}
272
Romain Guy726aeba2011-06-01 14:52:00 -0700273Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) {
Romain Guy51769a62010-07-23 00:28:00 -0700274 CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
Romain Guy694b5192010-07-21 21:33:20 -0700275 mCachedGlyphs.add(glyph, newGlyph);
276
Romain Guy726aeba2011-06-01 14:52:00 -0700277 const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
Romain Guy694b5192010-07-21 21:33:20 -0700278 newGlyph->mGlyphIndex = skiaGlyph.fID;
279 newGlyph->mIsValid = false;
280
281 updateGlyphCache(paint, skiaGlyph, newGlyph);
282
283 return newGlyph;
284}
285
Romain Guy2577db12011-01-18 13:02:38 -0800286Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
Romain Guybd496bc2011-08-02 17:32:41 -0700287 int flags, uint32_t italicStyle, uint32_t scaleX,
288 SkPaint::Style style, uint32_t strokeWidth) {
Romain Guy694b5192010-07-21 21:33:20 -0700289 Vector<Font*> &activeFonts = state->mActiveFonts;
290
291 for (uint32_t i = 0; i < activeFonts.size(); i++) {
Romain Guy51769a62010-07-23 00:28:00 -0700292 Font* font = activeFonts[i];
Romain Guy2577db12011-01-18 13:02:38 -0800293 if (font->mFontId == fontId && font->mFontSize == fontSize &&
Chet Haase8668f8a2011-03-02 13:51:36 -0800294 font->mFlags == flags && font->mItalicStyle == italicStyle &&
Romain Guybd496bc2011-08-02 17:32:41 -0700295 font->mScaleX == scaleX && font->mStyle == style &&
296 (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
Romain Guy51769a62010-07-23 00:28:00 -0700297 return font;
Romain Guy694b5192010-07-21 21:33:20 -0700298 }
299 }
300
Romain Guybd496bc2011-08-02 17:32:41 -0700301 Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
302 scaleX, style, strokeWidth);
Romain Guy694b5192010-07-21 21:33:20 -0700303 activeFonts.push(newFont);
304 return newFont;
305}
306
307///////////////////////////////////////////////////////////////////////////////
308// FontRenderer
309///////////////////////////////////////////////////////////////////////////////
310
Romain Guy514fb182011-01-19 14:38:29 -0800311static bool sLogFontRendererCreate = true;
312
Romain Guy694b5192010-07-21 21:33:20 -0700313FontRenderer::FontRenderer() {
Romain Guyc9855a52011-01-21 21:14:15 -0800314 if (sLogFontRendererCreate) {
315 INIT_LOGD("Creating FontRenderer");
316 }
Romain Guy51769a62010-07-23 00:28:00 -0700317
Romain Guyb45c0c92010-08-26 20:35:23 -0700318 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700319 mInitialized = false;
320 mMaxNumberOfQuads = 1024;
321 mCurrentQuadIndex = 0;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700322 mTextureId = 0;
Romain Guy694b5192010-07-21 21:33:20 -0700323
Romain Guy9cccc2b2010-08-07 23:46:15 -0700324 mTextMeshPtr = NULL;
325 mTextTexture = NULL;
326
Romain Guy694b5192010-07-21 21:33:20 -0700327 mIndexBufferID = 0;
Alex Sakhartchouk894df172011-02-17 16:45:37 -0800328 mPositionAttrSlot = -1;
329 mTexcoordAttrSlot = -1;
Romain Guy694b5192010-07-21 21:33:20 -0700330
Romain Guy51769a62010-07-23 00:28:00 -0700331 mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700332 mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -0700333
334 char property[PROPERTY_VALUE_MAX];
335 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800336 if (sLogFontRendererCreate) {
337 INIT_LOGD(" Setting text cache width to %s pixels", property);
338 }
Romain Guy51769a62010-07-23 00:28:00 -0700339 mCacheWidth = atoi(property);
340 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800341 if (sLogFontRendererCreate) {
Romain Guyc9855a52011-01-21 21:14:15 -0800342 INIT_LOGD(" Using default text cache width of %i pixels", mCacheWidth);
Romain Guy514fb182011-01-19 14:38:29 -0800343 }
Romain Guy51769a62010-07-23 00:28:00 -0700344 }
345
346 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800347 if (sLogFontRendererCreate) {
348 INIT_LOGD(" Setting text cache width to %s pixels", property);
349 }
Romain Guy51769a62010-07-23 00:28:00 -0700350 mCacheHeight = atoi(property);
351 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800352 if (sLogFontRendererCreate) {
Romain Guyc9855a52011-01-21 21:14:15 -0800353 INIT_LOGD(" Using default text cache height of %i pixels", mCacheHeight);
Romain Guy514fb182011-01-19 14:38:29 -0800354 }
Romain Guy51769a62010-07-23 00:28:00 -0700355 }
Romain Guy514fb182011-01-19 14:38:29 -0800356
357 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -0700358}
359
360FontRenderer::~FontRenderer() {
361 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
362 delete mCacheLines[i];
363 }
364 mCacheLines.clear();
365
Romain Guy9cccc2b2010-08-07 23:46:15 -0700366 if (mInitialized) {
367 delete[] mTextMeshPtr;
368 delete[] mTextTexture;
369 }
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700370
Romain Guy9cccc2b2010-08-07 23:46:15 -0700371 if (mTextureId) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700372 glDeleteTextures(1, &mTextureId);
373 }
Romain Guy694b5192010-07-21 21:33:20 -0700374
375 Vector<Font*> fontsToDereference = mActiveFonts;
376 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
377 delete fontsToDereference[i];
378 }
379}
380
381void FontRenderer::flushAllAndInvalidate() {
382 if (mCurrentQuadIndex != 0) {
383 issueDrawCommand();
384 mCurrentQuadIndex = 0;
385 }
386 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
387 mActiveFonts[i]->invalidateTextureCache();
388 }
389 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
390 mCacheLines[i]->mCurrentCol = 0;
391 }
392}
393
Romain Guy51769a62010-07-23 00:28:00 -0700394bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
Romain Guy694b5192010-07-21 21:33:20 -0700395 // If the glyph is too tall, don't cache it
Romain Guy799833a2011-08-30 14:41:48 -0700396 if (glyph.fHeight + 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
Chet Haase44984ea2011-05-19 13:50:47 -0700397 if (mCacheHeight < MAX_TEXT_CACHE_HEIGHT) {
398 // Default cache not large enough for large glyphs - resize cache to
399 // max size and try again
400 flushAllAndInvalidate();
401 initTextTexture(true);
402 }
Romain Guy799833a2011-08-30 14:41:48 -0700403 if (glyph.fHeight + 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
Chet Haase44984ea2011-05-19 13:50:47 -0700404 LOGE("Font size to large to fit in cache. width, height = %i, %i",
405 (int) glyph.fWidth, (int) glyph.fHeight);
406 return false;
407 }
Romain Guy694b5192010-07-21 21:33:20 -0700408 }
409
410 // Now copy the bitmap into the cache texture
411 uint32_t startX = 0;
412 uint32_t startY = 0;
413
414 bool bitmapFit = false;
415 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
416 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
417 if (bitmapFit) {
418 break;
419 }
420 }
421
422 // If the new glyph didn't fit, flush the state so far and invalidate everything
423 if (!bitmapFit) {
424 flushAllAndInvalidate();
425
426 // Try to fit it again
427 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
428 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
429 if (bitmapFit) {
430 break;
431 }
432 }
433
434 // if we still don't fit, something is wrong and we shouldn't draw
435 if (!bitmapFit) {
436 LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
437 (int) glyph.fWidth, (int) glyph.fHeight);
438 return false;
439 }
440 }
441
442 *retOriginX = startX;
443 *retOriginY = startY;
444
445 uint32_t endX = startX + glyph.fWidth;
446 uint32_t endY = startY + glyph.fHeight;
447
448 uint32_t cacheWidth = mCacheWidth;
449
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700450 uint8_t* cacheBuffer = mTextTexture;
451 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700452 unsigned int stride = glyph.rowBytes();
453
454 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
455 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
456 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700457 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
Romain Guyb45c0c92010-08-26 20:35:23 -0700458 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
Romain Guy694b5192010-07-21 21:33:20 -0700459 }
460 }
461
462 return true;
463}
464
Chet Haase44984ea2011-05-19 13:50:47 -0700465void FontRenderer::initTextTexture(bool largeFonts) {
466 mCacheLines.clear();
467 if (largeFonts) {
468 mCacheWidth = MAX_TEXT_CACHE_WIDTH;
469 mCacheHeight = MAX_TEXT_CACHE_HEIGHT;
470 }
471
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700472 mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
Romain Guy1de10832010-10-03 14:37:09 -0700473 memset(mTextTexture, 0, mCacheWidth * mCacheHeight * sizeof(uint8_t));
474
Romain Guy694b5192010-07-21 21:33:20 -0700475 mUploadTexture = false;
476
Chet Haase44984ea2011-05-19 13:50:47 -0700477 if (mTextureId != 0) {
478 glDeleteTextures(1, &mTextureId);
479 }
Romain Guy694b5192010-07-21 21:33:20 -0700480 glGenTextures(1, &mTextureId);
481 glBindTexture(GL_TEXTURE_2D, mTextureId);
482 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
Chet Haase44984ea2011-05-19 13:50:47 -0700483 // Initialize texture dimensions
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700484 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700485 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Romain Guy694b5192010-07-21 21:33:20 -0700486
Romain Guye8cb9c142010-10-04 14:14:11 -0700487 mLinearFiltering = false;
488 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
489 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
Romain Guy694b5192010-07-21 21:33:20 -0700490
491 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
492 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
493
494 // Split up our cache texture into lines of certain widths
495 int nextLine = 0;
Romain Guy7975fb62010-10-01 16:36:14 -0700496 mCacheLines.push(new CacheTextureLine(mCacheWidth, 18, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700497 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700498 mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700499 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700500 mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700501 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700502 mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700503 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700504 mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700505 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700506 mCacheLines.push(new CacheTextureLine(mCacheWidth, 42, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700507 nextLine += mCacheLines.top()->mMaxHeight;
Chet Haase44984ea2011-05-19 13:50:47 -0700508 if (largeFonts) {
509 int nextSize = 76;
510 // Make several new lines with increasing font sizes
511 while (nextSize < (int)(mCacheHeight - nextLine - (2 * nextSize))) {
512 mCacheLines.push(new CacheTextureLine(mCacheWidth, nextSize, nextLine, 0));
513 nextLine += mCacheLines.top()->mMaxHeight;
514 nextSize += 50;
515 }
516 }
Romain Guy51769a62010-07-23 00:28:00 -0700517 mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700518}
519
520// Avoid having to reallocate memory and render quad by quad
521void FontRenderer::initVertexArrayBuffers() {
522 uint32_t numIndicies = mMaxNumberOfQuads * 6;
523 uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700524 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700525
526 // Four verts, two triangles , six indices per quad
527 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
528 int i6 = i * 6;
529 int i4 = i * 4;
530
531 indexBufferData[i6 + 0] = i4 + 0;
532 indexBufferData[i6 + 1] = i4 + 1;
533 indexBufferData[i6 + 2] = i4 + 2;
534
535 indexBufferData[i6 + 3] = i4 + 0;
536 indexBufferData[i6 + 4] = i4 + 2;
537 indexBufferData[i6 + 5] = i4 + 3;
538 }
539
540 glGenBuffers(1, &mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700541 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
542 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
543 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
Romain Guy694b5192010-07-21 21:33:20 -0700544
545 free(indexBufferData);
546
547 uint32_t coordSize = 3;
548 uint32_t uvSize = 2;
549 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700550 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
551 mTextMeshPtr = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700552}
553
554// We don't want to allocate anything unless we actually draw text
555void FontRenderer::checkInit() {
556 if (mInitialized) {
557 return;
558 }
559
560 initTextTexture();
561 initVertexArrayBuffers();
562
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700563 // We store a string with letters in a rough frequency of occurrence
564 mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
565 mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
566 mLatinPrecache += String16(",.?!()-+@;:`'");
567 mLatinPrecache += String16("0123456789");
568
Romain Guy694b5192010-07-21 21:33:20 -0700569 mInitialized = true;
570}
571
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700572void FontRenderer::checkTextureUpdate() {
573 if (!mUploadTexture) {
574 return;
Romain Guy694b5192010-07-21 21:33:20 -0700575 }
576
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700577 glBindTexture(GL_TEXTURE_2D, mTextureId);
578
579 // Iterate over all the cache lines and see which ones need to be updated
580 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
581 CacheTextureLine* cl = mCacheLines[i];
582 if(cl->mDirty) {
583 uint32_t xOffset = 0;
584 uint32_t yOffset = cl->mCurrentRow;
585 uint32_t width = mCacheWidth;
586 uint32_t height = cl->mMaxHeight;
Romain Guy1e45aae2010-08-13 19:39:53 -0700587 void* textureData = mTextTexture + yOffset*width;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700588
589 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700590 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700591
592 cl->mDirty = false;
593 }
594 }
595
596 mUploadTexture = false;
597}
598
599void FontRenderer::issueDrawCommand() {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700600 checkTextureUpdate();
601
Romain Guy51769a62010-07-23 00:28:00 -0700602 float* vtx = mTextMeshPtr;
603 float* tex = vtx + 3;
Romain Guy694b5192010-07-21 21:33:20 -0700604
Alex Sakhartchouk894df172011-02-17 16:45:37 -0800605 glVertexAttribPointer(mPositionAttrSlot, 3, GL_FLOAT, false, 20, vtx);
606 glVertexAttribPointer(mTexcoordAttrSlot, 2, GL_FLOAT, false, 20, tex);
Romain Guy694b5192010-07-21 21:33:20 -0700607
608 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
609 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
Romain Guy5b3b3522010-10-27 18:57:51 -0700610
611 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700612}
613
614void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
615 float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
616 float x4, float y4, float z4, float u4, float v4) {
Romain Guyff98fa52011-11-28 09:35:09 -0800617 if (mClip &&
618 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
Romain Guy09147fb2010-07-22 13:08:20 -0700619 return;
620 }
621
Romain Guy694b5192010-07-21 21:33:20 -0700622 const uint32_t vertsPerQuad = 4;
623 const uint32_t floatsPerVert = 5;
Romain Guy51769a62010-07-23 00:28:00 -0700624 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700625
Romain Guy694b5192010-07-21 21:33:20 -0700626 (*currentPos++) = x1;
627 (*currentPos++) = y1;
628 (*currentPos++) = z1;
629 (*currentPos++) = u1;
630 (*currentPos++) = v1;
631
632 (*currentPos++) = x2;
633 (*currentPos++) = y2;
634 (*currentPos++) = z2;
635 (*currentPos++) = u2;
636 (*currentPos++) = v2;
637
638 (*currentPos++) = x3;
639 (*currentPos++) = y3;
640 (*currentPos++) = z3;
641 (*currentPos++) = u3;
642 (*currentPos++) = v3;
643
644 (*currentPos++) = x4;
645 (*currentPos++) = y4;
646 (*currentPos++) = z4;
647 (*currentPos++) = u4;
648 (*currentPos++) = v4;
649
650 mCurrentQuadIndex++;
651
Romain Guy5b3b3522010-10-27 18:57:51 -0700652 if (mBounds) {
653 mBounds->left = fmin(mBounds->left, x1);
654 mBounds->top = fmin(mBounds->top, y3);
655 mBounds->right = fmax(mBounds->right, x3);
656 mBounds->bottom = fmax(mBounds->bottom, y1);
657 }
658
Romain Guy694b5192010-07-21 21:33:20 -0700659 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
660 issueDrawCommand();
661 mCurrentQuadIndex = 0;
662 }
663}
664
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700665uint32_t FontRenderer::getRemainingCacheCapacity() {
666 uint32_t remainingCapacity = 0;
667 float totalPixels = 0;
668 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
669 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
670 totalPixels += mCacheLines[i]->mMaxWidth;
671 }
672 remainingCapacity = (remainingCapacity * 100) / totalPixels;
673 return remainingCapacity;
674}
675
676void FontRenderer::precacheLatin(SkPaint* paint) {
677 // Remaining capacity is measured in %
678 uint32_t remainingCapacity = getRemainingCacheCapacity();
679 uint32_t precacheIdx = 0;
Romain Guy054dc182010-10-15 17:55:25 -0700680 while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
Romain Guy726aeba2011-06-01 14:52:00 -0700681 mCurrentFont->getCachedGlyph(paint, (int32_t) mLatinPrecache[precacheIdx]);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700682 remainingCapacity = getRemainingCacheCapacity();
683 precacheIdx ++;
684 }
685}
686
687void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
688 uint32_t currentNumFonts = mActiveFonts.size();
Romain Guy325a0f92011-01-05 15:26:55 -0800689 int flags = 0;
690 if (paint->isFakeBoldText()) {
691 flags |= Font::kFakeBold;
692 }
Romain Guy2577db12011-01-18 13:02:38 -0800693
694 const float skewX = paint->getTextSkewX();
695 uint32_t italicStyle = *(uint32_t*) &skewX;
Chet Haase8668f8a2011-03-02 13:51:36 -0800696 const float scaleXFloat = paint->getTextScaleX();
697 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
Romain Guybd496bc2011-08-02 17:32:41 -0700698 SkPaint::Style style = paint->getStyle();
699 const float strokeWidthFloat = paint->getStrokeWidth();
700 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
701 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
702 scaleX, style, strokeWidth);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700703
704 const float maxPrecacheFontSize = 40.0f;
705 bool isNewFont = currentNumFonts != mActiveFonts.size();
706
Romain Guy2bffd262010-09-12 17:40:02 -0700707 if (isNewFont && fontSize <= maxPrecacheFontSize) {
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700708 precacheLatin(paint);
709 }
Romain Guy694b5192010-07-21 21:33:20 -0700710}
Romain Guy7975fb62010-10-01 16:36:14 -0700711
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700712FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Romain Guy1e45aae2010-08-13 19:39:53 -0700713 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
714 checkInit();
715
716 if (!mCurrentFont) {
717 DropShadow image;
718 image.width = 0;
719 image.height = 0;
720 image.image = NULL;
721 image.penX = 0;
722 image.penY = 0;
723 return image;
724 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700725
Romain Guyff98fa52011-11-28 09:35:09 -0800726 mClip = NULL;
727 mBounds = NULL;
728
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700729 Rect bounds;
Romain Guy726aeba2011-06-01 14:52:00 -0700730 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds);
Romain Guyff98fa52011-11-28 09:35:09 -0800731
Romain Guy1e45aae2010-08-13 19:39:53 -0700732 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
733 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700734 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guyff98fa52011-11-28 09:35:09 -0800735
Romain Guy1e45aae2010-08-13 19:39:53 -0700736 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700737 dataBuffer[i] = 0;
738 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700739
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700740 int penX = radius - bounds.left;
741 int penY = radius - bounds.bottom;
742
Romain Guy726aeba2011-06-01 14:52:00 -0700743 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
Romain Guy1e45aae2010-08-13 19:39:53 -0700744 dataBuffer, paddedWidth, paddedHeight);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700745 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
746
747 DropShadow image;
748 image.width = paddedWidth;
749 image.height = paddedHeight;
750 image.image = dataBuffer;
751 image.penX = penX;
752 image.penY = penY;
753 return image;
754}
Romain Guy694b5192010-07-21 21:33:20 -0700755
Romain Guy5b3b3522010-10-27 18:57:51 -0700756bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
757 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700758 checkInit();
759
Romain Guy09147fb2010-07-22 13:08:20 -0700760 if (!mCurrentFont) {
761 LOGE("No font set");
Romain Guy5b3b3522010-10-27 18:57:51 -0700762 return false;
Romain Guy694b5192010-07-21 21:33:20 -0700763 }
764
Alex Sakhartchouk894df172011-02-17 16:45:37 -0800765 if (mPositionAttrSlot < 0 || mTexcoordAttrSlot < 0) {
766 LOGE("Font renderer unable to draw, attribute slots undefined");
767 return false;
768 }
769
Romain Guy5b3b3522010-10-27 18:57:51 -0700770 mDrawn = false;
771 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -0700772 mClip = clip;
Romain Guyff98fa52011-11-28 09:35:09 -0800773
Romain Guy726aeba2011-06-01 14:52:00 -0700774 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
Romain Guyff98fa52011-11-28 09:35:09 -0800775
Romain Guy5b3b3522010-10-27 18:57:51 -0700776 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -0800777 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700778
779 if (mCurrentQuadIndex != 0) {
780 issueDrawCommand();
781 mCurrentQuadIndex = 0;
782 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700783
784 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700785}
786
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700787void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
788 // Compute gaussian weights for the blur
789 // e is the euler's number
790 float e = 2.718281828459045f;
791 float pi = 3.1415926535897932f;
792 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
793 // x is of the form [-radius .. 0 .. radius]
794 // and sigma varies with radius.
795 // Based on some experimental radius values and sigma's
796 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700797 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700798 // The larger the radius gets, the more our gaussian blur
799 // will resemble a box blur since with large sigma
800 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -0800801 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700802
803 // Now compute the coefficints
804 // We will store some redundant values to save some math during
805 // the blur calculations
806 // precompute some values
807 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
808 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
809
810 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -0800811 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700812 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700813 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
814 normalizeFactor += weights[r + radius];
815 }
816
817 //Now we need to normalize the weights because all our coefficients need to add up to one
818 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -0800819 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700820 weights[r + radius] *= normalizeFactor;
821 }
822}
823
824void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700825 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700826 float blurredPixel = 0.0f;
827 float currentPixel = 0.0f;
828
Romain Guy325a0f92011-01-05 15:26:55 -0800829 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700830
831 const uint8_t* input = source + y * width;
832 uint8_t* output = dest + y * width;
833
Romain Guy325a0f92011-01-05 15:26:55 -0800834 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700835 blurredPixel = 0.0f;
836 const float* gPtr = weights;
837 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800838 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700839 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -0800840 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700841 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700842 blurredPixel += currentPixel * gPtr[0];
843 gPtr++;
844 i++;
845 }
846 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800847 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700848 // Stepping left and right away from the pixel
849 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -0800850 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700851 validW = 0;
852 }
Romain Guy325a0f92011-01-05 15:26:55 -0800853 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700854 validW = width - 1;
855 }
856
Romain Guy325a0f92011-01-05 15:26:55 -0800857 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700858 blurredPixel += currentPixel * gPtr[0];
859 gPtr++;
860 }
861 }
862 *output = (uint8_t)blurredPixel;
863 output ++;
864 }
865 }
866}
867
868void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700869 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700870 float blurredPixel = 0.0f;
871 float currentPixel = 0.0f;
872
Romain Guy325a0f92011-01-05 15:26:55 -0800873 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700874
875 uint8_t* output = dest + y * width;
876
Romain Guy325a0f92011-01-05 15:26:55 -0800877 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700878 blurredPixel = 0.0f;
879 const float* gPtr = weights;
880 const uint8_t* input = source + x;
881 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800882 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700883 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -0800884 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700885 currentPixel = (float)(*i);
886 blurredPixel += currentPixel * gPtr[0];
887 gPtr++;
888 i += width;
889 }
890 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800891 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700892 int validH = y + r;
893 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -0800894 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700895 validH = 0;
896 }
Romain Guy325a0f92011-01-05 15:26:55 -0800897 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700898 validH = height - 1;
899 }
900
901 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -0800902 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700903 blurredPixel += currentPixel * gPtr[0];
904 gPtr++;
905 }
906 }
Romain Guy325a0f92011-01-05 15:26:55 -0800907 *output = (uint8_t) blurredPixel;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700908 output ++;
909 }
910 }
911}
912
913
914void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
915 float *gaussian = new float[2 * radius + 1];
916 computeGaussianWeights(gaussian, radius);
917 uint8_t* scratch = new uint8_t[width * height];
918 horizontalBlur(gaussian, radius, image, scratch, width, height);
919 verticalBlur(gaussian, radius, scratch, image, width, height);
920 delete[] gaussian;
921 delete[] scratch;
922}
923
Romain Guy694b5192010-07-21 21:33:20 -0700924}; // namespace uirenderer
925}; // namespace android