blob: a077cbc55f3441ab0fdc43a9cb91b94051ae999f [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 }
166
167}
168
Romain Guy726aeba2011-06-01 14:52:00 -0700169void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700170 int numGlyphs, Rect *bounds) {
171 if (bounds == NULL) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700172 LOGE("No return rectangle provided to measure text");
173 return;
174 }
175 bounds->set(1e6, -1e6, -1e6, 1e6);
Romain Guy726aeba2011-06-01 14:52:00 -0700176 render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700177}
178
Romain Guy58ef7fb2010-09-13 12:52:37 -0700179#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16)
Romain Guy2bffd262010-09-12 17:40:02 -0700180
Romain Guy726aeba2011-06-01 14:52:00 -0700181void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700182 int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
183 uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700184 if (numGlyphs == 0 || text == NULL || len == 0) {
185 return;
186 }
187
Romain Guy5a6d3a42011-10-07 15:03:24 -0700188 float penX = x;
Romain Guy2bffd262010-09-12 17:40:02 -0700189 int penY = y;
Romain Guy694b5192010-07-21 21:33:20 -0700190 int glyphsLeft = 1;
191 if (numGlyphs > 0) {
192 glyphsLeft = numGlyphs;
193 }
194
Romain Guy2bffd262010-09-12 17:40:02 -0700195 SkFixed prevRsbDelta = 0;
Romain Guy5a6d3a42011-10-07 15:03:24 -0700196 penX += 0.5f;
Romain Guy2bffd262010-09-12 17:40:02 -0700197
Romain Guy694b5192010-07-21 21:33:20 -0700198 text += start;
199
200 while (glyphsLeft > 0) {
Romain Guy726aeba2011-06-01 14:52:00 -0700201 glyph_t glyph = GET_GLYPH(text);
Romain Guy694b5192010-07-21 21:33:20 -0700202
Romain Guy61c8c9c2010-08-09 20:48:09 -0700203 // Reached the end of the string
Romain Guy726aeba2011-06-01 14:52:00 -0700204 if (IS_END_OF_STRING(glyph)) {
Romain Guy694b5192010-07-21 21:33:20 -0700205 break;
206 }
207
Romain Guy726aeba2011-06-01 14:52:00 -0700208 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
Romain Guy5a6d3a42011-10-07 15:03:24 -0700209 penX += SkFixedToFloat(SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta));
Romain Guy2bffd262010-09-12 17:40:02 -0700210 prevRsbDelta = cachedGlyph->mRsbDelta;
Romain Guy694b5192010-07-21 21:33:20 -0700211
212 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
213 if (cachedGlyph->mIsValid) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700214 switch(mode) {
215 case FRAMEBUFFER:
Romain Guy5a6d3a42011-10-07 15:03:24 -0700216 drawCachedGlyph(cachedGlyph, (int) floorf(penX), penY);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700217 break;
218 case BITMAP:
Romain Guy5a6d3a42011-10-07 15:03:24 -0700219 drawCachedGlyph(cachedGlyph, (int) floorf(penX), penY, bitmap, bitmapW, bitmapH);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700220 break;
221 case MEASURE:
Romain Guy5a6d3a42011-10-07 15:03:24 -0700222 measureCachedGlyph(cachedGlyph, (int) floorf(penX), penY, bounds);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700223 break;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700224 }
Romain Guy694b5192010-07-21 21:33:20 -0700225 }
226
Romain Guy5a6d3a42011-10-07 15:03:24 -0700227 penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
Romain Guy694b5192010-07-21 21:33:20 -0700228
229 // If we were given a specific number of glyphs, decrement
230 if (numGlyphs > 0) {
231 glyphsLeft--;
232 }
233 }
234}
235
Romain Guy51769a62010-07-23 00:28:00 -0700236void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
Romain Guy694b5192010-07-21 21:33:20 -0700237 glyph->mAdvanceX = skiaGlyph.fAdvanceX;
238 glyph->mAdvanceY = skiaGlyph.fAdvanceY;
239 glyph->mBitmapLeft = skiaGlyph.fLeft;
240 glyph->mBitmapTop = skiaGlyph.fTop;
Romain Guy2bffd262010-09-12 17:40:02 -0700241 glyph->mLsbDelta = skiaGlyph.fLsbDelta;
242 glyph->mRsbDelta = skiaGlyph.fRsbDelta;
Romain Guy694b5192010-07-21 21:33:20 -0700243
244 uint32_t startX = 0;
245 uint32_t startY = 0;
246
Romain Guy694b5192010-07-21 21:33:20 -0700247 // Get the bitmap for the glyph
248 paint->findImage(skiaGlyph);
Romain Guy51769a62010-07-23 00:28:00 -0700249 glyph->mIsValid = mState->cacheBitmap(skiaGlyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700250
251 if (!glyph->mIsValid) {
252 return;
253 }
254
255 uint32_t endX = startX + skiaGlyph.fWidth;
256 uint32_t endY = startY + skiaGlyph.fHeight;
257
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700258 glyph->mStartX = startX;
259 glyph->mStartY = startY;
Romain Guy694b5192010-07-21 21:33:20 -0700260 glyph->mBitmapWidth = skiaGlyph.fWidth;
261 glyph->mBitmapHeight = skiaGlyph.fHeight;
262
Romain Guy51769a62010-07-23 00:28:00 -0700263 uint32_t cacheWidth = mState->getCacheWidth();
264 uint32_t cacheHeight = mState->getCacheHeight();
Romain Guy694b5192010-07-21 21:33:20 -0700265
266 glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
267 glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
268 glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
269 glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
270
Romain Guy51769a62010-07-23 00:28:00 -0700271 mState->mUploadTexture = true;
Romain Guy694b5192010-07-21 21:33:20 -0700272}
273
Romain Guy726aeba2011-06-01 14:52:00 -0700274Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) {
Romain Guy51769a62010-07-23 00:28:00 -0700275 CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
Romain Guy694b5192010-07-21 21:33:20 -0700276 mCachedGlyphs.add(glyph, newGlyph);
277
Romain Guy726aeba2011-06-01 14:52:00 -0700278 const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
Romain Guy694b5192010-07-21 21:33:20 -0700279 newGlyph->mGlyphIndex = skiaGlyph.fID;
280 newGlyph->mIsValid = false;
281
282 updateGlyphCache(paint, skiaGlyph, newGlyph);
283
284 return newGlyph;
285}
286
Romain Guy2577db12011-01-18 13:02:38 -0800287Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
Romain Guybd496bc2011-08-02 17:32:41 -0700288 int flags, uint32_t italicStyle, uint32_t scaleX,
289 SkPaint::Style style, uint32_t strokeWidth) {
Romain Guy694b5192010-07-21 21:33:20 -0700290 Vector<Font*> &activeFonts = state->mActiveFonts;
291
292 for (uint32_t i = 0; i < activeFonts.size(); i++) {
Romain Guy51769a62010-07-23 00:28:00 -0700293 Font* font = activeFonts[i];
Romain Guy2577db12011-01-18 13:02:38 -0800294 if (font->mFontId == fontId && font->mFontSize == fontSize &&
Chet Haase8668f8a2011-03-02 13:51:36 -0800295 font->mFlags == flags && font->mItalicStyle == italicStyle &&
Romain Guybd496bc2011-08-02 17:32:41 -0700296 font->mScaleX == scaleX && font->mStyle == style &&
297 (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
Romain Guy51769a62010-07-23 00:28:00 -0700298 return font;
Romain Guy694b5192010-07-21 21:33:20 -0700299 }
300 }
301
Romain Guybd496bc2011-08-02 17:32:41 -0700302 Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
303 scaleX, style, strokeWidth);
Romain Guy694b5192010-07-21 21:33:20 -0700304 activeFonts.push(newFont);
305 return newFont;
306}
307
308///////////////////////////////////////////////////////////////////////////////
309// FontRenderer
310///////////////////////////////////////////////////////////////////////////////
311
Romain Guy514fb182011-01-19 14:38:29 -0800312static bool sLogFontRendererCreate = true;
313
Romain Guy694b5192010-07-21 21:33:20 -0700314FontRenderer::FontRenderer() {
Romain Guyc9855a52011-01-21 21:14:15 -0800315 if (sLogFontRendererCreate) {
316 INIT_LOGD("Creating FontRenderer");
317 }
Romain Guy51769a62010-07-23 00:28:00 -0700318
Romain Guyb45c0c92010-08-26 20:35:23 -0700319 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700320 mInitialized = false;
321 mMaxNumberOfQuads = 1024;
322 mCurrentQuadIndex = 0;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700323 mTextureId = 0;
Romain Guy694b5192010-07-21 21:33:20 -0700324
Romain Guy9cccc2b2010-08-07 23:46:15 -0700325 mTextMeshPtr = NULL;
326 mTextTexture = NULL;
327
Romain Guy694b5192010-07-21 21:33:20 -0700328 mIndexBufferID = 0;
Alex Sakhartchouk894df172011-02-17 16:45:37 -0800329 mPositionAttrSlot = -1;
330 mTexcoordAttrSlot = -1;
Romain Guy694b5192010-07-21 21:33:20 -0700331
Romain Guy51769a62010-07-23 00:28:00 -0700332 mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700333 mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -0700334
335 char property[PROPERTY_VALUE_MAX];
336 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800337 if (sLogFontRendererCreate) {
338 INIT_LOGD(" Setting text cache width to %s pixels", property);
339 }
Romain Guy51769a62010-07-23 00:28:00 -0700340 mCacheWidth = atoi(property);
341 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800342 if (sLogFontRendererCreate) {
Romain Guyc9855a52011-01-21 21:14:15 -0800343 INIT_LOGD(" Using default text cache width of %i pixels", mCacheWidth);
Romain Guy514fb182011-01-19 14:38:29 -0800344 }
Romain Guy51769a62010-07-23 00:28:00 -0700345 }
346
347 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800348 if (sLogFontRendererCreate) {
349 INIT_LOGD(" Setting text cache width to %s pixels", property);
350 }
Romain Guy51769a62010-07-23 00:28:00 -0700351 mCacheHeight = atoi(property);
352 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800353 if (sLogFontRendererCreate) {
Romain Guyc9855a52011-01-21 21:14:15 -0800354 INIT_LOGD(" Using default text cache height of %i pixels", mCacheHeight);
Romain Guy514fb182011-01-19 14:38:29 -0800355 }
Romain Guy51769a62010-07-23 00:28:00 -0700356 }
Romain Guy514fb182011-01-19 14:38:29 -0800357
358 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -0700359}
360
361FontRenderer::~FontRenderer() {
362 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
363 delete mCacheLines[i];
364 }
365 mCacheLines.clear();
366
Romain Guy9cccc2b2010-08-07 23:46:15 -0700367 if (mInitialized) {
368 delete[] mTextMeshPtr;
369 delete[] mTextTexture;
370 }
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700371
Romain Guy9cccc2b2010-08-07 23:46:15 -0700372 if (mTextureId) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700373 glDeleteTextures(1, &mTextureId);
374 }
Romain Guy694b5192010-07-21 21:33:20 -0700375
376 Vector<Font*> fontsToDereference = mActiveFonts;
377 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
378 delete fontsToDereference[i];
379 }
380}
381
382void FontRenderer::flushAllAndInvalidate() {
383 if (mCurrentQuadIndex != 0) {
384 issueDrawCommand();
385 mCurrentQuadIndex = 0;
386 }
387 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
388 mActiveFonts[i]->invalidateTextureCache();
389 }
390 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
391 mCacheLines[i]->mCurrentCol = 0;
392 }
393}
394
Romain Guy51769a62010-07-23 00:28:00 -0700395bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
Romain Guy694b5192010-07-21 21:33:20 -0700396 // If the glyph is too tall, don't cache it
Romain Guy799833a2011-08-30 14:41:48 -0700397 if (glyph.fHeight + 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
Chet Haase44984ea2011-05-19 13:50:47 -0700398 if (mCacheHeight < MAX_TEXT_CACHE_HEIGHT) {
399 // Default cache not large enough for large glyphs - resize cache to
400 // max size and try again
401 flushAllAndInvalidate();
402 initTextTexture(true);
403 }
Romain Guy799833a2011-08-30 14:41:48 -0700404 if (glyph.fHeight + 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
Chet Haase44984ea2011-05-19 13:50:47 -0700405 LOGE("Font size to large to fit in cache. width, height = %i, %i",
406 (int) glyph.fWidth, (int) glyph.fHeight);
407 return false;
408 }
Romain Guy694b5192010-07-21 21:33:20 -0700409 }
410
411 // Now copy the bitmap into the cache texture
412 uint32_t startX = 0;
413 uint32_t startY = 0;
414
415 bool bitmapFit = false;
416 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
417 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
418 if (bitmapFit) {
419 break;
420 }
421 }
422
423 // If the new glyph didn't fit, flush the state so far and invalidate everything
424 if (!bitmapFit) {
425 flushAllAndInvalidate();
426
427 // Try to fit it again
428 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
429 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
430 if (bitmapFit) {
431 break;
432 }
433 }
434
435 // if we still don't fit, something is wrong and we shouldn't draw
436 if (!bitmapFit) {
437 LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
438 (int) glyph.fWidth, (int) glyph.fHeight);
439 return false;
440 }
441 }
442
443 *retOriginX = startX;
444 *retOriginY = startY;
445
446 uint32_t endX = startX + glyph.fWidth;
447 uint32_t endY = startY + glyph.fHeight;
448
449 uint32_t cacheWidth = mCacheWidth;
450
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700451 uint8_t* cacheBuffer = mTextTexture;
452 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700453 unsigned int stride = glyph.rowBytes();
454
455 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
456 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
457 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700458 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
Romain Guyb45c0c92010-08-26 20:35:23 -0700459 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
Romain Guy694b5192010-07-21 21:33:20 -0700460 }
461 }
462
463 return true;
464}
465
Chet Haase44984ea2011-05-19 13:50:47 -0700466void FontRenderer::initTextTexture(bool largeFonts) {
467 mCacheLines.clear();
468 if (largeFonts) {
469 mCacheWidth = MAX_TEXT_CACHE_WIDTH;
470 mCacheHeight = MAX_TEXT_CACHE_HEIGHT;
471 }
472
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700473 mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
Romain Guy1de10832010-10-03 14:37:09 -0700474 memset(mTextTexture, 0, mCacheWidth * mCacheHeight * sizeof(uint8_t));
475
Romain Guy694b5192010-07-21 21:33:20 -0700476 mUploadTexture = false;
477
Chet Haase44984ea2011-05-19 13:50:47 -0700478 if (mTextureId != 0) {
479 glDeleteTextures(1, &mTextureId);
480 }
Romain Guy694b5192010-07-21 21:33:20 -0700481 glGenTextures(1, &mTextureId);
482 glBindTexture(GL_TEXTURE_2D, mTextureId);
483 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
Chet Haase44984ea2011-05-19 13:50:47 -0700484 // Initialize texture dimensions
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700485 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700486 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Romain Guy694b5192010-07-21 21:33:20 -0700487
Romain Guye8cb9c142010-10-04 14:14:11 -0700488 mLinearFiltering = false;
489 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
490 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
Romain Guy694b5192010-07-21 21:33:20 -0700491
492 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
493 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
494
495 // Split up our cache texture into lines of certain widths
496 int nextLine = 0;
Romain Guy7975fb62010-10-01 16:36:14 -0700497 mCacheLines.push(new CacheTextureLine(mCacheWidth, 18, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700498 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700499 mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700500 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700501 mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700502 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700503 mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700504 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700505 mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700506 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700507 mCacheLines.push(new CacheTextureLine(mCacheWidth, 42, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700508 nextLine += mCacheLines.top()->mMaxHeight;
Chet Haase44984ea2011-05-19 13:50:47 -0700509 if (largeFonts) {
510 int nextSize = 76;
511 // Make several new lines with increasing font sizes
512 while (nextSize < (int)(mCacheHeight - nextLine - (2 * nextSize))) {
513 mCacheLines.push(new CacheTextureLine(mCacheWidth, nextSize, nextLine, 0));
514 nextLine += mCacheLines.top()->mMaxHeight;
515 nextSize += 50;
516 }
517 }
Romain Guy51769a62010-07-23 00:28:00 -0700518 mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700519}
520
521// Avoid having to reallocate memory and render quad by quad
522void FontRenderer::initVertexArrayBuffers() {
523 uint32_t numIndicies = mMaxNumberOfQuads * 6;
524 uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700525 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700526
527 // Four verts, two triangles , six indices per quad
528 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
529 int i6 = i * 6;
530 int i4 = i * 4;
531
532 indexBufferData[i6 + 0] = i4 + 0;
533 indexBufferData[i6 + 1] = i4 + 1;
534 indexBufferData[i6 + 2] = i4 + 2;
535
536 indexBufferData[i6 + 3] = i4 + 0;
537 indexBufferData[i6 + 4] = i4 + 2;
538 indexBufferData[i6 + 5] = i4 + 3;
539 }
540
541 glGenBuffers(1, &mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700542 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
543 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
544 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
Romain Guy694b5192010-07-21 21:33:20 -0700545
546 free(indexBufferData);
547
548 uint32_t coordSize = 3;
549 uint32_t uvSize = 2;
550 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700551 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
552 mTextMeshPtr = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700553}
554
555// We don't want to allocate anything unless we actually draw text
556void FontRenderer::checkInit() {
557 if (mInitialized) {
558 return;
559 }
560
561 initTextTexture();
562 initVertexArrayBuffers();
563
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700564 // We store a string with letters in a rough frequency of occurrence
565 mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
566 mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
567 mLatinPrecache += String16(",.?!()-+@;:`'");
568 mLatinPrecache += String16("0123456789");
569
Romain Guy694b5192010-07-21 21:33:20 -0700570 mInitialized = true;
571}
572
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700573void FontRenderer::checkTextureUpdate() {
574 if (!mUploadTexture) {
575 return;
Romain Guy694b5192010-07-21 21:33:20 -0700576 }
577
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700578 glBindTexture(GL_TEXTURE_2D, mTextureId);
579
580 // Iterate over all the cache lines and see which ones need to be updated
581 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
582 CacheTextureLine* cl = mCacheLines[i];
583 if(cl->mDirty) {
584 uint32_t xOffset = 0;
585 uint32_t yOffset = cl->mCurrentRow;
586 uint32_t width = mCacheWidth;
587 uint32_t height = cl->mMaxHeight;
Romain Guy1e45aae2010-08-13 19:39:53 -0700588 void* textureData = mTextTexture + yOffset*width;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700589
590 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700591 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700592
593 cl->mDirty = false;
594 }
595 }
596
597 mUploadTexture = false;
598}
599
600void FontRenderer::issueDrawCommand() {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700601 checkTextureUpdate();
602
Romain Guy51769a62010-07-23 00:28:00 -0700603 float* vtx = mTextMeshPtr;
604 float* tex = vtx + 3;
Romain Guy694b5192010-07-21 21:33:20 -0700605
Alex Sakhartchouk894df172011-02-17 16:45:37 -0800606 glVertexAttribPointer(mPositionAttrSlot, 3, GL_FLOAT, false, 20, vtx);
607 glVertexAttribPointer(mTexcoordAttrSlot, 2, GL_FLOAT, false, 20, tex);
Romain Guy694b5192010-07-21 21:33:20 -0700608
609 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
610 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
Romain Guy5b3b3522010-10-27 18:57:51 -0700611
612 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700613}
614
615void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
616 float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
617 float x4, float y4, float z4, float u4, float v4) {
Romain Guy09147fb2010-07-22 13:08:20 -0700618 if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) {
619 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
726 Rect bounds;
Romain Guy726aeba2011-06-01 14:52:00 -0700727 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds);
Romain Guy1e45aae2010-08-13 19:39:53 -0700728 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
729 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700730 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guy1e45aae2010-08-13 19:39:53 -0700731 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700732 dataBuffer[i] = 0;
733 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700734
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700735 int penX = radius - bounds.left;
736 int penY = radius - bounds.bottom;
737
Romain Guy726aeba2011-06-01 14:52:00 -0700738 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
Romain Guy1e45aae2010-08-13 19:39:53 -0700739 dataBuffer, paddedWidth, paddedHeight);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700740 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
741
742 DropShadow image;
743 image.width = paddedWidth;
744 image.height = paddedHeight;
745 image.image = dataBuffer;
746 image.penX = penX;
747 image.penY = penY;
748 return image;
749}
Romain Guy694b5192010-07-21 21:33:20 -0700750
Romain Guy5b3b3522010-10-27 18:57:51 -0700751bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
752 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700753 checkInit();
754
Romain Guy09147fb2010-07-22 13:08:20 -0700755 if (!mCurrentFont) {
756 LOGE("No font set");
Romain Guy5b3b3522010-10-27 18:57:51 -0700757 return false;
Romain Guy694b5192010-07-21 21:33:20 -0700758 }
759
Alex Sakhartchouk894df172011-02-17 16:45:37 -0800760 if (mPositionAttrSlot < 0 || mTexcoordAttrSlot < 0) {
761 LOGE("Font renderer unable to draw, attribute slots undefined");
762 return false;
763 }
764
Romain Guy5b3b3522010-10-27 18:57:51 -0700765 mDrawn = false;
766 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -0700767 mClip = clip;
Romain Guy726aeba2011-06-01 14:52:00 -0700768 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
Romain Guy5b3b3522010-10-27 18:57:51 -0700769 mBounds = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700770
771 if (mCurrentQuadIndex != 0) {
772 issueDrawCommand();
773 mCurrentQuadIndex = 0;
774 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700775
776 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700777}
778
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700779void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
780 // Compute gaussian weights for the blur
781 // e is the euler's number
782 float e = 2.718281828459045f;
783 float pi = 3.1415926535897932f;
784 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
785 // x is of the form [-radius .. 0 .. radius]
786 // and sigma varies with radius.
787 // Based on some experimental radius values and sigma's
788 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700789 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700790 // The larger the radius gets, the more our gaussian blur
791 // will resemble a box blur since with large sigma
792 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -0800793 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700794
795 // Now compute the coefficints
796 // We will store some redundant values to save some math during
797 // the blur calculations
798 // precompute some values
799 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
800 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
801
802 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -0800803 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700804 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700805 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
806 normalizeFactor += weights[r + radius];
807 }
808
809 //Now we need to normalize the weights because all our coefficients need to add up to one
810 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -0800811 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700812 weights[r + radius] *= normalizeFactor;
813 }
814}
815
816void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700817 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700818 float blurredPixel = 0.0f;
819 float currentPixel = 0.0f;
820
Romain Guy325a0f92011-01-05 15:26:55 -0800821 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700822
823 const uint8_t* input = source + y * width;
824 uint8_t* output = dest + y * width;
825
Romain Guy325a0f92011-01-05 15:26:55 -0800826 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700827 blurredPixel = 0.0f;
828 const float* gPtr = weights;
829 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800830 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700831 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -0800832 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700833 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700834 blurredPixel += currentPixel * gPtr[0];
835 gPtr++;
836 i++;
837 }
838 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800839 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700840 // Stepping left and right away from the pixel
841 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -0800842 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700843 validW = 0;
844 }
Romain Guy325a0f92011-01-05 15:26:55 -0800845 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700846 validW = width - 1;
847 }
848
Romain Guy325a0f92011-01-05 15:26:55 -0800849 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700850 blurredPixel += currentPixel * gPtr[0];
851 gPtr++;
852 }
853 }
854 *output = (uint8_t)blurredPixel;
855 output ++;
856 }
857 }
858}
859
860void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700861 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700862 float blurredPixel = 0.0f;
863 float currentPixel = 0.0f;
864
Romain Guy325a0f92011-01-05 15:26:55 -0800865 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700866
867 uint8_t* output = dest + y * width;
868
Romain Guy325a0f92011-01-05 15:26:55 -0800869 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700870 blurredPixel = 0.0f;
871 const float* gPtr = weights;
872 const uint8_t* input = source + x;
873 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800874 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700875 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -0800876 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700877 currentPixel = (float)(*i);
878 blurredPixel += currentPixel * gPtr[0];
879 gPtr++;
880 i += width;
881 }
882 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800883 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700884 int validH = y + r;
885 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -0800886 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700887 validH = 0;
888 }
Romain Guy325a0f92011-01-05 15:26:55 -0800889 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700890 validH = height - 1;
891 }
892
893 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -0800894 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700895 blurredPixel += currentPixel * gPtr[0];
896 gPtr++;
897 }
898 }
Romain Guy325a0f92011-01-05 15:26:55 -0800899 *output = (uint8_t) blurredPixel;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700900 output ++;
901 }
902 }
903}
904
905
906void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
907 float *gaussian = new float[2 * radius + 1];
908 computeGaussianWeights(gaussian, radius);
909 uint8_t* scratch = new uint8_t[width * height];
910 horizontalBlur(gaussian, radius, image, scratch, width, height);
911 verticalBlur(gaussian, radius, scratch, image, width, height);
912 delete[] gaussian;
913 delete[] scratch;
914}
915
Romain Guy694b5192010-07-21 21:33:20 -0700916}; // namespace uirenderer
917}; // namespace android