blob: 102aea69f748a5846658ed252cec61ab8da5fae5 [file] [log] [blame]
Romain Guy694b5192010-07-21 21:33:20 -07001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "OpenGLRenderer"
18
Romain Guy694b5192010-07-21 21:33:20 -070019#include <SkUtils.h>
20
Romain Guy51769a62010-07-23 00:28:00 -070021#include <cutils/properties.h>
Romain Guye2d345e2010-09-24 18:39:22 -070022
Romain Guy51769a62010-07-23 00:28:00 -070023#include <utils/Log.h>
24
Romain Guy15bc6432011-12-13 13:11:32 -080025#include "Caches.h"
Romain Guyc9855a52011-01-21 21:14:15 -080026#include "Debug.h"
Romain Guy51769a62010-07-23 00:28:00 -070027#include "FontRenderer.h"
28
Romain Guy694b5192010-07-21 21:33:20 -070029namespace android {
30namespace uirenderer {
31
32///////////////////////////////////////////////////////////////////////////////
Romain Guy51769a62010-07-23 00:28:00 -070033// Defines
34///////////////////////////////////////////////////////////////////////////////
35
36#define DEFAULT_TEXT_CACHE_WIDTH 1024
37#define DEFAULT_TEXT_CACHE_HEIGHT 256
38
Romain Guy726aeba2011-06-01 14:52:00 -070039// We should query these values from the GL context
Chet Haase44984ea2011-05-19 13:50:47 -070040#define MAX_TEXT_CACHE_WIDTH 2048
41#define MAX_TEXT_CACHE_HEIGHT 2048
42
Romain Guy51769a62010-07-23 00:28:00 -070043///////////////////////////////////////////////////////////////////////////////
Romain Guy694b5192010-07-21 21:33:20 -070044// Font
45///////////////////////////////////////////////////////////////////////////////
46
Romain Guy2577db12011-01-18 13:02:38 -080047Font::Font(FontRenderer* state, uint32_t fontId, float fontSize,
Romain Guybd496bc2011-08-02 17:32:41 -070048 int flags, uint32_t italicStyle, uint32_t scaleX,
49 SkPaint::Style style, uint32_t strokeWidth) :
Romain Guy2577db12011-01-18 13:02:38 -080050 mState(state), mFontId(fontId), mFontSize(fontSize),
Romain Guybd496bc2011-08-02 17:32:41 -070051 mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX),
52 mStyle(style), mStrokeWidth(mStrokeWidth) {
Romain Guy694b5192010-07-21 21:33:20 -070053}
54
55
56Font::~Font() {
57 for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
58 if (mState->mActiveFonts[ct] == this) {
59 mState->mActiveFonts.removeAt(ct);
60 break;
61 }
62 }
63
64 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
Romain Guy726aeba2011-06-01 14:52:00 -070065 delete mCachedGlyphs.valueAt(i);
Romain Guy694b5192010-07-21 21:33:20 -070066 }
67}
68
69void Font::invalidateTextureCache() {
70 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
71 mCachedGlyphs.valueAt(i)->mIsValid = false;
72 }
73}
74
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070075void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds) {
76 int nPenX = x + glyph->mBitmapLeft;
77 int nPenY = y + glyph->mBitmapTop;
78
79 int width = (int) glyph->mBitmapWidth;
80 int height = (int) glyph->mBitmapHeight;
81
Romain Guy61c8c9c2010-08-09 20:48:09 -070082 if (bounds->bottom > nPenY) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070083 bounds->bottom = nPenY;
84 }
Romain Guy61c8c9c2010-08-09 20:48:09 -070085 if (bounds->left > nPenX) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070086 bounds->left = nPenX;
87 }
Romain Guy61c8c9c2010-08-09 20:48:09 -070088 if (bounds->right < nPenX + width) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070089 bounds->right = nPenX + width;
90 }
Romain Guy61c8c9c2010-08-09 20:48:09 -070091 if (bounds->top < nPenY + height) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070092 bounds->top = nPenY + height;
93 }
94}
95
Romain Guy694b5192010-07-21 21:33:20 -070096void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
Romain Guy694b5192010-07-21 21:33:20 -070097 int nPenX = x + glyph->mBitmapLeft;
98 int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
99
Romain Guy51769a62010-07-23 00:28:00 -0700100 float u1 = glyph->mBitmapMinU;
101 float u2 = glyph->mBitmapMaxU;
102 float v1 = glyph->mBitmapMinV;
103 float v2 = glyph->mBitmapMaxV;
104
105 int width = (int) glyph->mBitmapWidth;
106 int height = (int) glyph->mBitmapHeight;
107
Romain Guyd71dd362011-12-12 19:03:35 -0800108 mState->appendMeshQuad(nPenX, nPenY, u1, v2,
109 nPenX + width, nPenY, u2, v2,
110 nPenX + width, nPenY - height, u2, v1,
111 nPenX, nPenY - height, u1, v1);
Romain Guy694b5192010-07-21 21:33:20 -0700112}
113
Romain Guyb45c0c92010-08-26 20:35:23 -0700114void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
115 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700116 int nPenX = x + glyph->mBitmapLeft;
117 int nPenY = y + glyph->mBitmapTop;
118
119 uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
120 uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
121
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700122 uint32_t cacheWidth = mState->getCacheWidth();
123 const uint8_t* cacheBuffer = mState->getTextTextureData();
124
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700125 uint32_t cacheX = 0, cacheY = 0;
126 int32_t bX = 0, bY = 0;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700127 for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
128 for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
Romain Guyb45c0c92010-08-26 20:35:23 -0700129 if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700130 LOGE("Skipping invalid index");
131 continue;
132 }
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700133 uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
134 bitmap[bY * bitmapW + bX] = tempCol;
135 }
136 }
137
138}
139
Romain Guy726aeba2011-06-01 14:52:00 -0700140Font::CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700141 CachedGlyphInfo* cachedGlyph = NULL;
Romain Guy726aeba2011-06-01 14:52:00 -0700142 ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
Romain Guy1e45aae2010-08-13 19:39:53 -0700143 if (index >= 0) {
144 cachedGlyph = mCachedGlyphs.valueAt(index);
145 } else {
Romain Guy726aeba2011-06-01 14:52:00 -0700146 cachedGlyph = cacheGlyph(paint, textUnit);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700147 }
148
149 // Is the glyph still in texture cache?
150 if (!cachedGlyph->mIsValid) {
Romain Guy726aeba2011-06-01 14:52:00 -0700151 const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700152 updateGlyphCache(paint, skiaGlyph, cachedGlyph);
153 }
154
155 return cachedGlyph;
156}
157
Romain Guy726aeba2011-06-01 14:52:00 -0700158void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700159 int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
160 if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
Romain Guy726aeba2011-06-01 14:52:00 -0700161 render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700162 bitmapW, bitmapH, NULL);
163 } else {
Romain Guy726aeba2011-06-01 14:52:00 -0700164 render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
165 0, 0, NULL);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700166 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700167}
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;
329
Romain Guy51769a62010-07-23 00:28:00 -0700330 mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700331 mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -0700332
333 char property[PROPERTY_VALUE_MAX];
334 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800335 if (sLogFontRendererCreate) {
336 INIT_LOGD(" Setting text cache width to %s pixels", property);
337 }
Romain Guy51769a62010-07-23 00:28:00 -0700338 mCacheWidth = atoi(property);
339 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800340 if (sLogFontRendererCreate) {
Romain Guyc9855a52011-01-21 21:14:15 -0800341 INIT_LOGD(" Using default text cache width of %i pixels", mCacheWidth);
Romain Guy514fb182011-01-19 14:38:29 -0800342 }
Romain Guy51769a62010-07-23 00:28:00 -0700343 }
344
345 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800346 if (sLogFontRendererCreate) {
347 INIT_LOGD(" Setting text cache width to %s pixels", property);
348 }
Romain Guy51769a62010-07-23 00:28:00 -0700349 mCacheHeight = atoi(property);
350 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800351 if (sLogFontRendererCreate) {
Romain Guyc9855a52011-01-21 21:14:15 -0800352 INIT_LOGD(" Using default text cache height of %i pixels", mCacheHeight);
Romain Guy514fb182011-01-19 14:38:29 -0800353 }
Romain Guy51769a62010-07-23 00:28:00 -0700354 }
Romain Guy514fb182011-01-19 14:38:29 -0800355
356 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -0700357}
358
359FontRenderer::~FontRenderer() {
360 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
361 delete mCacheLines[i];
362 }
363 mCacheLines.clear();
364
Romain Guy9cccc2b2010-08-07 23:46:15 -0700365 if (mInitialized) {
366 delete[] mTextMeshPtr;
367 delete[] mTextTexture;
368 }
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700369
Romain Guy9cccc2b2010-08-07 23:46:15 -0700370 if (mTextureId) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700371 glDeleteTextures(1, &mTextureId);
372 }
Romain Guy694b5192010-07-21 21:33:20 -0700373
374 Vector<Font*> fontsToDereference = mActiveFonts;
375 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
376 delete fontsToDereference[i];
377 }
378}
379
380void FontRenderer::flushAllAndInvalidate() {
381 if (mCurrentQuadIndex != 0) {
382 issueDrawCommand();
383 mCurrentQuadIndex = 0;
384 }
385 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
386 mActiveFonts[i]->invalidateTextureCache();
387 }
388 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
389 mCacheLines[i]->mCurrentCol = 0;
390 }
391}
392
Romain Guy51769a62010-07-23 00:28:00 -0700393bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
Romain Guy694b5192010-07-21 21:33:20 -0700394 // If the glyph is too tall, don't cache it
Romain Guy799833a2011-08-30 14:41:48 -0700395 if (glyph.fHeight + 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
Chet Haase44984ea2011-05-19 13:50:47 -0700396 if (mCacheHeight < MAX_TEXT_CACHE_HEIGHT) {
397 // Default cache not large enough for large glyphs - resize cache to
398 // max size and try again
399 flushAllAndInvalidate();
400 initTextTexture(true);
401 }
Romain Guy799833a2011-08-30 14:41:48 -0700402 if (glyph.fHeight + 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
Chet Haase44984ea2011-05-19 13:50:47 -0700403 LOGE("Font size to large to fit in cache. width, height = %i, %i",
404 (int) glyph.fWidth, (int) glyph.fHeight);
405 return false;
406 }
Romain Guy694b5192010-07-21 21:33:20 -0700407 }
408
409 // Now copy the bitmap into the cache texture
410 uint32_t startX = 0;
411 uint32_t startY = 0;
412
413 bool bitmapFit = false;
414 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
415 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
416 if (bitmapFit) {
417 break;
418 }
419 }
420
421 // If the new glyph didn't fit, flush the state so far and invalidate everything
422 if (!bitmapFit) {
423 flushAllAndInvalidate();
424
425 // Try to fit it again
426 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
427 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
428 if (bitmapFit) {
429 break;
430 }
431 }
432
433 // if we still don't fit, something is wrong and we shouldn't draw
434 if (!bitmapFit) {
435 LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
436 (int) glyph.fWidth, (int) glyph.fHeight);
437 return false;
438 }
439 }
440
441 *retOriginX = startX;
442 *retOriginY = startY;
443
444 uint32_t endX = startX + glyph.fWidth;
445 uint32_t endY = startY + glyph.fHeight;
446
447 uint32_t cacheWidth = mCacheWidth;
448
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700449 uint8_t* cacheBuffer = mTextTexture;
450 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700451 unsigned int stride = glyph.rowBytes();
452
453 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
454 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
455 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700456 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
Romain Guyb45c0c92010-08-26 20:35:23 -0700457 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
Romain Guy694b5192010-07-21 21:33:20 -0700458 }
459 }
460
461 return true;
462}
463
Chet Haase44984ea2011-05-19 13:50:47 -0700464void FontRenderer::initTextTexture(bool largeFonts) {
465 mCacheLines.clear();
466 if (largeFonts) {
467 mCacheWidth = MAX_TEXT_CACHE_WIDTH;
468 mCacheHeight = MAX_TEXT_CACHE_HEIGHT;
469 }
470
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700471 mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
Romain Guy1de10832010-10-03 14:37:09 -0700472 memset(mTextTexture, 0, mCacheWidth * mCacheHeight * sizeof(uint8_t));
473
Romain Guy694b5192010-07-21 21:33:20 -0700474 mUploadTexture = false;
475
Chet Haase44984ea2011-05-19 13:50:47 -0700476 if (mTextureId != 0) {
477 glDeleteTextures(1, &mTextureId);
478 }
Romain Guy694b5192010-07-21 21:33:20 -0700479 glGenTextures(1, &mTextureId);
480 glBindTexture(GL_TEXTURE_2D, mTextureId);
481 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
Chet Haase44984ea2011-05-19 13:50:47 -0700482 // Initialize texture dimensions
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700483 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700484 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Romain Guy694b5192010-07-21 21:33:20 -0700485
Romain Guye8cb9c142010-10-04 14:14:11 -0700486 mLinearFiltering = false;
487 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
488 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
Romain Guy694b5192010-07-21 21:33:20 -0700489
490 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
491 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
492
493 // Split up our cache texture into lines of certain widths
494 int nextLine = 0;
Romain Guy7975fb62010-10-01 16:36:14 -0700495 mCacheLines.push(new CacheTextureLine(mCacheWidth, 18, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700496 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700497 mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, 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));
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700500 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700501 mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -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, 42, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700506 nextLine += mCacheLines.top()->mMaxHeight;
Chet Haase44984ea2011-05-19 13:50:47 -0700507 if (largeFonts) {
508 int nextSize = 76;
509 // Make several new lines with increasing font sizes
510 while (nextSize < (int)(mCacheHeight - nextLine - (2 * nextSize))) {
511 mCacheLines.push(new CacheTextureLine(mCacheWidth, nextSize, nextLine, 0));
512 nextLine += mCacheLines.top()->mMaxHeight;
513 nextSize += 50;
514 }
515 }
Romain Guy51769a62010-07-23 00:28:00 -0700516 mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700517}
518
519// Avoid having to reallocate memory and render quad by quad
520void FontRenderer::initVertexArrayBuffers() {
Romain Guyd71dd362011-12-12 19:03:35 -0800521 uint32_t numIndices = mMaxNumberOfQuads * 6;
522 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700523 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700524
525 // Four verts, two triangles , six indices per quad
526 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
527 int i6 = i * 6;
528 int i4 = i * 4;
529
530 indexBufferData[i6 + 0] = i4 + 0;
531 indexBufferData[i6 + 1] = i4 + 1;
532 indexBufferData[i6 + 2] = i4 + 2;
533
534 indexBufferData[i6 + 3] = i4 + 0;
535 indexBufferData[i6 + 4] = i4 + 2;
536 indexBufferData[i6 + 5] = i4 + 3;
537 }
538
539 glGenBuffers(1, &mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800540 Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700541 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
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 Guy15bc6432011-12-13 13:11:32 -0800600 Caches& caches = Caches::getInstance();
601 if (!mDrawn) {
602 float* buffer = mTextMeshPtr;
603 int offset = 2;
604
605 bool force = caches.unbindMeshBuffer();
606 caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
607 caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
608 buffer + offset);
609 }
610
611 caches.bindIndicesBuffer(mIndexBufferID);
Romain Guy694b5192010-07-21 21:33:20 -0700612 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
Romain Guy5b3b3522010-10-27 18:57:51 -0700613
614 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700615}
616
Romain Guyd71dd362011-12-12 19:03:35 -0800617void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
618 float x2, float y2, float u2, float v2,
619 float x3, float y3, float u3, float v3,
620 float x4, float y4, float u4, float v4) {
621
Romain Guyff98fa52011-11-28 09:35:09 -0800622 if (mClip &&
623 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
Romain Guy09147fb2010-07-22 13:08:20 -0700624 return;
625 }
626
Romain Guy694b5192010-07-21 21:33:20 -0700627 const uint32_t vertsPerQuad = 4;
Romain Guyd71dd362011-12-12 19:03:35 -0800628 const uint32_t floatsPerVert = 4;
Romain Guy51769a62010-07-23 00:28:00 -0700629 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700630
Romain Guy694b5192010-07-21 21:33:20 -0700631 (*currentPos++) = x1;
632 (*currentPos++) = y1;
Romain Guy694b5192010-07-21 21:33:20 -0700633 (*currentPos++) = u1;
634 (*currentPos++) = v1;
635
636 (*currentPos++) = x2;
637 (*currentPos++) = y2;
Romain Guy694b5192010-07-21 21:33:20 -0700638 (*currentPos++) = u2;
639 (*currentPos++) = v2;
640
641 (*currentPos++) = x3;
642 (*currentPos++) = y3;
Romain Guy694b5192010-07-21 21:33:20 -0700643 (*currentPos++) = u3;
644 (*currentPos++) = v3;
645
646 (*currentPos++) = x4;
647 (*currentPos++) = y4;
Romain Guy694b5192010-07-21 21:33:20 -0700648 (*currentPos++) = u4;
649 (*currentPos++) = v4;
650
651 mCurrentQuadIndex++;
652
Romain Guy5b3b3522010-10-27 18:57:51 -0700653 if (mBounds) {
654 mBounds->left = fmin(mBounds->left, x1);
655 mBounds->top = fmin(mBounds->top, y3);
656 mBounds->right = fmax(mBounds->right, x3);
657 mBounds->bottom = fmax(mBounds->bottom, y1);
658 }
659
Romain Guy694b5192010-07-21 21:33:20 -0700660 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
661 issueDrawCommand();
662 mCurrentQuadIndex = 0;
663 }
664}
665
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700666uint32_t FontRenderer::getRemainingCacheCapacity() {
667 uint32_t remainingCapacity = 0;
668 float totalPixels = 0;
669 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
670 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
671 totalPixels += mCacheLines[i]->mMaxWidth;
672 }
673 remainingCapacity = (remainingCapacity * 100) / totalPixels;
674 return remainingCapacity;
675}
676
677void FontRenderer::precacheLatin(SkPaint* paint) {
678 // Remaining capacity is measured in %
679 uint32_t remainingCapacity = getRemainingCacheCapacity();
680 uint32_t precacheIdx = 0;
Romain Guy054dc182010-10-15 17:55:25 -0700681 while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
Romain Guy726aeba2011-06-01 14:52:00 -0700682 mCurrentFont->getCachedGlyph(paint, (int32_t) mLatinPrecache[precacheIdx]);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700683 remainingCapacity = getRemainingCacheCapacity();
684 precacheIdx ++;
685 }
686}
687
688void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
689 uint32_t currentNumFonts = mActiveFonts.size();
Romain Guy325a0f92011-01-05 15:26:55 -0800690 int flags = 0;
691 if (paint->isFakeBoldText()) {
692 flags |= Font::kFakeBold;
693 }
Romain Guy2577db12011-01-18 13:02:38 -0800694
695 const float skewX = paint->getTextSkewX();
696 uint32_t italicStyle = *(uint32_t*) &skewX;
Chet Haase8668f8a2011-03-02 13:51:36 -0800697 const float scaleXFloat = paint->getTextScaleX();
698 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
Romain Guybd496bc2011-08-02 17:32:41 -0700699 SkPaint::Style style = paint->getStyle();
700 const float strokeWidthFloat = paint->getStrokeWidth();
701 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
702 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
703 scaleX, style, strokeWidth);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700704
705 const float maxPrecacheFontSize = 40.0f;
706 bool isNewFont = currentNumFonts != mActiveFonts.size();
707
Romain Guy2bffd262010-09-12 17:40:02 -0700708 if (isNewFont && fontSize <= maxPrecacheFontSize) {
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700709 precacheLatin(paint);
710 }
Romain Guy694b5192010-07-21 21:33:20 -0700711}
Romain Guy7975fb62010-10-01 16:36:14 -0700712
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700713FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Romain Guy1e45aae2010-08-13 19:39:53 -0700714 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
715 checkInit();
716
717 if (!mCurrentFont) {
718 DropShadow image;
719 image.width = 0;
720 image.height = 0;
721 image.image = NULL;
722 image.penX = 0;
723 image.penY = 0;
724 return image;
725 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700726
Romain Guyff98fa52011-11-28 09:35:09 -0800727 mClip = NULL;
728 mBounds = NULL;
729
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700730 Rect bounds;
Romain Guy726aeba2011-06-01 14:52:00 -0700731 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds);
Romain Guyff98fa52011-11-28 09:35:09 -0800732
Romain Guy1e45aae2010-08-13 19:39:53 -0700733 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
734 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700735 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guyff98fa52011-11-28 09:35:09 -0800736
Romain Guy1e45aae2010-08-13 19:39:53 -0700737 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700738 dataBuffer[i] = 0;
739 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700740
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700741 int penX = radius - bounds.left;
742 int penY = radius - bounds.bottom;
743
Romain Guy726aeba2011-06-01 14:52:00 -0700744 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
Romain Guy1e45aae2010-08-13 19:39:53 -0700745 dataBuffer, paddedWidth, paddedHeight);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700746 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
747
748 DropShadow image;
749 image.width = paddedWidth;
750 image.height = paddedHeight;
751 image.image = dataBuffer;
752 image.penX = penX;
753 image.penY = penY;
754 return image;
755}
Romain Guy694b5192010-07-21 21:33:20 -0700756
Romain Guy5b3b3522010-10-27 18:57:51 -0700757bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
758 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700759 checkInit();
760
Romain Guy09147fb2010-07-22 13:08:20 -0700761 if (!mCurrentFont) {
762 LOGE("No font set");
Romain Guy5b3b3522010-10-27 18:57:51 -0700763 return false;
Romain Guy694b5192010-07-21 21:33:20 -0700764 }
765
Romain Guy5b3b3522010-10-27 18:57:51 -0700766 mDrawn = false;
767 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -0700768 mClip = clip;
Romain Guyff98fa52011-11-28 09:35:09 -0800769
Romain Guy726aeba2011-06-01 14:52:00 -0700770 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
Romain Guyff98fa52011-11-28 09:35:09 -0800771
Romain Guy5b3b3522010-10-27 18:57:51 -0700772 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -0800773 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700774
775 if (mCurrentQuadIndex != 0) {
776 issueDrawCommand();
777 mCurrentQuadIndex = 0;
778 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700779
780 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700781}
782
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700783void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
784 // Compute gaussian weights for the blur
785 // e is the euler's number
786 float e = 2.718281828459045f;
787 float pi = 3.1415926535897932f;
788 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
789 // x is of the form [-radius .. 0 .. radius]
790 // and sigma varies with radius.
791 // Based on some experimental radius values and sigma's
792 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700793 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700794 // The larger the radius gets, the more our gaussian blur
795 // will resemble a box blur since with large sigma
796 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -0800797 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700798
799 // Now compute the coefficints
800 // We will store some redundant values to save some math during
801 // the blur calculations
802 // precompute some values
803 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
804 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
805
806 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -0800807 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700808 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700809 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
810 normalizeFactor += weights[r + radius];
811 }
812
813 //Now we need to normalize the weights because all our coefficients need to add up to one
814 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -0800815 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700816 weights[r + radius] *= normalizeFactor;
817 }
818}
819
820void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700821 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700822 float blurredPixel = 0.0f;
823 float currentPixel = 0.0f;
824
Romain Guy325a0f92011-01-05 15:26:55 -0800825 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700826
827 const uint8_t* input = source + y * width;
828 uint8_t* output = dest + y * width;
829
Romain Guy325a0f92011-01-05 15:26:55 -0800830 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700831 blurredPixel = 0.0f;
832 const float* gPtr = weights;
833 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800834 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700835 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -0800836 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700837 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700838 blurredPixel += currentPixel * gPtr[0];
839 gPtr++;
840 i++;
841 }
842 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800843 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700844 // Stepping left and right away from the pixel
845 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -0800846 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700847 validW = 0;
848 }
Romain Guy325a0f92011-01-05 15:26:55 -0800849 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700850 validW = width - 1;
851 }
852
Romain Guy325a0f92011-01-05 15:26:55 -0800853 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700854 blurredPixel += currentPixel * gPtr[0];
855 gPtr++;
856 }
857 }
858 *output = (uint8_t)blurredPixel;
859 output ++;
860 }
861 }
862}
863
864void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700865 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700866 float blurredPixel = 0.0f;
867 float currentPixel = 0.0f;
868
Romain Guy325a0f92011-01-05 15:26:55 -0800869 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700870
871 uint8_t* output = dest + y * width;
872
Romain Guy325a0f92011-01-05 15:26:55 -0800873 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700874 blurredPixel = 0.0f;
875 const float* gPtr = weights;
876 const uint8_t* input = source + x;
877 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800878 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700879 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -0800880 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700881 currentPixel = (float)(*i);
882 blurredPixel += currentPixel * gPtr[0];
883 gPtr++;
884 i += width;
885 }
886 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800887 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700888 int validH = y + r;
889 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -0800890 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700891 validH = 0;
892 }
Romain Guy325a0f92011-01-05 15:26:55 -0800893 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700894 validH = height - 1;
895 }
896
897 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -0800898 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700899 blurredPixel += currentPixel * gPtr[0];
900 gPtr++;
901 }
902 }
Romain Guy325a0f92011-01-05 15:26:55 -0800903 *output = (uint8_t) blurredPixel;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700904 output ++;
905 }
906 }
907}
908
909
910void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
911 float *gaussian = new float[2 * radius + 1];
912 computeGaussianWeights(gaussian, radius);
Romain Guyd71dd362011-12-12 19:03:35 -0800913
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700914 uint8_t* scratch = new uint8_t[width * height];
Romain Guyd71dd362011-12-12 19:03:35 -0800915
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700916 horizontalBlur(gaussian, radius, image, scratch, width, height);
917 verticalBlur(gaussian, radius, scratch, image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -0800918
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700919 delete[] gaussian;
920 delete[] scratch;
921}
922
Romain Guy694b5192010-07-21 21:33:20 -0700923}; // namespace uirenderer
924}; // namespace android