blob: f04ea6f3cfb487cdccfe22d177aff169ac927909 [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
Romain Guyd71dd362011-12-12 19:03:35 -0800107 mState->appendMeshQuad(nPenX, nPenY, u1, v2,
108 nPenX + width, nPenY, u2, v2,
109 nPenX + width, nPenY - height, u2, v1,
110 nPenX, nPenY - height, 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 Guy9cccc2b92010-08-07 23:46:15 -0700324 mTextMeshPtr = NULL;
325 mTextTexture = NULL;
326
Romain Guy694b5192010-07-21 21:33:20 -0700327 mIndexBufferID = 0;
328
Romain Guy51769a62010-07-23 00:28:00 -0700329 mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700330 mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -0700331
332 char property[PROPERTY_VALUE_MAX];
333 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800334 if (sLogFontRendererCreate) {
335 INIT_LOGD(" Setting text cache width to %s pixels", property);
336 }
Romain Guy51769a62010-07-23 00:28:00 -0700337 mCacheWidth = atoi(property);
338 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800339 if (sLogFontRendererCreate) {
Romain Guyc9855a52011-01-21 21:14:15 -0800340 INIT_LOGD(" Using default text cache width of %i pixels", mCacheWidth);
Romain Guy514fb182011-01-19 14:38:29 -0800341 }
Romain Guy51769a62010-07-23 00:28:00 -0700342 }
343
344 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800345 if (sLogFontRendererCreate) {
346 INIT_LOGD(" Setting text cache width to %s pixels", property);
347 }
Romain Guy51769a62010-07-23 00:28:00 -0700348 mCacheHeight = atoi(property);
349 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800350 if (sLogFontRendererCreate) {
Romain Guyc9855a52011-01-21 21:14:15 -0800351 INIT_LOGD(" Using default text cache height of %i pixels", mCacheHeight);
Romain Guy514fb182011-01-19 14:38:29 -0800352 }
Romain Guy51769a62010-07-23 00:28:00 -0700353 }
Romain Guy514fb182011-01-19 14:38:29 -0800354
355 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -0700356}
357
358FontRenderer::~FontRenderer() {
359 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
360 delete mCacheLines[i];
361 }
362 mCacheLines.clear();
363
Romain Guy9cccc2b92010-08-07 23:46:15 -0700364 if (mInitialized) {
365 delete[] mTextMeshPtr;
366 delete[] mTextTexture;
367 }
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700368
Romain Guy9cccc2b92010-08-07 23:46:15 -0700369 if (mTextureId) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700370 glDeleteTextures(1, &mTextureId);
371 }
Romain Guy694b5192010-07-21 21:33:20 -0700372
373 Vector<Font*> fontsToDereference = mActiveFonts;
374 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
375 delete fontsToDereference[i];
376 }
377}
378
379void FontRenderer::flushAllAndInvalidate() {
380 if (mCurrentQuadIndex != 0) {
381 issueDrawCommand();
382 mCurrentQuadIndex = 0;
383 }
384 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
385 mActiveFonts[i]->invalidateTextureCache();
386 }
387 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
388 mCacheLines[i]->mCurrentCol = 0;
389 }
390}
391
Romain Guy51769a62010-07-23 00:28:00 -0700392bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
Romain Guy694b5192010-07-21 21:33:20 -0700393 // If the glyph is too tall, don't cache it
Romain Guy799833a2011-08-30 14:41:48 -0700394 if (glyph.fHeight + 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
Chet Haase44984ea2011-05-19 13:50:47 -0700395 if (mCacheHeight < MAX_TEXT_CACHE_HEIGHT) {
396 // Default cache not large enough for large glyphs - resize cache to
397 // max size and try again
398 flushAllAndInvalidate();
399 initTextTexture(true);
400 }
Romain Guy799833a2011-08-30 14:41:48 -0700401 if (glyph.fHeight + 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
Chet Haase44984ea2011-05-19 13:50:47 -0700402 LOGE("Font size to large to fit in cache. width, height = %i, %i",
403 (int) glyph.fWidth, (int) glyph.fHeight);
404 return false;
405 }
Romain Guy694b5192010-07-21 21:33:20 -0700406 }
407
408 // Now copy the bitmap into the cache texture
409 uint32_t startX = 0;
410 uint32_t startY = 0;
411
412 bool bitmapFit = false;
413 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
414 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
415 if (bitmapFit) {
416 break;
417 }
418 }
419
420 // If the new glyph didn't fit, flush the state so far and invalidate everything
421 if (!bitmapFit) {
422 flushAllAndInvalidate();
423
424 // Try to fit it again
425 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
426 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
427 if (bitmapFit) {
428 break;
429 }
430 }
431
432 // if we still don't fit, something is wrong and we shouldn't draw
433 if (!bitmapFit) {
434 LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
435 (int) glyph.fWidth, (int) glyph.fHeight);
436 return false;
437 }
438 }
439
440 *retOriginX = startX;
441 *retOriginY = startY;
442
443 uint32_t endX = startX + glyph.fWidth;
444 uint32_t endY = startY + glyph.fHeight;
445
446 uint32_t cacheWidth = mCacheWidth;
447
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700448 uint8_t* cacheBuffer = mTextTexture;
449 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700450 unsigned int stride = glyph.rowBytes();
451
452 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
453 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
454 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700455 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
Romain Guyb45c0c92010-08-26 20:35:23 -0700456 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
Romain Guy694b5192010-07-21 21:33:20 -0700457 }
458 }
459
460 return true;
461}
462
Chet Haase44984ea2011-05-19 13:50:47 -0700463void FontRenderer::initTextTexture(bool largeFonts) {
464 mCacheLines.clear();
465 if (largeFonts) {
466 mCacheWidth = MAX_TEXT_CACHE_WIDTH;
467 mCacheHeight = MAX_TEXT_CACHE_HEIGHT;
468 }
469
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700470 mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
Romain Guy1de10832010-10-03 14:37:09 -0700471 memset(mTextTexture, 0, mCacheWidth * mCacheHeight * sizeof(uint8_t));
472
Romain Guy694b5192010-07-21 21:33:20 -0700473 mUploadTexture = false;
474
Chet Haase44984ea2011-05-19 13:50:47 -0700475 if (mTextureId != 0) {
476 glDeleteTextures(1, &mTextureId);
477 }
Romain Guy694b5192010-07-21 21:33:20 -0700478 glGenTextures(1, &mTextureId);
479 glBindTexture(GL_TEXTURE_2D, mTextureId);
480 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
Chet Haase44984ea2011-05-19 13:50:47 -0700481 // Initialize texture dimensions
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700482 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700483 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Romain Guy694b5192010-07-21 21:33:20 -0700484
Romain Guye8cb9c142010-10-04 14:14:11 -0700485 mLinearFiltering = false;
486 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
487 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
Romain Guy694b5192010-07-21 21:33:20 -0700488
489 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
490 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
491
492 // Split up our cache texture into lines of certain widths
493 int nextLine = 0;
Romain Guy7975fb62010-10-01 16:36:14 -0700494 mCacheLines.push(new CacheTextureLine(mCacheWidth, 18, 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));
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));
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -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, 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, 42, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700505 nextLine += mCacheLines.top()->mMaxHeight;
Chet Haase44984ea2011-05-19 13:50:47 -0700506 if (largeFonts) {
507 int nextSize = 76;
508 // Make several new lines with increasing font sizes
509 while (nextSize < (int)(mCacheHeight - nextLine - (2 * nextSize))) {
510 mCacheLines.push(new CacheTextureLine(mCacheWidth, nextSize, nextLine, 0));
511 nextLine += mCacheLines.top()->mMaxHeight;
512 nextSize += 50;
513 }
514 }
Romain Guy51769a62010-07-23 00:28:00 -0700515 mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700516}
517
518// Avoid having to reallocate memory and render quad by quad
519void FontRenderer::initVertexArrayBuffers() {
Romain Guyd71dd362011-12-12 19:03:35 -0800520 uint32_t numIndices = mMaxNumberOfQuads * 6;
521 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700522 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700523
524 // Four verts, two triangles , six indices per quad
525 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
526 int i6 = i * 6;
527 int i4 = i * 4;
528
529 indexBufferData[i6 + 0] = i4 + 0;
530 indexBufferData[i6 + 1] = i4 + 1;
531 indexBufferData[i6 + 2] = i4 + 2;
532
533 indexBufferData[i6 + 3] = i4 + 0;
534 indexBufferData[i6 + 4] = i4 + 2;
535 indexBufferData[i6 + 5] = i4 + 3;
536 }
537
538 glGenBuffers(1, &mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700539 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
540 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
541 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
Romain Guy694b5192010-07-21 21:33:20 -0700542
543 free(indexBufferData);
544
Romain Guyd71dd362011-12-12 19:03:35 -0800545 uint32_t coordSize = 2;
Romain Guy694b5192010-07-21 21:33:20 -0700546 uint32_t uvSize = 2;
547 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700548 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
549 mTextMeshPtr = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700550}
551
552// We don't want to allocate anything unless we actually draw text
553void FontRenderer::checkInit() {
554 if (mInitialized) {
555 return;
556 }
557
558 initTextTexture();
559 initVertexArrayBuffers();
560
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700561 // We store a string with letters in a rough frequency of occurrence
562 mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
563 mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
564 mLatinPrecache += String16(",.?!()-+@;:`'");
565 mLatinPrecache += String16("0123456789");
566
Romain Guy694b5192010-07-21 21:33:20 -0700567 mInitialized = true;
568}
569
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700570void FontRenderer::checkTextureUpdate() {
571 if (!mUploadTexture) {
572 return;
Romain Guy694b5192010-07-21 21:33:20 -0700573 }
574
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700575 glBindTexture(GL_TEXTURE_2D, mTextureId);
576
577 // Iterate over all the cache lines and see which ones need to be updated
578 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
579 CacheTextureLine* cl = mCacheLines[i];
Romain Guyd71dd362011-12-12 19:03:35 -0800580 if (cl->mDirty) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700581 uint32_t xOffset = 0;
582 uint32_t yOffset = cl->mCurrentRow;
583 uint32_t width = mCacheWidth;
584 uint32_t height = cl->mMaxHeight;
Romain Guyd71dd362011-12-12 19:03:35 -0800585 void* textureData = mTextTexture + yOffset * width;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700586
587 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700588 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700589
590 cl->mDirty = false;
591 }
592 }
593
594 mUploadTexture = false;
595}
596
597void FontRenderer::issueDrawCommand() {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700598 checkTextureUpdate();
599
Romain Guy694b5192010-07-21 21:33:20 -0700600 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
601 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
Romain Guy5b3b3522010-10-27 18:57:51 -0700602
603 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700604}
605
Romain Guyd71dd362011-12-12 19:03:35 -0800606void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
607 float x2, float y2, float u2, float v2,
608 float x3, float y3, float u3, float v3,
609 float x4, float y4, float u4, float v4) {
610
Romain Guyff98fa52011-11-28 09:35:09 -0800611 if (mClip &&
612 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
Romain Guy09147fb2010-07-22 13:08:20 -0700613 return;
614 }
615
Romain Guy694b5192010-07-21 21:33:20 -0700616 const uint32_t vertsPerQuad = 4;
Romain Guyd71dd362011-12-12 19:03:35 -0800617 const uint32_t floatsPerVert = 4;
Romain Guy51769a62010-07-23 00:28:00 -0700618 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700619
Romain Guy694b5192010-07-21 21:33:20 -0700620 (*currentPos++) = x1;
621 (*currentPos++) = y1;
Romain Guy694b5192010-07-21 21:33:20 -0700622 (*currentPos++) = u1;
623 (*currentPos++) = v1;
624
625 (*currentPos++) = x2;
626 (*currentPos++) = y2;
Romain Guy694b5192010-07-21 21:33:20 -0700627 (*currentPos++) = u2;
628 (*currentPos++) = v2;
629
630 (*currentPos++) = x3;
631 (*currentPos++) = y3;
Romain Guy694b5192010-07-21 21:33:20 -0700632 (*currentPos++) = u3;
633 (*currentPos++) = v3;
634
635 (*currentPos++) = x4;
636 (*currentPos++) = y4;
Romain Guy694b5192010-07-21 21:33:20 -0700637 (*currentPos++) = u4;
638 (*currentPos++) = v4;
639
640 mCurrentQuadIndex++;
641
Romain Guy5b3b3522010-10-27 18:57:51 -0700642 if (mBounds) {
643 mBounds->left = fmin(mBounds->left, x1);
644 mBounds->top = fmin(mBounds->top, y3);
645 mBounds->right = fmax(mBounds->right, x3);
646 mBounds->bottom = fmax(mBounds->bottom, y1);
647 }
648
Romain Guy694b5192010-07-21 21:33:20 -0700649 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
650 issueDrawCommand();
651 mCurrentQuadIndex = 0;
652 }
653}
654
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700655uint32_t FontRenderer::getRemainingCacheCapacity() {
656 uint32_t remainingCapacity = 0;
657 float totalPixels = 0;
658 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
659 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
660 totalPixels += mCacheLines[i]->mMaxWidth;
661 }
662 remainingCapacity = (remainingCapacity * 100) / totalPixels;
663 return remainingCapacity;
664}
665
666void FontRenderer::precacheLatin(SkPaint* paint) {
667 // Remaining capacity is measured in %
668 uint32_t remainingCapacity = getRemainingCacheCapacity();
669 uint32_t precacheIdx = 0;
Romain Guy054dc182010-10-15 17:55:25 -0700670 while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
Romain Guy726aeba2011-06-01 14:52:00 -0700671 mCurrentFont->getCachedGlyph(paint, (int32_t) mLatinPrecache[precacheIdx]);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700672 remainingCapacity = getRemainingCacheCapacity();
673 precacheIdx ++;
674 }
675}
676
677void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
678 uint32_t currentNumFonts = mActiveFonts.size();
Romain Guy325a0f92011-01-05 15:26:55 -0800679 int flags = 0;
680 if (paint->isFakeBoldText()) {
681 flags |= Font::kFakeBold;
682 }
Romain Guy2577db12011-01-18 13:02:38 -0800683
684 const float skewX = paint->getTextSkewX();
685 uint32_t italicStyle = *(uint32_t*) &skewX;
Chet Haase8668f8a2011-03-02 13:51:36 -0800686 const float scaleXFloat = paint->getTextScaleX();
687 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
Romain Guybd496bc2011-08-02 17:32:41 -0700688 SkPaint::Style style = paint->getStyle();
689 const float strokeWidthFloat = paint->getStrokeWidth();
690 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
691 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
692 scaleX, style, strokeWidth);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700693
694 const float maxPrecacheFontSize = 40.0f;
695 bool isNewFont = currentNumFonts != mActiveFonts.size();
696
Romain Guy2bffd262010-09-12 17:40:02 -0700697 if (isNewFont && fontSize <= maxPrecacheFontSize) {
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700698 precacheLatin(paint);
699 }
Romain Guy694b5192010-07-21 21:33:20 -0700700}
Romain Guy7975fb62010-10-01 16:36:14 -0700701
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700702FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Romain Guy1e45aae2010-08-13 19:39:53 -0700703 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
704 checkInit();
705
706 if (!mCurrentFont) {
707 DropShadow image;
708 image.width = 0;
709 image.height = 0;
710 image.image = NULL;
711 image.penX = 0;
712 image.penY = 0;
713 return image;
714 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700715
Romain Guyff98fa52011-11-28 09:35:09 -0800716 mClip = NULL;
717 mBounds = NULL;
718
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700719 Rect bounds;
Romain Guy726aeba2011-06-01 14:52:00 -0700720 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds);
Romain Guyff98fa52011-11-28 09:35:09 -0800721
Romain Guy1e45aae2010-08-13 19:39:53 -0700722 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
723 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700724 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guyff98fa52011-11-28 09:35:09 -0800725
Romain Guy1e45aae2010-08-13 19:39:53 -0700726 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700727 dataBuffer[i] = 0;
728 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700729
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700730 int penX = radius - bounds.left;
731 int penY = radius - bounds.bottom;
732
Romain Guy726aeba2011-06-01 14:52:00 -0700733 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
Romain Guy1e45aae2010-08-13 19:39:53 -0700734 dataBuffer, paddedWidth, paddedHeight);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700735 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
736
737 DropShadow image;
738 image.width = paddedWidth;
739 image.height = paddedHeight;
740 image.image = dataBuffer;
741 image.penX = penX;
742 image.penY = penY;
743 return image;
744}
Romain Guy694b5192010-07-21 21:33:20 -0700745
Romain Guy5b3b3522010-10-27 18:57:51 -0700746bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
747 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700748 checkInit();
749
Romain Guy09147fb2010-07-22 13:08:20 -0700750 if (!mCurrentFont) {
751 LOGE("No font set");
Romain Guy5b3b3522010-10-27 18:57:51 -0700752 return false;
Romain Guy694b5192010-07-21 21:33:20 -0700753 }
754
Romain Guy5b3b3522010-10-27 18:57:51 -0700755 mDrawn = false;
756 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -0700757 mClip = clip;
Romain Guyff98fa52011-11-28 09:35:09 -0800758
Romain Guy726aeba2011-06-01 14:52:00 -0700759 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
Romain Guyff98fa52011-11-28 09:35:09 -0800760
Romain Guy5b3b3522010-10-27 18:57:51 -0700761 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -0800762 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700763
764 if (mCurrentQuadIndex != 0) {
765 issueDrawCommand();
766 mCurrentQuadIndex = 0;
767 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700768
769 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700770}
771
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700772void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
773 // Compute gaussian weights for the blur
774 // e is the euler's number
775 float e = 2.718281828459045f;
776 float pi = 3.1415926535897932f;
777 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
778 // x is of the form [-radius .. 0 .. radius]
779 // and sigma varies with radius.
780 // Based on some experimental radius values and sigma's
781 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700782 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700783 // The larger the radius gets, the more our gaussian blur
784 // will resemble a box blur since with large sigma
785 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -0800786 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700787
788 // Now compute the coefficints
789 // We will store some redundant values to save some math during
790 // the blur calculations
791 // precompute some values
792 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
793 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
794
795 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -0800796 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700797 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700798 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
799 normalizeFactor += weights[r + radius];
800 }
801
802 //Now we need to normalize the weights because all our coefficients need to add up to one
803 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -0800804 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700805 weights[r + radius] *= normalizeFactor;
806 }
807}
808
809void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700810 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700811 float blurredPixel = 0.0f;
812 float currentPixel = 0.0f;
813
Romain Guy325a0f92011-01-05 15:26:55 -0800814 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700815
816 const uint8_t* input = source + y * width;
817 uint8_t* output = dest + y * width;
818
Romain Guy325a0f92011-01-05 15:26:55 -0800819 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700820 blurredPixel = 0.0f;
821 const float* gPtr = weights;
822 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800823 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700824 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -0800825 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700826 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700827 blurredPixel += currentPixel * gPtr[0];
828 gPtr++;
829 i++;
830 }
831 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800832 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700833 // Stepping left and right away from the pixel
834 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -0800835 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700836 validW = 0;
837 }
Romain Guy325a0f92011-01-05 15:26:55 -0800838 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700839 validW = width - 1;
840 }
841
Romain Guy325a0f92011-01-05 15:26:55 -0800842 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700843 blurredPixel += currentPixel * gPtr[0];
844 gPtr++;
845 }
846 }
847 *output = (uint8_t)blurredPixel;
848 output ++;
849 }
850 }
851}
852
853void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700854 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700855 float blurredPixel = 0.0f;
856 float currentPixel = 0.0f;
857
Romain Guy325a0f92011-01-05 15:26:55 -0800858 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700859
860 uint8_t* output = dest + y * width;
861
Romain Guy325a0f92011-01-05 15:26:55 -0800862 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700863 blurredPixel = 0.0f;
864 const float* gPtr = weights;
865 const uint8_t* input = source + x;
866 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800867 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700868 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -0800869 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700870 currentPixel = (float)(*i);
871 blurredPixel += currentPixel * gPtr[0];
872 gPtr++;
873 i += width;
874 }
875 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800876 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700877 int validH = y + r;
878 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -0800879 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700880 validH = 0;
881 }
Romain Guy325a0f92011-01-05 15:26:55 -0800882 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700883 validH = height - 1;
884 }
885
886 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -0800887 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700888 blurredPixel += currentPixel * gPtr[0];
889 gPtr++;
890 }
891 }
Romain Guy325a0f92011-01-05 15:26:55 -0800892 *output = (uint8_t) blurredPixel;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700893 output ++;
894 }
895 }
896}
897
898
899void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
900 float *gaussian = new float[2 * radius + 1];
901 computeGaussianWeights(gaussian, radius);
Romain Guyd71dd362011-12-12 19:03:35 -0800902
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700903 uint8_t* scratch = new uint8_t[width * height];
Romain Guyd71dd362011-12-12 19:03:35 -0800904
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700905 horizontalBlur(gaussian, radius, image, scratch, width, height);
906 verticalBlur(gaussian, radius, scratch, image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -0800907
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700908 delete[] gaussian;
909 delete[] scratch;
910}
911
Romain Guy694b5192010-07-21 21:33:20 -0700912}; // namespace uirenderer
913}; // namespace android