blob: c43e40dfcedda1a5e7c2f38b0f3bf820e4107913 [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
Romain Guy514fb182011-01-19 14:38:29 -0800301static bool sLogFontRendererCreate = true;
302
Romain Guy694b5192010-07-21 21:33:20 -0700303FontRenderer::FontRenderer() {
Romain Guy514fb182011-01-19 14:38:29 -0800304 if (sLogFontRendererCreate) LOGD("Creating FontRenderer");
Romain Guy51769a62010-07-23 00:28:00 -0700305
Romain Guyb45c0c92010-08-26 20:35:23 -0700306 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700307 mInitialized = false;
308 mMaxNumberOfQuads = 1024;
309 mCurrentQuadIndex = 0;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700310 mTextureId = 0;
Romain Guy694b5192010-07-21 21:33:20 -0700311
Romain Guy9cccc2b92010-08-07 23:46:15 -0700312 mTextMeshPtr = NULL;
313 mTextTexture = NULL;
314
Romain Guy694b5192010-07-21 21:33:20 -0700315 mIndexBufferID = 0;
316
Romain Guy51769a62010-07-23 00:28:00 -0700317 mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700318 mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -0700319
320 char property[PROPERTY_VALUE_MAX];
321 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
Romain Guy514fb182011-01-19 14:38:29 -0800322 if (sLogFontRendererCreate) LOGD(" Setting text cache width to %s pixels", property);
Romain Guy51769a62010-07-23 00:28:00 -0700323 mCacheWidth = atoi(property);
324 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800325 if (sLogFontRendererCreate) {
326 LOGD(" Using default text cache width of %i pixels", mCacheWidth);
327 }
Romain Guy51769a62010-07-23 00:28:00 -0700328 }
329
330 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
Romain Guy514fb182011-01-19 14:38:29 -0800331 if (sLogFontRendererCreate) LOGD(" Setting text cache width to %s pixels", property);
Romain Guy51769a62010-07-23 00:28:00 -0700332 mCacheHeight = atoi(property);
333 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800334 if (sLogFontRendererCreate) {
335 LOGD(" Using default text cache height of %i pixels", mCacheHeight);
336 }
Romain Guy51769a62010-07-23 00:28:00 -0700337 }
Romain Guy514fb182011-01-19 14:38:29 -0800338
339 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -0700340}
341
342FontRenderer::~FontRenderer() {
343 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
344 delete mCacheLines[i];
345 }
346 mCacheLines.clear();
347
Romain Guy9cccc2b92010-08-07 23:46:15 -0700348 if (mInitialized) {
349 delete[] mTextMeshPtr;
350 delete[] mTextTexture;
351 }
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700352
Romain Guy9cccc2b92010-08-07 23:46:15 -0700353 if (mTextureId) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700354 glDeleteTextures(1, &mTextureId);
355 }
Romain Guy694b5192010-07-21 21:33:20 -0700356
357 Vector<Font*> fontsToDereference = mActiveFonts;
358 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
359 delete fontsToDereference[i];
360 }
361}
362
363void FontRenderer::flushAllAndInvalidate() {
364 if (mCurrentQuadIndex != 0) {
365 issueDrawCommand();
366 mCurrentQuadIndex = 0;
367 }
368 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
369 mActiveFonts[i]->invalidateTextureCache();
370 }
371 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
372 mCacheLines[i]->mCurrentCol = 0;
373 }
374}
375
Romain Guy51769a62010-07-23 00:28:00 -0700376bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
Romain Guy694b5192010-07-21 21:33:20 -0700377 // If the glyph is too tall, don't cache it
Romain Guy2bffd262010-09-12 17:40:02 -0700378 if (glyph.fHeight > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
Romain Guy694b5192010-07-21 21:33:20 -0700379 LOGE("Font size to large to fit in cache. width, height = %i, %i",
380 (int) glyph.fWidth, (int) glyph.fHeight);
381 return false;
382 }
383
384 // Now copy the bitmap into the cache texture
385 uint32_t startX = 0;
386 uint32_t startY = 0;
387
388 bool bitmapFit = false;
389 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
390 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
391 if (bitmapFit) {
392 break;
393 }
394 }
395
396 // If the new glyph didn't fit, flush the state so far and invalidate everything
397 if (!bitmapFit) {
398 flushAllAndInvalidate();
399
400 // Try to fit it again
401 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
402 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
403 if (bitmapFit) {
404 break;
405 }
406 }
407
408 // if we still don't fit, something is wrong and we shouldn't draw
409 if (!bitmapFit) {
410 LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
411 (int) glyph.fWidth, (int) glyph.fHeight);
412 return false;
413 }
414 }
415
416 *retOriginX = startX;
417 *retOriginY = startY;
418
419 uint32_t endX = startX + glyph.fWidth;
420 uint32_t endY = startY + glyph.fHeight;
421
422 uint32_t cacheWidth = mCacheWidth;
423
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700424 uint8_t* cacheBuffer = mTextTexture;
425 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700426 unsigned int stride = glyph.rowBytes();
427
428 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
429 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
430 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700431 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
Romain Guyb45c0c92010-08-26 20:35:23 -0700432 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
Romain Guy694b5192010-07-21 21:33:20 -0700433 }
434 }
435
436 return true;
437}
438
439void FontRenderer::initTextTexture() {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700440 mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
Romain Guy1de10832010-10-03 14:37:09 -0700441 memset(mTextTexture, 0, mCacheWidth * mCacheHeight * sizeof(uint8_t));
442
Romain Guy694b5192010-07-21 21:33:20 -0700443 mUploadTexture = false;
444
445 glGenTextures(1, &mTextureId);
446 glBindTexture(GL_TEXTURE_2D, mTextureId);
447 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700448 // Initialize texture dimentions
449 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700450 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Romain Guy694b5192010-07-21 21:33:20 -0700451
Romain Guye8cb9c142010-10-04 14:14:11 -0700452 mLinearFiltering = false;
453 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
454 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
Romain Guy694b5192010-07-21 21:33:20 -0700455
456 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
457 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
458
459 // Split up our cache texture into lines of certain widths
460 int nextLine = 0;
Romain Guy7975fb62010-10-01 16:36:14 -0700461 mCacheLines.push(new CacheTextureLine(mCacheWidth, 18, 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, 26, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700464 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700465 mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700466 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700467 mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700468 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700469 mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700470 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700471 mCacheLines.push(new CacheTextureLine(mCacheWidth, 42, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700472 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700473 mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700474}
475
476// Avoid having to reallocate memory and render quad by quad
477void FontRenderer::initVertexArrayBuffers() {
478 uint32_t numIndicies = mMaxNumberOfQuads * 6;
479 uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700480 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700481
482 // Four verts, two triangles , six indices per quad
483 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
484 int i6 = i * 6;
485 int i4 = i * 4;
486
487 indexBufferData[i6 + 0] = i4 + 0;
488 indexBufferData[i6 + 1] = i4 + 1;
489 indexBufferData[i6 + 2] = i4 + 2;
490
491 indexBufferData[i6 + 3] = i4 + 0;
492 indexBufferData[i6 + 4] = i4 + 2;
493 indexBufferData[i6 + 5] = i4 + 3;
494 }
495
496 glGenBuffers(1, &mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700497 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
498 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
499 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
Romain Guy694b5192010-07-21 21:33:20 -0700500
501 free(indexBufferData);
502
503 uint32_t coordSize = 3;
504 uint32_t uvSize = 2;
505 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700506 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
507 mTextMeshPtr = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700508}
509
510// We don't want to allocate anything unless we actually draw text
511void FontRenderer::checkInit() {
512 if (mInitialized) {
513 return;
514 }
515
516 initTextTexture();
517 initVertexArrayBuffers();
518
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700519 // We store a string with letters in a rough frequency of occurrence
520 mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
521 mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
522 mLatinPrecache += String16(",.?!()-+@;:`'");
523 mLatinPrecache += String16("0123456789");
524
Romain Guy694b5192010-07-21 21:33:20 -0700525 mInitialized = true;
526}
527
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700528void FontRenderer::checkTextureUpdate() {
529 if (!mUploadTexture) {
530 return;
Romain Guy694b5192010-07-21 21:33:20 -0700531 }
532
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700533 glBindTexture(GL_TEXTURE_2D, mTextureId);
534
535 // Iterate over all the cache lines and see which ones need to be updated
536 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
537 CacheTextureLine* cl = mCacheLines[i];
538 if(cl->mDirty) {
539 uint32_t xOffset = 0;
540 uint32_t yOffset = cl->mCurrentRow;
541 uint32_t width = mCacheWidth;
542 uint32_t height = cl->mMaxHeight;
Romain Guy1e45aae2010-08-13 19:39:53 -0700543 void* textureData = mTextTexture + yOffset*width;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700544
545 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700546 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700547
548 cl->mDirty = false;
549 }
550 }
551
552 mUploadTexture = false;
553}
554
555void FontRenderer::issueDrawCommand() {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700556 checkTextureUpdate();
557
Romain Guy51769a62010-07-23 00:28:00 -0700558 float* vtx = mTextMeshPtr;
559 float* tex = vtx + 3;
Romain Guy694b5192010-07-21 21:33:20 -0700560
561 // position is slot 0
562 uint32_t slot = 0;
563 glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx);
564
565 // texture0 is slot 1
566 slot = 1;
567 glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex);
568
569 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
570 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
Romain Guy5b3b3522010-10-27 18:57:51 -0700571
572 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700573}
574
575void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
576 float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
577 float x4, float y4, float z4, float u4, float v4) {
Romain Guy09147fb2010-07-22 13:08:20 -0700578 if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) {
579 return;
580 }
581
Romain Guy694b5192010-07-21 21:33:20 -0700582 const uint32_t vertsPerQuad = 4;
583 const uint32_t floatsPerVert = 5;
Romain Guy51769a62010-07-23 00:28:00 -0700584 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700585
Romain Guy694b5192010-07-21 21:33:20 -0700586 (*currentPos++) = x1;
587 (*currentPos++) = y1;
588 (*currentPos++) = z1;
589 (*currentPos++) = u1;
590 (*currentPos++) = v1;
591
592 (*currentPos++) = x2;
593 (*currentPos++) = y2;
594 (*currentPos++) = z2;
595 (*currentPos++) = u2;
596 (*currentPos++) = v2;
597
598 (*currentPos++) = x3;
599 (*currentPos++) = y3;
600 (*currentPos++) = z3;
601 (*currentPos++) = u3;
602 (*currentPos++) = v3;
603
604 (*currentPos++) = x4;
605 (*currentPos++) = y4;
606 (*currentPos++) = z4;
607 (*currentPos++) = u4;
608 (*currentPos++) = v4;
609
610 mCurrentQuadIndex++;
611
Romain Guy5b3b3522010-10-27 18:57:51 -0700612 if (mBounds) {
613 mBounds->left = fmin(mBounds->left, x1);
614 mBounds->top = fmin(mBounds->top, y3);
615 mBounds->right = fmax(mBounds->right, x3);
616 mBounds->bottom = fmax(mBounds->bottom, y1);
617 }
618
Romain Guy694b5192010-07-21 21:33:20 -0700619 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
620 issueDrawCommand();
621 mCurrentQuadIndex = 0;
622 }
623}
624
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700625uint32_t FontRenderer::getRemainingCacheCapacity() {
626 uint32_t remainingCapacity = 0;
627 float totalPixels = 0;
628 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
629 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
630 totalPixels += mCacheLines[i]->mMaxWidth;
631 }
632 remainingCapacity = (remainingCapacity * 100) / totalPixels;
633 return remainingCapacity;
634}
635
636void FontRenderer::precacheLatin(SkPaint* paint) {
637 // Remaining capacity is measured in %
638 uint32_t remainingCapacity = getRemainingCacheCapacity();
639 uint32_t precacheIdx = 0;
Romain Guy054dc182010-10-15 17:55:25 -0700640 while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
641 mCurrentFont->getCachedUTFChar(paint, (int32_t) mLatinPrecache[precacheIdx]);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700642 remainingCapacity = getRemainingCacheCapacity();
643 precacheIdx ++;
644 }
645}
646
647void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
648 uint32_t currentNumFonts = mActiveFonts.size();
Romain Guy325a0f92011-01-05 15:26:55 -0800649 int flags = 0;
650 if (paint->isFakeBoldText()) {
651 flags |= Font::kFakeBold;
652 }
Romain Guy2577db12011-01-18 13:02:38 -0800653
654 const float skewX = paint->getTextSkewX();
655 uint32_t italicStyle = *(uint32_t*) &skewX;
656 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700657
658 const float maxPrecacheFontSize = 40.0f;
659 bool isNewFont = currentNumFonts != mActiveFonts.size();
660
Romain Guy2bffd262010-09-12 17:40:02 -0700661 if (isNewFont && fontSize <= maxPrecacheFontSize) {
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700662 precacheLatin(paint);
663 }
Romain Guy694b5192010-07-21 21:33:20 -0700664}
Romain Guy7975fb62010-10-01 16:36:14 -0700665
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700666FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Romain Guy1e45aae2010-08-13 19:39:53 -0700667 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
668 checkInit();
669
670 if (!mCurrentFont) {
671 DropShadow image;
672 image.width = 0;
673 image.height = 0;
674 image.image = NULL;
675 image.penX = 0;
676 image.penY = 0;
677 return image;
678 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700679
680 Rect bounds;
681 mCurrentFont->measureUTF(paint, text, startIndex, len, numGlyphs, &bounds);
Romain Guy1e45aae2010-08-13 19:39:53 -0700682 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
683 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700684 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guy1e45aae2010-08-13 19:39:53 -0700685 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700686 dataBuffer[i] = 0;
687 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700688
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700689 int penX = radius - bounds.left;
690 int penY = radius - bounds.bottom;
691
692 mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, penX, penY,
Romain Guy1e45aae2010-08-13 19:39:53 -0700693 dataBuffer, paddedWidth, paddedHeight);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700694 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
695
696 DropShadow image;
697 image.width = paddedWidth;
698 image.height = paddedHeight;
699 image.image = dataBuffer;
700 image.penX = penX;
701 image.penY = penY;
702 return image;
703}
Romain Guy694b5192010-07-21 21:33:20 -0700704
Romain Guy5b3b3522010-10-27 18:57:51 -0700705bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
706 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700707 checkInit();
708
Romain Guy09147fb2010-07-22 13:08:20 -0700709 if (!mCurrentFont) {
710 LOGE("No font set");
Romain Guy5b3b3522010-10-27 18:57:51 -0700711 return false;
Romain Guy694b5192010-07-21 21:33:20 -0700712 }
713
Romain Guy5b3b3522010-10-27 18:57:51 -0700714 mDrawn = false;
715 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -0700716 mClip = clip;
Romain Guy51769a62010-07-23 00:28:00 -0700717 mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y);
Romain Guy5b3b3522010-10-27 18:57:51 -0700718 mBounds = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700719
720 if (mCurrentQuadIndex != 0) {
721 issueDrawCommand();
722 mCurrentQuadIndex = 0;
723 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700724
725 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700726}
727
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700728void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
729 // Compute gaussian weights for the blur
730 // e is the euler's number
731 float e = 2.718281828459045f;
732 float pi = 3.1415926535897932f;
733 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
734 // x is of the form [-radius .. 0 .. radius]
735 // and sigma varies with radius.
736 // Based on some experimental radius values and sigma's
737 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700738 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700739 // The larger the radius gets, the more our gaussian blur
740 // will resemble a box blur since with large sigma
741 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -0800742 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700743
744 // Now compute the coefficints
745 // We will store some redundant values to save some math during
746 // the blur calculations
747 // precompute some values
748 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
749 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
750
751 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -0800752 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700753 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700754 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
755 normalizeFactor += weights[r + radius];
756 }
757
758 //Now we need to normalize the weights because all our coefficients need to add up to one
759 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -0800760 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700761 weights[r + radius] *= normalizeFactor;
762 }
763}
764
765void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700766 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700767 float blurredPixel = 0.0f;
768 float currentPixel = 0.0f;
769
Romain Guy325a0f92011-01-05 15:26:55 -0800770 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700771
772 const uint8_t* input = source + y * width;
773 uint8_t* output = dest + y * width;
774
Romain Guy325a0f92011-01-05 15:26:55 -0800775 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700776 blurredPixel = 0.0f;
777 const float* gPtr = weights;
778 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800779 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700780 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -0800781 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700782 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700783 blurredPixel += currentPixel * gPtr[0];
784 gPtr++;
785 i++;
786 }
787 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800788 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700789 // Stepping left and right away from the pixel
790 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -0800791 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700792 validW = 0;
793 }
Romain Guy325a0f92011-01-05 15:26:55 -0800794 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700795 validW = width - 1;
796 }
797
Romain Guy325a0f92011-01-05 15:26:55 -0800798 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700799 blurredPixel += currentPixel * gPtr[0];
800 gPtr++;
801 }
802 }
803 *output = (uint8_t)blurredPixel;
804 output ++;
805 }
806 }
807}
808
809void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700810 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700811 float blurredPixel = 0.0f;
812 float currentPixel = 0.0f;
813
Romain Guy325a0f92011-01-05 15:26:55 -0800814 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700815
816 uint8_t* output = dest + y * width;
817
Romain Guy325a0f92011-01-05 15:26:55 -0800818 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700819 blurredPixel = 0.0f;
820 const float* gPtr = weights;
821 const uint8_t* input = source + x;
822 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800823 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700824 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -0800825 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700826 currentPixel = (float)(*i);
827 blurredPixel += currentPixel * gPtr[0];
828 gPtr++;
829 i += width;
830 }
831 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800832 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700833 int validH = y + r;
834 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -0800835 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700836 validH = 0;
837 }
Romain Guy325a0f92011-01-05 15:26:55 -0800838 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700839 validH = height - 1;
840 }
841
842 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -0800843 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700844 blurredPixel += currentPixel * gPtr[0];
845 gPtr++;
846 }
847 }
Romain Guy325a0f92011-01-05 15:26:55 -0800848 *output = (uint8_t) blurredPixel;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700849 output ++;
850 }
851 }
852}
853
854
855void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
856 float *gaussian = new float[2 * radius + 1];
857 computeGaussianWeights(gaussian, radius);
858 uint8_t* scratch = new uint8_t[width * height];
859 horizontalBlur(gaussian, radius, image, scratch, width, height);
860 verticalBlur(gaussian, radius, scratch, image, width, height);
861 delete[] gaussian;
862 delete[] scratch;
863}
864
Romain Guy694b5192010-07-21 21:33:20 -0700865}; // namespace uirenderer
866}; // namespace android