blob: aa9b40e041cb6edf2873a61fa3dcf436fa596d81 [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,
Chet Haase8668f8a2011-03-02 13:51:36 -080043 int flags, uint32_t italicStyle, uint32_t scaleX) :
Romain Guy2577db12011-01-18 13:02:38 -080044 mState(state), mFontId(fontId), mFontSize(fontSize),
Chet Haase8668f8a2011-03-02 13:51:36 -080045 mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX) {
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,
Chet Haase8668f8a2011-03-02 13:51:36 -0800282 int flags, uint32_t italicStyle, uint32_t scaleX) {
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 &&
Chet Haase8668f8a2011-03-02 13:51:36 -0800288 font->mFlags == flags && font->mItalicStyle == italicStyle &&
289 font->mScaleX == scaleX) {
Romain Guy51769a62010-07-23 00:28:00 -0700290 return font;
Romain Guy694b5192010-07-21 21:33:20 -0700291 }
292 }
293
Chet Haase8668f8a2011-03-02 13:51:36 -0800294 Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle, scaleX);
Romain Guy694b5192010-07-21 21:33:20 -0700295 activeFonts.push(newFont);
296 return newFont;
297}
298
299///////////////////////////////////////////////////////////////////////////////
300// FontRenderer
301///////////////////////////////////////////////////////////////////////////////
302
Romain Guy514fb182011-01-19 14:38:29 -0800303static bool sLogFontRendererCreate = true;
304
Romain Guy694b5192010-07-21 21:33:20 -0700305FontRenderer::FontRenderer() {
Romain Guyc9855a52011-01-21 21:14:15 -0800306 if (sLogFontRendererCreate) {
307 INIT_LOGD("Creating FontRenderer");
308 }
Romain Guy51769a62010-07-23 00:28:00 -0700309
Romain Guyb45c0c92010-08-26 20:35:23 -0700310 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700311 mInitialized = false;
312 mMaxNumberOfQuads = 1024;
313 mCurrentQuadIndex = 0;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700314 mTextureId = 0;
Romain Guy694b5192010-07-21 21:33:20 -0700315
Romain Guy9cccc2b2010-08-07 23:46:15 -0700316 mTextMeshPtr = NULL;
317 mTextTexture = NULL;
318
Romain Guy694b5192010-07-21 21:33:20 -0700319 mIndexBufferID = 0;
Alex Sakhartchouk894df172011-02-17 16:45:37 -0800320 mPositionAttrSlot = -1;
321 mTexcoordAttrSlot = -1;
Romain Guy694b5192010-07-21 21:33:20 -0700322
Romain Guy51769a62010-07-23 00:28:00 -0700323 mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700324 mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -0700325
326 char property[PROPERTY_VALUE_MAX];
327 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800328 if (sLogFontRendererCreate) {
329 INIT_LOGD(" Setting text cache width to %s pixels", property);
330 }
Romain Guy51769a62010-07-23 00:28:00 -0700331 mCacheWidth = atoi(property);
332 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800333 if (sLogFontRendererCreate) {
Romain Guyc9855a52011-01-21 21:14:15 -0800334 INIT_LOGD(" Using default text cache width of %i pixels", mCacheWidth);
Romain Guy514fb182011-01-19 14:38:29 -0800335 }
Romain Guy51769a62010-07-23 00:28:00 -0700336 }
337
338 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800339 if (sLogFontRendererCreate) {
340 INIT_LOGD(" Setting text cache width to %s pixels", property);
341 }
Romain Guy51769a62010-07-23 00:28:00 -0700342 mCacheHeight = atoi(property);
343 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800344 if (sLogFontRendererCreate) {
Romain Guyc9855a52011-01-21 21:14:15 -0800345 INIT_LOGD(" Using default text cache height of %i pixels", mCacheHeight);
Romain Guy514fb182011-01-19 14:38:29 -0800346 }
Romain Guy51769a62010-07-23 00:28:00 -0700347 }
Romain Guy514fb182011-01-19 14:38:29 -0800348
349 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -0700350}
351
352FontRenderer::~FontRenderer() {
353 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
354 delete mCacheLines[i];
355 }
356 mCacheLines.clear();
357
Romain Guy9cccc2b2010-08-07 23:46:15 -0700358 if (mInitialized) {
359 delete[] mTextMeshPtr;
360 delete[] mTextTexture;
361 }
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700362
Romain Guy9cccc2b2010-08-07 23:46:15 -0700363 if (mTextureId) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700364 glDeleteTextures(1, &mTextureId);
365 }
Romain Guy694b5192010-07-21 21:33:20 -0700366
367 Vector<Font*> fontsToDereference = mActiveFonts;
368 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
369 delete fontsToDereference[i];
370 }
371}
372
373void FontRenderer::flushAllAndInvalidate() {
374 if (mCurrentQuadIndex != 0) {
375 issueDrawCommand();
376 mCurrentQuadIndex = 0;
377 }
378 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
379 mActiveFonts[i]->invalidateTextureCache();
380 }
381 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
382 mCacheLines[i]->mCurrentCol = 0;
383 }
384}
385
Romain Guy51769a62010-07-23 00:28:00 -0700386bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
Romain Guy694b5192010-07-21 21:33:20 -0700387 // If the glyph is too tall, don't cache it
Romain Guy2bffd262010-09-12 17:40:02 -0700388 if (glyph.fHeight > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
Romain Guy694b5192010-07-21 21:33:20 -0700389 LOGE("Font size to large to fit in cache. width, height = %i, %i",
390 (int) glyph.fWidth, (int) glyph.fHeight);
391 return false;
392 }
393
394 // Now copy the bitmap into the cache texture
395 uint32_t startX = 0;
396 uint32_t startY = 0;
397
398 bool bitmapFit = false;
399 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
400 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
401 if (bitmapFit) {
402 break;
403 }
404 }
405
406 // If the new glyph didn't fit, flush the state so far and invalidate everything
407 if (!bitmapFit) {
408 flushAllAndInvalidate();
409
410 // Try to fit it again
411 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
412 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
413 if (bitmapFit) {
414 break;
415 }
416 }
417
418 // if we still don't fit, something is wrong and we shouldn't draw
419 if (!bitmapFit) {
420 LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
421 (int) glyph.fWidth, (int) glyph.fHeight);
422 return false;
423 }
424 }
425
426 *retOriginX = startX;
427 *retOriginY = startY;
428
429 uint32_t endX = startX + glyph.fWidth;
430 uint32_t endY = startY + glyph.fHeight;
431
432 uint32_t cacheWidth = mCacheWidth;
433
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700434 uint8_t* cacheBuffer = mTextTexture;
435 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700436 unsigned int stride = glyph.rowBytes();
437
438 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
439 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
440 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700441 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
Romain Guyb45c0c92010-08-26 20:35:23 -0700442 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
Romain Guy694b5192010-07-21 21:33:20 -0700443 }
444 }
445
446 return true;
447}
448
449void FontRenderer::initTextTexture() {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700450 mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
Romain Guy1de10832010-10-03 14:37:09 -0700451 memset(mTextTexture, 0, mCacheWidth * mCacheHeight * sizeof(uint8_t));
452
Romain Guy694b5192010-07-21 21:33:20 -0700453 mUploadTexture = false;
454
455 glGenTextures(1, &mTextureId);
456 glBindTexture(GL_TEXTURE_2D, mTextureId);
457 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700458 // Initialize texture dimentions
459 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700460 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Romain Guy694b5192010-07-21 21:33:20 -0700461
Romain Guye8cb9c142010-10-04 14:14:11 -0700462 mLinearFiltering = false;
463 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
464 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
Romain Guy694b5192010-07-21 21:33:20 -0700465
466 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
467 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
468
469 // Split up our cache texture into lines of certain widths
470 int nextLine = 0;
Romain Guy7975fb62010-10-01 16:36:14 -0700471 mCacheLines.push(new CacheTextureLine(mCacheWidth, 18, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700472 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700473 mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700474 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700475 mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700476 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700477 mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700478 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700479 mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700480 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700481 mCacheLines.push(new CacheTextureLine(mCacheWidth, 42, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700482 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700483 mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700484}
485
486// Avoid having to reallocate memory and render quad by quad
487void FontRenderer::initVertexArrayBuffers() {
488 uint32_t numIndicies = mMaxNumberOfQuads * 6;
489 uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700490 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700491
492 // Four verts, two triangles , six indices per quad
493 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
494 int i6 = i * 6;
495 int i4 = i * 4;
496
497 indexBufferData[i6 + 0] = i4 + 0;
498 indexBufferData[i6 + 1] = i4 + 1;
499 indexBufferData[i6 + 2] = i4 + 2;
500
501 indexBufferData[i6 + 3] = i4 + 0;
502 indexBufferData[i6 + 4] = i4 + 2;
503 indexBufferData[i6 + 5] = i4 + 3;
504 }
505
506 glGenBuffers(1, &mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700507 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
508 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
509 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
Romain Guy694b5192010-07-21 21:33:20 -0700510
511 free(indexBufferData);
512
513 uint32_t coordSize = 3;
514 uint32_t uvSize = 2;
515 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700516 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
517 mTextMeshPtr = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700518}
519
520// We don't want to allocate anything unless we actually draw text
521void FontRenderer::checkInit() {
522 if (mInitialized) {
523 return;
524 }
525
526 initTextTexture();
527 initVertexArrayBuffers();
528
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700529 // We store a string with letters in a rough frequency of occurrence
530 mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
531 mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
532 mLatinPrecache += String16(",.?!()-+@;:`'");
533 mLatinPrecache += String16("0123456789");
534
Romain Guy694b5192010-07-21 21:33:20 -0700535 mInitialized = true;
536}
537
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700538void FontRenderer::checkTextureUpdate() {
539 if (!mUploadTexture) {
540 return;
Romain Guy694b5192010-07-21 21:33:20 -0700541 }
542
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700543 glBindTexture(GL_TEXTURE_2D, mTextureId);
544
545 // Iterate over all the cache lines and see which ones need to be updated
546 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
547 CacheTextureLine* cl = mCacheLines[i];
548 if(cl->mDirty) {
549 uint32_t xOffset = 0;
550 uint32_t yOffset = cl->mCurrentRow;
551 uint32_t width = mCacheWidth;
552 uint32_t height = cl->mMaxHeight;
Romain Guy1e45aae2010-08-13 19:39:53 -0700553 void* textureData = mTextTexture + yOffset*width;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700554
555 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700556 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700557
558 cl->mDirty = false;
559 }
560 }
561
562 mUploadTexture = false;
563}
564
565void FontRenderer::issueDrawCommand() {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700566 checkTextureUpdate();
567
Romain Guy51769a62010-07-23 00:28:00 -0700568 float* vtx = mTextMeshPtr;
569 float* tex = vtx + 3;
Romain Guy694b5192010-07-21 21:33:20 -0700570
Alex Sakhartchouk894df172011-02-17 16:45:37 -0800571 glVertexAttribPointer(mPositionAttrSlot, 3, GL_FLOAT, false, 20, vtx);
572 glVertexAttribPointer(mTexcoordAttrSlot, 2, GL_FLOAT, false, 20, tex);
Romain Guy694b5192010-07-21 21:33:20 -0700573
574 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
575 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
Romain Guy5b3b3522010-10-27 18:57:51 -0700576
577 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700578}
579
580void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
581 float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
582 float x4, float y4, float z4, float u4, float v4) {
Romain Guy09147fb2010-07-22 13:08:20 -0700583 if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) {
584 return;
585 }
586
Romain Guy694b5192010-07-21 21:33:20 -0700587 const uint32_t vertsPerQuad = 4;
588 const uint32_t floatsPerVert = 5;
Romain Guy51769a62010-07-23 00:28:00 -0700589 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700590
Romain Guy694b5192010-07-21 21:33:20 -0700591 (*currentPos++) = x1;
592 (*currentPos++) = y1;
593 (*currentPos++) = z1;
594 (*currentPos++) = u1;
595 (*currentPos++) = v1;
596
597 (*currentPos++) = x2;
598 (*currentPos++) = y2;
599 (*currentPos++) = z2;
600 (*currentPos++) = u2;
601 (*currentPos++) = v2;
602
603 (*currentPos++) = x3;
604 (*currentPos++) = y3;
605 (*currentPos++) = z3;
606 (*currentPos++) = u3;
607 (*currentPos++) = v3;
608
609 (*currentPos++) = x4;
610 (*currentPos++) = y4;
611 (*currentPos++) = z4;
612 (*currentPos++) = u4;
613 (*currentPos++) = v4;
614
615 mCurrentQuadIndex++;
616
Romain Guy5b3b3522010-10-27 18:57:51 -0700617 if (mBounds) {
618 mBounds->left = fmin(mBounds->left, x1);
619 mBounds->top = fmin(mBounds->top, y3);
620 mBounds->right = fmax(mBounds->right, x3);
621 mBounds->bottom = fmax(mBounds->bottom, y1);
622 }
623
Romain Guy694b5192010-07-21 21:33:20 -0700624 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
625 issueDrawCommand();
626 mCurrentQuadIndex = 0;
627 }
628}
629
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700630uint32_t FontRenderer::getRemainingCacheCapacity() {
631 uint32_t remainingCapacity = 0;
632 float totalPixels = 0;
633 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
634 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
635 totalPixels += mCacheLines[i]->mMaxWidth;
636 }
637 remainingCapacity = (remainingCapacity * 100) / totalPixels;
638 return remainingCapacity;
639}
640
641void FontRenderer::precacheLatin(SkPaint* paint) {
642 // Remaining capacity is measured in %
643 uint32_t remainingCapacity = getRemainingCacheCapacity();
644 uint32_t precacheIdx = 0;
Romain Guy054dc182010-10-15 17:55:25 -0700645 while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
646 mCurrentFont->getCachedUTFChar(paint, (int32_t) mLatinPrecache[precacheIdx]);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700647 remainingCapacity = getRemainingCacheCapacity();
648 precacheIdx ++;
649 }
650}
651
652void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
653 uint32_t currentNumFonts = mActiveFonts.size();
Romain Guy325a0f92011-01-05 15:26:55 -0800654 int flags = 0;
655 if (paint->isFakeBoldText()) {
656 flags |= Font::kFakeBold;
657 }
Romain Guy2577db12011-01-18 13:02:38 -0800658
659 const float skewX = paint->getTextSkewX();
660 uint32_t italicStyle = *(uint32_t*) &skewX;
Chet Haase8668f8a2011-03-02 13:51:36 -0800661 const float scaleXFloat = paint->getTextScaleX();
662 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
663 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle, scaleX);
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
Alex Sakhartchouk894df172011-02-17 16:45:37 -0800721 if (mPositionAttrSlot < 0 || mTexcoordAttrSlot < 0) {
722 LOGE("Font renderer unable to draw, attribute slots undefined");
723 return false;
724 }
725
Romain Guy5b3b3522010-10-27 18:57:51 -0700726 mDrawn = false;
727 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -0700728 mClip = clip;
Romain Guy51769a62010-07-23 00:28:00 -0700729 mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y);
Romain Guy5b3b3522010-10-27 18:57:51 -0700730 mBounds = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700731
732 if (mCurrentQuadIndex != 0) {
733 issueDrawCommand();
734 mCurrentQuadIndex = 0;
735 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700736
737 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700738}
739
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700740void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
741 // Compute gaussian weights for the blur
742 // e is the euler's number
743 float e = 2.718281828459045f;
744 float pi = 3.1415926535897932f;
745 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
746 // x is of the form [-radius .. 0 .. radius]
747 // and sigma varies with radius.
748 // Based on some experimental radius values and sigma's
749 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700750 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700751 // The larger the radius gets, the more our gaussian blur
752 // will resemble a box blur since with large sigma
753 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -0800754 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700755
756 // Now compute the coefficints
757 // We will store some redundant values to save some math during
758 // the blur calculations
759 // precompute some values
760 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
761 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
762
763 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -0800764 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700765 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700766 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
767 normalizeFactor += weights[r + radius];
768 }
769
770 //Now we need to normalize the weights because all our coefficients need to add up to one
771 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -0800772 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700773 weights[r + radius] *= normalizeFactor;
774 }
775}
776
777void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700778 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700779 float blurredPixel = 0.0f;
780 float currentPixel = 0.0f;
781
Romain Guy325a0f92011-01-05 15:26:55 -0800782 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700783
784 const uint8_t* input = source + y * width;
785 uint8_t* output = dest + y * width;
786
Romain Guy325a0f92011-01-05 15:26:55 -0800787 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700788 blurredPixel = 0.0f;
789 const float* gPtr = weights;
790 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800791 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700792 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -0800793 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700794 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700795 blurredPixel += currentPixel * gPtr[0];
796 gPtr++;
797 i++;
798 }
799 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800800 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700801 // Stepping left and right away from the pixel
802 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -0800803 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700804 validW = 0;
805 }
Romain Guy325a0f92011-01-05 15:26:55 -0800806 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700807 validW = width - 1;
808 }
809
Romain Guy325a0f92011-01-05 15:26:55 -0800810 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700811 blurredPixel += currentPixel * gPtr[0];
812 gPtr++;
813 }
814 }
815 *output = (uint8_t)blurredPixel;
816 output ++;
817 }
818 }
819}
820
821void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700822 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700823 float blurredPixel = 0.0f;
824 float currentPixel = 0.0f;
825
Romain Guy325a0f92011-01-05 15:26:55 -0800826 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700827
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 const uint8_t* input = source + x;
834 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800835 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700836 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -0800837 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700838 currentPixel = (float)(*i);
839 blurredPixel += currentPixel * gPtr[0];
840 gPtr++;
841 i += width;
842 }
843 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800844 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700845 int validH = y + r;
846 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -0800847 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700848 validH = 0;
849 }
Romain Guy325a0f92011-01-05 15:26:55 -0800850 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700851 validH = height - 1;
852 }
853
854 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -0800855 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700856 blurredPixel += currentPixel * gPtr[0];
857 gPtr++;
858 }
859 }
Romain Guy325a0f92011-01-05 15:26:55 -0800860 *output = (uint8_t) blurredPixel;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700861 output ++;
862 }
863 }
864}
865
866
867void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
868 float *gaussian = new float[2 * radius + 1];
869 computeGaussianWeights(gaussian, radius);
870 uint8_t* scratch = new uint8_t[width * height];
871 horizontalBlur(gaussian, radius, image, scratch, width, height);
872 verticalBlur(gaussian, radius, scratch, image, width, height);
873 delete[] gaussian;
874 delete[] scratch;
875}
876
Romain Guy694b5192010-07-21 21:33:20 -0700877}; // namespace uirenderer
878}; // namespace android