blob: 1ca0a19f14f31c33c76cd13444e4e9b977e3fb9e [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
Chet Haase44984ea2011-05-19 13:50:47 -070038#define MAX_TEXT_CACHE_WIDTH 2048
39#define MAX_TEXT_CACHE_HEIGHT 2048
40
Romain Guy51769a62010-07-23 00:28:00 -070041///////////////////////////////////////////////////////////////////////////////
Romain Guy694b5192010-07-21 21:33:20 -070042// Font
43///////////////////////////////////////////////////////////////////////////////
44
Romain Guy2577db12011-01-18 13:02:38 -080045Font::Font(FontRenderer* state, uint32_t fontId, float fontSize,
Chet Haase8668f8a2011-03-02 13:51:36 -080046 int flags, uint32_t italicStyle, uint32_t scaleX) :
Romain Guy2577db12011-01-18 13:02:38 -080047 mState(state), mFontId(fontId), mFontSize(fontSize),
Chet Haase8668f8a2011-03-02 13:51:36 -080048 mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX) {
Romain Guy694b5192010-07-21 21:33:20 -070049}
50
51
52Font::~Font() {
53 for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
54 if (mState->mActiveFonts[ct] == this) {
55 mState->mActiveFonts.removeAt(ct);
56 break;
57 }
58 }
59
60 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
Romain Guy51769a62010-07-23 00:28:00 -070061 CachedGlyphInfo* glyph = mCachedGlyphs.valueAt(i);
Romain Guy694b5192010-07-21 21:33:20 -070062 delete glyph;
63 }
64}
65
66void Font::invalidateTextureCache() {
67 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
68 mCachedGlyphs.valueAt(i)->mIsValid = false;
69 }
70}
71
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070072void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds) {
73 int nPenX = x + glyph->mBitmapLeft;
74 int nPenY = y + glyph->mBitmapTop;
75
76 int width = (int) glyph->mBitmapWidth;
77 int height = (int) glyph->mBitmapHeight;
78
Romain Guy61c8c9c2010-08-09 20:48:09 -070079 if (bounds->bottom > nPenY) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070080 bounds->bottom = nPenY;
81 }
Romain Guy61c8c9c2010-08-09 20:48:09 -070082 if (bounds->left > nPenX) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070083 bounds->left = nPenX;
84 }
Romain Guy61c8c9c2010-08-09 20:48:09 -070085 if (bounds->right < nPenX + width) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070086 bounds->right = nPenX + width;
87 }
Romain Guy61c8c9c2010-08-09 20:48:09 -070088 if (bounds->top < nPenY + height) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070089 bounds->top = nPenY + height;
90 }
91}
92
Romain Guy694b5192010-07-21 21:33:20 -070093void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
Romain Guy694b5192010-07-21 21:33:20 -070094 int nPenX = x + glyph->mBitmapLeft;
95 int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
96
Romain Guy51769a62010-07-23 00:28:00 -070097 float u1 = glyph->mBitmapMinU;
98 float u2 = glyph->mBitmapMaxU;
99 float v1 = glyph->mBitmapMinV;
100 float v2 = glyph->mBitmapMaxV;
101
102 int width = (int) glyph->mBitmapWidth;
103 int height = (int) glyph->mBitmapHeight;
104
105 mState->appendMeshQuad(nPenX, nPenY, 0, u1, v2,
106 nPenX + width, nPenY, 0, u2, v2,
107 nPenX + width, nPenY - height, 0, u2, v1,
108 nPenX, nPenY - height, 0, u1, v1);
Romain Guy694b5192010-07-21 21:33:20 -0700109}
110
Romain Guyb45c0c92010-08-26 20:35:23 -0700111void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
112 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700113 int nPenX = x + glyph->mBitmapLeft;
114 int nPenY = y + glyph->mBitmapTop;
115
116 uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
117 uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
118
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700119 uint32_t cacheWidth = mState->getCacheWidth();
120 const uint8_t* cacheBuffer = mState->getTextTextureData();
121
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700122 uint32_t cacheX = 0, cacheY = 0;
123 int32_t bX = 0, bY = 0;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700124 for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
125 for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
Romain Guyb45c0c92010-08-26 20:35:23 -0700126 if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700127 LOGE("Skipping invalid index");
128 continue;
129 }
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700130 uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
131 bitmap[bY * bitmapW + bX] = tempCol;
132 }
133 }
134
135}
136
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700137Font::CachedGlyphInfo* Font::getCachedUTFChar(SkPaint* paint, int32_t utfChar) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700138 CachedGlyphInfo* cachedGlyph = NULL;
139 ssize_t index = mCachedGlyphs.indexOfKey(utfChar);
140 if (index >= 0) {
141 cachedGlyph = mCachedGlyphs.valueAt(index);
142 } else {
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700143 cachedGlyph = cacheGlyph(paint, utfChar);
144 }
145
146 // Is the glyph still in texture cache?
147 if (!cachedGlyph->mIsValid) {
148 const SkGlyph& skiaGlyph = paint->getUnicharMetrics(utfChar);
149 updateGlyphCache(paint, skiaGlyph, cachedGlyph);
150 }
151
152 return cachedGlyph;
153}
154
Romain Guy51769a62010-07-23 00:28:00 -0700155void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700156 int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
157 if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
158 renderUTF(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
159 bitmapW, bitmapH, NULL);
160 } else {
161 renderUTF(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, 0, 0, NULL);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700162 }
163
164}
165
166void Font::measureUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700167 int numGlyphs, Rect *bounds) {
168 if (bounds == NULL) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700169 LOGE("No return rectangle provided to measure text");
170 return;
171 }
172 bounds->set(1e6, -1e6, -1e6, 1e6);
173 renderUTF(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds);
174}
175
Romain Guy58ef7fb2010-09-13 12:52:37 -0700176#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16)
Romain Guy2bffd262010-09-12 17:40:02 -0700177
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700178void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700179 int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
180 uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700181 if (numGlyphs == 0 || text == NULL || len == 0) {
182 return;
183 }
184
Romain Guy2bffd262010-09-12 17:40:02 -0700185 SkFixed penX = SkIntToFixed(x);
186 int penY = y;
Romain Guy694b5192010-07-21 21:33:20 -0700187 int glyphsLeft = 1;
188 if (numGlyphs > 0) {
189 glyphsLeft = numGlyphs;
190 }
191
Romain Guy2bffd262010-09-12 17:40:02 -0700192 SkFixed prevRsbDelta = 0;
193 penX += SK_Fixed1 / 2;
194
Romain Guy694b5192010-07-21 21:33:20 -0700195 text += start;
196
197 while (glyphsLeft > 0) {
Romain Guy694b5192010-07-21 21:33:20 -0700198 int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text);
199
Romain Guy61c8c9c2010-08-09 20:48:09 -0700200 // Reached the end of the string
Romain Guy694b5192010-07-21 21:33:20 -0700201 if (utfChar < 0) {
202 break;
203 }
204
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700205 CachedGlyphInfo* cachedGlyph = getCachedUTFChar(paint, utfChar);
Romain Guy2bffd262010-09-12 17:40:02 -0700206 penX += SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta);
207 prevRsbDelta = cachedGlyph->mRsbDelta;
Romain Guy694b5192010-07-21 21:33:20 -0700208
209 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
210 if (cachedGlyph->mIsValid) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700211 switch(mode) {
212 case FRAMEBUFFER:
Romain Guy2bffd262010-09-12 17:40:02 -0700213 drawCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700214 break;
215 case BITMAP:
Romain Guy2bffd262010-09-12 17:40:02 -0700216 drawCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY, bitmap, bitmapW, bitmapH);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700217 break;
218 case MEASURE:
Romain Guy2bffd262010-09-12 17:40:02 -0700219 measureCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY, bounds);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700220 break;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700221 }
Romain Guy694b5192010-07-21 21:33:20 -0700222 }
223
Romain Guy2bffd262010-09-12 17:40:02 -0700224 penX += cachedGlyph->mAdvanceX;
Romain Guy694b5192010-07-21 21:33:20 -0700225
226 // If we were given a specific number of glyphs, decrement
227 if (numGlyphs > 0) {
228 glyphsLeft--;
229 }
230 }
231}
232
Romain Guy51769a62010-07-23 00:28:00 -0700233void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
Romain Guy694b5192010-07-21 21:33:20 -0700234 glyph->mAdvanceX = skiaGlyph.fAdvanceX;
235 glyph->mAdvanceY = skiaGlyph.fAdvanceY;
236 glyph->mBitmapLeft = skiaGlyph.fLeft;
237 glyph->mBitmapTop = skiaGlyph.fTop;
Romain Guy2bffd262010-09-12 17:40:02 -0700238 glyph->mLsbDelta = skiaGlyph.fLsbDelta;
239 glyph->mRsbDelta = skiaGlyph.fRsbDelta;
Romain Guy694b5192010-07-21 21:33:20 -0700240
241 uint32_t startX = 0;
242 uint32_t startY = 0;
243
Romain Guy694b5192010-07-21 21:33:20 -0700244 // Get the bitmap for the glyph
245 paint->findImage(skiaGlyph);
Romain Guy51769a62010-07-23 00:28:00 -0700246 glyph->mIsValid = mState->cacheBitmap(skiaGlyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700247
248 if (!glyph->mIsValid) {
249 return;
250 }
251
252 uint32_t endX = startX + skiaGlyph.fWidth;
253 uint32_t endY = startY + skiaGlyph.fHeight;
254
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700255 glyph->mStartX = startX;
256 glyph->mStartY = startY;
Romain Guy694b5192010-07-21 21:33:20 -0700257 glyph->mBitmapWidth = skiaGlyph.fWidth;
258 glyph->mBitmapHeight = skiaGlyph.fHeight;
259
Romain Guy51769a62010-07-23 00:28:00 -0700260 uint32_t cacheWidth = mState->getCacheWidth();
261 uint32_t cacheHeight = mState->getCacheHeight();
Romain Guy694b5192010-07-21 21:33:20 -0700262
263 glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
264 glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
265 glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
266 glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
267
Romain Guy51769a62010-07-23 00:28:00 -0700268 mState->mUploadTexture = true;
Romain Guy694b5192010-07-21 21:33:20 -0700269}
270
Romain Guy51769a62010-07-23 00:28:00 -0700271Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, int32_t glyph) {
272 CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
Romain Guy694b5192010-07-21 21:33:20 -0700273 mCachedGlyphs.add(glyph, newGlyph);
274
275 const SkGlyph& skiaGlyph = paint->getUnicharMetrics(glyph);
276 newGlyph->mGlyphIndex = skiaGlyph.fID;
277 newGlyph->mIsValid = false;
278
279 updateGlyphCache(paint, skiaGlyph, newGlyph);
280
281 return newGlyph;
282}
283
Romain Guy2577db12011-01-18 13:02:38 -0800284Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
Chet Haase8668f8a2011-03-02 13:51:36 -0800285 int flags, uint32_t italicStyle, uint32_t scaleX) {
Romain Guy694b5192010-07-21 21:33:20 -0700286 Vector<Font*> &activeFonts = state->mActiveFonts;
287
288 for (uint32_t i = 0; i < activeFonts.size(); i++) {
Romain Guy51769a62010-07-23 00:28:00 -0700289 Font* font = activeFonts[i];
Romain Guy2577db12011-01-18 13:02:38 -0800290 if (font->mFontId == fontId && font->mFontSize == fontSize &&
Chet Haase8668f8a2011-03-02 13:51:36 -0800291 font->mFlags == flags && font->mItalicStyle == italicStyle &&
292 font->mScaleX == scaleX) {
Romain Guy51769a62010-07-23 00:28:00 -0700293 return font;
Romain Guy694b5192010-07-21 21:33:20 -0700294 }
295 }
296
Chet Haase8668f8a2011-03-02 13:51:36 -0800297 Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle, scaleX);
Romain Guy694b5192010-07-21 21:33:20 -0700298 activeFonts.push(newFont);
299 return newFont;
300}
301
302///////////////////////////////////////////////////////////////////////////////
303// FontRenderer
304///////////////////////////////////////////////////////////////////////////////
305
Romain Guy514fb182011-01-19 14:38:29 -0800306static bool sLogFontRendererCreate = true;
307
Romain Guy694b5192010-07-21 21:33:20 -0700308FontRenderer::FontRenderer() {
Romain Guyc9855a52011-01-21 21:14:15 -0800309 if (sLogFontRendererCreate) {
310 INIT_LOGD("Creating FontRenderer");
311 }
Romain Guy51769a62010-07-23 00:28:00 -0700312
Romain Guyb45c0c92010-08-26 20:35:23 -0700313 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700314 mInitialized = false;
315 mMaxNumberOfQuads = 1024;
316 mCurrentQuadIndex = 0;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700317 mTextureId = 0;
Romain Guy694b5192010-07-21 21:33:20 -0700318
Romain Guy9cccc2b92010-08-07 23:46:15 -0700319 mTextMeshPtr = NULL;
320 mTextTexture = NULL;
321
Romain Guy694b5192010-07-21 21:33:20 -0700322 mIndexBufferID = 0;
Alex Sakhartchouk894df172011-02-17 16:45:37 -0800323 mPositionAttrSlot = -1;
324 mTexcoordAttrSlot = -1;
Romain Guy694b5192010-07-21 21:33:20 -0700325
Romain Guy51769a62010-07-23 00:28:00 -0700326 mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700327 mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -0700328
329 char property[PROPERTY_VALUE_MAX];
330 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800331 if (sLogFontRendererCreate) {
332 INIT_LOGD(" Setting text cache width to %s pixels", property);
333 }
Romain Guy51769a62010-07-23 00:28:00 -0700334 mCacheWidth = atoi(property);
335 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800336 if (sLogFontRendererCreate) {
Romain Guyc9855a52011-01-21 21:14:15 -0800337 INIT_LOGD(" Using default text cache width of %i pixels", mCacheWidth);
Romain Guy514fb182011-01-19 14:38:29 -0800338 }
Romain Guy51769a62010-07-23 00:28:00 -0700339 }
340
341 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800342 if (sLogFontRendererCreate) {
343 INIT_LOGD(" Setting text cache width to %s pixels", property);
344 }
Romain Guy51769a62010-07-23 00:28:00 -0700345 mCacheHeight = atoi(property);
346 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800347 if (sLogFontRendererCreate) {
Romain Guyc9855a52011-01-21 21:14:15 -0800348 INIT_LOGD(" Using default text cache height of %i pixels", mCacheHeight);
Romain Guy514fb182011-01-19 14:38:29 -0800349 }
Romain Guy51769a62010-07-23 00:28:00 -0700350 }
Romain Guy514fb182011-01-19 14:38:29 -0800351
352 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -0700353}
354
355FontRenderer::~FontRenderer() {
356 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
357 delete mCacheLines[i];
358 }
359 mCacheLines.clear();
360
Romain Guy9cccc2b92010-08-07 23:46:15 -0700361 if (mInitialized) {
362 delete[] mTextMeshPtr;
363 delete[] mTextTexture;
364 }
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700365
Romain Guy9cccc2b92010-08-07 23:46:15 -0700366 if (mTextureId) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700367 glDeleteTextures(1, &mTextureId);
368 }
Romain Guy694b5192010-07-21 21:33:20 -0700369
370 Vector<Font*> fontsToDereference = mActiveFonts;
371 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
372 delete fontsToDereference[i];
373 }
374}
375
376void FontRenderer::flushAllAndInvalidate() {
377 if (mCurrentQuadIndex != 0) {
378 issueDrawCommand();
379 mCurrentQuadIndex = 0;
380 }
381 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
382 mActiveFonts[i]->invalidateTextureCache();
383 }
384 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
385 mCacheLines[i]->mCurrentCol = 0;
386 }
387}
388
Romain Guy51769a62010-07-23 00:28:00 -0700389bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
Romain Guy694b5192010-07-21 21:33:20 -0700390 // If the glyph is too tall, don't cache it
Romain Guy2bffd262010-09-12 17:40:02 -0700391 if (glyph.fHeight > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
Chet Haase44984ea2011-05-19 13:50:47 -0700392 if (mCacheHeight < MAX_TEXT_CACHE_HEIGHT) {
393 // Default cache not large enough for large glyphs - resize cache to
394 // max size and try again
395 flushAllAndInvalidate();
396 initTextTexture(true);
397 }
398 if (glyph.fHeight > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
399 LOGE("Font size to large to fit in cache. width, height = %i, %i",
400 (int) glyph.fWidth, (int) glyph.fHeight);
401 return false;
402 }
Romain Guy694b5192010-07-21 21:33:20 -0700403 }
404
405 // Now copy the bitmap into the cache texture
406 uint32_t startX = 0;
407 uint32_t startY = 0;
408
409 bool bitmapFit = false;
410 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
411 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
412 if (bitmapFit) {
413 break;
414 }
415 }
416
417 // If the new glyph didn't fit, flush the state so far and invalidate everything
418 if (!bitmapFit) {
419 flushAllAndInvalidate();
420
421 // Try to fit it again
422 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
423 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
424 if (bitmapFit) {
425 break;
426 }
427 }
428
429 // if we still don't fit, something is wrong and we shouldn't draw
430 if (!bitmapFit) {
431 LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
432 (int) glyph.fWidth, (int) glyph.fHeight);
433 return false;
434 }
435 }
436
437 *retOriginX = startX;
438 *retOriginY = startY;
439
440 uint32_t endX = startX + glyph.fWidth;
441 uint32_t endY = startY + glyph.fHeight;
442
443 uint32_t cacheWidth = mCacheWidth;
444
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700445 uint8_t* cacheBuffer = mTextTexture;
446 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700447 unsigned int stride = glyph.rowBytes();
448
449 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
450 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
451 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700452 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
Romain Guyb45c0c92010-08-26 20:35:23 -0700453 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
Romain Guy694b5192010-07-21 21:33:20 -0700454 }
455 }
456
457 return true;
458}
459
Chet Haase44984ea2011-05-19 13:50:47 -0700460void FontRenderer::initTextTexture(bool largeFonts) {
461 mCacheLines.clear();
462 if (largeFonts) {
463 mCacheWidth = MAX_TEXT_CACHE_WIDTH;
464 mCacheHeight = MAX_TEXT_CACHE_HEIGHT;
465 }
466
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700467 mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
Romain Guy1de10832010-10-03 14:37:09 -0700468 memset(mTextTexture, 0, mCacheWidth * mCacheHeight * sizeof(uint8_t));
469
Romain Guy694b5192010-07-21 21:33:20 -0700470 mUploadTexture = false;
471
Chet Haase44984ea2011-05-19 13:50:47 -0700472 if (mTextureId != 0) {
473 glDeleteTextures(1, &mTextureId);
474 }
Romain Guy694b5192010-07-21 21:33:20 -0700475 glGenTextures(1, &mTextureId);
476 glBindTexture(GL_TEXTURE_2D, mTextureId);
477 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
Chet Haase44984ea2011-05-19 13:50:47 -0700478 // Initialize texture dimensions
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700479 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700480 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Romain Guy694b5192010-07-21 21:33:20 -0700481
Romain Guye8cb9c142010-10-04 14:14:11 -0700482 mLinearFiltering = false;
483 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
484 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
Romain Guy694b5192010-07-21 21:33:20 -0700485
486 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
487 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
488
489 // Split up our cache texture into lines of certain widths
490 int nextLine = 0;
Romain Guy7975fb62010-10-01 16:36:14 -0700491 mCacheLines.push(new CacheTextureLine(mCacheWidth, 18, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700492 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700493 mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700494 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700495 mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700496 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700497 mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700498 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700499 mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700500 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700501 mCacheLines.push(new CacheTextureLine(mCacheWidth, 42, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700502 nextLine += mCacheLines.top()->mMaxHeight;
Chet Haase44984ea2011-05-19 13:50:47 -0700503 if (largeFonts) {
504 int nextSize = 76;
505 // Make several new lines with increasing font sizes
506 while (nextSize < (int)(mCacheHeight - nextLine - (2 * nextSize))) {
507 mCacheLines.push(new CacheTextureLine(mCacheWidth, nextSize, nextLine, 0));
508 nextLine += mCacheLines.top()->mMaxHeight;
509 nextSize += 50;
510 }
511 }
Romain Guy51769a62010-07-23 00:28:00 -0700512 mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700513}
514
515// Avoid having to reallocate memory and render quad by quad
516void FontRenderer::initVertexArrayBuffers() {
517 uint32_t numIndicies = mMaxNumberOfQuads * 6;
518 uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700519 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700520
521 // Four verts, two triangles , six indices per quad
522 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
523 int i6 = i * 6;
524 int i4 = i * 4;
525
526 indexBufferData[i6 + 0] = i4 + 0;
527 indexBufferData[i6 + 1] = i4 + 1;
528 indexBufferData[i6 + 2] = i4 + 2;
529
530 indexBufferData[i6 + 3] = i4 + 0;
531 indexBufferData[i6 + 4] = i4 + 2;
532 indexBufferData[i6 + 5] = i4 + 3;
533 }
534
535 glGenBuffers(1, &mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700536 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
537 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
538 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
Romain Guy694b5192010-07-21 21:33:20 -0700539
540 free(indexBufferData);
541
542 uint32_t coordSize = 3;
543 uint32_t uvSize = 2;
544 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700545 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
546 mTextMeshPtr = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700547}
548
549// We don't want to allocate anything unless we actually draw text
550void FontRenderer::checkInit() {
551 if (mInitialized) {
552 return;
553 }
554
555 initTextTexture();
556 initVertexArrayBuffers();
557
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700558 // We store a string with letters in a rough frequency of occurrence
559 mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
560 mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
561 mLatinPrecache += String16(",.?!()-+@;:`'");
562 mLatinPrecache += String16("0123456789");
563
Romain Guy694b5192010-07-21 21:33:20 -0700564 mInitialized = true;
565}
566
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700567void FontRenderer::checkTextureUpdate() {
568 if (!mUploadTexture) {
569 return;
Romain Guy694b5192010-07-21 21:33:20 -0700570 }
571
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700572 glBindTexture(GL_TEXTURE_2D, mTextureId);
573
574 // Iterate over all the cache lines and see which ones need to be updated
575 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
576 CacheTextureLine* cl = mCacheLines[i];
577 if(cl->mDirty) {
578 uint32_t xOffset = 0;
579 uint32_t yOffset = cl->mCurrentRow;
580 uint32_t width = mCacheWidth;
581 uint32_t height = cl->mMaxHeight;
Romain Guy1e45aae2010-08-13 19:39:53 -0700582 void* textureData = mTextTexture + yOffset*width;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700583
584 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700585 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700586
587 cl->mDirty = false;
588 }
589 }
590
591 mUploadTexture = false;
592}
593
594void FontRenderer::issueDrawCommand() {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700595 checkTextureUpdate();
596
Romain Guy51769a62010-07-23 00:28:00 -0700597 float* vtx = mTextMeshPtr;
598 float* tex = vtx + 3;
Romain Guy694b5192010-07-21 21:33:20 -0700599
Alex Sakhartchouk894df172011-02-17 16:45:37 -0800600 glVertexAttribPointer(mPositionAttrSlot, 3, GL_FLOAT, false, 20, vtx);
601 glVertexAttribPointer(mTexcoordAttrSlot, 2, GL_FLOAT, false, 20, tex);
Romain Guy694b5192010-07-21 21:33:20 -0700602
603 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
604 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
Romain Guy5b3b3522010-10-27 18:57:51 -0700605
606 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700607}
608
609void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
610 float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
611 float x4, float y4, float z4, float u4, float v4) {
Romain Guy09147fb2010-07-22 13:08:20 -0700612 if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) {
613 return;
614 }
615
Romain Guy694b5192010-07-21 21:33:20 -0700616 const uint32_t vertsPerQuad = 4;
617 const uint32_t floatsPerVert = 5;
Romain Guy51769a62010-07-23 00:28:00 -0700618 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700619
Romain Guy694b5192010-07-21 21:33:20 -0700620 (*currentPos++) = x1;
621 (*currentPos++) = y1;
622 (*currentPos++) = z1;
623 (*currentPos++) = u1;
624 (*currentPos++) = v1;
625
626 (*currentPos++) = x2;
627 (*currentPos++) = y2;
628 (*currentPos++) = z2;
629 (*currentPos++) = u2;
630 (*currentPos++) = v2;
631
632 (*currentPos++) = x3;
633 (*currentPos++) = y3;
634 (*currentPos++) = z3;
635 (*currentPos++) = u3;
636 (*currentPos++) = v3;
637
638 (*currentPos++) = x4;
639 (*currentPos++) = y4;
640 (*currentPos++) = z4;
641 (*currentPos++) = u4;
642 (*currentPos++) = v4;
643
644 mCurrentQuadIndex++;
645
Romain Guy5b3b3522010-10-27 18:57:51 -0700646 if (mBounds) {
647 mBounds->left = fmin(mBounds->left, x1);
648 mBounds->top = fmin(mBounds->top, y3);
649 mBounds->right = fmax(mBounds->right, x3);
650 mBounds->bottom = fmax(mBounds->bottom, y1);
651 }
652
Romain Guy694b5192010-07-21 21:33:20 -0700653 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
654 issueDrawCommand();
655 mCurrentQuadIndex = 0;
656 }
657}
658
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700659uint32_t FontRenderer::getRemainingCacheCapacity() {
660 uint32_t remainingCapacity = 0;
661 float totalPixels = 0;
662 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
663 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
664 totalPixels += mCacheLines[i]->mMaxWidth;
665 }
666 remainingCapacity = (remainingCapacity * 100) / totalPixels;
667 return remainingCapacity;
668}
669
670void FontRenderer::precacheLatin(SkPaint* paint) {
671 // Remaining capacity is measured in %
672 uint32_t remainingCapacity = getRemainingCacheCapacity();
673 uint32_t precacheIdx = 0;
Romain Guy054dc182010-10-15 17:55:25 -0700674 while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
675 mCurrentFont->getCachedUTFChar(paint, (int32_t) mLatinPrecache[precacheIdx]);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700676 remainingCapacity = getRemainingCacheCapacity();
677 precacheIdx ++;
678 }
679}
680
681void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
682 uint32_t currentNumFonts = mActiveFonts.size();
Romain Guy325a0f92011-01-05 15:26:55 -0800683 int flags = 0;
684 if (paint->isFakeBoldText()) {
685 flags |= Font::kFakeBold;
686 }
Romain Guy2577db12011-01-18 13:02:38 -0800687
688 const float skewX = paint->getTextSkewX();
689 uint32_t italicStyle = *(uint32_t*) &skewX;
Chet Haase8668f8a2011-03-02 13:51:36 -0800690 const float scaleXFloat = paint->getTextScaleX();
691 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
692 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle, scaleX);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700693
694 const float maxPrecacheFontSize = 40.0f;
695 bool isNewFont = currentNumFonts != mActiveFonts.size();
696
Romain Guy2bffd262010-09-12 17:40:02 -0700697 if (isNewFont && fontSize <= maxPrecacheFontSize) {
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700698 precacheLatin(paint);
699 }
Romain Guy694b5192010-07-21 21:33:20 -0700700}
Romain Guy7975fb62010-10-01 16:36:14 -0700701
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700702FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Romain Guy1e45aae2010-08-13 19:39:53 -0700703 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
704 checkInit();
705
706 if (!mCurrentFont) {
707 DropShadow image;
708 image.width = 0;
709 image.height = 0;
710 image.image = NULL;
711 image.penX = 0;
712 image.penY = 0;
713 return image;
714 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700715
716 Rect bounds;
717 mCurrentFont->measureUTF(paint, text, startIndex, len, numGlyphs, &bounds);
Romain Guy1e45aae2010-08-13 19:39:53 -0700718 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
719 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700720 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guy1e45aae2010-08-13 19:39:53 -0700721 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700722 dataBuffer[i] = 0;
723 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700724
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700725 int penX = radius - bounds.left;
726 int penY = radius - bounds.bottom;
727
728 mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, penX, penY,
Romain Guy1e45aae2010-08-13 19:39:53 -0700729 dataBuffer, paddedWidth, paddedHeight);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700730 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
731
732 DropShadow image;
733 image.width = paddedWidth;
734 image.height = paddedHeight;
735 image.image = dataBuffer;
736 image.penX = penX;
737 image.penY = penY;
738 return image;
739}
Romain Guy694b5192010-07-21 21:33:20 -0700740
Romain Guy5b3b3522010-10-27 18:57:51 -0700741bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
742 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700743 checkInit();
744
Romain Guy09147fb2010-07-22 13:08:20 -0700745 if (!mCurrentFont) {
746 LOGE("No font set");
Romain Guy5b3b3522010-10-27 18:57:51 -0700747 return false;
Romain Guy694b5192010-07-21 21:33:20 -0700748 }
749
Alex Sakhartchouk894df172011-02-17 16:45:37 -0800750 if (mPositionAttrSlot < 0 || mTexcoordAttrSlot < 0) {
751 LOGE("Font renderer unable to draw, attribute slots undefined");
752 return false;
753 }
754
Romain Guy5b3b3522010-10-27 18:57:51 -0700755 mDrawn = false;
756 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -0700757 mClip = clip;
Romain Guy51769a62010-07-23 00:28:00 -0700758 mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y);
Romain Guy5b3b3522010-10-27 18:57:51 -0700759 mBounds = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700760
761 if (mCurrentQuadIndex != 0) {
762 issueDrawCommand();
763 mCurrentQuadIndex = 0;
764 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700765
766 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700767}
768
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700769void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
770 // Compute gaussian weights for the blur
771 // e is the euler's number
772 float e = 2.718281828459045f;
773 float pi = 3.1415926535897932f;
774 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
775 // x is of the form [-radius .. 0 .. radius]
776 // and sigma varies with radius.
777 // Based on some experimental radius values and sigma's
778 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700779 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700780 // The larger the radius gets, the more our gaussian blur
781 // will resemble a box blur since with large sigma
782 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -0800783 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700784
785 // Now compute the coefficints
786 // We will store some redundant values to save some math during
787 // the blur calculations
788 // precompute some values
789 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
790 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
791
792 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -0800793 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700794 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700795 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
796 normalizeFactor += weights[r + radius];
797 }
798
799 //Now we need to normalize the weights because all our coefficients need to add up to one
800 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -0800801 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700802 weights[r + radius] *= normalizeFactor;
803 }
804}
805
806void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700807 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700808 float blurredPixel = 0.0f;
809 float currentPixel = 0.0f;
810
Romain Guy325a0f92011-01-05 15:26:55 -0800811 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700812
813 const uint8_t* input = source + y * width;
814 uint8_t* output = dest + y * width;
815
Romain Guy325a0f92011-01-05 15:26:55 -0800816 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700817 blurredPixel = 0.0f;
818 const float* gPtr = weights;
819 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800820 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700821 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -0800822 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700823 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700824 blurredPixel += currentPixel * gPtr[0];
825 gPtr++;
826 i++;
827 }
828 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800829 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700830 // Stepping left and right away from the pixel
831 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -0800832 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700833 validW = 0;
834 }
Romain Guy325a0f92011-01-05 15:26:55 -0800835 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700836 validW = width - 1;
837 }
838
Romain Guy325a0f92011-01-05 15:26:55 -0800839 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700840 blurredPixel += currentPixel * gPtr[0];
841 gPtr++;
842 }
843 }
844 *output = (uint8_t)blurredPixel;
845 output ++;
846 }
847 }
848}
849
850void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700851 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700852 float blurredPixel = 0.0f;
853 float currentPixel = 0.0f;
854
Romain Guy325a0f92011-01-05 15:26:55 -0800855 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700856
857 uint8_t* output = dest + y * width;
858
Romain Guy325a0f92011-01-05 15:26:55 -0800859 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700860 blurredPixel = 0.0f;
861 const float* gPtr = weights;
862 const uint8_t* input = source + x;
863 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800864 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700865 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -0800866 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700867 currentPixel = (float)(*i);
868 blurredPixel += currentPixel * gPtr[0];
869 gPtr++;
870 i += width;
871 }
872 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800873 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700874 int validH = y + r;
875 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -0800876 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700877 validH = 0;
878 }
Romain Guy325a0f92011-01-05 15:26:55 -0800879 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700880 validH = height - 1;
881 }
882
883 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -0800884 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700885 blurredPixel += currentPixel * gPtr[0];
886 gPtr++;
887 }
888 }
Romain Guy325a0f92011-01-05 15:26:55 -0800889 *output = (uint8_t) blurredPixel;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700890 output ++;
891 }
892 }
893}
894
895
896void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
897 float *gaussian = new float[2 * radius + 1];
898 computeGaussianWeights(gaussian, radius);
899 uint8_t* scratch = new uint8_t[width * height];
900 horizontalBlur(gaussian, radius, image, scratch, width, height);
901 verticalBlur(gaussian, radius, scratch, image, width, height);
902 delete[] gaussian;
903 delete[] scratch;
904}
905
Romain Guy694b5192010-07-21 21:33:20 -0700906}; // namespace uirenderer
907}; // namespace android