blob: 6e80d15a62ff3f6b82351319860a0d34aac4c79a [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
Romain Guy726aeba2011-06-01 14:52:00 -070038// We should query these values from the GL context
Chet Haase44984ea2011-05-19 13:50:47 -070039#define MAX_TEXT_CACHE_WIDTH 2048
40#define MAX_TEXT_CACHE_HEIGHT 2048
41
Romain Guy51769a62010-07-23 00:28:00 -070042///////////////////////////////////////////////////////////////////////////////
Romain Guy694b5192010-07-21 21:33:20 -070043// Font
44///////////////////////////////////////////////////////////////////////////////
45
Romain Guy2577db12011-01-18 13:02:38 -080046Font::Font(FontRenderer* state, uint32_t fontId, float fontSize,
Romain Guybd496bc2011-08-02 17:32:41 -070047 int flags, uint32_t italicStyle, uint32_t scaleX,
48 SkPaint::Style style, uint32_t strokeWidth) :
Romain Guy2577db12011-01-18 13:02:38 -080049 mState(state), mFontId(fontId), mFontSize(fontSize),
Romain Guybd496bc2011-08-02 17:32:41 -070050 mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX),
51 mStyle(style), mStrokeWidth(mStrokeWidth) {
Romain Guy694b5192010-07-21 21:33:20 -070052}
53
54
55Font::~Font() {
56 for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
57 if (mState->mActiveFonts[ct] == this) {
58 mState->mActiveFonts.removeAt(ct);
59 break;
60 }
61 }
62
63 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
Romain Guy726aeba2011-06-01 14:52:00 -070064 delete mCachedGlyphs.valueAt(i);
Romain Guy694b5192010-07-21 21:33:20 -070065 }
66}
67
68void Font::invalidateTextureCache() {
69 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
70 mCachedGlyphs.valueAt(i)->mIsValid = false;
71 }
72}
73
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070074void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds) {
75 int nPenX = x + glyph->mBitmapLeft;
76 int nPenY = y + glyph->mBitmapTop;
77
78 int width = (int) glyph->mBitmapWidth;
79 int height = (int) glyph->mBitmapHeight;
80
Romain Guy61c8c9c2010-08-09 20:48:09 -070081 if (bounds->bottom > nPenY) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070082 bounds->bottom = nPenY;
83 }
Romain Guy61c8c9c2010-08-09 20:48:09 -070084 if (bounds->left > nPenX) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070085 bounds->left = nPenX;
86 }
Romain Guy61c8c9c2010-08-09 20:48:09 -070087 if (bounds->right < nPenX + width) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070088 bounds->right = nPenX + width;
89 }
Romain Guy61c8c9c2010-08-09 20:48:09 -070090 if (bounds->top < nPenY + height) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070091 bounds->top = nPenY + height;
92 }
93}
94
Romain Guy694b5192010-07-21 21:33:20 -070095void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
Romain Guy694b5192010-07-21 21:33:20 -070096 int nPenX = x + glyph->mBitmapLeft;
97 int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
98
Romain Guy51769a62010-07-23 00:28:00 -070099 float u1 = glyph->mBitmapMinU;
100 float u2 = glyph->mBitmapMaxU;
101 float v1 = glyph->mBitmapMinV;
102 float v2 = glyph->mBitmapMaxV;
103
104 int width = (int) glyph->mBitmapWidth;
105 int height = (int) glyph->mBitmapHeight;
106
Romain Guyd71dd362011-12-12 19:03:35 -0800107 mState->appendMeshQuad(nPenX, nPenY, u1, v2,
108 nPenX + width, nPenY, u2, v2,
109 nPenX + width, nPenY - height, u2, v1,
110 nPenX, nPenY - height, u1, v1);
Romain Guy694b5192010-07-21 21:33:20 -0700111}
112
Romain Guyb45c0c92010-08-26 20:35:23 -0700113void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
114 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700115 int nPenX = x + glyph->mBitmapLeft;
116 int nPenY = y + glyph->mBitmapTop;
117
118 uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
119 uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
120
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700121 uint32_t cacheWidth = mState->getCacheWidth();
122 const uint8_t* cacheBuffer = mState->getTextTextureData();
123
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700124 uint32_t cacheX = 0, cacheY = 0;
125 int32_t bX = 0, bY = 0;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700126 for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
127 for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
Romain Guyb45c0c92010-08-26 20:35:23 -0700128 if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700129 LOGE("Skipping invalid index");
130 continue;
131 }
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700132 uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
133 bitmap[bY * bitmapW + bX] = tempCol;
134 }
135 }
136
137}
138
Romain Guy726aeba2011-06-01 14:52:00 -0700139Font::CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700140 CachedGlyphInfo* cachedGlyph = NULL;
Romain Guy726aeba2011-06-01 14:52:00 -0700141 ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
Romain Guy1e45aae2010-08-13 19:39:53 -0700142 if (index >= 0) {
143 cachedGlyph = mCachedGlyphs.valueAt(index);
144 } else {
Romain Guy726aeba2011-06-01 14:52:00 -0700145 cachedGlyph = cacheGlyph(paint, textUnit);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700146 }
147
148 // Is the glyph still in texture cache?
149 if (!cachedGlyph->mIsValid) {
Romain Guy726aeba2011-06-01 14:52:00 -0700150 const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700151 updateGlyphCache(paint, skiaGlyph, cachedGlyph);
152 }
153
154 return cachedGlyph;
155}
156
Romain Guy726aeba2011-06-01 14:52:00 -0700157void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700158 int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
159 if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
Romain Guy726aeba2011-06-01 14:52:00 -0700160 render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700161 bitmapW, bitmapH, NULL);
162 } else {
Romain Guy726aeba2011-06-01 14:52:00 -0700163 render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
164 0, 0, NULL);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700165 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700166}
167
Romain Guy726aeba2011-06-01 14:52:00 -0700168void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700169 int numGlyphs, Rect *bounds) {
170 if (bounds == NULL) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700171 LOGE("No return rectangle provided to measure text");
172 return;
173 }
174 bounds->set(1e6, -1e6, -1e6, 1e6);
Romain Guy726aeba2011-06-01 14:52:00 -0700175 render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700176}
177
Romain Guy58ef7fb2010-09-13 12:52:37 -0700178#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16)
Romain Guy2bffd262010-09-12 17:40:02 -0700179
Romain Guy726aeba2011-06-01 14:52:00 -0700180void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700181 int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
182 uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700183 if (numGlyphs == 0 || text == NULL || len == 0) {
184 return;
185 }
186
Romain Guy5a6d3a42011-10-07 15:03:24 -0700187 float penX = x;
Romain Guy2bffd262010-09-12 17:40:02 -0700188 int penY = y;
Romain Guy694b5192010-07-21 21:33:20 -0700189 int glyphsLeft = 1;
190 if (numGlyphs > 0) {
191 glyphsLeft = numGlyphs;
192 }
193
Romain Guy2bffd262010-09-12 17:40:02 -0700194 SkFixed prevRsbDelta = 0;
Romain Guy5a6d3a42011-10-07 15:03:24 -0700195 penX += 0.5f;
Romain Guy2bffd262010-09-12 17:40:02 -0700196
Romain Guy694b5192010-07-21 21:33:20 -0700197 text += start;
198
199 while (glyphsLeft > 0) {
Romain Guy726aeba2011-06-01 14:52:00 -0700200 glyph_t glyph = GET_GLYPH(text);
Romain Guy694b5192010-07-21 21:33:20 -0700201
Romain Guy61c8c9c2010-08-09 20:48:09 -0700202 // Reached the end of the string
Romain Guy726aeba2011-06-01 14:52:00 -0700203 if (IS_END_OF_STRING(glyph)) {
Romain Guy694b5192010-07-21 21:33:20 -0700204 break;
205 }
206
Romain Guy726aeba2011-06-01 14:52:00 -0700207 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
Romain Guy5a6d3a42011-10-07 15:03:24 -0700208 penX += SkFixedToFloat(SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta));
Romain Guy2bffd262010-09-12 17:40:02 -0700209 prevRsbDelta = cachedGlyph->mRsbDelta;
Romain Guy694b5192010-07-21 21:33:20 -0700210
211 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
212 if (cachedGlyph->mIsValid) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700213 switch(mode) {
214 case FRAMEBUFFER:
Romain Guy5a6d3a42011-10-07 15:03:24 -0700215 drawCachedGlyph(cachedGlyph, (int) floorf(penX), penY);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700216 break;
217 case BITMAP:
Romain Guy5a6d3a42011-10-07 15:03:24 -0700218 drawCachedGlyph(cachedGlyph, (int) floorf(penX), penY, bitmap, bitmapW, bitmapH);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700219 break;
220 case MEASURE:
Romain Guy5a6d3a42011-10-07 15:03:24 -0700221 measureCachedGlyph(cachedGlyph, (int) floorf(penX), penY, bounds);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700222 break;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700223 }
Romain Guy694b5192010-07-21 21:33:20 -0700224 }
225
Romain Guy5a6d3a42011-10-07 15:03:24 -0700226 penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
Romain Guy694b5192010-07-21 21:33:20 -0700227
228 // If we were given a specific number of glyphs, decrement
229 if (numGlyphs > 0) {
230 glyphsLeft--;
231 }
232 }
233}
234
Romain Guy51769a62010-07-23 00:28:00 -0700235void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
Romain Guy694b5192010-07-21 21:33:20 -0700236 glyph->mAdvanceX = skiaGlyph.fAdvanceX;
237 glyph->mAdvanceY = skiaGlyph.fAdvanceY;
238 glyph->mBitmapLeft = skiaGlyph.fLeft;
239 glyph->mBitmapTop = skiaGlyph.fTop;
Romain Guy2bffd262010-09-12 17:40:02 -0700240 glyph->mLsbDelta = skiaGlyph.fLsbDelta;
241 glyph->mRsbDelta = skiaGlyph.fRsbDelta;
Romain Guy694b5192010-07-21 21:33:20 -0700242
243 uint32_t startX = 0;
244 uint32_t startY = 0;
245
Romain Guy694b5192010-07-21 21:33:20 -0700246 // Get the bitmap for the glyph
247 paint->findImage(skiaGlyph);
Romain Guy51769a62010-07-23 00:28:00 -0700248 glyph->mIsValid = mState->cacheBitmap(skiaGlyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700249
250 if (!glyph->mIsValid) {
251 return;
252 }
253
254 uint32_t endX = startX + skiaGlyph.fWidth;
255 uint32_t endY = startY + skiaGlyph.fHeight;
256
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700257 glyph->mStartX = startX;
258 glyph->mStartY = startY;
Romain Guy694b5192010-07-21 21:33:20 -0700259 glyph->mBitmapWidth = skiaGlyph.fWidth;
260 glyph->mBitmapHeight = skiaGlyph.fHeight;
261
Romain Guy51769a62010-07-23 00:28:00 -0700262 uint32_t cacheWidth = mState->getCacheWidth();
263 uint32_t cacheHeight = mState->getCacheHeight();
Romain Guy694b5192010-07-21 21:33:20 -0700264
265 glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
266 glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
267 glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
268 glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
269
Romain Guy51769a62010-07-23 00:28:00 -0700270 mState->mUploadTexture = true;
Romain Guy694b5192010-07-21 21:33:20 -0700271}
272
Romain Guy726aeba2011-06-01 14:52:00 -0700273Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) {
Romain Guy51769a62010-07-23 00:28:00 -0700274 CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
Romain Guy694b5192010-07-21 21:33:20 -0700275 mCachedGlyphs.add(glyph, newGlyph);
276
Romain Guy726aeba2011-06-01 14:52:00 -0700277 const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
Romain Guy694b5192010-07-21 21:33:20 -0700278 newGlyph->mGlyphIndex = skiaGlyph.fID;
279 newGlyph->mIsValid = false;
280
281 updateGlyphCache(paint, skiaGlyph, newGlyph);
282
283 return newGlyph;
284}
285
Romain Guy2577db12011-01-18 13:02:38 -0800286Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
Romain Guybd496bc2011-08-02 17:32:41 -0700287 int flags, uint32_t italicStyle, uint32_t scaleX,
288 SkPaint::Style style, uint32_t strokeWidth) {
Romain Guy694b5192010-07-21 21:33:20 -0700289 Vector<Font*> &activeFonts = state->mActiveFonts;
290
291 for (uint32_t i = 0; i < activeFonts.size(); i++) {
Romain Guy51769a62010-07-23 00:28:00 -0700292 Font* font = activeFonts[i];
Romain Guy2577db12011-01-18 13:02:38 -0800293 if (font->mFontId == fontId && font->mFontSize == fontSize &&
Chet Haase8668f8a2011-03-02 13:51:36 -0800294 font->mFlags == flags && font->mItalicStyle == italicStyle &&
Romain Guybd496bc2011-08-02 17:32:41 -0700295 font->mScaleX == scaleX && font->mStyle == style &&
296 (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
Romain Guy51769a62010-07-23 00:28:00 -0700297 return font;
Romain Guy694b5192010-07-21 21:33:20 -0700298 }
299 }
300
Romain Guybd496bc2011-08-02 17:32:41 -0700301 Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
302 scaleX, style, strokeWidth);
Romain Guy694b5192010-07-21 21:33:20 -0700303 activeFonts.push(newFont);
304 return newFont;
305}
306
307///////////////////////////////////////////////////////////////////////////////
308// FontRenderer
309///////////////////////////////////////////////////////////////////////////////
310
Romain Guy514fb182011-01-19 14:38:29 -0800311static bool sLogFontRendererCreate = true;
312
Romain Guy694b5192010-07-21 21:33:20 -0700313FontRenderer::FontRenderer() {
Romain Guyc9855a52011-01-21 21:14:15 -0800314 if (sLogFontRendererCreate) {
315 INIT_LOGD("Creating FontRenderer");
316 }
Romain Guy51769a62010-07-23 00:28:00 -0700317
Romain Guyb45c0c92010-08-26 20:35:23 -0700318 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700319 mInitialized = false;
320 mMaxNumberOfQuads = 1024;
321 mCurrentQuadIndex = 0;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700322 mTextureId = 0;
Romain Guy694b5192010-07-21 21:33:20 -0700323
Romain Guy9cccc2b92010-08-07 23:46:15 -0700324 mTextMeshPtr = NULL;
325 mTextTexture = NULL;
326
Romain Guy694b5192010-07-21 21:33:20 -0700327 mIndexBufferID = 0;
Alex Sakhartchouk894df172011-02-17 16:45:37 -0800328 mPositionAttrSlot = -1;
329 mTexcoordAttrSlot = -1;
Romain Guy694b5192010-07-21 21:33:20 -0700330
Romain Guy51769a62010-07-23 00:28:00 -0700331 mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700332 mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -0700333
334 char property[PROPERTY_VALUE_MAX];
335 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800336 if (sLogFontRendererCreate) {
337 INIT_LOGD(" Setting text cache width to %s pixels", property);
338 }
Romain Guy51769a62010-07-23 00:28:00 -0700339 mCacheWidth = atoi(property);
340 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800341 if (sLogFontRendererCreate) {
Romain Guyc9855a52011-01-21 21:14:15 -0800342 INIT_LOGD(" Using default text cache width of %i pixels", mCacheWidth);
Romain Guy514fb182011-01-19 14:38:29 -0800343 }
Romain Guy51769a62010-07-23 00:28:00 -0700344 }
345
346 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800347 if (sLogFontRendererCreate) {
348 INIT_LOGD(" Setting text cache width to %s pixels", property);
349 }
Romain Guy51769a62010-07-23 00:28:00 -0700350 mCacheHeight = atoi(property);
351 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800352 if (sLogFontRendererCreate) {
Romain Guyc9855a52011-01-21 21:14:15 -0800353 INIT_LOGD(" Using default text cache height of %i pixels", mCacheHeight);
Romain Guy514fb182011-01-19 14:38:29 -0800354 }
Romain Guy51769a62010-07-23 00:28:00 -0700355 }
Romain Guy514fb182011-01-19 14:38:29 -0800356
357 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -0700358}
359
360FontRenderer::~FontRenderer() {
361 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
362 delete mCacheLines[i];
363 }
364 mCacheLines.clear();
365
Romain Guy9cccc2b92010-08-07 23:46:15 -0700366 if (mInitialized) {
367 delete[] mTextMeshPtr;
368 delete[] mTextTexture;
369 }
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700370
Romain Guy9cccc2b92010-08-07 23:46:15 -0700371 if (mTextureId) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700372 glDeleteTextures(1, &mTextureId);
373 }
Romain Guy694b5192010-07-21 21:33:20 -0700374
375 Vector<Font*> fontsToDereference = mActiveFonts;
376 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
377 delete fontsToDereference[i];
378 }
379}
380
381void FontRenderer::flushAllAndInvalidate() {
382 if (mCurrentQuadIndex != 0) {
383 issueDrawCommand();
384 mCurrentQuadIndex = 0;
385 }
386 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
387 mActiveFonts[i]->invalidateTextureCache();
388 }
389 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
390 mCacheLines[i]->mCurrentCol = 0;
391 }
392}
393
Romain Guy51769a62010-07-23 00:28:00 -0700394bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
Romain Guy694b5192010-07-21 21:33:20 -0700395 // If the glyph is too tall, don't cache it
Romain Guy799833a2011-08-30 14:41:48 -0700396 if (glyph.fHeight + 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
Chet Haase44984ea2011-05-19 13:50:47 -0700397 if (mCacheHeight < MAX_TEXT_CACHE_HEIGHT) {
398 // Default cache not large enough for large glyphs - resize cache to
399 // max size and try again
400 flushAllAndInvalidate();
401 initTextTexture(true);
402 }
Romain Guy799833a2011-08-30 14:41:48 -0700403 if (glyph.fHeight + 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
Chet Haase44984ea2011-05-19 13:50:47 -0700404 LOGE("Font size to large to fit in cache. width, height = %i, %i",
405 (int) glyph.fWidth, (int) glyph.fHeight);
406 return false;
407 }
Romain Guy694b5192010-07-21 21:33:20 -0700408 }
409
410 // Now copy the bitmap into the cache texture
411 uint32_t startX = 0;
412 uint32_t startY = 0;
413
414 bool bitmapFit = false;
415 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
416 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
417 if (bitmapFit) {
418 break;
419 }
420 }
421
422 // If the new glyph didn't fit, flush the state so far and invalidate everything
423 if (!bitmapFit) {
424 flushAllAndInvalidate();
425
426 // Try to fit it again
427 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
428 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
429 if (bitmapFit) {
430 break;
431 }
432 }
433
434 // if we still don't fit, something is wrong and we shouldn't draw
435 if (!bitmapFit) {
436 LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
437 (int) glyph.fWidth, (int) glyph.fHeight);
438 return false;
439 }
440 }
441
442 *retOriginX = startX;
443 *retOriginY = startY;
444
445 uint32_t endX = startX + glyph.fWidth;
446 uint32_t endY = startY + glyph.fHeight;
447
448 uint32_t cacheWidth = mCacheWidth;
449
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700450 uint8_t* cacheBuffer = mTextTexture;
451 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700452 unsigned int stride = glyph.rowBytes();
453
454 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
455 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
456 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700457 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
Romain Guyb45c0c92010-08-26 20:35:23 -0700458 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
Romain Guy694b5192010-07-21 21:33:20 -0700459 }
460 }
461
462 return true;
463}
464
Chet Haase44984ea2011-05-19 13:50:47 -0700465void FontRenderer::initTextTexture(bool largeFonts) {
466 mCacheLines.clear();
467 if (largeFonts) {
468 mCacheWidth = MAX_TEXT_CACHE_WIDTH;
469 mCacheHeight = MAX_TEXT_CACHE_HEIGHT;
470 }
471
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700472 mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
Romain Guy1de10832010-10-03 14:37:09 -0700473 memset(mTextTexture, 0, mCacheWidth * mCacheHeight * sizeof(uint8_t));
474
Romain Guy694b5192010-07-21 21:33:20 -0700475 mUploadTexture = false;
476
Chet Haase44984ea2011-05-19 13:50:47 -0700477 if (mTextureId != 0) {
478 glDeleteTextures(1, &mTextureId);
479 }
Romain Guy694b5192010-07-21 21:33:20 -0700480 glGenTextures(1, &mTextureId);
481 glBindTexture(GL_TEXTURE_2D, mTextureId);
482 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
Chet Haase44984ea2011-05-19 13:50:47 -0700483 // Initialize texture dimensions
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700484 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700485 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Romain Guy694b5192010-07-21 21:33:20 -0700486
Romain Guye8cb9c142010-10-04 14:14:11 -0700487 mLinearFiltering = false;
488 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
489 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
Romain Guy694b5192010-07-21 21:33:20 -0700490
491 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
492 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
493
494 // Split up our cache texture into lines of certain widths
495 int nextLine = 0;
Romain Guy7975fb62010-10-01 16:36:14 -0700496 mCacheLines.push(new CacheTextureLine(mCacheWidth, 18, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700497 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700498 mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700499 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700500 mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700501 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700502 mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700503 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700504 mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700505 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy7975fb62010-10-01 16:36:14 -0700506 mCacheLines.push(new CacheTextureLine(mCacheWidth, 42, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700507 nextLine += mCacheLines.top()->mMaxHeight;
Chet Haase44984ea2011-05-19 13:50:47 -0700508 if (largeFonts) {
509 int nextSize = 76;
510 // Make several new lines with increasing font sizes
511 while (nextSize < (int)(mCacheHeight - nextLine - (2 * nextSize))) {
512 mCacheLines.push(new CacheTextureLine(mCacheWidth, nextSize, nextLine, 0));
513 nextLine += mCacheLines.top()->mMaxHeight;
514 nextSize += 50;
515 }
516 }
Romain Guy51769a62010-07-23 00:28:00 -0700517 mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700518}
519
520// Avoid having to reallocate memory and render quad by quad
521void FontRenderer::initVertexArrayBuffers() {
Romain Guyd71dd362011-12-12 19:03:35 -0800522 uint32_t numIndices = mMaxNumberOfQuads * 6;
523 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700524 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700525
526 // Four verts, two triangles , six indices per quad
527 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
528 int i6 = i * 6;
529 int i4 = i * 4;
530
531 indexBufferData[i6 + 0] = i4 + 0;
532 indexBufferData[i6 + 1] = i4 + 1;
533 indexBufferData[i6 + 2] = i4 + 2;
534
535 indexBufferData[i6 + 3] = i4 + 0;
536 indexBufferData[i6 + 4] = i4 + 2;
537 indexBufferData[i6 + 5] = i4 + 3;
538 }
539
540 glGenBuffers(1, &mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700541 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
542 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
543 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
Romain Guy694b5192010-07-21 21:33:20 -0700544
545 free(indexBufferData);
546
Romain Guyd71dd362011-12-12 19:03:35 -0800547 uint32_t coordSize = 2;
Romain Guy694b5192010-07-21 21:33:20 -0700548 uint32_t uvSize = 2;
549 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700550 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
551 mTextMeshPtr = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700552}
553
554// We don't want to allocate anything unless we actually draw text
555void FontRenderer::checkInit() {
556 if (mInitialized) {
557 return;
558 }
559
560 initTextTexture();
561 initVertexArrayBuffers();
562
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700563 // We store a string with letters in a rough frequency of occurrence
564 mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
565 mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
566 mLatinPrecache += String16(",.?!()-+@;:`'");
567 mLatinPrecache += String16("0123456789");
568
Romain Guy694b5192010-07-21 21:33:20 -0700569 mInitialized = true;
570}
571
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700572void FontRenderer::checkTextureUpdate() {
573 if (!mUploadTexture) {
574 return;
Romain Guy694b5192010-07-21 21:33:20 -0700575 }
576
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700577 glBindTexture(GL_TEXTURE_2D, mTextureId);
578
579 // Iterate over all the cache lines and see which ones need to be updated
580 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
581 CacheTextureLine* cl = mCacheLines[i];
Romain Guyd71dd362011-12-12 19:03:35 -0800582 if (cl->mDirty) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700583 uint32_t xOffset = 0;
584 uint32_t yOffset = cl->mCurrentRow;
585 uint32_t width = mCacheWidth;
586 uint32_t height = cl->mMaxHeight;
Romain Guyd71dd362011-12-12 19:03:35 -0800587 void* textureData = mTextTexture + yOffset * width;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700588
589 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700590 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700591
592 cl->mDirty = false;
593 }
594 }
595
596 mUploadTexture = false;
597}
598
599void FontRenderer::issueDrawCommand() {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700600 checkTextureUpdate();
601
Romain Guy51769a62010-07-23 00:28:00 -0700602 float* vtx = mTextMeshPtr;
Romain Guyd71dd362011-12-12 19:03:35 -0800603 float* tex = vtx + 2;
Romain Guy694b5192010-07-21 21:33:20 -0700604
Romain Guyd71dd362011-12-12 19:03:35 -0800605 glVertexAttribPointer(mPositionAttrSlot, 2, GL_FLOAT, GL_FALSE, 16, vtx);
606 glVertexAttribPointer(mTexcoordAttrSlot, 2, GL_FLOAT, GL_FALSE, 16, tex);
Romain Guy694b5192010-07-21 21:33:20 -0700607
608 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
609 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
Romain Guy5b3b3522010-10-27 18:57:51 -0700610
611 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700612}
613
Romain Guyd71dd362011-12-12 19:03:35 -0800614void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
615 float x2, float y2, float u2, float v2,
616 float x3, float y3, float u3, float v3,
617 float x4, float y4, float u4, float v4) {
618
Romain Guyff98fa52011-11-28 09:35:09 -0800619 if (mClip &&
620 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
Romain Guy09147fb2010-07-22 13:08:20 -0700621 return;
622 }
623
Romain Guy694b5192010-07-21 21:33:20 -0700624 const uint32_t vertsPerQuad = 4;
Romain Guyd71dd362011-12-12 19:03:35 -0800625 const uint32_t floatsPerVert = 4;
Romain Guy51769a62010-07-23 00:28:00 -0700626 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700627
Romain Guy694b5192010-07-21 21:33:20 -0700628 (*currentPos++) = x1;
629 (*currentPos++) = y1;
Romain Guy694b5192010-07-21 21:33:20 -0700630 (*currentPos++) = u1;
631 (*currentPos++) = v1;
632
633 (*currentPos++) = x2;
634 (*currentPos++) = y2;
Romain Guy694b5192010-07-21 21:33:20 -0700635 (*currentPos++) = u2;
636 (*currentPos++) = v2;
637
638 (*currentPos++) = x3;
639 (*currentPos++) = y3;
Romain Guy694b5192010-07-21 21:33:20 -0700640 (*currentPos++) = u3;
641 (*currentPos++) = v3;
642
643 (*currentPos++) = x4;
644 (*currentPos++) = y4;
Romain Guy694b5192010-07-21 21:33:20 -0700645 (*currentPos++) = u4;
646 (*currentPos++) = v4;
647
648 mCurrentQuadIndex++;
649
Romain Guy5b3b3522010-10-27 18:57:51 -0700650 if (mBounds) {
651 mBounds->left = fmin(mBounds->left, x1);
652 mBounds->top = fmin(mBounds->top, y3);
653 mBounds->right = fmax(mBounds->right, x3);
654 mBounds->bottom = fmax(mBounds->bottom, y1);
655 }
656
Romain Guy694b5192010-07-21 21:33:20 -0700657 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
658 issueDrawCommand();
659 mCurrentQuadIndex = 0;
660 }
661}
662
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700663uint32_t FontRenderer::getRemainingCacheCapacity() {
664 uint32_t remainingCapacity = 0;
665 float totalPixels = 0;
666 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
667 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
668 totalPixels += mCacheLines[i]->mMaxWidth;
669 }
670 remainingCapacity = (remainingCapacity * 100) / totalPixels;
671 return remainingCapacity;
672}
673
674void FontRenderer::precacheLatin(SkPaint* paint) {
675 // Remaining capacity is measured in %
676 uint32_t remainingCapacity = getRemainingCacheCapacity();
677 uint32_t precacheIdx = 0;
Romain Guy054dc182010-10-15 17:55:25 -0700678 while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
Romain Guy726aeba2011-06-01 14:52:00 -0700679 mCurrentFont->getCachedGlyph(paint, (int32_t) mLatinPrecache[precacheIdx]);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700680 remainingCapacity = getRemainingCacheCapacity();
681 precacheIdx ++;
682 }
683}
684
685void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
686 uint32_t currentNumFonts = mActiveFonts.size();
Romain Guy325a0f92011-01-05 15:26:55 -0800687 int flags = 0;
688 if (paint->isFakeBoldText()) {
689 flags |= Font::kFakeBold;
690 }
Romain Guy2577db12011-01-18 13:02:38 -0800691
692 const float skewX = paint->getTextSkewX();
693 uint32_t italicStyle = *(uint32_t*) &skewX;
Chet Haase8668f8a2011-03-02 13:51:36 -0800694 const float scaleXFloat = paint->getTextScaleX();
695 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
Romain Guybd496bc2011-08-02 17:32:41 -0700696 SkPaint::Style style = paint->getStyle();
697 const float strokeWidthFloat = paint->getStrokeWidth();
698 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
699 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
700 scaleX, style, strokeWidth);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700701
702 const float maxPrecacheFontSize = 40.0f;
703 bool isNewFont = currentNumFonts != mActiveFonts.size();
704
Romain Guy2bffd262010-09-12 17:40:02 -0700705 if (isNewFont && fontSize <= maxPrecacheFontSize) {
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700706 precacheLatin(paint);
707 }
Romain Guy694b5192010-07-21 21:33:20 -0700708}
Romain Guy7975fb62010-10-01 16:36:14 -0700709
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700710FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Romain Guy1e45aae2010-08-13 19:39:53 -0700711 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
712 checkInit();
713
714 if (!mCurrentFont) {
715 DropShadow image;
716 image.width = 0;
717 image.height = 0;
718 image.image = NULL;
719 image.penX = 0;
720 image.penY = 0;
721 return image;
722 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700723
Romain Guyff98fa52011-11-28 09:35:09 -0800724 mClip = NULL;
725 mBounds = NULL;
726
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700727 Rect bounds;
Romain Guy726aeba2011-06-01 14:52:00 -0700728 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds);
Romain Guyff98fa52011-11-28 09:35:09 -0800729
Romain Guy1e45aae2010-08-13 19:39:53 -0700730 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
731 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700732 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guyff98fa52011-11-28 09:35:09 -0800733
Romain Guy1e45aae2010-08-13 19:39:53 -0700734 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700735 dataBuffer[i] = 0;
736 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700737
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700738 int penX = radius - bounds.left;
739 int penY = radius - bounds.bottom;
740
Romain Guy726aeba2011-06-01 14:52:00 -0700741 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
Romain Guy1e45aae2010-08-13 19:39:53 -0700742 dataBuffer, paddedWidth, paddedHeight);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700743 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
744
745 DropShadow image;
746 image.width = paddedWidth;
747 image.height = paddedHeight;
748 image.image = dataBuffer;
749 image.penX = penX;
750 image.penY = penY;
751 return image;
752}
Romain Guy694b5192010-07-21 21:33:20 -0700753
Romain Guy5b3b3522010-10-27 18:57:51 -0700754bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
755 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700756 checkInit();
757
Romain Guy09147fb2010-07-22 13:08:20 -0700758 if (!mCurrentFont) {
759 LOGE("No font set");
Romain Guy5b3b3522010-10-27 18:57:51 -0700760 return false;
Romain Guy694b5192010-07-21 21:33:20 -0700761 }
762
Alex Sakhartchouk894df172011-02-17 16:45:37 -0800763 if (mPositionAttrSlot < 0 || mTexcoordAttrSlot < 0) {
764 LOGE("Font renderer unable to draw, attribute slots undefined");
765 return false;
766 }
767
Romain Guy5b3b3522010-10-27 18:57:51 -0700768 mDrawn = false;
769 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -0700770 mClip = clip;
Romain Guyff98fa52011-11-28 09:35:09 -0800771
Romain Guy726aeba2011-06-01 14:52:00 -0700772 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
Romain Guyff98fa52011-11-28 09:35:09 -0800773
Romain Guy5b3b3522010-10-27 18:57:51 -0700774 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -0800775 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700776
777 if (mCurrentQuadIndex != 0) {
778 issueDrawCommand();
779 mCurrentQuadIndex = 0;
780 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700781
782 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700783}
784
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700785void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
786 // Compute gaussian weights for the blur
787 // e is the euler's number
788 float e = 2.718281828459045f;
789 float pi = 3.1415926535897932f;
790 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
791 // x is of the form [-radius .. 0 .. radius]
792 // and sigma varies with radius.
793 // Based on some experimental radius values and sigma's
794 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700795 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700796 // The larger the radius gets, the more our gaussian blur
797 // will resemble a box blur since with large sigma
798 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -0800799 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700800
801 // Now compute the coefficints
802 // We will store some redundant values to save some math during
803 // the blur calculations
804 // precompute some values
805 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
806 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
807
808 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -0800809 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700810 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700811 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
812 normalizeFactor += weights[r + radius];
813 }
814
815 //Now we need to normalize the weights because all our coefficients need to add up to one
816 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -0800817 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700818 weights[r + radius] *= normalizeFactor;
819 }
820}
821
822void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700823 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700824 float blurredPixel = 0.0f;
825 float currentPixel = 0.0f;
826
Romain Guy325a0f92011-01-05 15:26:55 -0800827 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700828
829 const uint8_t* input = source + y * width;
830 uint8_t* output = dest + y * width;
831
Romain Guy325a0f92011-01-05 15:26:55 -0800832 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700833 blurredPixel = 0.0f;
834 const float* gPtr = weights;
835 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800836 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700837 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -0800838 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700839 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700840 blurredPixel += currentPixel * gPtr[0];
841 gPtr++;
842 i++;
843 }
844 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800845 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700846 // Stepping left and right away from the pixel
847 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -0800848 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700849 validW = 0;
850 }
Romain Guy325a0f92011-01-05 15:26:55 -0800851 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700852 validW = width - 1;
853 }
854
Romain Guy325a0f92011-01-05 15:26:55 -0800855 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700856 blurredPixel += currentPixel * gPtr[0];
857 gPtr++;
858 }
859 }
860 *output = (uint8_t)blurredPixel;
861 output ++;
862 }
863 }
864}
865
866void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700867 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700868 float blurredPixel = 0.0f;
869 float currentPixel = 0.0f;
870
Romain Guy325a0f92011-01-05 15:26:55 -0800871 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700872
873 uint8_t* output = dest + y * width;
874
Romain Guy325a0f92011-01-05 15:26:55 -0800875 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700876 blurredPixel = 0.0f;
877 const float* gPtr = weights;
878 const uint8_t* input = source + x;
879 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800880 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700881 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -0800882 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700883 currentPixel = (float)(*i);
884 blurredPixel += currentPixel * gPtr[0];
885 gPtr++;
886 i += width;
887 }
888 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800889 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700890 int validH = y + r;
891 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -0800892 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700893 validH = 0;
894 }
Romain Guy325a0f92011-01-05 15:26:55 -0800895 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700896 validH = height - 1;
897 }
898
899 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -0800900 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700901 blurredPixel += currentPixel * gPtr[0];
902 gPtr++;
903 }
904 }
Romain Guy325a0f92011-01-05 15:26:55 -0800905 *output = (uint8_t) blurredPixel;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700906 output ++;
907 }
908 }
909}
910
911
912void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
913 float *gaussian = new float[2 * radius + 1];
914 computeGaussianWeights(gaussian, radius);
Romain Guyd71dd362011-12-12 19:03:35 -0800915
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700916 uint8_t* scratch = new uint8_t[width * height];
Romain Guyd71dd362011-12-12 19:03:35 -0800917
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700918 horizontalBlur(gaussian, radius, image, scratch, width, height);
919 verticalBlur(gaussian, radius, scratch, image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -0800920
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700921 delete[] gaussian;
922 delete[] scratch;
923}
924
Romain Guy694b5192010-07-21 21:33:20 -0700925}; // namespace uirenderer
926}; // namespace android