blob: 8bae684f18e698e19bfdacdd7e06f235d5845cd2 [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;
Alex Sakhartchouk894df172011-02-17 16:45:37 -0800319 mPositionAttrSlot = -1;
320 mTexcoordAttrSlot = -1;
Romain Guy694b5192010-07-21 21:33:20 -0700321
Romain Guy51769a62010-07-23 00:28:00 -0700322 mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700323 mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -0700324
325 char property[PROPERTY_VALUE_MAX];
326 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800327 if (sLogFontRendererCreate) {
328 INIT_LOGD(" Setting text cache width to %s pixels", property);
329 }
Romain Guy51769a62010-07-23 00:28:00 -0700330 mCacheWidth = atoi(property);
331 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800332 if (sLogFontRendererCreate) {
Romain Guyc9855a52011-01-21 21:14:15 -0800333 INIT_LOGD(" Using default text cache width of %i pixels", mCacheWidth);
Romain Guy514fb182011-01-19 14:38:29 -0800334 }
Romain Guy51769a62010-07-23 00:28:00 -0700335 }
336
337 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800338 if (sLogFontRendererCreate) {
339 INIT_LOGD(" Setting text cache width to %s pixels", property);
340 }
Romain Guy51769a62010-07-23 00:28:00 -0700341 mCacheHeight = atoi(property);
342 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800343 if (sLogFontRendererCreate) {
Romain Guyc9855a52011-01-21 21:14:15 -0800344 INIT_LOGD(" Using default text cache height of %i pixels", mCacheHeight);
Romain Guy514fb182011-01-19 14:38:29 -0800345 }
Romain Guy51769a62010-07-23 00:28:00 -0700346 }
Romain Guy514fb182011-01-19 14:38:29 -0800347
348 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -0700349}
350
351FontRenderer::~FontRenderer() {
352 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
353 delete mCacheLines[i];
354 }
355 mCacheLines.clear();
356
Romain Guy9cccc2b92010-08-07 23:46:15 -0700357 if (mInitialized) {
358 delete[] mTextMeshPtr;
359 delete[] mTextTexture;
360 }
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700361
Romain Guy9cccc2b92010-08-07 23:46:15 -0700362 if (mTextureId) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700363 glDeleteTextures(1, &mTextureId);
364 }
Romain Guy694b5192010-07-21 21:33:20 -0700365
366 Vector<Font*> fontsToDereference = mActiveFonts;
367 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
368 delete fontsToDereference[i];
369 }
370}
371
372void FontRenderer::flushAllAndInvalidate() {
373 if (mCurrentQuadIndex != 0) {
374 issueDrawCommand();
375 mCurrentQuadIndex = 0;
376 }
377 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
378 mActiveFonts[i]->invalidateTextureCache();
379 }
380 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
381 mCacheLines[i]->mCurrentCol = 0;
382 }
383}
384
Romain Guy51769a62010-07-23 00:28:00 -0700385bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
Romain Guy694b5192010-07-21 21:33:20 -0700386 // If the glyph is too tall, don't cache it
Romain Guy2bffd262010-09-12 17:40:02 -0700387 if (glyph.fHeight > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
Romain Guy694b5192010-07-21 21:33:20 -0700388 LOGE("Font size to large to fit in cache. width, height = %i, %i",
389 (int) glyph.fWidth, (int) glyph.fHeight);
390 return false;
391 }
392
393 // Now copy the bitmap into the cache texture
394 uint32_t startX = 0;
395 uint32_t startY = 0;
396
397 bool bitmapFit = false;
398 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
399 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
400 if (bitmapFit) {
401 break;
402 }
403 }
404
405 // If the new glyph didn't fit, flush the state so far and invalidate everything
406 if (!bitmapFit) {
407 flushAllAndInvalidate();
408
409 // Try to fit it again
410 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
411 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
412 if (bitmapFit) {
413 break;
414 }
415 }
416
417 // if we still don't fit, something is wrong and we shouldn't draw
418 if (!bitmapFit) {
419 LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
420 (int) glyph.fWidth, (int) glyph.fHeight);
421 return false;
422 }
423 }
424
425 *retOriginX = startX;
426 *retOriginY = startY;
427
428 uint32_t endX = startX + glyph.fWidth;
429 uint32_t endY = startY + glyph.fHeight;
430
431 uint32_t cacheWidth = mCacheWidth;
432
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700433 uint8_t* cacheBuffer = mTextTexture;
434 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700435 unsigned int stride = glyph.rowBytes();
436
437 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
438 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
439 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700440 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
Romain Guyb45c0c92010-08-26 20:35:23 -0700441 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
Romain Guy694b5192010-07-21 21:33:20 -0700442 }
443 }
444
445 return true;
446}
447
448void FontRenderer::initTextTexture() {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700449 mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
Romain Guy1de10832010-10-03 14:37:09 -0700450 memset(mTextTexture, 0, mCacheWidth * mCacheHeight * sizeof(uint8_t));
451
Romain Guy694b5192010-07-21 21:33:20 -0700452 mUploadTexture = false;
453
454 glGenTextures(1, &mTextureId);
455 glBindTexture(GL_TEXTURE_2D, mTextureId);
456 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700457 // Initialize texture dimentions
458 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700459 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Romain Guy694b5192010-07-21 21:33:20 -0700460
Romain Guye8cb9c142010-10-04 14:14:11 -0700461 mLinearFiltering = false;
462 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
463 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
Romain Guy694b5192010-07-21 21:33:20 -0700464
465 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
466 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
467
468 // Split up our cache texture into lines of certain widths
469 int nextLine = 0;
Romain Guy7975fb62010-10-01 16:36:14 -0700470 mCacheLines.push(new CacheTextureLine(mCacheWidth, 18, 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));
Romain Guy694b5192010-07-21 21:33:20 -0700473 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700474 mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -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, 34, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700479 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700480 mCacheLines.push(new CacheTextureLine(mCacheWidth, 42, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700481 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700482 mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700483}
484
485// Avoid having to reallocate memory and render quad by quad
486void FontRenderer::initVertexArrayBuffers() {
487 uint32_t numIndicies = mMaxNumberOfQuads * 6;
488 uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700489 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700490
491 // Four verts, two triangles , six indices per quad
492 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
493 int i6 = i * 6;
494 int i4 = i * 4;
495
496 indexBufferData[i6 + 0] = i4 + 0;
497 indexBufferData[i6 + 1] = i4 + 1;
498 indexBufferData[i6 + 2] = i4 + 2;
499
500 indexBufferData[i6 + 3] = i4 + 0;
501 indexBufferData[i6 + 4] = i4 + 2;
502 indexBufferData[i6 + 5] = i4 + 3;
503 }
504
505 glGenBuffers(1, &mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700506 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
507 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
508 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
Romain Guy694b5192010-07-21 21:33:20 -0700509
510 free(indexBufferData);
511
512 uint32_t coordSize = 3;
513 uint32_t uvSize = 2;
514 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700515 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
516 mTextMeshPtr = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700517}
518
519// We don't want to allocate anything unless we actually draw text
520void FontRenderer::checkInit() {
521 if (mInitialized) {
522 return;
523 }
524
525 initTextTexture();
526 initVertexArrayBuffers();
527
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700528 // We store a string with letters in a rough frequency of occurrence
529 mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
530 mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
531 mLatinPrecache += String16(",.?!()-+@;:`'");
532 mLatinPrecache += String16("0123456789");
533
Romain Guy694b5192010-07-21 21:33:20 -0700534 mInitialized = true;
535}
536
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700537void FontRenderer::checkTextureUpdate() {
538 if (!mUploadTexture) {
539 return;
Romain Guy694b5192010-07-21 21:33:20 -0700540 }
541
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700542 glBindTexture(GL_TEXTURE_2D, mTextureId);
543
544 // Iterate over all the cache lines and see which ones need to be updated
545 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
546 CacheTextureLine* cl = mCacheLines[i];
547 if(cl->mDirty) {
548 uint32_t xOffset = 0;
549 uint32_t yOffset = cl->mCurrentRow;
550 uint32_t width = mCacheWidth;
551 uint32_t height = cl->mMaxHeight;
Romain Guy1e45aae2010-08-13 19:39:53 -0700552 void* textureData = mTextTexture + yOffset*width;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700553
554 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700555 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700556
557 cl->mDirty = false;
558 }
559 }
560
561 mUploadTexture = false;
562}
563
564void FontRenderer::issueDrawCommand() {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700565 checkTextureUpdate();
566
Romain Guy51769a62010-07-23 00:28:00 -0700567 float* vtx = mTextMeshPtr;
568 float* tex = vtx + 3;
Romain Guy694b5192010-07-21 21:33:20 -0700569
Alex Sakhartchouk894df172011-02-17 16:45:37 -0800570 glVertexAttribPointer(mPositionAttrSlot, 3, GL_FLOAT, false, 20, vtx);
571 glVertexAttribPointer(mTexcoordAttrSlot, 2, GL_FLOAT, false, 20, tex);
Romain Guy694b5192010-07-21 21:33:20 -0700572
573 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
574 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
Romain Guy5b3b3522010-10-27 18:57:51 -0700575
576 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700577}
578
579void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
580 float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
581 float x4, float y4, float z4, float u4, float v4) {
Romain Guy09147fb2010-07-22 13:08:20 -0700582 if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) {
583 return;
584 }
585
Romain Guy694b5192010-07-21 21:33:20 -0700586 const uint32_t vertsPerQuad = 4;
587 const uint32_t floatsPerVert = 5;
Romain Guy51769a62010-07-23 00:28:00 -0700588 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700589
Romain Guy694b5192010-07-21 21:33:20 -0700590 (*currentPos++) = x1;
591 (*currentPos++) = y1;
592 (*currentPos++) = z1;
593 (*currentPos++) = u1;
594 (*currentPos++) = v1;
595
596 (*currentPos++) = x2;
597 (*currentPos++) = y2;
598 (*currentPos++) = z2;
599 (*currentPos++) = u2;
600 (*currentPos++) = v2;
601
602 (*currentPos++) = x3;
603 (*currentPos++) = y3;
604 (*currentPos++) = z3;
605 (*currentPos++) = u3;
606 (*currentPos++) = v3;
607
608 (*currentPos++) = x4;
609 (*currentPos++) = y4;
610 (*currentPos++) = z4;
611 (*currentPos++) = u4;
612 (*currentPos++) = v4;
613
614 mCurrentQuadIndex++;
615
Romain Guy5b3b3522010-10-27 18:57:51 -0700616 if (mBounds) {
617 mBounds->left = fmin(mBounds->left, x1);
618 mBounds->top = fmin(mBounds->top, y3);
619 mBounds->right = fmax(mBounds->right, x3);
620 mBounds->bottom = fmax(mBounds->bottom, y1);
621 }
622
Romain Guy694b5192010-07-21 21:33:20 -0700623 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
624 issueDrawCommand();
625 mCurrentQuadIndex = 0;
626 }
627}
628
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700629uint32_t FontRenderer::getRemainingCacheCapacity() {
630 uint32_t remainingCapacity = 0;
631 float totalPixels = 0;
632 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
633 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
634 totalPixels += mCacheLines[i]->mMaxWidth;
635 }
636 remainingCapacity = (remainingCapacity * 100) / totalPixels;
637 return remainingCapacity;
638}
639
640void FontRenderer::precacheLatin(SkPaint* paint) {
641 // Remaining capacity is measured in %
642 uint32_t remainingCapacity = getRemainingCacheCapacity();
643 uint32_t precacheIdx = 0;
Romain Guy054dc182010-10-15 17:55:25 -0700644 while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
645 mCurrentFont->getCachedUTFChar(paint, (int32_t) mLatinPrecache[precacheIdx]);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700646 remainingCapacity = getRemainingCacheCapacity();
647 precacheIdx ++;
648 }
649}
650
651void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
652 uint32_t currentNumFonts = mActiveFonts.size();
Romain Guy325a0f92011-01-05 15:26:55 -0800653 int flags = 0;
654 if (paint->isFakeBoldText()) {
655 flags |= Font::kFakeBold;
656 }
Romain Guy2577db12011-01-18 13:02:38 -0800657
658 const float skewX = paint->getTextSkewX();
659 uint32_t italicStyle = *(uint32_t*) &skewX;
660 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700661
662 const float maxPrecacheFontSize = 40.0f;
663 bool isNewFont = currentNumFonts != mActiveFonts.size();
664
Romain Guy2bffd262010-09-12 17:40:02 -0700665 if (isNewFont && fontSize <= maxPrecacheFontSize) {
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700666 precacheLatin(paint);
667 }
Romain Guy694b5192010-07-21 21:33:20 -0700668}
Romain Guy7975fb62010-10-01 16:36:14 -0700669
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700670FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Romain Guy1e45aae2010-08-13 19:39:53 -0700671 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
672 checkInit();
673
674 if (!mCurrentFont) {
675 DropShadow image;
676 image.width = 0;
677 image.height = 0;
678 image.image = NULL;
679 image.penX = 0;
680 image.penY = 0;
681 return image;
682 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700683
684 Rect bounds;
685 mCurrentFont->measureUTF(paint, text, startIndex, len, numGlyphs, &bounds);
Romain Guy1e45aae2010-08-13 19:39:53 -0700686 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
687 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700688 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guy1e45aae2010-08-13 19:39:53 -0700689 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700690 dataBuffer[i] = 0;
691 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700692
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700693 int penX = radius - bounds.left;
694 int penY = radius - bounds.bottom;
695
696 mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, penX, penY,
Romain Guy1e45aae2010-08-13 19:39:53 -0700697 dataBuffer, paddedWidth, paddedHeight);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700698 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
699
700 DropShadow image;
701 image.width = paddedWidth;
702 image.height = paddedHeight;
703 image.image = dataBuffer;
704 image.penX = penX;
705 image.penY = penY;
706 return image;
707}
Romain Guy694b5192010-07-21 21:33:20 -0700708
Romain Guy5b3b3522010-10-27 18:57:51 -0700709bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
710 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700711 checkInit();
712
Romain Guy09147fb2010-07-22 13:08:20 -0700713 if (!mCurrentFont) {
714 LOGE("No font set");
Romain Guy5b3b3522010-10-27 18:57:51 -0700715 return false;
Romain Guy694b5192010-07-21 21:33:20 -0700716 }
717
Alex Sakhartchouk894df172011-02-17 16:45:37 -0800718 if (mPositionAttrSlot < 0 || mTexcoordAttrSlot < 0) {
719 LOGE("Font renderer unable to draw, attribute slots undefined");
720 return false;
721 }
722
Romain Guy5b3b3522010-10-27 18:57:51 -0700723 mDrawn = false;
724 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -0700725 mClip = clip;
Romain Guy51769a62010-07-23 00:28:00 -0700726 mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y);
Romain Guy5b3b3522010-10-27 18:57:51 -0700727 mBounds = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700728
729 if (mCurrentQuadIndex != 0) {
730 issueDrawCommand();
731 mCurrentQuadIndex = 0;
732 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700733
734 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700735}
736
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700737void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
738 // Compute gaussian weights for the blur
739 // e is the euler's number
740 float e = 2.718281828459045f;
741 float pi = 3.1415926535897932f;
742 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
743 // x is of the form [-radius .. 0 .. radius]
744 // and sigma varies with radius.
745 // Based on some experimental radius values and sigma's
746 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700747 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700748 // The larger the radius gets, the more our gaussian blur
749 // will resemble a box blur since with large sigma
750 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -0800751 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700752
753 // Now compute the coefficints
754 // We will store some redundant values to save some math during
755 // the blur calculations
756 // precompute some values
757 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
758 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
759
760 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -0800761 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700762 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700763 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
764 normalizeFactor += weights[r + radius];
765 }
766
767 //Now we need to normalize the weights because all our coefficients need to add up to one
768 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -0800769 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700770 weights[r + radius] *= normalizeFactor;
771 }
772}
773
774void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700775 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700776 float blurredPixel = 0.0f;
777 float currentPixel = 0.0f;
778
Romain Guy325a0f92011-01-05 15:26:55 -0800779 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700780
781 const uint8_t* input = source + y * width;
782 uint8_t* output = dest + y * width;
783
Romain Guy325a0f92011-01-05 15:26:55 -0800784 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700785 blurredPixel = 0.0f;
786 const float* gPtr = weights;
787 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800788 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700789 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -0800790 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700791 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700792 blurredPixel += currentPixel * gPtr[0];
793 gPtr++;
794 i++;
795 }
796 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800797 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700798 // Stepping left and right away from the pixel
799 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -0800800 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700801 validW = 0;
802 }
Romain Guy325a0f92011-01-05 15:26:55 -0800803 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700804 validW = width - 1;
805 }
806
Romain Guy325a0f92011-01-05 15:26:55 -0800807 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700808 blurredPixel += currentPixel * gPtr[0];
809 gPtr++;
810 }
811 }
812 *output = (uint8_t)blurredPixel;
813 output ++;
814 }
815 }
816}
817
818void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700819 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700820 float blurredPixel = 0.0f;
821 float currentPixel = 0.0f;
822
Romain Guy325a0f92011-01-05 15:26:55 -0800823 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700824
825 uint8_t* output = dest + y * width;
826
Romain Guy325a0f92011-01-05 15:26:55 -0800827 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700828 blurredPixel = 0.0f;
829 const float* gPtr = weights;
830 const uint8_t* input = source + x;
831 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800832 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700833 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -0800834 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700835 currentPixel = (float)(*i);
836 blurredPixel += currentPixel * gPtr[0];
837 gPtr++;
838 i += width;
839 }
840 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800841 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700842 int validH = y + r;
843 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -0800844 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700845 validH = 0;
846 }
Romain Guy325a0f92011-01-05 15:26:55 -0800847 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700848 validH = height - 1;
849 }
850
851 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -0800852 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700853 blurredPixel += currentPixel * gPtr[0];
854 gPtr++;
855 }
856 }
Romain Guy325a0f92011-01-05 15:26:55 -0800857 *output = (uint8_t) blurredPixel;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700858 output ++;
859 }
860 }
861}
862
863
864void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
865 float *gaussian = new float[2 * radius + 1];
866 computeGaussianWeights(gaussian, radius);
867 uint8_t* scratch = new uint8_t[width * height];
868 horizontalBlur(gaussian, radius, image, scratch, width, height);
869 verticalBlur(gaussian, radius, scratch, image, width, height);
870 delete[] gaussian;
871 delete[] scratch;
872}
873
Romain Guy694b5192010-07-21 21:33:20 -0700874}; // namespace uirenderer
875}; // namespace android