blob: 0042f496fac41d4b8e275eec6da05a14d0e12e9a [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
38///////////////////////////////////////////////////////////////////////////////
Romain Guy694b5192010-07-21 21:33:20 -070039// Font
40///////////////////////////////////////////////////////////////////////////////
41
Romain Guy2577db12011-01-18 13:02:38 -080042Font::Font(FontRenderer* state, uint32_t fontId, float fontSize,
43 int flags, uint32_t italicStyle) :
44 mState(state), mFontId(fontId), mFontSize(fontSize),
45 mFlags(flags), mItalicStyle(italicStyle) {
Romain Guy694b5192010-07-21 21:33:20 -070046}
47
48
49Font::~Font() {
50 for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
51 if (mState->mActiveFonts[ct] == this) {
52 mState->mActiveFonts.removeAt(ct);
53 break;
54 }
55 }
56
57 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
Romain Guy51769a62010-07-23 00:28:00 -070058 CachedGlyphInfo* glyph = mCachedGlyphs.valueAt(i);
Romain Guy694b5192010-07-21 21:33:20 -070059 delete glyph;
60 }
61}
62
63void Font::invalidateTextureCache() {
64 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
65 mCachedGlyphs.valueAt(i)->mIsValid = false;
66 }
67}
68
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070069void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds) {
70 int nPenX = x + glyph->mBitmapLeft;
71 int nPenY = y + glyph->mBitmapTop;
72
73 int width = (int) glyph->mBitmapWidth;
74 int height = (int) glyph->mBitmapHeight;
75
Romain Guy61c8c9c2010-08-09 20:48:09 -070076 if (bounds->bottom > nPenY) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070077 bounds->bottom = nPenY;
78 }
Romain Guy61c8c9c2010-08-09 20:48:09 -070079 if (bounds->left > nPenX) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070080 bounds->left = nPenX;
81 }
Romain Guy61c8c9c2010-08-09 20:48:09 -070082 if (bounds->right < nPenX + width) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070083 bounds->right = nPenX + width;
84 }
Romain Guy61c8c9c2010-08-09 20:48:09 -070085 if (bounds->top < nPenY + height) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070086 bounds->top = nPenY + height;
87 }
88}
89
Romain Guy694b5192010-07-21 21:33:20 -070090void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
Romain Guy694b5192010-07-21 21:33:20 -070091 int nPenX = x + glyph->mBitmapLeft;
92 int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
93
Romain Guy51769a62010-07-23 00:28:00 -070094 float u1 = glyph->mBitmapMinU;
95 float u2 = glyph->mBitmapMaxU;
96 float v1 = glyph->mBitmapMinV;
97 float v2 = glyph->mBitmapMaxV;
98
99 int width = (int) glyph->mBitmapWidth;
100 int height = (int) glyph->mBitmapHeight;
101
102 mState->appendMeshQuad(nPenX, nPenY, 0, u1, v2,
103 nPenX + width, nPenY, 0, u2, v2,
104 nPenX + width, nPenY - height, 0, u2, v1,
105 nPenX, nPenY - height, 0, u1, v1);
Romain Guy694b5192010-07-21 21:33:20 -0700106}
107
Romain Guyb45c0c92010-08-26 20:35:23 -0700108void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
109 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700110 int nPenX = x + glyph->mBitmapLeft;
111 int nPenY = y + glyph->mBitmapTop;
112
113 uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
114 uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
115
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700116 uint32_t cacheWidth = mState->getCacheWidth();
117 const uint8_t* cacheBuffer = mState->getTextTextureData();
118
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700119 uint32_t cacheX = 0, cacheY = 0;
120 int32_t bX = 0, bY = 0;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700121 for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
122 for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
Romain Guyb45c0c92010-08-26 20:35:23 -0700123 if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700124 LOGE("Skipping invalid index");
125 continue;
126 }
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700127 uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
128 bitmap[bY * bitmapW + bX] = tempCol;
129 }
130 }
131
132}
133
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700134Font::CachedGlyphInfo* Font::getCachedUTFChar(SkPaint* paint, int32_t utfChar) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700135 CachedGlyphInfo* cachedGlyph = NULL;
136 ssize_t index = mCachedGlyphs.indexOfKey(utfChar);
137 if (index >= 0) {
138 cachedGlyph = mCachedGlyphs.valueAt(index);
139 } else {
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700140 cachedGlyph = cacheGlyph(paint, utfChar);
141 }
142
143 // Is the glyph still in texture cache?
144 if (!cachedGlyph->mIsValid) {
145 const SkGlyph& skiaGlyph = paint->getUnicharMetrics(utfChar);
146 updateGlyphCache(paint, skiaGlyph, cachedGlyph);
147 }
148
149 return cachedGlyph;
150}
151
Romain Guy51769a62010-07-23 00:28:00 -0700152void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700153 int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
154 if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
155 renderUTF(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
156 bitmapW, bitmapH, NULL);
157 } else {
158 renderUTF(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, 0, 0, NULL);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700159 }
160
161}
162
163void Font::measureUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700164 int numGlyphs, Rect *bounds) {
165 if (bounds == NULL) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700166 LOGE("No return rectangle provided to measure text");
167 return;
168 }
169 bounds->set(1e6, -1e6, -1e6, 1e6);
170 renderUTF(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds);
171}
172
Romain Guy58ef7fb2010-09-13 12:52:37 -0700173#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16)
Romain Guy2bffd262010-09-12 17:40:02 -0700174
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700175void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700176 int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
177 uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700178 if (numGlyphs == 0 || text == NULL || len == 0) {
179 return;
180 }
181
Romain Guy2bffd262010-09-12 17:40:02 -0700182 SkFixed penX = SkIntToFixed(x);
183 int penY = y;
Romain Guy694b5192010-07-21 21:33:20 -0700184 int glyphsLeft = 1;
185 if (numGlyphs > 0) {
186 glyphsLeft = numGlyphs;
187 }
188
Romain Guy2bffd262010-09-12 17:40:02 -0700189 SkFixed prevRsbDelta = 0;
190 penX += SK_Fixed1 / 2;
191
Romain Guy694b5192010-07-21 21:33:20 -0700192 text += start;
193
194 while (glyphsLeft > 0) {
Romain Guy694b5192010-07-21 21:33:20 -0700195 int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text);
196
Romain Guy61c8c9c2010-08-09 20:48:09 -0700197 // Reached the end of the string
Romain Guy694b5192010-07-21 21:33:20 -0700198 if (utfChar < 0) {
199 break;
200 }
201
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700202 CachedGlyphInfo* cachedGlyph = getCachedUTFChar(paint, utfChar);
Romain Guy2bffd262010-09-12 17:40:02 -0700203 penX += SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta);
204 prevRsbDelta = cachedGlyph->mRsbDelta;
Romain Guy694b5192010-07-21 21:33:20 -0700205
206 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
207 if (cachedGlyph->mIsValid) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700208 switch(mode) {
209 case FRAMEBUFFER:
Romain Guy2bffd262010-09-12 17:40:02 -0700210 drawCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700211 break;
212 case BITMAP:
Romain Guy2bffd262010-09-12 17:40:02 -0700213 drawCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY, bitmap, bitmapW, bitmapH);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700214 break;
215 case MEASURE:
Romain Guy2bffd262010-09-12 17:40:02 -0700216 measureCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY, bounds);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700217 break;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700218 }
Romain Guy694b5192010-07-21 21:33:20 -0700219 }
220
Romain Guy2bffd262010-09-12 17:40:02 -0700221 penX += cachedGlyph->mAdvanceX;
Romain Guy694b5192010-07-21 21:33:20 -0700222
223 // If we were given a specific number of glyphs, decrement
224 if (numGlyphs > 0) {
225 glyphsLeft--;
226 }
227 }
228}
229
Romain Guy51769a62010-07-23 00:28:00 -0700230void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
Romain Guy694b5192010-07-21 21:33:20 -0700231 glyph->mAdvanceX = skiaGlyph.fAdvanceX;
232 glyph->mAdvanceY = skiaGlyph.fAdvanceY;
233 glyph->mBitmapLeft = skiaGlyph.fLeft;
234 glyph->mBitmapTop = skiaGlyph.fTop;
Romain Guy2bffd262010-09-12 17:40:02 -0700235 glyph->mLsbDelta = skiaGlyph.fLsbDelta;
236 glyph->mRsbDelta = skiaGlyph.fRsbDelta;
Romain Guy694b5192010-07-21 21:33:20 -0700237
238 uint32_t startX = 0;
239 uint32_t startY = 0;
240
Romain Guy694b5192010-07-21 21:33:20 -0700241 // Get the bitmap for the glyph
242 paint->findImage(skiaGlyph);
Romain Guy51769a62010-07-23 00:28:00 -0700243 glyph->mIsValid = mState->cacheBitmap(skiaGlyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700244
245 if (!glyph->mIsValid) {
246 return;
247 }
248
249 uint32_t endX = startX + skiaGlyph.fWidth;
250 uint32_t endY = startY + skiaGlyph.fHeight;
251
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700252 glyph->mStartX = startX;
253 glyph->mStartY = startY;
Romain Guy694b5192010-07-21 21:33:20 -0700254 glyph->mBitmapWidth = skiaGlyph.fWidth;
255 glyph->mBitmapHeight = skiaGlyph.fHeight;
256
Romain Guy51769a62010-07-23 00:28:00 -0700257 uint32_t cacheWidth = mState->getCacheWidth();
258 uint32_t cacheHeight = mState->getCacheHeight();
Romain Guy694b5192010-07-21 21:33:20 -0700259
260 glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
261 glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
262 glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
263 glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
264
Romain Guy51769a62010-07-23 00:28:00 -0700265 mState->mUploadTexture = true;
Romain Guy694b5192010-07-21 21:33:20 -0700266}
267
Romain Guy51769a62010-07-23 00:28:00 -0700268Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, int32_t glyph) {
269 CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
Romain Guy694b5192010-07-21 21:33:20 -0700270 mCachedGlyphs.add(glyph, newGlyph);
271
272 const SkGlyph& skiaGlyph = paint->getUnicharMetrics(glyph);
273 newGlyph->mGlyphIndex = skiaGlyph.fID;
274 newGlyph->mIsValid = false;
275
276 updateGlyphCache(paint, skiaGlyph, newGlyph);
277
278 return newGlyph;
279}
280
Romain Guy2577db12011-01-18 13:02:38 -0800281Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
282 int flags, uint32_t italicStyle) {
Romain Guy694b5192010-07-21 21:33:20 -0700283 Vector<Font*> &activeFonts = state->mActiveFonts;
284
285 for (uint32_t i = 0; i < activeFonts.size(); i++) {
Romain Guy51769a62010-07-23 00:28:00 -0700286 Font* font = activeFonts[i];
Romain Guy2577db12011-01-18 13:02:38 -0800287 if (font->mFontId == fontId && font->mFontSize == fontSize &&
288 font->mFlags == flags && font->mItalicStyle == italicStyle) {
Romain Guy51769a62010-07-23 00:28:00 -0700289 return font;
Romain Guy694b5192010-07-21 21:33:20 -0700290 }
291 }
292
Romain Guy2577db12011-01-18 13:02:38 -0800293 Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle);
Romain Guy694b5192010-07-21 21:33:20 -0700294 activeFonts.push(newFont);
295 return newFont;
296}
297
298///////////////////////////////////////////////////////////////////////////////
299// FontRenderer
300///////////////////////////////////////////////////////////////////////////////
301
Romain Guy514fb182011-01-19 14:38:29 -0800302static bool sLogFontRendererCreate = true;
303
Romain Guy694b5192010-07-21 21:33:20 -0700304FontRenderer::FontRenderer() {
Romain Guyc9855a52011-01-21 21:14:15 -0800305 if (sLogFontRendererCreate) {
306 INIT_LOGD("Creating FontRenderer");
307 }
Romain Guy51769a62010-07-23 00:28:00 -0700308
Romain Guyb45c0c92010-08-26 20:35:23 -0700309 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700310 mInitialized = false;
311 mMaxNumberOfQuads = 1024;
312 mCurrentQuadIndex = 0;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700313 mTextureId = 0;
Romain Guy694b5192010-07-21 21:33:20 -0700314
Romain Guy9cccc2b92010-08-07 23:46:15 -0700315 mTextMeshPtr = NULL;
316 mTextTexture = NULL;
317
Romain Guy694b5192010-07-21 21:33:20 -0700318 mIndexBufferID = 0;
319
Romain Guy51769a62010-07-23 00:28:00 -0700320 mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700321 mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -0700322
323 char property[PROPERTY_VALUE_MAX];
324 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800325 if (sLogFontRendererCreate) {
326 INIT_LOGD(" Setting text cache width to %s pixels", property);
327 }
Romain Guy51769a62010-07-23 00:28:00 -0700328 mCacheWidth = atoi(property);
329 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800330 if (sLogFontRendererCreate) {
Romain Guyc9855a52011-01-21 21:14:15 -0800331 INIT_LOGD(" Using default text cache width of %i pixels", mCacheWidth);
Romain Guy514fb182011-01-19 14:38:29 -0800332 }
Romain Guy51769a62010-07-23 00:28:00 -0700333 }
334
335 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800336 if (sLogFontRendererCreate) {
337 INIT_LOGD(" Setting text cache width to %s pixels", property);
338 }
Romain Guy51769a62010-07-23 00:28:00 -0700339 mCacheHeight = atoi(property);
340 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800341 if (sLogFontRendererCreate) {
Romain Guyc9855a52011-01-21 21:14:15 -0800342 INIT_LOGD(" Using default text cache height of %i pixels", mCacheHeight);
Romain Guy514fb182011-01-19 14:38:29 -0800343 }
Romain Guy51769a62010-07-23 00:28:00 -0700344 }
Romain Guy514fb182011-01-19 14:38:29 -0800345
346 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -0700347}
348
349FontRenderer::~FontRenderer() {
350 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
351 delete mCacheLines[i];
352 }
353 mCacheLines.clear();
354
Romain Guy9cccc2b92010-08-07 23:46:15 -0700355 if (mInitialized) {
356 delete[] mTextMeshPtr;
357 delete[] mTextTexture;
358 }
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700359
Romain Guy9cccc2b92010-08-07 23:46:15 -0700360 if (mTextureId) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700361 glDeleteTextures(1, &mTextureId);
362 }
Romain Guy694b5192010-07-21 21:33:20 -0700363
364 Vector<Font*> fontsToDereference = mActiveFonts;
365 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
366 delete fontsToDereference[i];
367 }
368}
369
370void FontRenderer::flushAllAndInvalidate() {
371 if (mCurrentQuadIndex != 0) {
372 issueDrawCommand();
373 mCurrentQuadIndex = 0;
374 }
375 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
376 mActiveFonts[i]->invalidateTextureCache();
377 }
378 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
379 mCacheLines[i]->mCurrentCol = 0;
380 }
381}
382
Romain Guy51769a62010-07-23 00:28:00 -0700383bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
Romain Guy694b5192010-07-21 21:33:20 -0700384 // If the glyph is too tall, don't cache it
Romain Guy2bffd262010-09-12 17:40:02 -0700385 if (glyph.fHeight > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
Romain Guy694b5192010-07-21 21:33:20 -0700386 LOGE("Font size to large to fit in cache. width, height = %i, %i",
387 (int) glyph.fWidth, (int) glyph.fHeight);
388 return false;
389 }
390
391 // Now copy the bitmap into the cache texture
392 uint32_t startX = 0;
393 uint32_t startY = 0;
394
395 bool bitmapFit = false;
396 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
397 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
398 if (bitmapFit) {
399 break;
400 }
401 }
402
403 // If the new glyph didn't fit, flush the state so far and invalidate everything
404 if (!bitmapFit) {
405 flushAllAndInvalidate();
406
407 // Try to fit it again
408 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
409 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
410 if (bitmapFit) {
411 break;
412 }
413 }
414
415 // if we still don't fit, something is wrong and we shouldn't draw
416 if (!bitmapFit) {
417 LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
418 (int) glyph.fWidth, (int) glyph.fHeight);
419 return false;
420 }
421 }
422
423 *retOriginX = startX;
424 *retOriginY = startY;
425
426 uint32_t endX = startX + glyph.fWidth;
427 uint32_t endY = startY + glyph.fHeight;
428
429 uint32_t cacheWidth = mCacheWidth;
430
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700431 uint8_t* cacheBuffer = mTextTexture;
432 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700433 unsigned int stride = glyph.rowBytes();
434
435 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
436 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
437 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700438 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
Romain Guyb45c0c92010-08-26 20:35:23 -0700439 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
Romain Guy694b5192010-07-21 21:33:20 -0700440 }
441 }
442
443 return true;
444}
445
446void FontRenderer::initTextTexture() {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700447 mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
Romain Guy1de10832010-10-03 14:37:09 -0700448 memset(mTextTexture, 0, mCacheWidth * mCacheHeight * sizeof(uint8_t));
449
Romain Guy694b5192010-07-21 21:33:20 -0700450 mUploadTexture = false;
451
452 glGenTextures(1, &mTextureId);
453 glBindTexture(GL_TEXTURE_2D, mTextureId);
454 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700455 // Initialize texture dimentions
456 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700457 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Romain Guy694b5192010-07-21 21:33:20 -0700458
Romain Guye8cb9c142010-10-04 14:14:11 -0700459 mLinearFiltering = false;
460 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
461 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
Romain Guy694b5192010-07-21 21:33:20 -0700462
463 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
464 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
465
466 // Split up our cache texture into lines of certain widths
467 int nextLine = 0;
Romain Guy7975fb62010-10-01 16:36:14 -0700468 mCacheLines.push(new CacheTextureLine(mCacheWidth, 18, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700469 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700470 mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700471 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700472 mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700473 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700474 mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700475 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700476 mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700477 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700478 mCacheLines.push(new CacheTextureLine(mCacheWidth, 42, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700479 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700480 mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700481}
482
483// Avoid having to reallocate memory and render quad by quad
484void FontRenderer::initVertexArrayBuffers() {
485 uint32_t numIndicies = mMaxNumberOfQuads * 6;
486 uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700487 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700488
489 // Four verts, two triangles , six indices per quad
490 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
491 int i6 = i * 6;
492 int i4 = i * 4;
493
494 indexBufferData[i6 + 0] = i4 + 0;
495 indexBufferData[i6 + 1] = i4 + 1;
496 indexBufferData[i6 + 2] = i4 + 2;
497
498 indexBufferData[i6 + 3] = i4 + 0;
499 indexBufferData[i6 + 4] = i4 + 2;
500 indexBufferData[i6 + 5] = i4 + 3;
501 }
502
503 glGenBuffers(1, &mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700504 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
505 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
506 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
Romain Guy694b5192010-07-21 21:33:20 -0700507
508 free(indexBufferData);
509
510 uint32_t coordSize = 3;
511 uint32_t uvSize = 2;
512 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700513 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
514 mTextMeshPtr = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700515}
516
517// We don't want to allocate anything unless we actually draw text
518void FontRenderer::checkInit() {
519 if (mInitialized) {
520 return;
521 }
522
523 initTextTexture();
524 initVertexArrayBuffers();
525
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700526 // We store a string with letters in a rough frequency of occurrence
527 mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
528 mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
529 mLatinPrecache += String16(",.?!()-+@;:`'");
530 mLatinPrecache += String16("0123456789");
531
Romain Guy694b5192010-07-21 21:33:20 -0700532 mInitialized = true;
533}
534
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700535void FontRenderer::checkTextureUpdate() {
536 if (!mUploadTexture) {
537 return;
Romain Guy694b5192010-07-21 21:33:20 -0700538 }
539
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700540 glBindTexture(GL_TEXTURE_2D, mTextureId);
541
542 // Iterate over all the cache lines and see which ones need to be updated
543 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
544 CacheTextureLine* cl = mCacheLines[i];
545 if(cl->mDirty) {
546 uint32_t xOffset = 0;
547 uint32_t yOffset = cl->mCurrentRow;
548 uint32_t width = mCacheWidth;
549 uint32_t height = cl->mMaxHeight;
Romain Guy1e45aae2010-08-13 19:39:53 -0700550 void* textureData = mTextTexture + yOffset*width;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700551
552 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700553 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700554
555 cl->mDirty = false;
556 }
557 }
558
559 mUploadTexture = false;
560}
561
562void FontRenderer::issueDrawCommand() {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700563 checkTextureUpdate();
564
Romain Guy51769a62010-07-23 00:28:00 -0700565 float* vtx = mTextMeshPtr;
566 float* tex = vtx + 3;
Romain Guy694b5192010-07-21 21:33:20 -0700567
568 // position is slot 0
569 uint32_t slot = 0;
570 glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx);
571
572 // texture0 is slot 1
573 slot = 1;
574 glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex);
575
576 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
577 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
Romain Guy5b3b3522010-10-27 18:57:51 -0700578
579 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700580}
581
582void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
583 float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
584 float x4, float y4, float z4, float u4, float v4) {
Romain Guy09147fb2010-07-22 13:08:20 -0700585 if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) {
586 return;
587 }
588
Romain Guy694b5192010-07-21 21:33:20 -0700589 const uint32_t vertsPerQuad = 4;
590 const uint32_t floatsPerVert = 5;
Romain Guy51769a62010-07-23 00:28:00 -0700591 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700592
Romain Guy694b5192010-07-21 21:33:20 -0700593 (*currentPos++) = x1;
594 (*currentPos++) = y1;
595 (*currentPos++) = z1;
596 (*currentPos++) = u1;
597 (*currentPos++) = v1;
598
599 (*currentPos++) = x2;
600 (*currentPos++) = y2;
601 (*currentPos++) = z2;
602 (*currentPos++) = u2;
603 (*currentPos++) = v2;
604
605 (*currentPos++) = x3;
606 (*currentPos++) = y3;
607 (*currentPos++) = z3;
608 (*currentPos++) = u3;
609 (*currentPos++) = v3;
610
611 (*currentPos++) = x4;
612 (*currentPos++) = y4;
613 (*currentPos++) = z4;
614 (*currentPos++) = u4;
615 (*currentPos++) = v4;
616
617 mCurrentQuadIndex++;
618
Romain Guy5b3b3522010-10-27 18:57:51 -0700619 if (mBounds) {
620 mBounds->left = fmin(mBounds->left, x1);
621 mBounds->top = fmin(mBounds->top, y3);
622 mBounds->right = fmax(mBounds->right, x3);
623 mBounds->bottom = fmax(mBounds->bottom, y1);
624 }
625
Romain Guy694b5192010-07-21 21:33:20 -0700626 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
627 issueDrawCommand();
628 mCurrentQuadIndex = 0;
629 }
630}
631
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700632uint32_t FontRenderer::getRemainingCacheCapacity() {
633 uint32_t remainingCapacity = 0;
634 float totalPixels = 0;
635 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
636 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
637 totalPixels += mCacheLines[i]->mMaxWidth;
638 }
639 remainingCapacity = (remainingCapacity * 100) / totalPixels;
640 return remainingCapacity;
641}
642
643void FontRenderer::precacheLatin(SkPaint* paint) {
644 // Remaining capacity is measured in %
645 uint32_t remainingCapacity = getRemainingCacheCapacity();
646 uint32_t precacheIdx = 0;
Romain Guy054dc182010-10-15 17:55:25 -0700647 while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
648 mCurrentFont->getCachedUTFChar(paint, (int32_t) mLatinPrecache[precacheIdx]);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700649 remainingCapacity = getRemainingCacheCapacity();
650 precacheIdx ++;
651 }
652}
653
654void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
655 uint32_t currentNumFonts = mActiveFonts.size();
Romain Guy325a0f92011-01-05 15:26:55 -0800656 int flags = 0;
657 if (paint->isFakeBoldText()) {
658 flags |= Font::kFakeBold;
659 }
Romain Guy2577db12011-01-18 13:02:38 -0800660
661 const float skewX = paint->getTextSkewX();
662 uint32_t italicStyle = *(uint32_t*) &skewX;
663 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700664
665 const float maxPrecacheFontSize = 40.0f;
666 bool isNewFont = currentNumFonts != mActiveFonts.size();
667
Romain Guy2bffd262010-09-12 17:40:02 -0700668 if (isNewFont && fontSize <= maxPrecacheFontSize) {
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700669 precacheLatin(paint);
670 }
Romain Guy694b5192010-07-21 21:33:20 -0700671}
Romain Guy7975fb62010-10-01 16:36:14 -0700672
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700673FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Romain Guy1e45aae2010-08-13 19:39:53 -0700674 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
675 checkInit();
676
677 if (!mCurrentFont) {
678 DropShadow image;
679 image.width = 0;
680 image.height = 0;
681 image.image = NULL;
682 image.penX = 0;
683 image.penY = 0;
684 return image;
685 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700686
687 Rect bounds;
688 mCurrentFont->measureUTF(paint, text, startIndex, len, numGlyphs, &bounds);
Romain Guy1e45aae2010-08-13 19:39:53 -0700689 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
690 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700691 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guy1e45aae2010-08-13 19:39:53 -0700692 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700693 dataBuffer[i] = 0;
694 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700695
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700696 int penX = radius - bounds.left;
697 int penY = radius - bounds.bottom;
698
699 mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, penX, penY,
Romain Guy1e45aae2010-08-13 19:39:53 -0700700 dataBuffer, paddedWidth, paddedHeight);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700701 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
702
703 DropShadow image;
704 image.width = paddedWidth;
705 image.height = paddedHeight;
706 image.image = dataBuffer;
707 image.penX = penX;
708 image.penY = penY;
709 return image;
710}
Romain Guy694b5192010-07-21 21:33:20 -0700711
Romain Guy5b3b3522010-10-27 18:57:51 -0700712bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
713 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700714 checkInit();
715
Romain Guy09147fb2010-07-22 13:08:20 -0700716 if (!mCurrentFont) {
717 LOGE("No font set");
Romain Guy5b3b3522010-10-27 18:57:51 -0700718 return false;
Romain Guy694b5192010-07-21 21:33:20 -0700719 }
720
Romain Guy5b3b3522010-10-27 18:57:51 -0700721 mDrawn = false;
722 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -0700723 mClip = clip;
Romain Guy51769a62010-07-23 00:28:00 -0700724 mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y);
Romain Guy5b3b3522010-10-27 18:57:51 -0700725 mBounds = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700726
727 if (mCurrentQuadIndex != 0) {
728 issueDrawCommand();
729 mCurrentQuadIndex = 0;
730 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700731
732 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700733}
734
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700735void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
736 // Compute gaussian weights for the blur
737 // e is the euler's number
738 float e = 2.718281828459045f;
739 float pi = 3.1415926535897932f;
740 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
741 // x is of the form [-radius .. 0 .. radius]
742 // and sigma varies with radius.
743 // Based on some experimental radius values and sigma's
744 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700745 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700746 // The larger the radius gets, the more our gaussian blur
747 // will resemble a box blur since with large sigma
748 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -0800749 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700750
751 // Now compute the coefficints
752 // We will store some redundant values to save some math during
753 // the blur calculations
754 // precompute some values
755 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
756 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
757
758 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -0800759 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700760 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700761 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
762 normalizeFactor += weights[r + radius];
763 }
764
765 //Now we need to normalize the weights because all our coefficients need to add up to one
766 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -0800767 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700768 weights[r + radius] *= normalizeFactor;
769 }
770}
771
772void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700773 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700774 float blurredPixel = 0.0f;
775 float currentPixel = 0.0f;
776
Romain Guy325a0f92011-01-05 15:26:55 -0800777 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700778
779 const uint8_t* input = source + y * width;
780 uint8_t* output = dest + y * width;
781
Romain Guy325a0f92011-01-05 15:26:55 -0800782 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700783 blurredPixel = 0.0f;
784 const float* gPtr = weights;
785 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800786 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700787 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -0800788 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700789 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700790 blurredPixel += currentPixel * gPtr[0];
791 gPtr++;
792 i++;
793 }
794 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800795 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700796 // Stepping left and right away from the pixel
797 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -0800798 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700799 validW = 0;
800 }
Romain Guy325a0f92011-01-05 15:26:55 -0800801 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700802 validW = width - 1;
803 }
804
Romain Guy325a0f92011-01-05 15:26:55 -0800805 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700806 blurredPixel += currentPixel * gPtr[0];
807 gPtr++;
808 }
809 }
810 *output = (uint8_t)blurredPixel;
811 output ++;
812 }
813 }
814}
815
816void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700817 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700818 float blurredPixel = 0.0f;
819 float currentPixel = 0.0f;
820
Romain Guy325a0f92011-01-05 15:26:55 -0800821 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700822
823 uint8_t* output = dest + y * width;
824
Romain Guy325a0f92011-01-05 15:26:55 -0800825 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700826 blurredPixel = 0.0f;
827 const float* gPtr = weights;
828 const uint8_t* input = source + x;
829 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800830 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700831 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -0800832 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700833 currentPixel = (float)(*i);
834 blurredPixel += currentPixel * gPtr[0];
835 gPtr++;
836 i += width;
837 }
838 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800839 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700840 int validH = y + r;
841 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -0800842 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700843 validH = 0;
844 }
Romain Guy325a0f92011-01-05 15:26:55 -0800845 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700846 validH = height - 1;
847 }
848
849 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -0800850 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700851 blurredPixel += currentPixel * gPtr[0];
852 gPtr++;
853 }
854 }
Romain Guy325a0f92011-01-05 15:26:55 -0800855 *output = (uint8_t) blurredPixel;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700856 output ++;
857 }
858 }
859}
860
861
862void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
863 float *gaussian = new float[2 * radius + 1];
864 computeGaussianWeights(gaussian, radius);
865 uint8_t* scratch = new uint8_t[width * height];
866 horizontalBlur(gaussian, radius, image, scratch, width, height);
867 verticalBlur(gaussian, radius, scratch, image, width, height);
868 delete[] gaussian;
869 delete[] scratch;
870}
871
Romain Guy694b5192010-07-21 21:33:20 -0700872}; // namespace uirenderer
873}; // namespace android