blob: 92a7573dde54a70b0caec0874a7a20b1cec7e84c [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) {
521 delete[] mTextMeshPtr;
Chet Haase7de0cb12011-12-05 16:35:38 -0800522 delete mCacheTextureSmall;
523 delete mCacheTexture128;
524 delete mCacheTexture256;
525 delete mCacheTexture512;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700526 }
Romain Guy694b5192010-07-21 21:33:20 -0700527
528 Vector<Font*> fontsToDereference = mActiveFonts;
529 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
530 delete fontsToDereference[i];
531 }
532}
533
534void FontRenderer::flushAllAndInvalidate() {
535 if (mCurrentQuadIndex != 0) {
536 issueDrawCommand();
537 mCurrentQuadIndex = 0;
538 }
539 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
540 mActiveFonts[i]->invalidateTextureCache();
541 }
542 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
543 mCacheLines[i]->mCurrentCol = 0;
544 }
545}
546
Chet Haase9a824562011-12-16 15:44:59 -0800547void FontRenderer::deallocateTextureMemory(CacheTexture *cacheTexture) {
548 if (cacheTexture && cacheTexture->mTexture) {
549 glDeleteTextures(1, &cacheTexture->mTextureId);
550 delete cacheTexture->mTexture;
551 cacheTexture->mTexture = NULL;
552 }
553}
554
555void FontRenderer::flushLargeCaches() {
556 if ((!mCacheTexture128 || !mCacheTexture128->mTexture) &&
557 (!mCacheTexture256 || !mCacheTexture256->mTexture) &&
558 (!mCacheTexture512 || !mCacheTexture512->mTexture)) {
559 // Typical case; no large glyph caches allocated
560 return;
561 }
562
563 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
564 CacheTextureLine* cacheLine = mCacheLines[i];
565 if ((cacheLine->mCacheTexture == mCacheTexture128 ||
566 cacheLine->mCacheTexture == mCacheTexture256 ||
567 cacheLine->mCacheTexture == mCacheTexture512) &&
568 cacheLine->mCacheTexture->mTexture != NULL) {
569 cacheLine->mCurrentCol = 0;
570 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
571 mActiveFonts[i]->invalidateTextureCache(cacheLine);
572 }
573 }
574 }
575
576 deallocateTextureMemory(mCacheTexture128);
577 deallocateTextureMemory(mCacheTexture256);
578 deallocateTextureMemory(mCacheTexture512);
579}
580
Chet Haase2a47c142011-12-14 15:22:56 -0800581void FontRenderer::allocateTextureMemory(CacheTexture *cacheTexture) {
582 int width = cacheTexture->mWidth;
583 int height = cacheTexture->mHeight;
584 cacheTexture->mTexture = new uint8_t[width * height];
585 memset(cacheTexture->mTexture, 0, width * height * sizeof(uint8_t));
586 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
587 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
588 // Initialize texture dimensions
589 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
590 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800591
Chet Haase2a47c142011-12-14 15:22:56 -0800592 const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST;
593 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
594 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
595
596 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
597 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
Chet Haase7de0cb12011-12-05 16:35:38 -0800598}
599
600void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
601 uint32_t* retOriginX, uint32_t* retOriginY) {
602 cachedGlyph->mIsValid = false;
Romain Guy694b5192010-07-21 21:33:20 -0700603 // If the glyph is too tall, don't cache it
Chet Haase7de0cb12011-12-05 16:35:38 -0800604 if (glyph.fHeight + TEXTURE_BORDER_SIZE > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
Steve Block3762c312012-01-06 19:20:56 +0000605 ALOGE("Font size to large to fit in cache. width, height = %i, %i",
Chet Haase7de0cb12011-12-05 16:35:38 -0800606 (int) glyph.fWidth, (int) glyph.fHeight);
607 return;
Romain Guy694b5192010-07-21 21:33:20 -0700608 }
609
610 // Now copy the bitmap into the cache texture
611 uint32_t startX = 0;
612 uint32_t startY = 0;
613
614 bool bitmapFit = false;
Chet Haase7de0cb12011-12-05 16:35:38 -0800615 CacheTextureLine *cacheLine;
Romain Guy694b5192010-07-21 21:33:20 -0700616 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
617 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
618 if (bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800619 cacheLine = mCacheLines[i];
Romain Guy694b5192010-07-21 21:33:20 -0700620 break;
621 }
622 }
623
624 // If the new glyph didn't fit, flush the state so far and invalidate everything
625 if (!bitmapFit) {
626 flushAllAndInvalidate();
627
628 // Try to fit it again
629 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
630 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
631 if (bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800632 cacheLine = mCacheLines[i];
Romain Guy694b5192010-07-21 21:33:20 -0700633 break;
634 }
635 }
636
637 // if we still don't fit, something is wrong and we shouldn't draw
638 if (!bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800639 return;
Romain Guy694b5192010-07-21 21:33:20 -0700640 }
641 }
642
Chet Haase7de0cb12011-12-05 16:35:38 -0800643 cachedGlyph->mCachedTextureLine = cacheLine;
644
Romain Guy694b5192010-07-21 21:33:20 -0700645 *retOriginX = startX;
646 *retOriginY = startY;
647
648 uint32_t endX = startX + glyph.fWidth;
649 uint32_t endY = startY + glyph.fHeight;
650
Chet Haase7de0cb12011-12-05 16:35:38 -0800651 uint32_t cacheWidth = cacheLine->mMaxWidth;
Romain Guy694b5192010-07-21 21:33:20 -0700652
Chet Haase7de0cb12011-12-05 16:35:38 -0800653 CacheTexture *cacheTexture = cacheLine->mCacheTexture;
654 if (cacheTexture->mTexture == NULL) {
655 // Large-glyph texture memory is allocated only as needed
Chet Haase2a47c142011-12-14 15:22:56 -0800656 allocateTextureMemory(cacheTexture);
Chet Haase7de0cb12011-12-05 16:35:38 -0800657 }
658 uint8_t* cacheBuffer = cacheTexture->mTexture;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700659 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700660 unsigned int stride = glyph.rowBytes();
661
662 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
663 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
664 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700665 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
Romain Guyb45c0c92010-08-26 20:35:23 -0700666 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
Romain Guy694b5192010-07-21 21:33:20 -0700667 }
668 }
Romain Guy97771732012-02-28 18:17:02 -0800669
Chet Haase7de0cb12011-12-05 16:35:38 -0800670 cachedGlyph->mIsValid = true;
Romain Guy694b5192010-07-21 21:33:20 -0700671}
672
Chet Haase7de0cb12011-12-05 16:35:38 -0800673CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800674 GLuint textureId;
675 glGenTextures(1, &textureId);
Chet Haase2a47c142011-12-14 15:22:56 -0800676 uint8_t* textureMemory = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700677
Chet Haase2a47c142011-12-14 15:22:56 -0800678 CacheTexture* cacheTexture = new CacheTexture(textureMemory, textureId, width, height);
679 if (allocate) {
680 allocateTextureMemory(cacheTexture);
681 }
682 return cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800683}
684
685void FontRenderer::initTextTexture() {
686 mCacheLines.clear();
687
688 // Next, use other, separate caches for large glyphs.
689 uint16_t maxWidth = 0;
690 if (Caches::hasInstance()) {
691 maxWidth = Caches::getInstance().maxTextureSize;
Chet Haase44984ea2011-05-19 13:50:47 -0700692 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800693 if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) {
694 maxWidth = MAX_TEXT_CACHE_WIDTH;
695 }
696 if (mCacheTextureSmall != NULL) {
697 delete mCacheTextureSmall;
698 delete mCacheTexture128;
699 delete mCacheTexture256;
700 delete mCacheTexture512;
701 }
702 mCacheTextureSmall = createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true);
703 mCacheTexture128 = createCacheTexture(maxWidth, 256, false);
704 mCacheTexture256 = createCacheTexture(maxWidth, 256, false);
705 mCacheTexture512 = createCacheTexture(maxWidth, 512, false);
706 mCurrentCacheTexture = mCacheTextureSmall;
707
708 mUploadTexture = false;
709 // Split up our default cache texture into lines of certain widths
710 int nextLine = 0;
711 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 18, nextLine, 0, mCacheTextureSmall));
712 nextLine += mCacheLines.top()->mMaxHeight;
713 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, 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, 34, 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, 42, nextLine, 0, mCacheTextureSmall));
722 nextLine += mCacheLines.top()->mMaxHeight;
723 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, mSmallCacheHeight - nextLine,
724 nextLine, 0, mCacheTextureSmall));
725
726 // The first cache is split into 2 lines of height 128, the rest have just one cache line.
727 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 0, 0, mCacheTexture128));
728 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 128, 0, mCacheTexture128));
729 mCacheLines.push(new CacheTextureLine(maxWidth, 256, 0, 0, mCacheTexture256));
730 mCacheLines.push(new CacheTextureLine(maxWidth, 512, 0, 0, mCacheTexture512));
Romain Guy694b5192010-07-21 21:33:20 -0700731}
732
733// Avoid having to reallocate memory and render quad by quad
734void FontRenderer::initVertexArrayBuffers() {
Romain Guyd71dd362011-12-12 19:03:35 -0800735 uint32_t numIndices = mMaxNumberOfQuads * 6;
736 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700737 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700738
739 // Four verts, two triangles , six indices per quad
740 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
741 int i6 = i * 6;
742 int i4 = i * 4;
743
744 indexBufferData[i6 + 0] = i4 + 0;
745 indexBufferData[i6 + 1] = i4 + 1;
746 indexBufferData[i6 + 2] = i4 + 2;
747
748 indexBufferData[i6 + 3] = i4 + 0;
749 indexBufferData[i6 + 4] = i4 + 2;
750 indexBufferData[i6 + 5] = i4 + 3;
751 }
752
753 glGenBuffers(1, &mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800754 Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700755 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
Romain Guy694b5192010-07-21 21:33:20 -0700756
757 free(indexBufferData);
758
Romain Guyd71dd362011-12-12 19:03:35 -0800759 uint32_t coordSize = 2;
Romain Guy694b5192010-07-21 21:33:20 -0700760 uint32_t uvSize = 2;
761 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700762 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
763 mTextMeshPtr = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700764}
765
766// We don't want to allocate anything unless we actually draw text
767void FontRenderer::checkInit() {
768 if (mInitialized) {
769 return;
770 }
771
772 initTextTexture();
773 initVertexArrayBuffers();
774
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700775 // We store a string with letters in a rough frequency of occurrence
776 mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
777 mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
778 mLatinPrecache += String16(",.?!()-+@;:`'");
779 mLatinPrecache += String16("0123456789");
780
Romain Guy694b5192010-07-21 21:33:20 -0700781 mInitialized = true;
782}
783
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700784void FontRenderer::checkTextureUpdate() {
Chet Haase7de0cb12011-12-05 16:35:38 -0800785 if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700786 return;
Romain Guy694b5192010-07-21 21:33:20 -0700787 }
788
Romain Guy2d4fd362011-12-13 22:00:19 -0800789 Caches& caches = Caches::getInstance();
790 GLuint lastTextureId = 0;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700791 // Iterate over all the cache lines and see which ones need to be updated
792 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
793 CacheTextureLine* cl = mCacheLines[i];
Chet Haase7de0cb12011-12-05 16:35:38 -0800794 if (cl->mDirty && cl->mCacheTexture->mTexture != NULL) {
795 CacheTexture* cacheTexture = cl->mCacheTexture;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700796 uint32_t xOffset = 0;
797 uint32_t yOffset = cl->mCurrentRow;
Chet Haase7de0cb12011-12-05 16:35:38 -0800798 uint32_t width = cl->mMaxWidth;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700799 uint32_t height = cl->mMaxHeight;
Chet Haase7de0cb12011-12-05 16:35:38 -0800800 void* textureData = cacheTexture->mTexture + (yOffset * width);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700801
Romain Guy2d4fd362011-12-13 22:00:19 -0800802 if (cacheTexture->mTextureId != lastTextureId) {
803 caches.activeTexture(0);
804 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
805 lastTextureId = cacheTexture->mTextureId;
806 }
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700807 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700808 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700809
810 cl->mDirty = false;
811 }
812 }
813
Chet Haase7de0cb12011-12-05 16:35:38 -0800814 glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
Chet Haase2a47c142011-12-14 15:22:56 -0800815 if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) {
816 const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST;
817 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
818 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
819 mCurrentCacheTexture->mLinearFiltering = mLinearFiltering;
820 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800821 mLastCacheTexture = mCurrentCacheTexture;
822
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700823 mUploadTexture = false;
824}
825
826void FontRenderer::issueDrawCommand() {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700827 checkTextureUpdate();
828
Romain Guy15bc6432011-12-13 13:11:32 -0800829 Caches& caches = Caches::getInstance();
Romain Guy2d4fd362011-12-13 22:00:19 -0800830 caches.bindIndicesBuffer(mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800831 if (!mDrawn) {
832 float* buffer = mTextMeshPtr;
833 int offset = 2;
834
835 bool force = caches.unbindMeshBuffer();
836 caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
837 caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
838 buffer + offset);
839 }
840
Romain Guy694b5192010-07-21 21:33:20 -0700841 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
Romain Guy5b3b3522010-10-27 18:57:51 -0700842
843 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700844}
845
Romain Guy97771732012-02-28 18:17:02 -0800846void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
847 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800848 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800849 if (texture != mCurrentCacheTexture) {
850 if (mCurrentQuadIndex != 0) {
851 // First, draw everything stored already which uses the previous texture
852 issueDrawCommand();
853 mCurrentQuadIndex = 0;
854 }
855 // Now use the new texture id
856 mCurrentCacheTexture = texture;
857 }
Romain Guy09147fb2010-07-22 13:08:20 -0700858
Romain Guy694b5192010-07-21 21:33:20 -0700859 const uint32_t vertsPerQuad = 4;
Romain Guyd71dd362011-12-12 19:03:35 -0800860 const uint32_t floatsPerVert = 4;
Romain Guy51769a62010-07-23 00:28:00 -0700861 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700862
Romain Guy694b5192010-07-21 21:33:20 -0700863 (*currentPos++) = x1;
864 (*currentPos++) = y1;
Romain Guy694b5192010-07-21 21:33:20 -0700865 (*currentPos++) = u1;
866 (*currentPos++) = v1;
867
868 (*currentPos++) = x2;
869 (*currentPos++) = y2;
Romain Guy694b5192010-07-21 21:33:20 -0700870 (*currentPos++) = u2;
871 (*currentPos++) = v2;
872
873 (*currentPos++) = x3;
874 (*currentPos++) = y3;
Romain Guy694b5192010-07-21 21:33:20 -0700875 (*currentPos++) = u3;
876 (*currentPos++) = v3;
877
878 (*currentPos++) = x4;
879 (*currentPos++) = y4;
Romain Guy694b5192010-07-21 21:33:20 -0700880 (*currentPos++) = u4;
881 (*currentPos++) = v4;
882
883 mCurrentQuadIndex++;
Romain Guy97771732012-02-28 18:17:02 -0800884}
885
886void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
887 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
888 float x4, float y4, float u4, float v4, CacheTexture* texture) {
889
890 if (mClip &&
891 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
892 return;
893 }
894
895 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 -0700896
Romain Guy5b3b3522010-10-27 18:57:51 -0700897 if (mBounds) {
898 mBounds->left = fmin(mBounds->left, x1);
899 mBounds->top = fmin(mBounds->top, y3);
900 mBounds->right = fmax(mBounds->right, x3);
901 mBounds->bottom = fmax(mBounds->bottom, y1);
902 }
903
Romain Guy694b5192010-07-21 21:33:20 -0700904 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
905 issueDrawCommand();
906 mCurrentQuadIndex = 0;
907 }
908}
909
Romain Guy97771732012-02-28 18:17:02 -0800910void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
911 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
912 float x4, float y4, float u4, float v4, CacheTexture* texture) {
913
914 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
915
916 if (mBounds) {
917 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
918 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
919 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
920 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
921 }
922
923 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
924 issueDrawCommand();
925 mCurrentQuadIndex = 0;
926 }
927}
928
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700929uint32_t FontRenderer::getRemainingCacheCapacity() {
930 uint32_t remainingCapacity = 0;
931 float totalPixels = 0;
932 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
933 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
934 totalPixels += mCacheLines[i]->mMaxWidth;
935 }
936 remainingCapacity = (remainingCapacity * 100) / totalPixels;
937 return remainingCapacity;
938}
939
940void FontRenderer::precacheLatin(SkPaint* paint) {
941 // Remaining capacity is measured in %
942 uint32_t remainingCapacity = getRemainingCacheCapacity();
943 uint32_t precacheIdx = 0;
Romain Guy054dc182010-10-15 17:55:25 -0700944 while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
Romain Guy726aeba2011-06-01 14:52:00 -0700945 mCurrentFont->getCachedGlyph(paint, (int32_t) mLatinPrecache[precacheIdx]);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700946 remainingCapacity = getRemainingCacheCapacity();
947 precacheIdx ++;
948 }
949}
950
951void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
952 uint32_t currentNumFonts = mActiveFonts.size();
Romain Guy325a0f92011-01-05 15:26:55 -0800953 int flags = 0;
954 if (paint->isFakeBoldText()) {
955 flags |= Font::kFakeBold;
956 }
Romain Guy2577db12011-01-18 13:02:38 -0800957
958 const float skewX = paint->getTextSkewX();
959 uint32_t italicStyle = *(uint32_t*) &skewX;
Chet Haase8668f8a2011-03-02 13:51:36 -0800960 const float scaleXFloat = paint->getTextScaleX();
961 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
Romain Guybd496bc2011-08-02 17:32:41 -0700962 SkPaint::Style style = paint->getStyle();
963 const float strokeWidthFloat = paint->getStrokeWidth();
964 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
965 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
966 scaleX, style, strokeWidth);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700967
968 const float maxPrecacheFontSize = 40.0f;
969 bool isNewFont = currentNumFonts != mActiveFonts.size();
970
Romain Guy2bffd262010-09-12 17:40:02 -0700971 if (isNewFont && fontSize <= maxPrecacheFontSize) {
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700972 precacheLatin(paint);
973 }
Romain Guy694b5192010-07-21 21:33:20 -0700974}
Romain Guy7975fb62010-10-01 16:36:14 -0700975
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700976FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Romain Guy1e45aae2010-08-13 19:39:53 -0700977 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
978 checkInit();
979
980 if (!mCurrentFont) {
981 DropShadow image;
982 image.width = 0;
983 image.height = 0;
984 image.image = NULL;
985 image.penX = 0;
986 image.penY = 0;
987 return image;
988 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700989
Romain Guy2d4fd362011-12-13 22:00:19 -0800990 mDrawn = false;
Romain Guyff98fa52011-11-28 09:35:09 -0800991 mClip = NULL;
992 mBounds = NULL;
993
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700994 Rect bounds;
Romain Guy726aeba2011-06-01 14:52:00 -0700995 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds);
Romain Guyff98fa52011-11-28 09:35:09 -0800996
Romain Guy1e45aae2010-08-13 19:39:53 -0700997 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
998 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700999 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guyff98fa52011-11-28 09:35:09 -08001000
Romain Guy1e45aae2010-08-13 19:39:53 -07001001 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001002 dataBuffer[i] = 0;
1003 }
Romain Guy1e45aae2010-08-13 19:39:53 -07001004
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001005 int penX = radius - bounds.left;
1006 int penY = radius - bounds.bottom;
1007
Romain Guy726aeba2011-06-01 14:52:00 -07001008 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
Romain Guy1e45aae2010-08-13 19:39:53 -07001009 dataBuffer, paddedWidth, paddedHeight);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001010 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
1011
1012 DropShadow image;
1013 image.width = paddedWidth;
1014 image.height = paddedHeight;
1015 image.image = dataBuffer;
1016 image.penX = penX;
1017 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -08001018
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001019 return image;
1020}
Romain Guy694b5192010-07-21 21:33:20 -07001021
Romain Guy671d6cf2012-01-18 12:39:17 -08001022void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -07001023 checkInit();
1024
Romain Guy5b3b3522010-10-27 18:57:51 -07001025 mDrawn = false;
1026 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -07001027 mClip = clip;
Romain Guy671d6cf2012-01-18 12:39:17 -08001028}
Romain Guyff98fa52011-11-28 09:35:09 -08001029
Romain Guy671d6cf2012-01-18 12:39:17 -08001030void FontRenderer::finishRender() {
Romain Guy5b3b3522010-10-27 18:57:51 -07001031 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -08001032 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -07001033
1034 if (mCurrentQuadIndex != 0) {
1035 issueDrawCommand();
1036 mCurrentQuadIndex = 0;
1037 }
Romain Guy671d6cf2012-01-18 12:39:17 -08001038}
1039
1040bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
1041 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
1042 if (!mCurrentFont) {
1043 ALOGE("No font set");
1044 return false;
1045 }
1046
1047 initRender(clip, bounds);
1048 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
1049 finishRender();
1050
1051 return mDrawn;
1052}
1053
1054bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
1055 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
1056 const float* positions, Rect* bounds) {
1057 if (!mCurrentFont) {
1058 ALOGE("No font set");
1059 return false;
1060 }
1061
1062 initRender(clip, bounds);
1063 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
1064 finishRender();
Romain Guy5b3b3522010-10-27 18:57:51 -07001065
1066 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -07001067}
1068
Romain Guy97771732012-02-28 18:17:02 -08001069bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
1070 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
1071 float hOffset, float vOffset, Rect* bounds) {
1072 if (!mCurrentFont) {
1073 ALOGE("No font set");
1074 return false;
1075 }
1076
1077 initRender(clip, bounds);
1078 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
1079 finishRender();
1080
1081 return mDrawn;
1082}
1083
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001084void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
1085 // Compute gaussian weights for the blur
1086 // e is the euler's number
1087 float e = 2.718281828459045f;
1088 float pi = 3.1415926535897932f;
1089 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
1090 // x is of the form [-radius .. 0 .. radius]
1091 // and sigma varies with radius.
1092 // Based on some experimental radius values and sigma's
1093 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001094 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001095 // The larger the radius gets, the more our gaussian blur
1096 // will resemble a box blur since with large sigma
1097 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -08001098 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001099
1100 // Now compute the coefficints
1101 // We will store some redundant values to save some math during
1102 // the blur calculations
1103 // precompute some values
1104 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
1105 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
1106
1107 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -08001108 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -07001109 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001110 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
1111 normalizeFactor += weights[r + radius];
1112 }
1113
1114 //Now we need to normalize the weights because all our coefficients need to add up to one
1115 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -08001116 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001117 weights[r + radius] *= normalizeFactor;
1118 }
1119}
1120
1121void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -07001122 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001123 float blurredPixel = 0.0f;
1124 float currentPixel = 0.0f;
1125
Romain Guy325a0f92011-01-05 15:26:55 -08001126 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001127
1128 const uint8_t* input = source + y * width;
1129 uint8_t* output = dest + y * width;
1130
Romain Guy325a0f92011-01-05 15:26:55 -08001131 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001132 blurredPixel = 0.0f;
1133 const float* gPtr = weights;
1134 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -08001135 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001136 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -08001137 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -07001138 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001139 blurredPixel += currentPixel * gPtr[0];
1140 gPtr++;
1141 i++;
1142 }
1143 } else {
Romain Guy325a0f92011-01-05 15:26:55 -08001144 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001145 // Stepping left and right away from the pixel
1146 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -08001147 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001148 validW = 0;
1149 }
Romain Guy325a0f92011-01-05 15:26:55 -08001150 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001151 validW = width - 1;
1152 }
1153
Romain Guy325a0f92011-01-05 15:26:55 -08001154 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001155 blurredPixel += currentPixel * gPtr[0];
1156 gPtr++;
1157 }
1158 }
1159 *output = (uint8_t)blurredPixel;
1160 output ++;
1161 }
1162 }
1163}
1164
1165void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -07001166 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001167 float blurredPixel = 0.0f;
1168 float currentPixel = 0.0f;
1169
Romain Guy325a0f92011-01-05 15:26:55 -08001170 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001171
1172 uint8_t* output = dest + y * width;
1173
Romain Guy325a0f92011-01-05 15:26:55 -08001174 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001175 blurredPixel = 0.0f;
1176 const float* gPtr = weights;
1177 const uint8_t* input = source + x;
1178 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -08001179 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001180 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -08001181 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001182 currentPixel = (float)(*i);
1183 blurredPixel += currentPixel * gPtr[0];
1184 gPtr++;
1185 i += width;
1186 }
1187 } else {
Romain Guy325a0f92011-01-05 15:26:55 -08001188 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001189 int validH = y + r;
1190 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -08001191 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001192 validH = 0;
1193 }
Romain Guy325a0f92011-01-05 15:26:55 -08001194 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001195 validH = height - 1;
1196 }
1197
1198 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -08001199 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001200 blurredPixel += currentPixel * gPtr[0];
1201 gPtr++;
1202 }
1203 }
Romain Guy325a0f92011-01-05 15:26:55 -08001204 *output = (uint8_t) blurredPixel;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001205 output ++;
1206 }
1207 }
1208}
1209
1210
1211void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
1212 float *gaussian = new float[2 * radius + 1];
1213 computeGaussianWeights(gaussian, radius);
Romain Guyd71dd362011-12-12 19:03:35 -08001214
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001215 uint8_t* scratch = new uint8_t[width * height];
Romain Guyd71dd362011-12-12 19:03:35 -08001216
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001217 horizontalBlur(gaussian, radius, image, scratch, width, height);
1218 verticalBlur(gaussian, radius, scratch, image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -08001219
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001220 delete[] gaussian;
1221 delete[] scratch;
1222}
1223
Romain Guy694b5192010-07-21 21:33:20 -07001224}; // namespace uirenderer
1225}; // namespace android