blob: 9bf3de81ba93e11e0c5c234f8134f50d60ddfdf3 [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,
Chet Haase8668f8a2011-03-02 13:51:36 -080047 int flags, uint32_t italicStyle, uint32_t scaleX) :
Romain Guy2577db12011-01-18 13:02:38 -080048 mState(state), mFontId(fontId), mFontSize(fontSize),
Chet Haase8668f8a2011-03-02 13:51:36 -080049 mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX) {
Romain Guy694b5192010-07-21 21:33:20 -070050}
51
52
53Font::~Font() {
54 for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
55 if (mState->mActiveFonts[ct] == this) {
56 mState->mActiveFonts.removeAt(ct);
57 break;
58 }
59 }
60
61 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
Romain Guy726aeba2011-06-01 14:52:00 -070062 delete mCachedGlyphs.valueAt(i);
Romain Guy694b5192010-07-21 21:33:20 -070063 }
64}
65
66void Font::invalidateTextureCache() {
67 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
68 mCachedGlyphs.valueAt(i)->mIsValid = false;
69 }
70}
71
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070072void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds) {
73 int nPenX = x + glyph->mBitmapLeft;
74 int nPenY = y + glyph->mBitmapTop;
75
76 int width = (int) glyph->mBitmapWidth;
77 int height = (int) glyph->mBitmapHeight;
78
Romain Guy61c8c9c2010-08-09 20:48:09 -070079 if (bounds->bottom > nPenY) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070080 bounds->bottom = nPenY;
81 }
Romain Guy61c8c9c2010-08-09 20:48:09 -070082 if (bounds->left > nPenX) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070083 bounds->left = nPenX;
84 }
Romain Guy61c8c9c2010-08-09 20:48:09 -070085 if (bounds->right < nPenX + width) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070086 bounds->right = nPenX + width;
87 }
Romain Guy61c8c9c2010-08-09 20:48:09 -070088 if (bounds->top < nPenY + height) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070089 bounds->top = nPenY + height;
90 }
91}
92
Romain Guy694b5192010-07-21 21:33:20 -070093void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
Romain Guy694b5192010-07-21 21:33:20 -070094 int nPenX = x + glyph->mBitmapLeft;
95 int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
96
Romain Guy51769a62010-07-23 00:28:00 -070097 float u1 = glyph->mBitmapMinU;
98 float u2 = glyph->mBitmapMaxU;
99 float v1 = glyph->mBitmapMinV;
100 float v2 = glyph->mBitmapMaxV;
101
102 int width = (int) glyph->mBitmapWidth;
103 int height = (int) glyph->mBitmapHeight;
104
105 mState->appendMeshQuad(nPenX, nPenY, 0, u1, v2,
106 nPenX + width, nPenY, 0, u2, v2,
107 nPenX + width, nPenY - height, 0, u2, v1,
108 nPenX, nPenY - height, 0, u1, v1);
Romain Guy694b5192010-07-21 21:33:20 -0700109}
110
Romain Guyb45c0c92010-08-26 20:35:23 -0700111void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
112 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700113 int nPenX = x + glyph->mBitmapLeft;
114 int nPenY = y + glyph->mBitmapTop;
115
116 uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
117 uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
118
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700119 uint32_t cacheWidth = mState->getCacheWidth();
120 const uint8_t* cacheBuffer = mState->getTextTextureData();
121
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700122 uint32_t cacheX = 0, cacheY = 0;
123 int32_t bX = 0, bY = 0;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700124 for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
125 for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
Romain Guyb45c0c92010-08-26 20:35:23 -0700126 if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700127 LOGE("Skipping invalid index");
128 continue;
129 }
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700130 uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
131 bitmap[bY * bitmapW + bX] = tempCol;
132 }
133 }
134
135}
136
Romain Guy726aeba2011-06-01 14:52:00 -0700137Font::CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700138 CachedGlyphInfo* cachedGlyph = NULL;
Romain Guy726aeba2011-06-01 14:52:00 -0700139 ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
Romain Guy1e45aae2010-08-13 19:39:53 -0700140 if (index >= 0) {
141 cachedGlyph = mCachedGlyphs.valueAt(index);
142 } else {
Romain Guy726aeba2011-06-01 14:52:00 -0700143 cachedGlyph = cacheGlyph(paint, textUnit);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700144 }
145
146 // Is the glyph still in texture cache?
147 if (!cachedGlyph->mIsValid) {
Romain Guy726aeba2011-06-01 14:52:00 -0700148 const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700149 updateGlyphCache(paint, skiaGlyph, cachedGlyph);
150 }
151
152 return cachedGlyph;
153}
154
Romain Guy726aeba2011-06-01 14:52:00 -0700155void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700156 int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
157 if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
Romain Guy726aeba2011-06-01 14:52:00 -0700158 render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700159 bitmapW, bitmapH, NULL);
160 } else {
Romain Guy726aeba2011-06-01 14:52:00 -0700161 render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
162 0, 0, NULL);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700163 }
164
165}
166
Romain Guy726aeba2011-06-01 14:52:00 -0700167void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700168 int numGlyphs, Rect *bounds) {
169 if (bounds == NULL) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700170 LOGE("No return rectangle provided to measure text");
171 return;
172 }
173 bounds->set(1e6, -1e6, -1e6, 1e6);
Romain Guy726aeba2011-06-01 14:52:00 -0700174 render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700175}
176
Romain Guy58ef7fb2010-09-13 12:52:37 -0700177#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16)
Romain Guy2bffd262010-09-12 17:40:02 -0700178
Romain Guy726aeba2011-06-01 14:52:00 -0700179void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700180 int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
181 uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700182 if (numGlyphs == 0 || text == NULL || len == 0) {
183 return;
184 }
185
Romain Guy2bffd262010-09-12 17:40:02 -0700186 SkFixed penX = SkIntToFixed(x);
187 int penY = y;
Romain Guy694b5192010-07-21 21:33:20 -0700188 int glyphsLeft = 1;
189 if (numGlyphs > 0) {
190 glyphsLeft = numGlyphs;
191 }
192
Romain Guy2bffd262010-09-12 17:40:02 -0700193 SkFixed prevRsbDelta = 0;
194 penX += SK_Fixed1 / 2;
195
Romain Guy694b5192010-07-21 21:33:20 -0700196 text += start;
197
198 while (glyphsLeft > 0) {
Romain Guy726aeba2011-06-01 14:52:00 -0700199 glyph_t glyph = GET_GLYPH(text);
Romain Guy694b5192010-07-21 21:33:20 -0700200
Romain Guy61c8c9c2010-08-09 20:48:09 -0700201 // Reached the end of the string
Romain Guy726aeba2011-06-01 14:52:00 -0700202 if (IS_END_OF_STRING(glyph)) {
Romain Guy694b5192010-07-21 21:33:20 -0700203 break;
204 }
205
Romain Guy726aeba2011-06-01 14:52:00 -0700206 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
Romain Guy2bffd262010-09-12 17:40:02 -0700207 penX += SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta);
208 prevRsbDelta = cachedGlyph->mRsbDelta;
Romain Guy694b5192010-07-21 21:33:20 -0700209
210 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
211 if (cachedGlyph->mIsValid) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700212 switch(mode) {
213 case FRAMEBUFFER:
Romain Guy2bffd262010-09-12 17:40:02 -0700214 drawCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700215 break;
216 case BITMAP:
Romain Guy2bffd262010-09-12 17:40:02 -0700217 drawCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY, bitmap, bitmapW, bitmapH);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700218 break;
219 case MEASURE:
Romain Guy2bffd262010-09-12 17:40:02 -0700220 measureCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY, bounds);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700221 break;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700222 }
Romain Guy694b5192010-07-21 21:33:20 -0700223 }
224
Romain Guy2bffd262010-09-12 17:40:02 -0700225 penX += cachedGlyph->mAdvanceX;
Romain Guy694b5192010-07-21 21:33:20 -0700226
227 // If we were given a specific number of glyphs, decrement
228 if (numGlyphs > 0) {
229 glyphsLeft--;
230 }
231 }
232}
233
Romain Guy51769a62010-07-23 00:28:00 -0700234void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
Romain Guy694b5192010-07-21 21:33:20 -0700235 glyph->mAdvanceX = skiaGlyph.fAdvanceX;
236 glyph->mAdvanceY = skiaGlyph.fAdvanceY;
237 glyph->mBitmapLeft = skiaGlyph.fLeft;
238 glyph->mBitmapTop = skiaGlyph.fTop;
Romain Guy2bffd262010-09-12 17:40:02 -0700239 glyph->mLsbDelta = skiaGlyph.fLsbDelta;
240 glyph->mRsbDelta = skiaGlyph.fRsbDelta;
Romain Guy694b5192010-07-21 21:33:20 -0700241
242 uint32_t startX = 0;
243 uint32_t startY = 0;
244
Romain Guy694b5192010-07-21 21:33:20 -0700245 // Get the bitmap for the glyph
246 paint->findImage(skiaGlyph);
Romain Guy51769a62010-07-23 00:28:00 -0700247 glyph->mIsValid = mState->cacheBitmap(skiaGlyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700248
249 if (!glyph->mIsValid) {
250 return;
251 }
252
253 uint32_t endX = startX + skiaGlyph.fWidth;
254 uint32_t endY = startY + skiaGlyph.fHeight;
255
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700256 glyph->mStartX = startX;
257 glyph->mStartY = startY;
Romain Guy694b5192010-07-21 21:33:20 -0700258 glyph->mBitmapWidth = skiaGlyph.fWidth;
259 glyph->mBitmapHeight = skiaGlyph.fHeight;
260
Romain Guy51769a62010-07-23 00:28:00 -0700261 uint32_t cacheWidth = mState->getCacheWidth();
262 uint32_t cacheHeight = mState->getCacheHeight();
Romain Guy694b5192010-07-21 21:33:20 -0700263
264 glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
265 glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
266 glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
267 glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
268
Romain Guy51769a62010-07-23 00:28:00 -0700269 mState->mUploadTexture = true;
Romain Guy694b5192010-07-21 21:33:20 -0700270}
271
Romain Guy726aeba2011-06-01 14:52:00 -0700272Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) {
Romain Guy51769a62010-07-23 00:28:00 -0700273 CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
Romain Guy694b5192010-07-21 21:33:20 -0700274 mCachedGlyphs.add(glyph, newGlyph);
275
Romain Guy726aeba2011-06-01 14:52:00 -0700276 const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
Romain Guy694b5192010-07-21 21:33:20 -0700277 newGlyph->mGlyphIndex = skiaGlyph.fID;
278 newGlyph->mIsValid = false;
279
280 updateGlyphCache(paint, skiaGlyph, newGlyph);
281
282 return newGlyph;
283}
284
Romain Guy2577db12011-01-18 13:02:38 -0800285Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
Chet Haase8668f8a2011-03-02 13:51:36 -0800286 int flags, uint32_t italicStyle, uint32_t scaleX) {
Romain Guy694b5192010-07-21 21:33:20 -0700287 Vector<Font*> &activeFonts = state->mActiveFonts;
288
289 for (uint32_t i = 0; i < activeFonts.size(); i++) {
Romain Guy51769a62010-07-23 00:28:00 -0700290 Font* font = activeFonts[i];
Romain Guy2577db12011-01-18 13:02:38 -0800291 if (font->mFontId == fontId && font->mFontSize == fontSize &&
Chet Haase8668f8a2011-03-02 13:51:36 -0800292 font->mFlags == flags && font->mItalicStyle == italicStyle &&
293 font->mScaleX == scaleX) {
Romain Guy51769a62010-07-23 00:28:00 -0700294 return font;
Romain Guy694b5192010-07-21 21:33:20 -0700295 }
296 }
297
Chet Haase8668f8a2011-03-02 13:51:36 -0800298 Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle, scaleX);
Romain Guy694b5192010-07-21 21:33:20 -0700299 activeFonts.push(newFont);
300 return newFont;
301}
302
303///////////////////////////////////////////////////////////////////////////////
304// FontRenderer
305///////////////////////////////////////////////////////////////////////////////
306
Romain Guy514fb182011-01-19 14:38:29 -0800307static bool sLogFontRendererCreate = true;
308
Romain Guy694b5192010-07-21 21:33:20 -0700309FontRenderer::FontRenderer() {
Romain Guyc9855a52011-01-21 21:14:15 -0800310 if (sLogFontRendererCreate) {
311 INIT_LOGD("Creating FontRenderer");
312 }
Romain Guy51769a62010-07-23 00:28:00 -0700313
Romain Guyb45c0c92010-08-26 20:35:23 -0700314 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700315 mInitialized = false;
316 mMaxNumberOfQuads = 1024;
317 mCurrentQuadIndex = 0;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700318 mTextureId = 0;
Romain Guy694b5192010-07-21 21:33:20 -0700319
Romain Guy9cccc2b92010-08-07 23:46:15 -0700320 mTextMeshPtr = NULL;
321 mTextTexture = NULL;
322
Romain Guy694b5192010-07-21 21:33:20 -0700323 mIndexBufferID = 0;
Alex Sakhartchouk894df172011-02-17 16:45:37 -0800324 mPositionAttrSlot = -1;
325 mTexcoordAttrSlot = -1;
Romain Guy694b5192010-07-21 21:33:20 -0700326
Romain Guy51769a62010-07-23 00:28:00 -0700327 mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700328 mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -0700329
330 char property[PROPERTY_VALUE_MAX];
331 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800332 if (sLogFontRendererCreate) {
333 INIT_LOGD(" Setting text cache width to %s pixels", property);
334 }
Romain Guy51769a62010-07-23 00:28:00 -0700335 mCacheWidth = atoi(property);
336 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800337 if (sLogFontRendererCreate) {
Romain Guyc9855a52011-01-21 21:14:15 -0800338 INIT_LOGD(" Using default text cache width of %i pixels", mCacheWidth);
Romain Guy514fb182011-01-19 14:38:29 -0800339 }
Romain Guy51769a62010-07-23 00:28:00 -0700340 }
341
342 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800343 if (sLogFontRendererCreate) {
344 INIT_LOGD(" Setting text cache width to %s pixels", property);
345 }
Romain Guy51769a62010-07-23 00:28:00 -0700346 mCacheHeight = atoi(property);
347 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800348 if (sLogFontRendererCreate) {
Romain Guyc9855a52011-01-21 21:14:15 -0800349 INIT_LOGD(" Using default text cache height of %i pixels", mCacheHeight);
Romain Guy514fb182011-01-19 14:38:29 -0800350 }
Romain Guy51769a62010-07-23 00:28:00 -0700351 }
Romain Guy514fb182011-01-19 14:38:29 -0800352
353 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -0700354}
355
356FontRenderer::~FontRenderer() {
357 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
358 delete mCacheLines[i];
359 }
360 mCacheLines.clear();
361
Romain Guy9cccc2b92010-08-07 23:46:15 -0700362 if (mInitialized) {
363 delete[] mTextMeshPtr;
364 delete[] mTextTexture;
365 }
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700366
Romain Guy9cccc2b92010-08-07 23:46:15 -0700367 if (mTextureId) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700368 glDeleteTextures(1, &mTextureId);
369 }
Romain Guy694b5192010-07-21 21:33:20 -0700370
371 Vector<Font*> fontsToDereference = mActiveFonts;
372 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
373 delete fontsToDereference[i];
374 }
375}
376
377void FontRenderer::flushAllAndInvalidate() {
378 if (mCurrentQuadIndex != 0) {
379 issueDrawCommand();
380 mCurrentQuadIndex = 0;
381 }
382 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
383 mActiveFonts[i]->invalidateTextureCache();
384 }
385 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
386 mCacheLines[i]->mCurrentCol = 0;
387 }
388}
389
Romain Guy51769a62010-07-23 00:28:00 -0700390bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
Romain Guy694b5192010-07-21 21:33:20 -0700391 // If the glyph is too tall, don't cache it
Romain Guy2bffd262010-09-12 17:40:02 -0700392 if (glyph.fHeight > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
Chet Haase44984ea2011-05-19 13:50:47 -0700393 if (mCacheHeight < MAX_TEXT_CACHE_HEIGHT) {
394 // Default cache not large enough for large glyphs - resize cache to
395 // max size and try again
396 flushAllAndInvalidate();
397 initTextTexture(true);
398 }
399 if (glyph.fHeight > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
400 LOGE("Font size to large to fit in cache. width, height = %i, %i",
401 (int) glyph.fWidth, (int) glyph.fHeight);
402 return false;
403 }
Romain Guy694b5192010-07-21 21:33:20 -0700404 }
405
406 // Now copy the bitmap into the cache texture
407 uint32_t startX = 0;
408 uint32_t startY = 0;
409
410 bool bitmapFit = false;
411 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
412 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
413 if (bitmapFit) {
414 break;
415 }
416 }
417
418 // If the new glyph didn't fit, flush the state so far and invalidate everything
419 if (!bitmapFit) {
420 flushAllAndInvalidate();
421
422 // Try to fit it again
423 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
424 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
425 if (bitmapFit) {
426 break;
427 }
428 }
429
430 // if we still don't fit, something is wrong and we shouldn't draw
431 if (!bitmapFit) {
432 LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
433 (int) glyph.fWidth, (int) glyph.fHeight);
434 return false;
435 }
436 }
437
438 *retOriginX = startX;
439 *retOriginY = startY;
440
441 uint32_t endX = startX + glyph.fWidth;
442 uint32_t endY = startY + glyph.fHeight;
443
444 uint32_t cacheWidth = mCacheWidth;
445
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700446 uint8_t* cacheBuffer = mTextTexture;
447 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700448 unsigned int stride = glyph.rowBytes();
449
450 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
451 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
452 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700453 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
Romain Guyb45c0c92010-08-26 20:35:23 -0700454 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
Romain Guy694b5192010-07-21 21:33:20 -0700455 }
456 }
457
458 return true;
459}
460
Chet Haase44984ea2011-05-19 13:50:47 -0700461void FontRenderer::initTextTexture(bool largeFonts) {
462 mCacheLines.clear();
463 if (largeFonts) {
464 mCacheWidth = MAX_TEXT_CACHE_WIDTH;
465 mCacheHeight = MAX_TEXT_CACHE_HEIGHT;
466 }
467
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700468 mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
Romain Guy1de10832010-10-03 14:37:09 -0700469 memset(mTextTexture, 0, mCacheWidth * mCacheHeight * sizeof(uint8_t));
470
Romain Guy694b5192010-07-21 21:33:20 -0700471 mUploadTexture = false;
472
Chet Haase44984ea2011-05-19 13:50:47 -0700473 if (mTextureId != 0) {
474 glDeleteTextures(1, &mTextureId);
475 }
Romain Guy694b5192010-07-21 21:33:20 -0700476 glGenTextures(1, &mTextureId);
477 glBindTexture(GL_TEXTURE_2D, mTextureId);
478 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
Chet Haase44984ea2011-05-19 13:50:47 -0700479 // Initialize texture dimensions
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700480 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700481 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Romain Guy694b5192010-07-21 21:33:20 -0700482
Romain Guye8cb9c142010-10-04 14:14:11 -0700483 mLinearFiltering = false;
484 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
485 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
Romain Guy694b5192010-07-21 21:33:20 -0700486
487 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
488 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
489
490 // Split up our cache texture into lines of certain widths
491 int nextLine = 0;
Romain Guy7975fb62010-10-01 16:36:14 -0700492 mCacheLines.push(new CacheTextureLine(mCacheWidth, 18, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700493 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700494 mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700495 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700496 mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700497 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700498 mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, 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, 34, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700501 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700502 mCacheLines.push(new CacheTextureLine(mCacheWidth, 42, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700503 nextLine += mCacheLines.top()->mMaxHeight;
Chet Haase44984ea2011-05-19 13:50:47 -0700504 if (largeFonts) {
505 int nextSize = 76;
506 // Make several new lines with increasing font sizes
507 while (nextSize < (int)(mCacheHeight - nextLine - (2 * nextSize))) {
508 mCacheLines.push(new CacheTextureLine(mCacheWidth, nextSize, nextLine, 0));
509 nextLine += mCacheLines.top()->mMaxHeight;
510 nextSize += 50;
511 }
512 }
Romain Guy51769a62010-07-23 00:28:00 -0700513 mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700514}
515
516// Avoid having to reallocate memory and render quad by quad
517void FontRenderer::initVertexArrayBuffers() {
518 uint32_t numIndicies = mMaxNumberOfQuads * 6;
519 uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700520 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700521
522 // Four verts, two triangles , six indices per quad
523 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
524 int i6 = i * 6;
525 int i4 = i * 4;
526
527 indexBufferData[i6 + 0] = i4 + 0;
528 indexBufferData[i6 + 1] = i4 + 1;
529 indexBufferData[i6 + 2] = i4 + 2;
530
531 indexBufferData[i6 + 3] = i4 + 0;
532 indexBufferData[i6 + 4] = i4 + 2;
533 indexBufferData[i6 + 5] = i4 + 3;
534 }
535
536 glGenBuffers(1, &mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700537 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
538 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
539 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
Romain Guy694b5192010-07-21 21:33:20 -0700540
541 free(indexBufferData);
542
543 uint32_t coordSize = 3;
544 uint32_t uvSize = 2;
545 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700546 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
547 mTextMeshPtr = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700548}
549
550// We don't want to allocate anything unless we actually draw text
551void FontRenderer::checkInit() {
552 if (mInitialized) {
553 return;
554 }
555
556 initTextTexture();
557 initVertexArrayBuffers();
558
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700559 // We store a string with letters in a rough frequency of occurrence
560 mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
561 mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
562 mLatinPrecache += String16(",.?!()-+@;:`'");
563 mLatinPrecache += String16("0123456789");
564
Romain Guy694b5192010-07-21 21:33:20 -0700565 mInitialized = true;
566}
567
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700568void FontRenderer::checkTextureUpdate() {
569 if (!mUploadTexture) {
570 return;
Romain Guy694b5192010-07-21 21:33:20 -0700571 }
572
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700573 glBindTexture(GL_TEXTURE_2D, mTextureId);
574
575 // Iterate over all the cache lines and see which ones need to be updated
576 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
577 CacheTextureLine* cl = mCacheLines[i];
578 if(cl->mDirty) {
579 uint32_t xOffset = 0;
580 uint32_t yOffset = cl->mCurrentRow;
581 uint32_t width = mCacheWidth;
582 uint32_t height = cl->mMaxHeight;
Romain Guy1e45aae2010-08-13 19:39:53 -0700583 void* textureData = mTextTexture + yOffset*width;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700584
585 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700586 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700587
588 cl->mDirty = false;
589 }
590 }
591
592 mUploadTexture = false;
593}
594
595void FontRenderer::issueDrawCommand() {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700596 checkTextureUpdate();
597
Romain Guy51769a62010-07-23 00:28:00 -0700598 float* vtx = mTextMeshPtr;
599 float* tex = vtx + 3;
Romain Guy694b5192010-07-21 21:33:20 -0700600
Alex Sakhartchouk894df172011-02-17 16:45:37 -0800601 glVertexAttribPointer(mPositionAttrSlot, 3, GL_FLOAT, false, 20, vtx);
602 glVertexAttribPointer(mTexcoordAttrSlot, 2, GL_FLOAT, false, 20, tex);
Romain Guy694b5192010-07-21 21:33:20 -0700603
604 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
605 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
Romain Guy5b3b3522010-10-27 18:57:51 -0700606
607 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700608}
609
610void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
611 float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
612 float x4, float y4, float z4, float u4, float v4) {
Romain Guy09147fb2010-07-22 13:08:20 -0700613 if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) {
614 return;
615 }
616
Romain Guy694b5192010-07-21 21:33:20 -0700617 const uint32_t vertsPerQuad = 4;
618 const uint32_t floatsPerVert = 5;
Romain Guy51769a62010-07-23 00:28:00 -0700619 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700620
Romain Guy694b5192010-07-21 21:33:20 -0700621 (*currentPos++) = x1;
622 (*currentPos++) = y1;
623 (*currentPos++) = z1;
624 (*currentPos++) = u1;
625 (*currentPos++) = v1;
626
627 (*currentPos++) = x2;
628 (*currentPos++) = y2;
629 (*currentPos++) = z2;
630 (*currentPos++) = u2;
631 (*currentPos++) = v2;
632
633 (*currentPos++) = x3;
634 (*currentPos++) = y3;
635 (*currentPos++) = z3;
636 (*currentPos++) = u3;
637 (*currentPos++) = v3;
638
639 (*currentPos++) = x4;
640 (*currentPos++) = y4;
641 (*currentPos++) = z4;
642 (*currentPos++) = u4;
643 (*currentPos++) = v4;
644
645 mCurrentQuadIndex++;
646
Romain Guy5b3b3522010-10-27 18:57:51 -0700647 if (mBounds) {
648 mBounds->left = fmin(mBounds->left, x1);
649 mBounds->top = fmin(mBounds->top, y3);
650 mBounds->right = fmax(mBounds->right, x3);
651 mBounds->bottom = fmax(mBounds->bottom, y1);
652 }
653
Romain Guy694b5192010-07-21 21:33:20 -0700654 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
655 issueDrawCommand();
656 mCurrentQuadIndex = 0;
657 }
658}
659
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700660uint32_t FontRenderer::getRemainingCacheCapacity() {
661 uint32_t remainingCapacity = 0;
662 float totalPixels = 0;
663 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
664 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
665 totalPixels += mCacheLines[i]->mMaxWidth;
666 }
667 remainingCapacity = (remainingCapacity * 100) / totalPixels;
668 return remainingCapacity;
669}
670
671void FontRenderer::precacheLatin(SkPaint* paint) {
672 // Remaining capacity is measured in %
673 uint32_t remainingCapacity = getRemainingCacheCapacity();
674 uint32_t precacheIdx = 0;
Romain Guy054dc182010-10-15 17:55:25 -0700675 while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
Romain Guy726aeba2011-06-01 14:52:00 -0700676 mCurrentFont->getCachedGlyph(paint, (int32_t) mLatinPrecache[precacheIdx]);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700677 remainingCapacity = getRemainingCacheCapacity();
678 precacheIdx ++;
679 }
680}
681
682void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
683 uint32_t currentNumFonts = mActiveFonts.size();
Romain Guy325a0f92011-01-05 15:26:55 -0800684 int flags = 0;
685 if (paint->isFakeBoldText()) {
686 flags |= Font::kFakeBold;
687 }
Romain Guy2577db12011-01-18 13:02:38 -0800688
689 const float skewX = paint->getTextSkewX();
690 uint32_t italicStyle = *(uint32_t*) &skewX;
Chet Haase8668f8a2011-03-02 13:51:36 -0800691 const float scaleXFloat = paint->getTextScaleX();
692 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
693 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle, scaleX);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700694
695 const float maxPrecacheFontSize = 40.0f;
696 bool isNewFont = currentNumFonts != mActiveFonts.size();
697
Romain Guy2bffd262010-09-12 17:40:02 -0700698 if (isNewFont && fontSize <= maxPrecacheFontSize) {
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700699 precacheLatin(paint);
700 }
Romain Guy694b5192010-07-21 21:33:20 -0700701}
Romain Guy7975fb62010-10-01 16:36:14 -0700702
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700703FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Romain Guy1e45aae2010-08-13 19:39:53 -0700704 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
705 checkInit();
706
707 if (!mCurrentFont) {
708 DropShadow image;
709 image.width = 0;
710 image.height = 0;
711 image.image = NULL;
712 image.penX = 0;
713 image.penY = 0;
714 return image;
715 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700716
717 Rect bounds;
Romain Guy726aeba2011-06-01 14:52:00 -0700718 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds);
Romain Guy1e45aae2010-08-13 19:39:53 -0700719 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
720 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700721 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guy1e45aae2010-08-13 19:39:53 -0700722 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700723 dataBuffer[i] = 0;
724 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700725
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700726 int penX = radius - bounds.left;
727 int penY = radius - bounds.bottom;
728
Romain Guy726aeba2011-06-01 14:52:00 -0700729 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
Romain Guy1e45aae2010-08-13 19:39:53 -0700730 dataBuffer, paddedWidth, paddedHeight);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700731 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
732
733 DropShadow image;
734 image.width = paddedWidth;
735 image.height = paddedHeight;
736 image.image = dataBuffer;
737 image.penX = penX;
738 image.penY = penY;
739 return image;
740}
Romain Guy694b5192010-07-21 21:33:20 -0700741
Romain Guy5b3b3522010-10-27 18:57:51 -0700742bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
743 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700744 checkInit();
745
Romain Guy09147fb2010-07-22 13:08:20 -0700746 if (!mCurrentFont) {
747 LOGE("No font set");
Romain Guy5b3b3522010-10-27 18:57:51 -0700748 return false;
Romain Guy694b5192010-07-21 21:33:20 -0700749 }
750
Alex Sakhartchouk894df172011-02-17 16:45:37 -0800751 if (mPositionAttrSlot < 0 || mTexcoordAttrSlot < 0) {
752 LOGE("Font renderer unable to draw, attribute slots undefined");
753 return false;
754 }
755
Romain Guy5b3b3522010-10-27 18:57:51 -0700756 mDrawn = false;
757 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -0700758 mClip = clip;
Romain Guy726aeba2011-06-01 14:52:00 -0700759 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
Romain Guy5b3b3522010-10-27 18:57:51 -0700760 mBounds = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700761
762 if (mCurrentQuadIndex != 0) {
763 issueDrawCommand();
764 mCurrentQuadIndex = 0;
765 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700766
767 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700768}
769
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700770void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
771 // Compute gaussian weights for the blur
772 // e is the euler's number
773 float e = 2.718281828459045f;
774 float pi = 3.1415926535897932f;
775 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
776 // x is of the form [-radius .. 0 .. radius]
777 // and sigma varies with radius.
778 // Based on some experimental radius values and sigma's
779 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700780 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700781 // The larger the radius gets, the more our gaussian blur
782 // will resemble a box blur since with large sigma
783 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -0800784 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700785
786 // Now compute the coefficints
787 // We will store some redundant values to save some math during
788 // the blur calculations
789 // precompute some values
790 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
791 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
792
793 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -0800794 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700795 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700796 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
797 normalizeFactor += weights[r + radius];
798 }
799
800 //Now we need to normalize the weights because all our coefficients need to add up to one
801 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -0800802 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700803 weights[r + radius] *= normalizeFactor;
804 }
805}
806
807void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700808 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700809 float blurredPixel = 0.0f;
810 float currentPixel = 0.0f;
811
Romain Guy325a0f92011-01-05 15:26:55 -0800812 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700813
814 const uint8_t* input = source + y * width;
815 uint8_t* output = dest + y * width;
816
Romain Guy325a0f92011-01-05 15:26:55 -0800817 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700818 blurredPixel = 0.0f;
819 const float* gPtr = weights;
820 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800821 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700822 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -0800823 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700824 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700825 blurredPixel += currentPixel * gPtr[0];
826 gPtr++;
827 i++;
828 }
829 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800830 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700831 // Stepping left and right away from the pixel
832 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -0800833 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700834 validW = 0;
835 }
Romain Guy325a0f92011-01-05 15:26:55 -0800836 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700837 validW = width - 1;
838 }
839
Romain Guy325a0f92011-01-05 15:26:55 -0800840 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700841 blurredPixel += currentPixel * gPtr[0];
842 gPtr++;
843 }
844 }
845 *output = (uint8_t)blurredPixel;
846 output ++;
847 }
848 }
849}
850
851void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700852 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700853 float blurredPixel = 0.0f;
854 float currentPixel = 0.0f;
855
Romain Guy325a0f92011-01-05 15:26:55 -0800856 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700857
858 uint8_t* output = dest + y * width;
859
Romain Guy325a0f92011-01-05 15:26:55 -0800860 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700861 blurredPixel = 0.0f;
862 const float* gPtr = weights;
863 const uint8_t* input = source + x;
864 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800865 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700866 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -0800867 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700868 currentPixel = (float)(*i);
869 blurredPixel += currentPixel * gPtr[0];
870 gPtr++;
871 i += width;
872 }
873 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800874 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700875 int validH = y + r;
876 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -0800877 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700878 validH = 0;
879 }
Romain Guy325a0f92011-01-05 15:26:55 -0800880 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700881 validH = height - 1;
882 }
883
884 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -0800885 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700886 blurredPixel += currentPixel * gPtr[0];
887 gPtr++;
888 }
889 }
Romain Guy325a0f92011-01-05 15:26:55 -0800890 *output = (uint8_t) blurredPixel;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700891 output ++;
892 }
893 }
894}
895
896
897void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
898 float *gaussian = new float[2 * radius + 1];
899 computeGaussianWeights(gaussian, radius);
900 uint8_t* scratch = new uint8_t[width * height];
901 horizontalBlur(gaussian, radius, image, scratch, width, height);
902 verticalBlur(gaussian, radius, scratch, image, width, height);
903 delete[] gaussian;
904 delete[] scratch;
905}
906
Romain Guy694b5192010-07-21 21:33:20 -0700907}; // namespace uirenderer
908}; // namespace android