blob: d7937c74f25e2809e514434caac004b131180574 [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 Guy15bc6432011-12-13 13:11:32 -080025#include "Caches.h"
Romain Guyc9855a52011-01-21 21:14:15 -080026#include "Debug.h"
Romain Guy51769a62010-07-23 00:28:00 -070027#include "FontRenderer.h"
Chet Haase7de0cb12011-12-05 16:35:38 -080028#include "Caches.h"
Romain Guy51769a62010-07-23 00:28:00 -070029
Romain Guy694b5192010-07-21 21:33:20 -070030namespace android {
31namespace uirenderer {
32
33///////////////////////////////////////////////////////////////////////////////
Romain Guy51769a62010-07-23 00:28:00 -070034// Defines
35///////////////////////////////////////////////////////////////////////////////
36
37#define DEFAULT_TEXT_CACHE_WIDTH 1024
38#define DEFAULT_TEXT_CACHE_HEIGHT 256
Chet Haase44984ea2011-05-19 13:50:47 -070039#define MAX_TEXT_CACHE_WIDTH 2048
Chet Haase7de0cb12011-12-05 16:35:38 -080040#define TEXTURE_BORDER_SIZE 2
41
Romain Guy97771732012-02-28 18:17:02 -080042#define AUTO_KERN(prev, next) (((next) - (prev) + 32) >> 6 << 16)
43
Chet Haase7de0cb12011-12-05 16:35:38 -080044///////////////////////////////////////////////////////////////////////////////
45// CacheTextureLine
46///////////////////////////////////////////////////////////////////////////////
47
48bool CacheTextureLine::fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
49 if (glyph.fHeight + TEXTURE_BORDER_SIZE > mMaxHeight) {
50 return false;
51 }
52
53 if (mCurrentCol + glyph.fWidth + TEXTURE_BORDER_SIZE < mMaxWidth) {
54 *retOriginX = mCurrentCol + 1;
55 *retOriginY = mCurrentRow + 1;
56 mCurrentCol += glyph.fWidth + TEXTURE_BORDER_SIZE;
57 mDirty = true;
58 return true;
59 }
60
61 return false;
62}
Chet Haase44984ea2011-05-19 13:50:47 -070063
Romain Guy51769a62010-07-23 00:28:00 -070064///////////////////////////////////////////////////////////////////////////////
Romain Guy694b5192010-07-21 21:33:20 -070065// Font
66///////////////////////////////////////////////////////////////////////////////
67
Romain Guy2577db12011-01-18 13:02:38 -080068Font::Font(FontRenderer* state, uint32_t fontId, float fontSize,
Romain Guybd496bc2011-08-02 17:32:41 -070069 int flags, uint32_t italicStyle, uint32_t scaleX,
70 SkPaint::Style style, uint32_t strokeWidth) :
Romain Guy2577db12011-01-18 13:02:38 -080071 mState(state), mFontId(fontId), mFontSize(fontSize),
Romain Guybd496bc2011-08-02 17:32:41 -070072 mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX),
73 mStyle(style), mStrokeWidth(mStrokeWidth) {
Romain Guy694b5192010-07-21 21:33:20 -070074}
75
76
77Font::~Font() {
78 for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
79 if (mState->mActiveFonts[ct] == this) {
80 mState->mActiveFonts.removeAt(ct);
81 break;
82 }
83 }
84
85 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
Romain Guy726aeba2011-06-01 14:52:00 -070086 delete mCachedGlyphs.valueAt(i);
Romain Guy694b5192010-07-21 21:33:20 -070087 }
88}
89
Chet Haase9a824562011-12-16 15:44:59 -080090void Font::invalidateTextureCache(CacheTextureLine *cacheLine) {
Romain Guy694b5192010-07-21 21:33:20 -070091 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
Chet Haase9a824562011-12-16 15:44:59 -080092 CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i);
93 if (cacheLine == NULL || cachedGlyph->mCachedTextureLine == cacheLine) {
94 cachedGlyph->mIsValid = false;
95 }
Romain Guy694b5192010-07-21 21:33:20 -070096 }
97}
98
Romain Guy671d6cf2012-01-18 12:39:17 -080099void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
100 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700101 int nPenX = x + glyph->mBitmapLeft;
102 int nPenY = y + glyph->mBitmapTop;
103
104 int width = (int) glyph->mBitmapWidth;
105 int height = (int) glyph->mBitmapHeight;
106
Romain Guy61c8c9c2010-08-09 20:48:09 -0700107 if (bounds->bottom > nPenY) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700108 bounds->bottom = nPenY;
109 }
Romain Guy61c8c9c2010-08-09 20:48:09 -0700110 if (bounds->left > nPenX) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700111 bounds->left = nPenX;
112 }
Romain Guy61c8c9c2010-08-09 20:48:09 -0700113 if (bounds->right < nPenX + width) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700114 bounds->right = nPenX + width;
115 }
Romain Guy61c8c9c2010-08-09 20:48:09 -0700116 if (bounds->top < nPenY + height) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700117 bounds->top = nPenY + height;
118 }
119}
120
Romain Guy671d6cf2012-01-18 12:39:17 -0800121void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
122 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
Romain Guy694b5192010-07-21 21:33:20 -0700123 int nPenX = x + glyph->mBitmapLeft;
124 int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
125
Romain Guy51769a62010-07-23 00:28:00 -0700126 float u1 = glyph->mBitmapMinU;
127 float u2 = glyph->mBitmapMaxU;
128 float v1 = glyph->mBitmapMinV;
129 float v2 = glyph->mBitmapMaxV;
130
131 int width = (int) glyph->mBitmapWidth;
132 int height = (int) glyph->mBitmapHeight;
133
Romain Guyd71dd362011-12-12 19:03:35 -0800134 mState->appendMeshQuad(nPenX, nPenY, u1, v2,
135 nPenX + width, nPenY, u2, v2,
136 nPenX + width, nPenY - height, u2, v1,
Chet Haase7de0cb12011-12-05 16:35:38 -0800137 nPenX, nPenY - height, u1, v1, glyph->mCachedTextureLine->mCacheTexture);
Romain Guy694b5192010-07-21 21:33:20 -0700138}
139
Romain Guy671d6cf2012-01-18 12:39:17 -0800140void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
141 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700142 int nPenX = x + glyph->mBitmapLeft;
143 int nPenY = y + glyph->mBitmapTop;
144
145 uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
146 uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
147
Chet Haase7de0cb12011-12-05 16:35:38 -0800148 CacheTexture *cacheTexture = glyph->mCachedTextureLine->mCacheTexture;
149 uint32_t cacheWidth = cacheTexture->mWidth;
150 const uint8_t* cacheBuffer = cacheTexture->mTexture;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700151
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700152 uint32_t cacheX = 0, cacheY = 0;
153 int32_t bX = 0, bY = 0;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700154 for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
155 for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
Romain Guyb6294902012-02-02 15:13:18 -0800156#if DEBUG_FONT_RENDERER
Romain Guyb45c0c92010-08-26 20:35:23 -0700157 if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
Steve Block3762c312012-01-06 19:20:56 +0000158 ALOGE("Skipping invalid index");
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700159 continue;
160 }
Romain Guyb6294902012-02-02 15:13:18 -0800161#endif
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700162 uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
163 bitmap[bY * bitmapW + bX] = tempCol;
164 }
165 }
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700166}
167
Romain Guy97771732012-02-28 18:17:02 -0800168void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
169 SkPathMeasure& measure, SkPoint* position, SkVector* tangent) {
170 const float halfWidth = glyph->mBitmapWidth * 0.5f;
171 const float height = glyph->mBitmapHeight;
172
Romain Guy97771732012-02-28 18:17:02 -0800173 vOffset += glyph->mBitmapTop + height;
174
Romain Guy97771732012-02-28 18:17:02 -0800175 SkPoint destination[4];
Romain Guydd7c8e4c2012-03-01 12:08:38 -0800176 measure.getPosTan(x + hOffset + glyph->mBitmapLeft + halfWidth, position, tangent);
Romain Guy97771732012-02-28 18:17:02 -0800177
178 // Move along the tangent and offset by the normal
179 destination[0].set(-tangent->fX * halfWidth - tangent->fY * vOffset,
180 -tangent->fY * halfWidth + tangent->fX * vOffset);
181 destination[1].set(tangent->fX * halfWidth - tangent->fY * vOffset,
182 tangent->fY * halfWidth + tangent->fX * vOffset);
183 destination[2].set(destination[1].fX + tangent->fY * height,
184 destination[1].fY - tangent->fX * height);
185 destination[3].set(destination[0].fX + tangent->fY * height,
186 destination[0].fY - tangent->fX * height);
187
Romain Guydd7c8e4c2012-03-01 12:08:38 -0800188 const float u1 = glyph->mBitmapMinU;
189 const float u2 = glyph->mBitmapMaxU;
190 const float v1 = glyph->mBitmapMinV;
191 const float v2 = glyph->mBitmapMaxV;
192
Romain Guy97771732012-02-28 18:17:02 -0800193 mState->appendRotatedMeshQuad(
194 position->fX + destination[0].fX,
195 position->fY + destination[0].fY, u1, v2,
196 position->fX + destination[1].fX,
197 position->fY + destination[1].fY, u2, v2,
198 position->fX + destination[2].fX,
199 position->fY + destination[2].fY, u2, v1,
200 position->fX + destination[3].fX,
201 position->fY + destination[3].fY, u1, v1,
202 glyph->mCachedTextureLine->mCacheTexture);
203}
204
Chet Haase7de0cb12011-12-05 16:35:38 -0800205CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700206 CachedGlyphInfo* cachedGlyph = NULL;
Romain Guy726aeba2011-06-01 14:52:00 -0700207 ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
Romain Guy1e45aae2010-08-13 19:39:53 -0700208 if (index >= 0) {
209 cachedGlyph = mCachedGlyphs.valueAt(index);
210 } else {
Romain Guy726aeba2011-06-01 14:52:00 -0700211 cachedGlyph = cacheGlyph(paint, textUnit);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700212 }
213
214 // Is the glyph still in texture cache?
215 if (!cachedGlyph->mIsValid) {
Romain Guy726aeba2011-06-01 14:52:00 -0700216 const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700217 updateGlyphCache(paint, skiaGlyph, cachedGlyph);
218 }
219
220 return cachedGlyph;
221}
222
Romain Guy726aeba2011-06-01 14:52:00 -0700223void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700224 int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
225 if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
Romain Guy726aeba2011-06-01 14:52:00 -0700226 render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
Romain Guy671d6cf2012-01-18 12:39:17 -0800227 bitmapW, bitmapH, NULL, NULL);
Romain Guy61c8c9c2010-08-09 20:48:09 -0700228 } else {
Romain Guy726aeba2011-06-01 14:52:00 -0700229 render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
Romain Guy671d6cf2012-01-18 12:39:17 -0800230 0, 0, NULL, NULL);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700231 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700232}
233
Romain Guy671d6cf2012-01-18 12:39:17 -0800234void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
235 int numGlyphs, int x, int y, const float* positions) {
236 render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
237 0, 0, NULL, positions);
238}
239
Romain Guy97771732012-02-28 18:17:02 -0800240void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
241 int numGlyphs, SkPath* path, float hOffset, float vOffset) {
242 if (numGlyphs == 0 || text == NULL || len == 0) {
243 return;
244 }
245
246 text += start;
247
248 int glyphsCount = 0;
249 SkFixed prevRsbDelta = 0;
250
251 float penX = 0.0f;
252
253 SkPoint position;
254 SkVector tangent;
255
256 SkPathMeasure measure(*path, false);
257 float pathLength = SkScalarToFloat(measure.getLength());
258
259 if (paint->getTextAlign() != SkPaint::kLeft_Align) {
260 float textWidth = SkScalarToFloat(paint->measureText(text, len));
261 float pathOffset = pathLength;
262 if (paint->getTextAlign() == SkPaint::kCenter_Align) {
263 textWidth *= 0.5f;
264 pathOffset *= 0.5f;
265 }
266 penX += pathOffset - textWidth;
267 }
268
Romain Guydd7c8e4c2012-03-01 12:08:38 -0800269 while (glyphsCount < numGlyphs && penX < pathLength) {
Romain Guy97771732012-02-28 18:17:02 -0800270 glyph_t glyph = GET_GLYPH(text);
271
272 if (IS_END_OF_STRING(glyph)) {
273 break;
274 }
275
276 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
277 penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
278 prevRsbDelta = cachedGlyph->mRsbDelta;
279
280 if (cachedGlyph->mIsValid) {
Romain Guydd7c8e4c2012-03-01 12:08:38 -0800281 drawCachedGlyph(cachedGlyph, penX, hOffset, vOffset, measure, &position, &tangent);
Romain Guy97771732012-02-28 18:17:02 -0800282 }
283
284 penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
285
286 glyphsCount++;
287 }
288}
289
Romain Guy726aeba2011-06-01 14:52:00 -0700290void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700291 int numGlyphs, Rect *bounds) {
292 if (bounds == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000293 ALOGE("No return rectangle provided to measure text");
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700294 return;
295 }
296 bounds->set(1e6, -1e6, -1e6, 1e6);
Romain Guy671d6cf2012-01-18 12:39:17 -0800297 render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, NULL);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700298}
299
Romain Guy726aeba2011-06-01 14:52:00 -0700300void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700301 int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
Romain Guy97771732012-02-28 18:17:02 -0800302 uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) {
Romain Guy694b5192010-07-21 21:33:20 -0700303 if (numGlyphs == 0 || text == NULL || len == 0) {
304 return;
305 }
306
Romain Guy671d6cf2012-01-18 12:39:17 -0800307 static RenderGlyph gRenderGlyph[] = {
308 &android::uirenderer::Font::drawCachedGlyph,
309 &android::uirenderer::Font::drawCachedGlyphBitmap,
310 &android::uirenderer::Font::measureCachedGlyph
311 };
312 RenderGlyph render = gRenderGlyph[mode];
Romain Guy694b5192010-07-21 21:33:20 -0700313
Romain Guy97771732012-02-28 18:17:02 -0800314 text += start;
315 int glyphsCount = 0;
316
Romain Guyb6294902012-02-02 15:13:18 -0800317 if (CC_LIKELY(positions == NULL)) {
Romain Guy671d6cf2012-01-18 12:39:17 -0800318 SkFixed prevRsbDelta = 0;
Romain Guy694b5192010-07-21 21:33:20 -0700319
Romain Guy97771732012-02-28 18:17:02 -0800320 float penX = x + 0.5f;
Romain Guy671d6cf2012-01-18 12:39:17 -0800321 int penY = y;
Romain Guy694b5192010-07-21 21:33:20 -0700322
Romain Guy671d6cf2012-01-18 12:39:17 -0800323 while (glyphsCount < numGlyphs) {
324 glyph_t glyph = GET_GLYPH(text);
325
326 // Reached the end of the string
327 if (IS_END_OF_STRING(glyph)) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700328 break;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700329 }
Romain Guy671d6cf2012-01-18 12:39:17 -0800330
331 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
Romain Guy97771732012-02-28 18:17:02 -0800332 penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
Romain Guy671d6cf2012-01-18 12:39:17 -0800333 prevRsbDelta = cachedGlyph->mRsbDelta;
334
335 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
336 if (cachedGlyph->mIsValid) {
337 (*this.*render)(cachedGlyph, (int) floorf(penX), penY,
338 bitmap, bitmapW, bitmapH, bounds, positions);
339 }
340
341 penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
342
343 glyphsCount++;
Romain Guy694b5192010-07-21 21:33:20 -0700344 }
Romain Guy671d6cf2012-01-18 12:39:17 -0800345 } else {
346 const SkPaint::Align align = paint->getTextAlign();
Romain Guy694b5192010-07-21 21:33:20 -0700347
Romain Guy671d6cf2012-01-18 12:39:17 -0800348 // This is for renderPosText()
349 while (glyphsCount < numGlyphs) {
350 glyph_t glyph = GET_GLYPH(text);
Romain Guy694b5192010-07-21 21:33:20 -0700351
Romain Guy671d6cf2012-01-18 12:39:17 -0800352 // Reached the end of the string
353 if (IS_END_OF_STRING(glyph)) {
354 break;
355 }
356
357 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
358
359 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
360 if (cachedGlyph->mIsValid) {
361 int penX = x + positions[(glyphsCount << 1)];
362 int penY = y + positions[(glyphsCount << 1) + 1];
363
364 switch (align) {
365 case SkPaint::kRight_Align:
366 penX -= SkFixedToFloat(cachedGlyph->mAdvanceX);
367 penY -= SkFixedToFloat(cachedGlyph->mAdvanceY);
368 break;
369 case SkPaint::kCenter_Align:
370 penX -= SkFixedToFloat(cachedGlyph->mAdvanceX >> 1);
371 penY -= SkFixedToFloat(cachedGlyph->mAdvanceY >> 1);
372 default:
373 break;
374 }
375
376 (*this.*render)(cachedGlyph, penX, penY,
377 bitmap, bitmapW, bitmapH, bounds, positions);
378 }
379
380 glyphsCount++;
Romain Guy694b5192010-07-21 21:33:20 -0700381 }
382 }
383}
384
Romain Guy51769a62010-07-23 00:28:00 -0700385void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
Romain Guy694b5192010-07-21 21:33:20 -0700386 glyph->mAdvanceX = skiaGlyph.fAdvanceX;
387 glyph->mAdvanceY = skiaGlyph.fAdvanceY;
388 glyph->mBitmapLeft = skiaGlyph.fLeft;
389 glyph->mBitmapTop = skiaGlyph.fTop;
Romain Guy2bffd262010-09-12 17:40:02 -0700390 glyph->mLsbDelta = skiaGlyph.fLsbDelta;
391 glyph->mRsbDelta = skiaGlyph.fRsbDelta;
Romain Guy694b5192010-07-21 21:33:20 -0700392
393 uint32_t startX = 0;
394 uint32_t startY = 0;
395
Romain Guy694b5192010-07-21 21:33:20 -0700396 // Get the bitmap for the glyph
397 paint->findImage(skiaGlyph);
Chet Haase7de0cb12011-12-05 16:35:38 -0800398 mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700399
400 if (!glyph->mIsValid) {
401 return;
402 }
403
404 uint32_t endX = startX + skiaGlyph.fWidth;
405 uint32_t endY = startY + skiaGlyph.fHeight;
406
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700407 glyph->mStartX = startX;
408 glyph->mStartY = startY;
Romain Guy694b5192010-07-21 21:33:20 -0700409 glyph->mBitmapWidth = skiaGlyph.fWidth;
410 glyph->mBitmapHeight = skiaGlyph.fHeight;
411
Chet Haase7de0cb12011-12-05 16:35:38 -0800412 uint32_t cacheWidth = glyph->mCachedTextureLine->mCacheTexture->mWidth;
413 uint32_t cacheHeight = glyph->mCachedTextureLine->mCacheTexture->mHeight;
Romain Guy694b5192010-07-21 21:33:20 -0700414
415 glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
416 glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
417 glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
418 glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
419
Romain Guy51769a62010-07-23 00:28:00 -0700420 mState->mUploadTexture = true;
Romain Guy694b5192010-07-21 21:33:20 -0700421}
422
Chet Haase7de0cb12011-12-05 16:35:38 -0800423CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) {
Romain Guy51769a62010-07-23 00:28:00 -0700424 CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
Romain Guy694b5192010-07-21 21:33:20 -0700425 mCachedGlyphs.add(glyph, newGlyph);
426
Romain Guy726aeba2011-06-01 14:52:00 -0700427 const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
Romain Guy694b5192010-07-21 21:33:20 -0700428 newGlyph->mGlyphIndex = skiaGlyph.fID;
429 newGlyph->mIsValid = false;
430
431 updateGlyphCache(paint, skiaGlyph, newGlyph);
432
433 return newGlyph;
434}
435
Romain Guy2577db12011-01-18 13:02:38 -0800436Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
Romain Guybd496bc2011-08-02 17:32:41 -0700437 int flags, uint32_t italicStyle, uint32_t scaleX,
438 SkPaint::Style style, uint32_t strokeWidth) {
Romain Guy694b5192010-07-21 21:33:20 -0700439 Vector<Font*> &activeFonts = state->mActiveFonts;
440
441 for (uint32_t i = 0; i < activeFonts.size(); i++) {
Romain Guy51769a62010-07-23 00:28:00 -0700442 Font* font = activeFonts[i];
Romain Guy2577db12011-01-18 13:02:38 -0800443 if (font->mFontId == fontId && font->mFontSize == fontSize &&
Chet Haase8668f8a2011-03-02 13:51:36 -0800444 font->mFlags == flags && font->mItalicStyle == italicStyle &&
Romain Guybd496bc2011-08-02 17:32:41 -0700445 font->mScaleX == scaleX && font->mStyle == style &&
446 (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
Romain Guy51769a62010-07-23 00:28:00 -0700447 return font;
Romain Guy694b5192010-07-21 21:33:20 -0700448 }
449 }
450
Romain Guybd496bc2011-08-02 17:32:41 -0700451 Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
452 scaleX, style, strokeWidth);
Romain Guy694b5192010-07-21 21:33:20 -0700453 activeFonts.push(newFont);
454 return newFont;
455}
456
457///////////////////////////////////////////////////////////////////////////////
458// FontRenderer
459///////////////////////////////////////////////////////////////////////////////
460
Romain Guy514fb182011-01-19 14:38:29 -0800461static bool sLogFontRendererCreate = true;
462
Romain Guy694b5192010-07-21 21:33:20 -0700463FontRenderer::FontRenderer() {
Romain Guyc9855a52011-01-21 21:14:15 -0800464 if (sLogFontRendererCreate) {
465 INIT_LOGD("Creating FontRenderer");
466 }
Romain Guy51769a62010-07-23 00:28:00 -0700467
Romain Guyb45c0c92010-08-26 20:35:23 -0700468 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700469 mInitialized = false;
470 mMaxNumberOfQuads = 1024;
471 mCurrentQuadIndex = 0;
472
Romain Guy9cccc2b92010-08-07 23:46:15 -0700473 mTextMeshPtr = NULL;
Chet Haase7de0cb12011-12-05 16:35:38 -0800474 mCurrentCacheTexture = NULL;
475 mLastCacheTexture = NULL;
476 mCacheTextureSmall = NULL;
477 mCacheTexture128 = NULL;
478 mCacheTexture256 = NULL;
479 mCacheTexture512 = NULL;
Romain Guy9cccc2b92010-08-07 23:46:15 -0700480
Chet Haase2a47c142011-12-14 15:22:56 -0800481 mLinearFiltering = false;
482
Romain Guy694b5192010-07-21 21:33:20 -0700483 mIndexBufferID = 0;
484
Chet Haase7de0cb12011-12-05 16:35:38 -0800485 mSmallCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
486 mSmallCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -0700487
488 char property[PROPERTY_VALUE_MAX];
489 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800490 if (sLogFontRendererCreate) {
491 INIT_LOGD(" Setting text cache width to %s pixels", property);
492 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800493 mSmallCacheWidth = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -0700494 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800495 if (sLogFontRendererCreate) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800496 INIT_LOGD(" Using default text cache width of %i pixels", mSmallCacheWidth);
Romain Guy514fb182011-01-19 14:38:29 -0800497 }
Romain Guy51769a62010-07-23 00:28:00 -0700498 }
499
500 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800501 if (sLogFontRendererCreate) {
502 INIT_LOGD(" Setting text cache width to %s pixels", property);
503 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800504 mSmallCacheHeight = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -0700505 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800506 if (sLogFontRendererCreate) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800507 INIT_LOGD(" Using default text cache height of %i pixels", mSmallCacheHeight);
Romain Guy514fb182011-01-19 14:38:29 -0800508 }
Romain Guy51769a62010-07-23 00:28:00 -0700509 }
Romain Guy514fb182011-01-19 14:38:29 -0800510
511 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -0700512}
513
514FontRenderer::~FontRenderer() {
515 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
516 delete mCacheLines[i];
517 }
518 mCacheLines.clear();
519
Romain Guy9cccc2b92010-08-07 23:46:15 -0700520 if (mInitialized) {
Romain Guyb0317982012-03-21 11:52:52 -0700521 glDeleteBuffers(1, &mIndexBufferID);
522
Romain Guy9cccc2b92010-08-07 23:46:15 -0700523 delete[] mTextMeshPtr;
Chet Haase7de0cb12011-12-05 16:35:38 -0800524 delete mCacheTextureSmall;
525 delete mCacheTexture128;
526 delete mCacheTexture256;
527 delete mCacheTexture512;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700528 }
Romain Guy694b5192010-07-21 21:33:20 -0700529
530 Vector<Font*> fontsToDereference = mActiveFonts;
531 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
532 delete fontsToDereference[i];
533 }
534}
535
536void FontRenderer::flushAllAndInvalidate() {
537 if (mCurrentQuadIndex != 0) {
538 issueDrawCommand();
539 mCurrentQuadIndex = 0;
540 }
541 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
542 mActiveFonts[i]->invalidateTextureCache();
543 }
544 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
545 mCacheLines[i]->mCurrentCol = 0;
546 }
547}
548
Chet Haase9a824562011-12-16 15:44:59 -0800549void FontRenderer::deallocateTextureMemory(CacheTexture *cacheTexture) {
550 if (cacheTexture && cacheTexture->mTexture) {
551 glDeleteTextures(1, &cacheTexture->mTextureId);
552 delete cacheTexture->mTexture;
553 cacheTexture->mTexture = NULL;
554 }
555}
556
557void FontRenderer::flushLargeCaches() {
558 if ((!mCacheTexture128 || !mCacheTexture128->mTexture) &&
559 (!mCacheTexture256 || !mCacheTexture256->mTexture) &&
560 (!mCacheTexture512 || !mCacheTexture512->mTexture)) {
561 // Typical case; no large glyph caches allocated
562 return;
563 }
564
565 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
566 CacheTextureLine* cacheLine = mCacheLines[i];
567 if ((cacheLine->mCacheTexture == mCacheTexture128 ||
568 cacheLine->mCacheTexture == mCacheTexture256 ||
569 cacheLine->mCacheTexture == mCacheTexture512) &&
570 cacheLine->mCacheTexture->mTexture != NULL) {
571 cacheLine->mCurrentCol = 0;
572 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
573 mActiveFonts[i]->invalidateTextureCache(cacheLine);
574 }
575 }
576 }
577
578 deallocateTextureMemory(mCacheTexture128);
579 deallocateTextureMemory(mCacheTexture256);
580 deallocateTextureMemory(mCacheTexture512);
581}
582
Chet Haase2a47c142011-12-14 15:22:56 -0800583void FontRenderer::allocateTextureMemory(CacheTexture *cacheTexture) {
584 int width = cacheTexture->mWidth;
585 int height = cacheTexture->mHeight;
586 cacheTexture->mTexture = new uint8_t[width * height];
587 memset(cacheTexture->mTexture, 0, width * height * sizeof(uint8_t));
588 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
589 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
590 // Initialize texture dimensions
591 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
592 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800593
Chet Haase2a47c142011-12-14 15:22:56 -0800594 const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST;
595 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
596 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
597
598 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
599 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
Chet Haase7de0cb12011-12-05 16:35:38 -0800600}
601
602void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
603 uint32_t* retOriginX, uint32_t* retOriginY) {
604 cachedGlyph->mIsValid = false;
Romain Guy694b5192010-07-21 21:33:20 -0700605 // If the glyph is too tall, don't cache it
Chet Haase7de0cb12011-12-05 16:35:38 -0800606 if (glyph.fHeight + TEXTURE_BORDER_SIZE > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
Steve Block3762c312012-01-06 19:20:56 +0000607 ALOGE("Font size to large to fit in cache. width, height = %i, %i",
Chet Haase7de0cb12011-12-05 16:35:38 -0800608 (int) glyph.fWidth, (int) glyph.fHeight);
609 return;
Romain Guy694b5192010-07-21 21:33:20 -0700610 }
611
612 // Now copy the bitmap into the cache texture
613 uint32_t startX = 0;
614 uint32_t startY = 0;
615
616 bool bitmapFit = false;
Chet Haase7de0cb12011-12-05 16:35:38 -0800617 CacheTextureLine *cacheLine;
Romain Guy694b5192010-07-21 21:33:20 -0700618 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
619 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
620 if (bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800621 cacheLine = mCacheLines[i];
Romain Guy694b5192010-07-21 21:33:20 -0700622 break;
623 }
624 }
625
626 // If the new glyph didn't fit, flush the state so far and invalidate everything
627 if (!bitmapFit) {
628 flushAllAndInvalidate();
629
630 // Try to fit it again
631 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
632 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
633 if (bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800634 cacheLine = mCacheLines[i];
Romain Guy694b5192010-07-21 21:33:20 -0700635 break;
636 }
637 }
638
639 // if we still don't fit, something is wrong and we shouldn't draw
640 if (!bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800641 return;
Romain Guy694b5192010-07-21 21:33:20 -0700642 }
643 }
644
Chet Haase7de0cb12011-12-05 16:35:38 -0800645 cachedGlyph->mCachedTextureLine = cacheLine;
646
Romain Guy694b5192010-07-21 21:33:20 -0700647 *retOriginX = startX;
648 *retOriginY = startY;
649
650 uint32_t endX = startX + glyph.fWidth;
651 uint32_t endY = startY + glyph.fHeight;
652
Chet Haase7de0cb12011-12-05 16:35:38 -0800653 uint32_t cacheWidth = cacheLine->mMaxWidth;
Romain Guy694b5192010-07-21 21:33:20 -0700654
Chet Haase7de0cb12011-12-05 16:35:38 -0800655 CacheTexture *cacheTexture = cacheLine->mCacheTexture;
656 if (cacheTexture->mTexture == NULL) {
657 // Large-glyph texture memory is allocated only as needed
Chet Haase2a47c142011-12-14 15:22:56 -0800658 allocateTextureMemory(cacheTexture);
Chet Haase7de0cb12011-12-05 16:35:38 -0800659 }
660 uint8_t* cacheBuffer = cacheTexture->mTexture;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700661 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700662 unsigned int stride = glyph.rowBytes();
663
664 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
665 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
666 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700667 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
Romain Guyb45c0c92010-08-26 20:35:23 -0700668 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
Romain Guy694b5192010-07-21 21:33:20 -0700669 }
670 }
Romain Guy97771732012-02-28 18:17:02 -0800671
Chet Haase7de0cb12011-12-05 16:35:38 -0800672 cachedGlyph->mIsValid = true;
Romain Guy694b5192010-07-21 21:33:20 -0700673}
674
Chet Haase7de0cb12011-12-05 16:35:38 -0800675CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800676 GLuint textureId;
677 glGenTextures(1, &textureId);
Chet Haase2a47c142011-12-14 15:22:56 -0800678 uint8_t* textureMemory = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700679
Chet Haase2a47c142011-12-14 15:22:56 -0800680 CacheTexture* cacheTexture = new CacheTexture(textureMemory, textureId, width, height);
681 if (allocate) {
682 allocateTextureMemory(cacheTexture);
683 }
684 return cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800685}
686
687void FontRenderer::initTextTexture() {
688 mCacheLines.clear();
689
690 // Next, use other, separate caches for large glyphs.
691 uint16_t maxWidth = 0;
692 if (Caches::hasInstance()) {
693 maxWidth = Caches::getInstance().maxTextureSize;
Chet Haase44984ea2011-05-19 13:50:47 -0700694 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800695 if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) {
696 maxWidth = MAX_TEXT_CACHE_WIDTH;
697 }
698 if (mCacheTextureSmall != NULL) {
699 delete mCacheTextureSmall;
700 delete mCacheTexture128;
701 delete mCacheTexture256;
702 delete mCacheTexture512;
703 }
704 mCacheTextureSmall = createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true);
705 mCacheTexture128 = createCacheTexture(maxWidth, 256, false);
706 mCacheTexture256 = createCacheTexture(maxWidth, 256, false);
707 mCacheTexture512 = createCacheTexture(maxWidth, 512, false);
708 mCurrentCacheTexture = mCacheTextureSmall;
709
710 mUploadTexture = false;
711 // Split up our default cache texture into lines of certain widths
712 int nextLine = 0;
713 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 18, nextLine, 0, mCacheTextureSmall));
714 nextLine += mCacheLines.top()->mMaxHeight;
715 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
716 nextLine += mCacheLines.top()->mMaxHeight;
717 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
718 nextLine += mCacheLines.top()->mMaxHeight;
719 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
720 nextLine += mCacheLines.top()->mMaxHeight;
721 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
722 nextLine += mCacheLines.top()->mMaxHeight;
723 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 42, nextLine, 0, mCacheTextureSmall));
724 nextLine += mCacheLines.top()->mMaxHeight;
725 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, mSmallCacheHeight - nextLine,
726 nextLine, 0, mCacheTextureSmall));
727
728 // The first cache is split into 2 lines of height 128, the rest have just one cache line.
729 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 0, 0, mCacheTexture128));
730 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 128, 0, mCacheTexture128));
731 mCacheLines.push(new CacheTextureLine(maxWidth, 256, 0, 0, mCacheTexture256));
732 mCacheLines.push(new CacheTextureLine(maxWidth, 512, 0, 0, mCacheTexture512));
Romain Guy694b5192010-07-21 21:33:20 -0700733}
734
735// Avoid having to reallocate memory and render quad by quad
736void FontRenderer::initVertexArrayBuffers() {
Romain Guyd71dd362011-12-12 19:03:35 -0800737 uint32_t numIndices = mMaxNumberOfQuads * 6;
738 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700739 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700740
741 // Four verts, two triangles , six indices per quad
742 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
743 int i6 = i * 6;
744 int i4 = i * 4;
745
746 indexBufferData[i6 + 0] = i4 + 0;
747 indexBufferData[i6 + 1] = i4 + 1;
748 indexBufferData[i6 + 2] = i4 + 2;
749
750 indexBufferData[i6 + 3] = i4 + 0;
751 indexBufferData[i6 + 4] = i4 + 2;
752 indexBufferData[i6 + 5] = i4 + 3;
753 }
754
755 glGenBuffers(1, &mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800756 Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700757 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
Romain Guy694b5192010-07-21 21:33:20 -0700758
759 free(indexBufferData);
760
Romain Guyd71dd362011-12-12 19:03:35 -0800761 uint32_t coordSize = 2;
Romain Guy694b5192010-07-21 21:33:20 -0700762 uint32_t uvSize = 2;
763 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700764 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
765 mTextMeshPtr = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700766}
767
768// We don't want to allocate anything unless we actually draw text
769void FontRenderer::checkInit() {
770 if (mInitialized) {
771 return;
772 }
773
774 initTextTexture();
775 initVertexArrayBuffers();
776
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700777 // We store a string with letters in a rough frequency of occurrence
778 mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
779 mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
780 mLatinPrecache += String16(",.?!()-+@;:`'");
781 mLatinPrecache += String16("0123456789");
782
Romain Guy694b5192010-07-21 21:33:20 -0700783 mInitialized = true;
784}
785
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700786void FontRenderer::checkTextureUpdate() {
Chet Haase7de0cb12011-12-05 16:35:38 -0800787 if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700788 return;
Romain Guy694b5192010-07-21 21:33:20 -0700789 }
790
Romain Guy2d4fd362011-12-13 22:00:19 -0800791 Caches& caches = Caches::getInstance();
792 GLuint lastTextureId = 0;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700793 // Iterate over all the cache lines and see which ones need to be updated
794 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
795 CacheTextureLine* cl = mCacheLines[i];
Chet Haase7de0cb12011-12-05 16:35:38 -0800796 if (cl->mDirty && cl->mCacheTexture->mTexture != NULL) {
797 CacheTexture* cacheTexture = cl->mCacheTexture;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700798 uint32_t xOffset = 0;
799 uint32_t yOffset = cl->mCurrentRow;
Chet Haase7de0cb12011-12-05 16:35:38 -0800800 uint32_t width = cl->mMaxWidth;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700801 uint32_t height = cl->mMaxHeight;
Chet Haase7de0cb12011-12-05 16:35:38 -0800802 void* textureData = cacheTexture->mTexture + (yOffset * width);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700803
Romain Guy2d4fd362011-12-13 22:00:19 -0800804 if (cacheTexture->mTextureId != lastTextureId) {
805 caches.activeTexture(0);
806 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
807 lastTextureId = cacheTexture->mTextureId;
808 }
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700809 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700810 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700811
812 cl->mDirty = false;
813 }
814 }
815
Chet Haase7de0cb12011-12-05 16:35:38 -0800816 glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
Chet Haase2a47c142011-12-14 15:22:56 -0800817 if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) {
818 const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST;
819 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
820 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
821 mCurrentCacheTexture->mLinearFiltering = mLinearFiltering;
822 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800823 mLastCacheTexture = mCurrentCacheTexture;
824
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700825 mUploadTexture = false;
826}
827
828void FontRenderer::issueDrawCommand() {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700829 checkTextureUpdate();
830
Romain Guy15bc6432011-12-13 13:11:32 -0800831 Caches& caches = Caches::getInstance();
Romain Guy2d4fd362011-12-13 22:00:19 -0800832 caches.bindIndicesBuffer(mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800833 if (!mDrawn) {
834 float* buffer = mTextMeshPtr;
835 int offset = 2;
836
837 bool force = caches.unbindMeshBuffer();
838 caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
839 caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
840 buffer + offset);
841 }
842
Romain Guy694b5192010-07-21 21:33:20 -0700843 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
Romain Guy5b3b3522010-10-27 18:57:51 -0700844
845 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700846}
847
Romain Guy97771732012-02-28 18:17:02 -0800848void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
849 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800850 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800851 if (texture != mCurrentCacheTexture) {
852 if (mCurrentQuadIndex != 0) {
853 // First, draw everything stored already which uses the previous texture
854 issueDrawCommand();
855 mCurrentQuadIndex = 0;
856 }
857 // Now use the new texture id
858 mCurrentCacheTexture = texture;
859 }
Romain Guy09147fb2010-07-22 13:08:20 -0700860
Romain Guy694b5192010-07-21 21:33:20 -0700861 const uint32_t vertsPerQuad = 4;
Romain Guyd71dd362011-12-12 19:03:35 -0800862 const uint32_t floatsPerVert = 4;
Romain Guy51769a62010-07-23 00:28:00 -0700863 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700864
Romain Guy694b5192010-07-21 21:33:20 -0700865 (*currentPos++) = x1;
866 (*currentPos++) = y1;
Romain Guy694b5192010-07-21 21:33:20 -0700867 (*currentPos++) = u1;
868 (*currentPos++) = v1;
869
870 (*currentPos++) = x2;
871 (*currentPos++) = y2;
Romain Guy694b5192010-07-21 21:33:20 -0700872 (*currentPos++) = u2;
873 (*currentPos++) = v2;
874
875 (*currentPos++) = x3;
876 (*currentPos++) = y3;
Romain Guy694b5192010-07-21 21:33:20 -0700877 (*currentPos++) = u3;
878 (*currentPos++) = v3;
879
880 (*currentPos++) = x4;
881 (*currentPos++) = y4;
Romain Guy694b5192010-07-21 21:33:20 -0700882 (*currentPos++) = u4;
883 (*currentPos++) = v4;
884
885 mCurrentQuadIndex++;
Romain Guy97771732012-02-28 18:17:02 -0800886}
887
888void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
889 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
890 float x4, float y4, float u4, float v4, CacheTexture* texture) {
891
892 if (mClip &&
893 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
894 return;
895 }
896
897 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
Romain Guy694b5192010-07-21 21:33:20 -0700898
Romain Guy5b3b3522010-10-27 18:57:51 -0700899 if (mBounds) {
900 mBounds->left = fmin(mBounds->left, x1);
901 mBounds->top = fmin(mBounds->top, y3);
902 mBounds->right = fmax(mBounds->right, x3);
903 mBounds->bottom = fmax(mBounds->bottom, y1);
904 }
905
Romain Guy694b5192010-07-21 21:33:20 -0700906 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
907 issueDrawCommand();
908 mCurrentQuadIndex = 0;
909 }
910}
911
Romain Guy97771732012-02-28 18:17:02 -0800912void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
913 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
914 float x4, float y4, float u4, float v4, CacheTexture* texture) {
915
916 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
917
918 if (mBounds) {
919 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
920 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
921 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
922 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
923 }
924
925 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
926 issueDrawCommand();
927 mCurrentQuadIndex = 0;
928 }
929}
930
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700931uint32_t FontRenderer::getRemainingCacheCapacity() {
932 uint32_t remainingCapacity = 0;
933 float totalPixels = 0;
934 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
935 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
936 totalPixels += mCacheLines[i]->mMaxWidth;
937 }
938 remainingCapacity = (remainingCapacity * 100) / totalPixels;
939 return remainingCapacity;
940}
941
942void FontRenderer::precacheLatin(SkPaint* paint) {
943 // Remaining capacity is measured in %
944 uint32_t remainingCapacity = getRemainingCacheCapacity();
945 uint32_t precacheIdx = 0;
Romain Guy054dc182010-10-15 17:55:25 -0700946 while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
Romain Guy726aeba2011-06-01 14:52:00 -0700947 mCurrentFont->getCachedGlyph(paint, (int32_t) mLatinPrecache[precacheIdx]);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700948 remainingCapacity = getRemainingCacheCapacity();
949 precacheIdx ++;
950 }
951}
952
953void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
954 uint32_t currentNumFonts = mActiveFonts.size();
Romain Guy325a0f92011-01-05 15:26:55 -0800955 int flags = 0;
956 if (paint->isFakeBoldText()) {
957 flags |= Font::kFakeBold;
958 }
Romain Guy2577db12011-01-18 13:02:38 -0800959
960 const float skewX = paint->getTextSkewX();
961 uint32_t italicStyle = *(uint32_t*) &skewX;
Chet Haase8668f8a2011-03-02 13:51:36 -0800962 const float scaleXFloat = paint->getTextScaleX();
963 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
Romain Guybd496bc2011-08-02 17:32:41 -0700964 SkPaint::Style style = paint->getStyle();
965 const float strokeWidthFloat = paint->getStrokeWidth();
966 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
967 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
968 scaleX, style, strokeWidth);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700969
970 const float maxPrecacheFontSize = 40.0f;
971 bool isNewFont = currentNumFonts != mActiveFonts.size();
972
Romain Guy2bffd262010-09-12 17:40:02 -0700973 if (isNewFont && fontSize <= maxPrecacheFontSize) {
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700974 precacheLatin(paint);
975 }
Romain Guy694b5192010-07-21 21:33:20 -0700976}
Romain Guy7975fb62010-10-01 16:36:14 -0700977
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700978FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Romain Guy1e45aae2010-08-13 19:39:53 -0700979 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
980 checkInit();
981
982 if (!mCurrentFont) {
983 DropShadow image;
984 image.width = 0;
985 image.height = 0;
986 image.image = NULL;
987 image.penX = 0;
988 image.penY = 0;
989 return image;
990 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700991
Romain Guy2d4fd362011-12-13 22:00:19 -0800992 mDrawn = false;
Romain Guyff98fa52011-11-28 09:35:09 -0800993 mClip = NULL;
994 mBounds = NULL;
995
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700996 Rect bounds;
Romain Guy726aeba2011-06-01 14:52:00 -0700997 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds);
Romain Guyff98fa52011-11-28 09:35:09 -0800998
Romain Guy1e45aae2010-08-13 19:39:53 -0700999 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
1000 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001001 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guyff98fa52011-11-28 09:35:09 -08001002
Romain Guy1e45aae2010-08-13 19:39:53 -07001003 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001004 dataBuffer[i] = 0;
1005 }
Romain Guy1e45aae2010-08-13 19:39:53 -07001006
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001007 int penX = radius - bounds.left;
1008 int penY = radius - bounds.bottom;
1009
Romain Guy726aeba2011-06-01 14:52:00 -07001010 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
Romain Guy1e45aae2010-08-13 19:39:53 -07001011 dataBuffer, paddedWidth, paddedHeight);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001012 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
1013
1014 DropShadow image;
1015 image.width = paddedWidth;
1016 image.height = paddedHeight;
1017 image.image = dataBuffer;
1018 image.penX = penX;
1019 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -08001020
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001021 return image;
1022}
Romain Guy694b5192010-07-21 21:33:20 -07001023
Romain Guy671d6cf2012-01-18 12:39:17 -08001024void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -07001025 checkInit();
1026
Romain Guy5b3b3522010-10-27 18:57:51 -07001027 mDrawn = false;
1028 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -07001029 mClip = clip;
Romain Guy671d6cf2012-01-18 12:39:17 -08001030}
Romain Guyff98fa52011-11-28 09:35:09 -08001031
Romain Guy671d6cf2012-01-18 12:39:17 -08001032void FontRenderer::finishRender() {
Romain Guy5b3b3522010-10-27 18:57:51 -07001033 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -08001034 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -07001035
1036 if (mCurrentQuadIndex != 0) {
1037 issueDrawCommand();
1038 mCurrentQuadIndex = 0;
1039 }
Romain Guy671d6cf2012-01-18 12:39:17 -08001040}
1041
1042bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
1043 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
1044 if (!mCurrentFont) {
1045 ALOGE("No font set");
1046 return false;
1047 }
1048
1049 initRender(clip, bounds);
1050 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
1051 finishRender();
1052
1053 return mDrawn;
1054}
1055
1056bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
1057 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
1058 const float* positions, Rect* bounds) {
1059 if (!mCurrentFont) {
1060 ALOGE("No font set");
1061 return false;
1062 }
1063
1064 initRender(clip, bounds);
1065 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
1066 finishRender();
Romain Guy5b3b3522010-10-27 18:57:51 -07001067
1068 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -07001069}
1070
Romain Guy97771732012-02-28 18:17:02 -08001071bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
1072 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
1073 float hOffset, float vOffset, Rect* bounds) {
1074 if (!mCurrentFont) {
1075 ALOGE("No font set");
1076 return false;
1077 }
1078
1079 initRender(clip, bounds);
1080 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
1081 finishRender();
1082
1083 return mDrawn;
1084}
1085
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001086void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
1087 // Compute gaussian weights for the blur
1088 // e is the euler's number
1089 float e = 2.718281828459045f;
1090 float pi = 3.1415926535897932f;
1091 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
1092 // x is of the form [-radius .. 0 .. radius]
1093 // and sigma varies with radius.
1094 // Based on some experimental radius values and sigma's
1095 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001096 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001097 // The larger the radius gets, the more our gaussian blur
1098 // will resemble a box blur since with large sigma
1099 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -08001100 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001101
1102 // Now compute the coefficints
1103 // We will store some redundant values to save some math during
1104 // the blur calculations
1105 // precompute some values
1106 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
1107 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
1108
1109 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -08001110 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -07001111 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001112 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
1113 normalizeFactor += weights[r + radius];
1114 }
1115
1116 //Now we need to normalize the weights because all our coefficients need to add up to one
1117 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -08001118 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001119 weights[r + radius] *= normalizeFactor;
1120 }
1121}
1122
1123void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -07001124 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001125 float blurredPixel = 0.0f;
1126 float currentPixel = 0.0f;
1127
Romain Guy325a0f92011-01-05 15:26:55 -08001128 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001129
1130 const uint8_t* input = source + y * width;
1131 uint8_t* output = dest + y * width;
1132
Romain Guy325a0f92011-01-05 15:26:55 -08001133 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001134 blurredPixel = 0.0f;
1135 const float* gPtr = weights;
1136 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -08001137 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001138 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -08001139 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -07001140 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001141 blurredPixel += currentPixel * gPtr[0];
1142 gPtr++;
1143 i++;
1144 }
1145 } else {
Romain Guy325a0f92011-01-05 15:26:55 -08001146 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001147 // Stepping left and right away from the pixel
1148 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -08001149 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001150 validW = 0;
1151 }
Romain Guy325a0f92011-01-05 15:26:55 -08001152 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001153 validW = width - 1;
1154 }
1155
Romain Guy325a0f92011-01-05 15:26:55 -08001156 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001157 blurredPixel += currentPixel * gPtr[0];
1158 gPtr++;
1159 }
1160 }
1161 *output = (uint8_t)blurredPixel;
1162 output ++;
1163 }
1164 }
1165}
1166
1167void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -07001168 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001169 float blurredPixel = 0.0f;
1170 float currentPixel = 0.0f;
1171
Romain Guy325a0f92011-01-05 15:26:55 -08001172 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001173
1174 uint8_t* output = dest + y * width;
1175
Romain Guy325a0f92011-01-05 15:26:55 -08001176 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001177 blurredPixel = 0.0f;
1178 const float* gPtr = weights;
1179 const uint8_t* input = source + x;
1180 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -08001181 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001182 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -08001183 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001184 currentPixel = (float)(*i);
1185 blurredPixel += currentPixel * gPtr[0];
1186 gPtr++;
1187 i += width;
1188 }
1189 } else {
Romain Guy325a0f92011-01-05 15:26:55 -08001190 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001191 int validH = y + r;
1192 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -08001193 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001194 validH = 0;
1195 }
Romain Guy325a0f92011-01-05 15:26:55 -08001196 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001197 validH = height - 1;
1198 }
1199
1200 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -08001201 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001202 blurredPixel += currentPixel * gPtr[0];
1203 gPtr++;
1204 }
1205 }
Romain Guy325a0f92011-01-05 15:26:55 -08001206 *output = (uint8_t) blurredPixel;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001207 output ++;
1208 }
1209 }
1210}
1211
1212
1213void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
1214 float *gaussian = new float[2 * radius + 1];
1215 computeGaussianWeights(gaussian, radius);
Romain Guyd71dd362011-12-12 19:03:35 -08001216
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001217 uint8_t* scratch = new uint8_t[width * height];
Romain Guyd71dd362011-12-12 19:03:35 -08001218
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001219 horizontalBlur(gaussian, radius, image, scratch, width, height);
1220 verticalBlur(gaussian, radius, scratch, image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -08001221
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001222 delete[] gaussian;
1223 delete[] scratch;
1224}
1225
Romain Guy694b5192010-07-21 21:33:20 -07001226}; // namespace uirenderer
1227}; // namespace android