blob: c080501df338d1d533d8878e9b3721a007477821 [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
25#include "FontRenderer.h"
26
Romain Guy694b5192010-07-21 21:33:20 -070027namespace android {
28namespace uirenderer {
29
30///////////////////////////////////////////////////////////////////////////////
Romain Guy51769a62010-07-23 00:28:00 -070031// Defines
32///////////////////////////////////////////////////////////////////////////////
33
34#define DEFAULT_TEXT_CACHE_WIDTH 1024
35#define DEFAULT_TEXT_CACHE_HEIGHT 256
36
37///////////////////////////////////////////////////////////////////////////////
Romain Guy694b5192010-07-21 21:33:20 -070038// Font
39///////////////////////////////////////////////////////////////////////////////
40
Romain Guy2577db12011-01-18 13:02:38 -080041Font::Font(FontRenderer* state, uint32_t fontId, float fontSize,
42 int flags, uint32_t italicStyle) :
43 mState(state), mFontId(fontId), mFontSize(fontSize),
44 mFlags(flags), mItalicStyle(italicStyle) {
Romain Guy694b5192010-07-21 21:33:20 -070045}
46
47
48Font::~Font() {
49 for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
50 if (mState->mActiveFonts[ct] == this) {
51 mState->mActiveFonts.removeAt(ct);
52 break;
53 }
54 }
55
56 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
Romain Guy51769a62010-07-23 00:28:00 -070057 CachedGlyphInfo* glyph = mCachedGlyphs.valueAt(i);
Romain Guy694b5192010-07-21 21:33:20 -070058 delete glyph;
59 }
60}
61
62void Font::invalidateTextureCache() {
63 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
64 mCachedGlyphs.valueAt(i)->mIsValid = false;
65 }
66}
67
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070068void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds) {
69 int nPenX = x + glyph->mBitmapLeft;
70 int nPenY = y + glyph->mBitmapTop;
71
72 int width = (int) glyph->mBitmapWidth;
73 int height = (int) glyph->mBitmapHeight;
74
Romain Guy61c8c9c2010-08-09 20:48:09 -070075 if (bounds->bottom > nPenY) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070076 bounds->bottom = nPenY;
77 }
Romain Guy61c8c9c2010-08-09 20:48:09 -070078 if (bounds->left > nPenX) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070079 bounds->left = nPenX;
80 }
Romain Guy61c8c9c2010-08-09 20:48:09 -070081 if (bounds->right < nPenX + width) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070082 bounds->right = nPenX + width;
83 }
Romain Guy61c8c9c2010-08-09 20:48:09 -070084 if (bounds->top < nPenY + height) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070085 bounds->top = nPenY + height;
86 }
87}
88
Romain Guy694b5192010-07-21 21:33:20 -070089void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
Romain Guy694b5192010-07-21 21:33:20 -070090 int nPenX = x + glyph->mBitmapLeft;
91 int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
92
Romain Guy51769a62010-07-23 00:28:00 -070093 float u1 = glyph->mBitmapMinU;
94 float u2 = glyph->mBitmapMaxU;
95 float v1 = glyph->mBitmapMinV;
96 float v2 = glyph->mBitmapMaxV;
97
98 int width = (int) glyph->mBitmapWidth;
99 int height = (int) glyph->mBitmapHeight;
100
101 mState->appendMeshQuad(nPenX, nPenY, 0, u1, v2,
102 nPenX + width, nPenY, 0, u2, v2,
103 nPenX + width, nPenY - height, 0, u2, v1,
104 nPenX, nPenY - height, 0, u1, v1);
Romain Guy694b5192010-07-21 21:33:20 -0700105}
106
Romain Guyb45c0c92010-08-26 20:35:23 -0700107void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
108 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700109 int nPenX = x + glyph->mBitmapLeft;
110 int nPenY = y + glyph->mBitmapTop;
111
112 uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
113 uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
114
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700115 uint32_t cacheWidth = mState->getCacheWidth();
116 const uint8_t* cacheBuffer = mState->getTextTextureData();
117
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700118 uint32_t cacheX = 0, cacheY = 0;
119 int32_t bX = 0, bY = 0;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700120 for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
121 for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
Romain Guyb45c0c92010-08-26 20:35:23 -0700122 if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700123 LOGE("Skipping invalid index");
124 continue;
125 }
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700126 uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
127 bitmap[bY * bitmapW + bX] = tempCol;
128 }
129 }
130
131}
132
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700133Font::CachedGlyphInfo* Font::getCachedUTFChar(SkPaint* paint, int32_t utfChar) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700134 CachedGlyphInfo* cachedGlyph = NULL;
135 ssize_t index = mCachedGlyphs.indexOfKey(utfChar);
136 if (index >= 0) {
137 cachedGlyph = mCachedGlyphs.valueAt(index);
138 } else {
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700139 cachedGlyph = cacheGlyph(paint, utfChar);
140 }
141
142 // Is the glyph still in texture cache?
143 if (!cachedGlyph->mIsValid) {
144 const SkGlyph& skiaGlyph = paint->getUnicharMetrics(utfChar);
145 updateGlyphCache(paint, skiaGlyph, cachedGlyph);
146 }
147
148 return cachedGlyph;
149}
150
Romain Guy51769a62010-07-23 00:28:00 -0700151void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700152 int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
153 if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
154 renderUTF(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
155 bitmapW, bitmapH, NULL);
156 } else {
157 renderUTF(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, 0, 0, NULL);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700158 }
159
160}
161
162void Font::measureUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700163 int numGlyphs, Rect *bounds) {
164 if (bounds == NULL) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700165 LOGE("No return rectangle provided to measure text");
166 return;
167 }
168 bounds->set(1e6, -1e6, -1e6, 1e6);
169 renderUTF(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds);
170}
171
Romain Guy58ef7fb2010-09-13 12:52:37 -0700172#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16)
Romain Guy2bffd262010-09-12 17:40:02 -0700173
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700174void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700175 int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
176 uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700177 if (numGlyphs == 0 || text == NULL || len == 0) {
178 return;
179 }
180
Romain Guy2bffd262010-09-12 17:40:02 -0700181 SkFixed penX = SkIntToFixed(x);
182 int penY = y;
Romain Guy694b5192010-07-21 21:33:20 -0700183 int glyphsLeft = 1;
184 if (numGlyphs > 0) {
185 glyphsLeft = numGlyphs;
186 }
187
Romain Guy2bffd262010-09-12 17:40:02 -0700188 SkFixed prevRsbDelta = 0;
189 penX += SK_Fixed1 / 2;
190
Romain Guy694b5192010-07-21 21:33:20 -0700191 text += start;
192
193 while (glyphsLeft > 0) {
Romain Guy694b5192010-07-21 21:33:20 -0700194 int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text);
195
Romain Guy61c8c9c2010-08-09 20:48:09 -0700196 // Reached the end of the string
Romain Guy694b5192010-07-21 21:33:20 -0700197 if (utfChar < 0) {
198 break;
199 }
200
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700201 CachedGlyphInfo* cachedGlyph = getCachedUTFChar(paint, utfChar);
Romain Guy2bffd262010-09-12 17:40:02 -0700202 penX += SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta);
203 prevRsbDelta = cachedGlyph->mRsbDelta;
Romain Guy694b5192010-07-21 21:33:20 -0700204
205 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
206 if (cachedGlyph->mIsValid) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700207 switch(mode) {
208 case FRAMEBUFFER:
Romain Guy2bffd262010-09-12 17:40:02 -0700209 drawCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700210 break;
211 case BITMAP:
Romain Guy2bffd262010-09-12 17:40:02 -0700212 drawCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY, bitmap, bitmapW, bitmapH);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700213 break;
214 case MEASURE:
Romain Guy2bffd262010-09-12 17:40:02 -0700215 measureCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY, bounds);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700216 break;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700217 }
Romain Guy694b5192010-07-21 21:33:20 -0700218 }
219
Romain Guy2bffd262010-09-12 17:40:02 -0700220 penX += cachedGlyph->mAdvanceX;
Romain Guy694b5192010-07-21 21:33:20 -0700221
222 // If we were given a specific number of glyphs, decrement
223 if (numGlyphs > 0) {
224 glyphsLeft--;
225 }
226 }
227}
228
Romain Guy51769a62010-07-23 00:28:00 -0700229void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
Romain Guy694b5192010-07-21 21:33:20 -0700230 glyph->mAdvanceX = skiaGlyph.fAdvanceX;
231 glyph->mAdvanceY = skiaGlyph.fAdvanceY;
232 glyph->mBitmapLeft = skiaGlyph.fLeft;
233 glyph->mBitmapTop = skiaGlyph.fTop;
Romain Guy2bffd262010-09-12 17:40:02 -0700234 glyph->mLsbDelta = skiaGlyph.fLsbDelta;
235 glyph->mRsbDelta = skiaGlyph.fRsbDelta;
Romain Guy694b5192010-07-21 21:33:20 -0700236
237 uint32_t startX = 0;
238 uint32_t startY = 0;
239
Romain Guy694b5192010-07-21 21:33:20 -0700240 // Get the bitmap for the glyph
241 paint->findImage(skiaGlyph);
Romain Guy51769a62010-07-23 00:28:00 -0700242 glyph->mIsValid = mState->cacheBitmap(skiaGlyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700243
244 if (!glyph->mIsValid) {
245 return;
246 }
247
248 uint32_t endX = startX + skiaGlyph.fWidth;
249 uint32_t endY = startY + skiaGlyph.fHeight;
250
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700251 glyph->mStartX = startX;
252 glyph->mStartY = startY;
Romain Guy694b5192010-07-21 21:33:20 -0700253 glyph->mBitmapWidth = skiaGlyph.fWidth;
254 glyph->mBitmapHeight = skiaGlyph.fHeight;
255
Romain Guy51769a62010-07-23 00:28:00 -0700256 uint32_t cacheWidth = mState->getCacheWidth();
257 uint32_t cacheHeight = mState->getCacheHeight();
Romain Guy694b5192010-07-21 21:33:20 -0700258
259 glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
260 glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
261 glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
262 glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
263
Romain Guy51769a62010-07-23 00:28:00 -0700264 mState->mUploadTexture = true;
Romain Guy694b5192010-07-21 21:33:20 -0700265}
266
Romain Guy51769a62010-07-23 00:28:00 -0700267Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, int32_t glyph) {
268 CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
Romain Guy694b5192010-07-21 21:33:20 -0700269 mCachedGlyphs.add(glyph, newGlyph);
270
271 const SkGlyph& skiaGlyph = paint->getUnicharMetrics(glyph);
272 newGlyph->mGlyphIndex = skiaGlyph.fID;
273 newGlyph->mIsValid = false;
274
275 updateGlyphCache(paint, skiaGlyph, newGlyph);
276
277 return newGlyph;
278}
279
Romain Guy2577db12011-01-18 13:02:38 -0800280Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
281 int flags, uint32_t italicStyle) {
Romain Guy694b5192010-07-21 21:33:20 -0700282 Vector<Font*> &activeFonts = state->mActiveFonts;
283
284 for (uint32_t i = 0; i < activeFonts.size(); i++) {
Romain Guy51769a62010-07-23 00:28:00 -0700285 Font* font = activeFonts[i];
Romain Guy2577db12011-01-18 13:02:38 -0800286 if (font->mFontId == fontId && font->mFontSize == fontSize &&
287 font->mFlags == flags && font->mItalicStyle == italicStyle) {
Romain Guy51769a62010-07-23 00:28:00 -0700288 return font;
Romain Guy694b5192010-07-21 21:33:20 -0700289 }
290 }
291
Romain Guy2577db12011-01-18 13:02:38 -0800292 Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle);
Romain Guy694b5192010-07-21 21:33:20 -0700293 activeFonts.push(newFont);
294 return newFont;
295}
296
297///////////////////////////////////////////////////////////////////////////////
298// FontRenderer
299///////////////////////////////////////////////////////////////////////////////
300
301FontRenderer::FontRenderer() {
Romain Guy51769a62010-07-23 00:28:00 -0700302 LOGD("Creating FontRenderer");
303
Romain Guyb45c0c92010-08-26 20:35:23 -0700304 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700305 mInitialized = false;
306 mMaxNumberOfQuads = 1024;
307 mCurrentQuadIndex = 0;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700308 mTextureId = 0;
Romain Guy694b5192010-07-21 21:33:20 -0700309
Romain Guy9cccc2b92010-08-07 23:46:15 -0700310 mTextMeshPtr = NULL;
311 mTextTexture = NULL;
312
Romain Guy694b5192010-07-21 21:33:20 -0700313 mIndexBufferID = 0;
314
Romain Guy51769a62010-07-23 00:28:00 -0700315 mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700316 mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -0700317
318 char property[PROPERTY_VALUE_MAX];
319 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
320 LOGD(" Setting text cache width to %s pixels", property);
321 mCacheWidth = atoi(property);
322 } else {
323 LOGD(" Using default text cache width of %i pixels", mCacheWidth);
324 }
325
326 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
327 LOGD(" Setting text cache width to %s pixels", property);
328 mCacheHeight = atoi(property);
329 } else {
330 LOGD(" Using default text cache height of %i pixels", mCacheHeight);
331 }
Romain Guy694b5192010-07-21 21:33:20 -0700332}
333
334FontRenderer::~FontRenderer() {
335 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
336 delete mCacheLines[i];
337 }
338 mCacheLines.clear();
339
Romain Guy9cccc2b92010-08-07 23:46:15 -0700340 if (mInitialized) {
341 delete[] mTextMeshPtr;
342 delete[] mTextTexture;
343 }
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700344
Romain Guy9cccc2b92010-08-07 23:46:15 -0700345 if (mTextureId) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700346 glDeleteTextures(1, &mTextureId);
347 }
Romain Guy694b5192010-07-21 21:33:20 -0700348
349 Vector<Font*> fontsToDereference = mActiveFonts;
350 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
351 delete fontsToDereference[i];
352 }
353}
354
355void FontRenderer::flushAllAndInvalidate() {
356 if (mCurrentQuadIndex != 0) {
357 issueDrawCommand();
358 mCurrentQuadIndex = 0;
359 }
360 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
361 mActiveFonts[i]->invalidateTextureCache();
362 }
363 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
364 mCacheLines[i]->mCurrentCol = 0;
365 }
366}
367
Romain Guy51769a62010-07-23 00:28:00 -0700368bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
Romain Guy694b5192010-07-21 21:33:20 -0700369 // If the glyph is too tall, don't cache it
Romain Guy2bffd262010-09-12 17:40:02 -0700370 if (glyph.fHeight > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
Romain Guy694b5192010-07-21 21:33:20 -0700371 LOGE("Font size to large to fit in cache. width, height = %i, %i",
372 (int) glyph.fWidth, (int) glyph.fHeight);
373 return false;
374 }
375
376 // Now copy the bitmap into the cache texture
377 uint32_t startX = 0;
378 uint32_t startY = 0;
379
380 bool bitmapFit = false;
381 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
382 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
383 if (bitmapFit) {
384 break;
385 }
386 }
387
388 // If the new glyph didn't fit, flush the state so far and invalidate everything
389 if (!bitmapFit) {
390 flushAllAndInvalidate();
391
392 // Try to fit it again
393 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
394 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
395 if (bitmapFit) {
396 break;
397 }
398 }
399
400 // if we still don't fit, something is wrong and we shouldn't draw
401 if (!bitmapFit) {
402 LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
403 (int) glyph.fWidth, (int) glyph.fHeight);
404 return false;
405 }
406 }
407
408 *retOriginX = startX;
409 *retOriginY = startY;
410
411 uint32_t endX = startX + glyph.fWidth;
412 uint32_t endY = startY + glyph.fHeight;
413
414 uint32_t cacheWidth = mCacheWidth;
415
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700416 uint8_t* cacheBuffer = mTextTexture;
417 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700418 unsigned int stride = glyph.rowBytes();
419
420 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
421 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
422 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700423 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
Romain Guyb45c0c92010-08-26 20:35:23 -0700424 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
Romain Guy694b5192010-07-21 21:33:20 -0700425 }
426 }
427
428 return true;
429}
430
431void FontRenderer::initTextTexture() {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700432 mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
Romain Guy1de10832010-10-03 14:37:09 -0700433 memset(mTextTexture, 0, mCacheWidth * mCacheHeight * sizeof(uint8_t));
434
Romain Guy694b5192010-07-21 21:33:20 -0700435 mUploadTexture = false;
436
437 glGenTextures(1, &mTextureId);
438 glBindTexture(GL_TEXTURE_2D, mTextureId);
439 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700440 // Initialize texture dimentions
441 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700442 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Romain Guy694b5192010-07-21 21:33:20 -0700443
Romain Guye8cb9c142010-10-04 14:14:11 -0700444 mLinearFiltering = false;
445 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
446 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
Romain Guy694b5192010-07-21 21:33:20 -0700447
448 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
449 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
450
451 // Split up our cache texture into lines of certain widths
452 int nextLine = 0;
Romain Guy7975fb62010-10-01 16:36:14 -0700453 mCacheLines.push(new CacheTextureLine(mCacheWidth, 18, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700454 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700455 mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700456 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700457 mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700458 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700459 mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700460 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700461 mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700462 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700463 mCacheLines.push(new CacheTextureLine(mCacheWidth, 42, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700464 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700465 mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700466}
467
468// Avoid having to reallocate memory and render quad by quad
469void FontRenderer::initVertexArrayBuffers() {
470 uint32_t numIndicies = mMaxNumberOfQuads * 6;
471 uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700472 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700473
474 // Four verts, two triangles , six indices per quad
475 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
476 int i6 = i * 6;
477 int i4 = i * 4;
478
479 indexBufferData[i6 + 0] = i4 + 0;
480 indexBufferData[i6 + 1] = i4 + 1;
481 indexBufferData[i6 + 2] = i4 + 2;
482
483 indexBufferData[i6 + 3] = i4 + 0;
484 indexBufferData[i6 + 4] = i4 + 2;
485 indexBufferData[i6 + 5] = i4 + 3;
486 }
487
488 glGenBuffers(1, &mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700489 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
490 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
491 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
Romain Guy694b5192010-07-21 21:33:20 -0700492
493 free(indexBufferData);
494
495 uint32_t coordSize = 3;
496 uint32_t uvSize = 2;
497 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700498 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
499 mTextMeshPtr = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700500}
501
502// We don't want to allocate anything unless we actually draw text
503void FontRenderer::checkInit() {
504 if (mInitialized) {
505 return;
506 }
507
508 initTextTexture();
509 initVertexArrayBuffers();
510
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700511 // We store a string with letters in a rough frequency of occurrence
512 mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
513 mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
514 mLatinPrecache += String16(",.?!()-+@;:`'");
515 mLatinPrecache += String16("0123456789");
516
Romain Guy694b5192010-07-21 21:33:20 -0700517 mInitialized = true;
518}
519
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700520void FontRenderer::checkTextureUpdate() {
521 if (!mUploadTexture) {
522 return;
Romain Guy694b5192010-07-21 21:33:20 -0700523 }
524
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700525 glBindTexture(GL_TEXTURE_2D, mTextureId);
526
527 // Iterate over all the cache lines and see which ones need to be updated
528 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
529 CacheTextureLine* cl = mCacheLines[i];
530 if(cl->mDirty) {
531 uint32_t xOffset = 0;
532 uint32_t yOffset = cl->mCurrentRow;
533 uint32_t width = mCacheWidth;
534 uint32_t height = cl->mMaxHeight;
Romain Guy1e45aae2010-08-13 19:39:53 -0700535 void* textureData = mTextTexture + yOffset*width;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700536
537 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700538 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700539
540 cl->mDirty = false;
541 }
542 }
543
544 mUploadTexture = false;
545}
546
547void FontRenderer::issueDrawCommand() {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700548 checkTextureUpdate();
549
Romain Guy51769a62010-07-23 00:28:00 -0700550 float* vtx = mTextMeshPtr;
551 float* tex = vtx + 3;
Romain Guy694b5192010-07-21 21:33:20 -0700552
553 // position is slot 0
554 uint32_t slot = 0;
555 glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx);
556
557 // texture0 is slot 1
558 slot = 1;
559 glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex);
560
561 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
562 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
Romain Guy5b3b3522010-10-27 18:57:51 -0700563
564 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700565}
566
567void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
568 float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
569 float x4, float y4, float z4, float u4, float v4) {
Romain Guy09147fb2010-07-22 13:08:20 -0700570 if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) {
571 return;
572 }
573
Romain Guy694b5192010-07-21 21:33:20 -0700574 const uint32_t vertsPerQuad = 4;
575 const uint32_t floatsPerVert = 5;
Romain Guy51769a62010-07-23 00:28:00 -0700576 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700577
Romain Guy694b5192010-07-21 21:33:20 -0700578 (*currentPos++) = x1;
579 (*currentPos++) = y1;
580 (*currentPos++) = z1;
581 (*currentPos++) = u1;
582 (*currentPos++) = v1;
583
584 (*currentPos++) = x2;
585 (*currentPos++) = y2;
586 (*currentPos++) = z2;
587 (*currentPos++) = u2;
588 (*currentPos++) = v2;
589
590 (*currentPos++) = x3;
591 (*currentPos++) = y3;
592 (*currentPos++) = z3;
593 (*currentPos++) = u3;
594 (*currentPos++) = v3;
595
596 (*currentPos++) = x4;
597 (*currentPos++) = y4;
598 (*currentPos++) = z4;
599 (*currentPos++) = u4;
600 (*currentPos++) = v4;
601
602 mCurrentQuadIndex++;
603
Romain Guy5b3b3522010-10-27 18:57:51 -0700604 if (mBounds) {
605 mBounds->left = fmin(mBounds->left, x1);
606 mBounds->top = fmin(mBounds->top, y3);
607 mBounds->right = fmax(mBounds->right, x3);
608 mBounds->bottom = fmax(mBounds->bottom, y1);
609 }
610
Romain Guy694b5192010-07-21 21:33:20 -0700611 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
612 issueDrawCommand();
613 mCurrentQuadIndex = 0;
614 }
615}
616
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700617uint32_t FontRenderer::getRemainingCacheCapacity() {
618 uint32_t remainingCapacity = 0;
619 float totalPixels = 0;
620 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
621 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
622 totalPixels += mCacheLines[i]->mMaxWidth;
623 }
624 remainingCapacity = (remainingCapacity * 100) / totalPixels;
625 return remainingCapacity;
626}
627
628void FontRenderer::precacheLatin(SkPaint* paint) {
629 // Remaining capacity is measured in %
630 uint32_t remainingCapacity = getRemainingCacheCapacity();
631 uint32_t precacheIdx = 0;
Romain Guy054dc182010-10-15 17:55:25 -0700632 while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
633 mCurrentFont->getCachedUTFChar(paint, (int32_t) mLatinPrecache[precacheIdx]);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700634 remainingCapacity = getRemainingCacheCapacity();
635 precacheIdx ++;
636 }
637}
638
639void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
640 uint32_t currentNumFonts = mActiveFonts.size();
Romain Guy325a0f92011-01-05 15:26:55 -0800641 int flags = 0;
642 if (paint->isFakeBoldText()) {
643 flags |= Font::kFakeBold;
644 }
Romain Guy2577db12011-01-18 13:02:38 -0800645
646 const float skewX = paint->getTextSkewX();
647 uint32_t italicStyle = *(uint32_t*) &skewX;
648 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700649
650 const float maxPrecacheFontSize = 40.0f;
651 bool isNewFont = currentNumFonts != mActiveFonts.size();
652
Romain Guy2bffd262010-09-12 17:40:02 -0700653 if (isNewFont && fontSize <= maxPrecacheFontSize) {
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700654 precacheLatin(paint);
655 }
Romain Guy694b5192010-07-21 21:33:20 -0700656}
Romain Guy7975fb62010-10-01 16:36:14 -0700657
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700658FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Romain Guy1e45aae2010-08-13 19:39:53 -0700659 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
660 checkInit();
661
662 if (!mCurrentFont) {
663 DropShadow image;
664 image.width = 0;
665 image.height = 0;
666 image.image = NULL;
667 image.penX = 0;
668 image.penY = 0;
669 return image;
670 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700671
672 Rect bounds;
673 mCurrentFont->measureUTF(paint, text, startIndex, len, numGlyphs, &bounds);
Romain Guy1e45aae2010-08-13 19:39:53 -0700674 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
675 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700676 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guy1e45aae2010-08-13 19:39:53 -0700677 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700678 dataBuffer[i] = 0;
679 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700680
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700681 int penX = radius - bounds.left;
682 int penY = radius - bounds.bottom;
683
684 mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, penX, penY,
Romain Guy1e45aae2010-08-13 19:39:53 -0700685 dataBuffer, paddedWidth, paddedHeight);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700686 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
687
688 DropShadow image;
689 image.width = paddedWidth;
690 image.height = paddedHeight;
691 image.image = dataBuffer;
692 image.penX = penX;
693 image.penY = penY;
694 return image;
695}
Romain Guy694b5192010-07-21 21:33:20 -0700696
Romain Guy5b3b3522010-10-27 18:57:51 -0700697bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
698 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700699 checkInit();
700
Romain Guy09147fb2010-07-22 13:08:20 -0700701 if (!mCurrentFont) {
702 LOGE("No font set");
Romain Guy5b3b3522010-10-27 18:57:51 -0700703 return false;
Romain Guy694b5192010-07-21 21:33:20 -0700704 }
705
Romain Guy5b3b3522010-10-27 18:57:51 -0700706 mDrawn = false;
707 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -0700708 mClip = clip;
Romain Guy51769a62010-07-23 00:28:00 -0700709 mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y);
Romain Guy5b3b3522010-10-27 18:57:51 -0700710 mBounds = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700711
712 if (mCurrentQuadIndex != 0) {
713 issueDrawCommand();
714 mCurrentQuadIndex = 0;
715 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700716
717 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700718}
719
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700720void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
721 // Compute gaussian weights for the blur
722 // e is the euler's number
723 float e = 2.718281828459045f;
724 float pi = 3.1415926535897932f;
725 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
726 // x is of the form [-radius .. 0 .. radius]
727 // and sigma varies with radius.
728 // Based on some experimental radius values and sigma's
729 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700730 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700731 // The larger the radius gets, the more our gaussian blur
732 // will resemble a box blur since with large sigma
733 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -0800734 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700735
736 // Now compute the coefficints
737 // We will store some redundant values to save some math during
738 // the blur calculations
739 // precompute some values
740 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
741 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
742
743 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -0800744 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700745 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700746 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
747 normalizeFactor += weights[r + radius];
748 }
749
750 //Now we need to normalize the weights because all our coefficients need to add up to one
751 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -0800752 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700753 weights[r + radius] *= normalizeFactor;
754 }
755}
756
757void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700758 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700759 float blurredPixel = 0.0f;
760 float currentPixel = 0.0f;
761
Romain Guy325a0f92011-01-05 15:26:55 -0800762 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700763
764 const uint8_t* input = source + y * width;
765 uint8_t* output = dest + y * width;
766
Romain Guy325a0f92011-01-05 15:26:55 -0800767 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700768 blurredPixel = 0.0f;
769 const float* gPtr = weights;
770 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800771 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700772 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -0800773 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700774 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700775 blurredPixel += currentPixel * gPtr[0];
776 gPtr++;
777 i++;
778 }
779 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800780 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700781 // Stepping left and right away from the pixel
782 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -0800783 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700784 validW = 0;
785 }
Romain Guy325a0f92011-01-05 15:26:55 -0800786 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700787 validW = width - 1;
788 }
789
Romain Guy325a0f92011-01-05 15:26:55 -0800790 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700791 blurredPixel += currentPixel * gPtr[0];
792 gPtr++;
793 }
794 }
795 *output = (uint8_t)blurredPixel;
796 output ++;
797 }
798 }
799}
800
801void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700802 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700803 float blurredPixel = 0.0f;
804 float currentPixel = 0.0f;
805
Romain Guy325a0f92011-01-05 15:26:55 -0800806 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700807
808 uint8_t* output = dest + y * width;
809
Romain Guy325a0f92011-01-05 15:26:55 -0800810 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700811 blurredPixel = 0.0f;
812 const float* gPtr = weights;
813 const uint8_t* input = source + x;
814 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800815 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700816 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -0800817 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700818 currentPixel = (float)(*i);
819 blurredPixel += currentPixel * gPtr[0];
820 gPtr++;
821 i += width;
822 }
823 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800824 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700825 int validH = y + r;
826 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -0800827 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700828 validH = 0;
829 }
Romain Guy325a0f92011-01-05 15:26:55 -0800830 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700831 validH = height - 1;
832 }
833
834 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -0800835 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700836 blurredPixel += currentPixel * gPtr[0];
837 gPtr++;
838 }
839 }
Romain Guy325a0f92011-01-05 15:26:55 -0800840 *output = (uint8_t) blurredPixel;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700841 output ++;
842 }
843 }
844}
845
846
847void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
848 float *gaussian = new float[2 * radius + 1];
849 computeGaussianWeights(gaussian, radius);
850 uint8_t* scratch = new uint8_t[width * height];
851 horizontalBlur(gaussian, radius, image, scratch, width, height);
852 verticalBlur(gaussian, radius, scratch, image, width, height);
853 delete[] gaussian;
854 delete[] scratch;
855}
856
Romain Guy694b5192010-07-21 21:33:20 -0700857}; // namespace uirenderer
858}; // namespace android