blob: f2bb6ec7f723d368577b868a512692d4e8cd8727 [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
173 float nPenX = glyph->mBitmapLeft;
174 vOffset += glyph->mBitmapTop + height;
175
176 const float u1 = glyph->mBitmapMinU;
177 const float u2 = glyph->mBitmapMaxU;
178 const float v1 = glyph->mBitmapMinV;
179 const float v2 = glyph->mBitmapMaxV;
180
181 SkPoint destination[4];
182 measure.getPosTan(x + hOffset + nPenX + halfWidth, position, tangent);
183
184 // Move along the tangent and offset by the normal
185 destination[0].set(-tangent->fX * halfWidth - tangent->fY * vOffset,
186 -tangent->fY * halfWidth + tangent->fX * vOffset);
187 destination[1].set(tangent->fX * halfWidth - tangent->fY * vOffset,
188 tangent->fY * halfWidth + tangent->fX * vOffset);
189 destination[2].set(destination[1].fX + tangent->fY * height,
190 destination[1].fY - tangent->fX * height);
191 destination[3].set(destination[0].fX + tangent->fY * height,
192 destination[0].fY - tangent->fX * height);
193
194 mState->appendRotatedMeshQuad(
195 position->fX + destination[0].fX,
196 position->fY + destination[0].fY, u1, v2,
197 position->fX + destination[1].fX,
198 position->fY + destination[1].fY, u2, v2,
199 position->fX + destination[2].fX,
200 position->fY + destination[2].fY, u2, v1,
201 position->fX + destination[3].fX,
202 position->fY + destination[3].fY, u1, v1,
203 glyph->mCachedTextureLine->mCacheTexture);
204}
205
Chet Haase7de0cb12011-12-05 16:35:38 -0800206CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700207 CachedGlyphInfo* cachedGlyph = NULL;
Romain Guy726aeba2011-06-01 14:52:00 -0700208 ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
Romain Guy1e45aae2010-08-13 19:39:53 -0700209 if (index >= 0) {
210 cachedGlyph = mCachedGlyphs.valueAt(index);
211 } else {
Romain Guy726aeba2011-06-01 14:52:00 -0700212 cachedGlyph = cacheGlyph(paint, textUnit);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700213 }
214
215 // Is the glyph still in texture cache?
216 if (!cachedGlyph->mIsValid) {
Romain Guy726aeba2011-06-01 14:52:00 -0700217 const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700218 updateGlyphCache(paint, skiaGlyph, cachedGlyph);
219 }
220
221 return cachedGlyph;
222}
223
Romain Guy726aeba2011-06-01 14:52:00 -0700224void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700225 int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
226 if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
Romain Guy726aeba2011-06-01 14:52:00 -0700227 render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
Romain Guy671d6cf2012-01-18 12:39:17 -0800228 bitmapW, bitmapH, NULL, NULL);
Romain Guy61c8c9c2010-08-09 20:48:09 -0700229 } else {
Romain Guy726aeba2011-06-01 14:52:00 -0700230 render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
Romain Guy671d6cf2012-01-18 12:39:17 -0800231 0, 0, NULL, NULL);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700232 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700233}
234
Romain Guy671d6cf2012-01-18 12:39:17 -0800235void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
236 int numGlyphs, int x, int y, const float* positions) {
237 render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
238 0, 0, NULL, positions);
239}
240
Romain Guy97771732012-02-28 18:17:02 -0800241void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
242 int numGlyphs, SkPath* path, float hOffset, float vOffset) {
243 if (numGlyphs == 0 || text == NULL || len == 0) {
244 return;
245 }
246
247 text += start;
248
249 int glyphsCount = 0;
250 SkFixed prevRsbDelta = 0;
251
252 float penX = 0.0f;
253
254 SkPoint position;
255 SkVector tangent;
256
257 SkPathMeasure measure(*path, false);
258 float pathLength = SkScalarToFloat(measure.getLength());
259
260 if (paint->getTextAlign() != SkPaint::kLeft_Align) {
261 float textWidth = SkScalarToFloat(paint->measureText(text, len));
262 float pathOffset = pathLength;
263 if (paint->getTextAlign() == SkPaint::kCenter_Align) {
264 textWidth *= 0.5f;
265 pathOffset *= 0.5f;
266 }
267 penX += pathOffset - textWidth;
268 }
269
270 while (glyphsCount < numGlyphs && penX <= pathLength) {
271 glyph_t glyph = GET_GLYPH(text);
272
273 if (IS_END_OF_STRING(glyph)) {
274 break;
275 }
276
277 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
278 penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
279 prevRsbDelta = cachedGlyph->mRsbDelta;
280
281 if (cachedGlyph->mIsValid) {
282 drawCachedGlyph(cachedGlyph, roundf(penX), hOffset, vOffset, measure, &position, &tangent);
283 }
284
285 penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
286
287 glyphsCount++;
288 }
289}
290
Romain Guy726aeba2011-06-01 14:52:00 -0700291void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700292 int numGlyphs, Rect *bounds) {
293 if (bounds == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000294 ALOGE("No return rectangle provided to measure text");
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700295 return;
296 }
297 bounds->set(1e6, -1e6, -1e6, 1e6);
Romain Guy671d6cf2012-01-18 12:39:17 -0800298 render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, NULL);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700299}
300
Romain Guy726aeba2011-06-01 14:52:00 -0700301void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700302 int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
Romain Guy97771732012-02-28 18:17:02 -0800303 uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) {
Romain Guy694b5192010-07-21 21:33:20 -0700304 if (numGlyphs == 0 || text == NULL || len == 0) {
305 return;
306 }
307
Romain Guy671d6cf2012-01-18 12:39:17 -0800308 static RenderGlyph gRenderGlyph[] = {
309 &android::uirenderer::Font::drawCachedGlyph,
310 &android::uirenderer::Font::drawCachedGlyphBitmap,
311 &android::uirenderer::Font::measureCachedGlyph
312 };
313 RenderGlyph render = gRenderGlyph[mode];
Romain Guy694b5192010-07-21 21:33:20 -0700314
Romain Guy97771732012-02-28 18:17:02 -0800315 text += start;
316 int glyphsCount = 0;
317
Romain Guyb6294902012-02-02 15:13:18 -0800318 if (CC_LIKELY(positions == NULL)) {
Romain Guy671d6cf2012-01-18 12:39:17 -0800319 SkFixed prevRsbDelta = 0;
Romain Guy694b5192010-07-21 21:33:20 -0700320
Romain Guy97771732012-02-28 18:17:02 -0800321 float penX = x + 0.5f;
Romain Guy671d6cf2012-01-18 12:39:17 -0800322 int penY = y;
Romain Guy694b5192010-07-21 21:33:20 -0700323
Romain Guy671d6cf2012-01-18 12:39:17 -0800324 while (glyphsCount < numGlyphs) {
325 glyph_t glyph = GET_GLYPH(text);
326
327 // Reached the end of the string
328 if (IS_END_OF_STRING(glyph)) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700329 break;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700330 }
Romain Guy671d6cf2012-01-18 12:39:17 -0800331
332 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
Romain Guy97771732012-02-28 18:17:02 -0800333 penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
Romain Guy671d6cf2012-01-18 12:39:17 -0800334 prevRsbDelta = cachedGlyph->mRsbDelta;
335
336 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
337 if (cachedGlyph->mIsValid) {
338 (*this.*render)(cachedGlyph, (int) floorf(penX), penY,
339 bitmap, bitmapW, bitmapH, bounds, positions);
340 }
341
342 penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
343
344 glyphsCount++;
Romain Guy694b5192010-07-21 21:33:20 -0700345 }
Romain Guy671d6cf2012-01-18 12:39:17 -0800346 } else {
347 const SkPaint::Align align = paint->getTextAlign();
Romain Guy694b5192010-07-21 21:33:20 -0700348
Romain Guy671d6cf2012-01-18 12:39:17 -0800349 // This is for renderPosText()
350 while (glyphsCount < numGlyphs) {
351 glyph_t glyph = GET_GLYPH(text);
Romain Guy694b5192010-07-21 21:33:20 -0700352
Romain Guy671d6cf2012-01-18 12:39:17 -0800353 // Reached the end of the string
354 if (IS_END_OF_STRING(glyph)) {
355 break;
356 }
357
358 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
359
360 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
361 if (cachedGlyph->mIsValid) {
362 int penX = x + positions[(glyphsCount << 1)];
363 int penY = y + positions[(glyphsCount << 1) + 1];
364
365 switch (align) {
366 case SkPaint::kRight_Align:
367 penX -= SkFixedToFloat(cachedGlyph->mAdvanceX);
368 penY -= SkFixedToFloat(cachedGlyph->mAdvanceY);
369 break;
370 case SkPaint::kCenter_Align:
371 penX -= SkFixedToFloat(cachedGlyph->mAdvanceX >> 1);
372 penY -= SkFixedToFloat(cachedGlyph->mAdvanceY >> 1);
373 default:
374 break;
375 }
376
377 (*this.*render)(cachedGlyph, penX, penY,
378 bitmap, bitmapW, bitmapH, bounds, positions);
379 }
380
381 glyphsCount++;
Romain Guy694b5192010-07-21 21:33:20 -0700382 }
383 }
384}
385
Romain Guy51769a62010-07-23 00:28:00 -0700386void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
Romain Guy694b5192010-07-21 21:33:20 -0700387 glyph->mAdvanceX = skiaGlyph.fAdvanceX;
388 glyph->mAdvanceY = skiaGlyph.fAdvanceY;
389 glyph->mBitmapLeft = skiaGlyph.fLeft;
390 glyph->mBitmapTop = skiaGlyph.fTop;
Romain Guy2bffd262010-09-12 17:40:02 -0700391 glyph->mLsbDelta = skiaGlyph.fLsbDelta;
392 glyph->mRsbDelta = skiaGlyph.fRsbDelta;
Romain Guy694b5192010-07-21 21:33:20 -0700393
394 uint32_t startX = 0;
395 uint32_t startY = 0;
396
Romain Guy694b5192010-07-21 21:33:20 -0700397 // Get the bitmap for the glyph
398 paint->findImage(skiaGlyph);
Chet Haase7de0cb12011-12-05 16:35:38 -0800399 mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700400
401 if (!glyph->mIsValid) {
402 return;
403 }
404
405 uint32_t endX = startX + skiaGlyph.fWidth;
406 uint32_t endY = startY + skiaGlyph.fHeight;
407
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700408 glyph->mStartX = startX;
409 glyph->mStartY = startY;
Romain Guy694b5192010-07-21 21:33:20 -0700410 glyph->mBitmapWidth = skiaGlyph.fWidth;
411 glyph->mBitmapHeight = skiaGlyph.fHeight;
412
Chet Haase7de0cb12011-12-05 16:35:38 -0800413 uint32_t cacheWidth = glyph->mCachedTextureLine->mCacheTexture->mWidth;
414 uint32_t cacheHeight = glyph->mCachedTextureLine->mCacheTexture->mHeight;
Romain Guy694b5192010-07-21 21:33:20 -0700415
416 glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
417 glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
418 glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
419 glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
420
Romain Guy51769a62010-07-23 00:28:00 -0700421 mState->mUploadTexture = true;
Romain Guy694b5192010-07-21 21:33:20 -0700422}
423
Chet Haase7de0cb12011-12-05 16:35:38 -0800424CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) {
Romain Guy51769a62010-07-23 00:28:00 -0700425 CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
Romain Guy694b5192010-07-21 21:33:20 -0700426 mCachedGlyphs.add(glyph, newGlyph);
427
Romain Guy726aeba2011-06-01 14:52:00 -0700428 const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
Romain Guy694b5192010-07-21 21:33:20 -0700429 newGlyph->mGlyphIndex = skiaGlyph.fID;
430 newGlyph->mIsValid = false;
431
432 updateGlyphCache(paint, skiaGlyph, newGlyph);
433
434 return newGlyph;
435}
436
Romain Guy2577db12011-01-18 13:02:38 -0800437Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
Romain Guybd496bc2011-08-02 17:32:41 -0700438 int flags, uint32_t italicStyle, uint32_t scaleX,
439 SkPaint::Style style, uint32_t strokeWidth) {
Romain Guy694b5192010-07-21 21:33:20 -0700440 Vector<Font*> &activeFonts = state->mActiveFonts;
441
442 for (uint32_t i = 0; i < activeFonts.size(); i++) {
Romain Guy51769a62010-07-23 00:28:00 -0700443 Font* font = activeFonts[i];
Romain Guy2577db12011-01-18 13:02:38 -0800444 if (font->mFontId == fontId && font->mFontSize == fontSize &&
Chet Haase8668f8a2011-03-02 13:51:36 -0800445 font->mFlags == flags && font->mItalicStyle == italicStyle &&
Romain Guybd496bc2011-08-02 17:32:41 -0700446 font->mScaleX == scaleX && font->mStyle == style &&
447 (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
Romain Guy51769a62010-07-23 00:28:00 -0700448 return font;
Romain Guy694b5192010-07-21 21:33:20 -0700449 }
450 }
451
Romain Guybd496bc2011-08-02 17:32:41 -0700452 Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
453 scaleX, style, strokeWidth);
Romain Guy694b5192010-07-21 21:33:20 -0700454 activeFonts.push(newFont);
455 return newFont;
456}
457
458///////////////////////////////////////////////////////////////////////////////
459// FontRenderer
460///////////////////////////////////////////////////////////////////////////////
461
Romain Guy514fb182011-01-19 14:38:29 -0800462static bool sLogFontRendererCreate = true;
463
Romain Guy694b5192010-07-21 21:33:20 -0700464FontRenderer::FontRenderer() {
Romain Guyc9855a52011-01-21 21:14:15 -0800465 if (sLogFontRendererCreate) {
466 INIT_LOGD("Creating FontRenderer");
467 }
Romain Guy51769a62010-07-23 00:28:00 -0700468
Romain Guyb45c0c92010-08-26 20:35:23 -0700469 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700470 mInitialized = false;
471 mMaxNumberOfQuads = 1024;
472 mCurrentQuadIndex = 0;
473
Romain Guy9cccc2b92010-08-07 23:46:15 -0700474 mTextMeshPtr = NULL;
Chet Haase7de0cb12011-12-05 16:35:38 -0800475 mCurrentCacheTexture = NULL;
476 mLastCacheTexture = NULL;
477 mCacheTextureSmall = NULL;
478 mCacheTexture128 = NULL;
479 mCacheTexture256 = NULL;
480 mCacheTexture512 = NULL;
Romain Guy9cccc2b92010-08-07 23:46:15 -0700481
Chet Haase2a47c142011-12-14 15:22:56 -0800482 mLinearFiltering = false;
483
Romain Guy694b5192010-07-21 21:33:20 -0700484 mIndexBufferID = 0;
485
Chet Haase7de0cb12011-12-05 16:35:38 -0800486 mSmallCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
487 mSmallCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -0700488
489 char property[PROPERTY_VALUE_MAX];
490 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800491 if (sLogFontRendererCreate) {
492 INIT_LOGD(" Setting text cache width to %s pixels", property);
493 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800494 mSmallCacheWidth = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -0700495 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800496 if (sLogFontRendererCreate) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800497 INIT_LOGD(" Using default text cache width of %i pixels", mSmallCacheWidth);
Romain Guy514fb182011-01-19 14:38:29 -0800498 }
Romain Guy51769a62010-07-23 00:28:00 -0700499 }
500
501 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800502 if (sLogFontRendererCreate) {
503 INIT_LOGD(" Setting text cache width to %s pixels", property);
504 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800505 mSmallCacheHeight = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -0700506 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800507 if (sLogFontRendererCreate) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800508 INIT_LOGD(" Using default text cache height of %i pixels", mSmallCacheHeight);
Romain Guy514fb182011-01-19 14:38:29 -0800509 }
Romain Guy51769a62010-07-23 00:28:00 -0700510 }
Romain Guy514fb182011-01-19 14:38:29 -0800511
512 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -0700513}
514
515FontRenderer::~FontRenderer() {
516 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
517 delete mCacheLines[i];
518 }
519 mCacheLines.clear();
520
Romain Guy9cccc2b92010-08-07 23:46:15 -0700521 if (mInitialized) {
522 delete[] mTextMeshPtr;
Chet Haase7de0cb12011-12-05 16:35:38 -0800523 delete mCacheTextureSmall;
524 delete mCacheTexture128;
525 delete mCacheTexture256;
526 delete mCacheTexture512;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700527 }
Romain Guy694b5192010-07-21 21:33:20 -0700528
529 Vector<Font*> fontsToDereference = mActiveFonts;
530 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
531 delete fontsToDereference[i];
532 }
533}
534
535void FontRenderer::flushAllAndInvalidate() {
536 if (mCurrentQuadIndex != 0) {
537 issueDrawCommand();
538 mCurrentQuadIndex = 0;
539 }
540 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
541 mActiveFonts[i]->invalidateTextureCache();
542 }
543 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
544 mCacheLines[i]->mCurrentCol = 0;
545 }
546}
547
Chet Haase9a824562011-12-16 15:44:59 -0800548void FontRenderer::deallocateTextureMemory(CacheTexture *cacheTexture) {
549 if (cacheTexture && cacheTexture->mTexture) {
550 glDeleteTextures(1, &cacheTexture->mTextureId);
551 delete cacheTexture->mTexture;
552 cacheTexture->mTexture = NULL;
553 }
554}
555
556void FontRenderer::flushLargeCaches() {
557 if ((!mCacheTexture128 || !mCacheTexture128->mTexture) &&
558 (!mCacheTexture256 || !mCacheTexture256->mTexture) &&
559 (!mCacheTexture512 || !mCacheTexture512->mTexture)) {
560 // Typical case; no large glyph caches allocated
561 return;
562 }
563
564 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
565 CacheTextureLine* cacheLine = mCacheLines[i];
566 if ((cacheLine->mCacheTexture == mCacheTexture128 ||
567 cacheLine->mCacheTexture == mCacheTexture256 ||
568 cacheLine->mCacheTexture == mCacheTexture512) &&
569 cacheLine->mCacheTexture->mTexture != NULL) {
570 cacheLine->mCurrentCol = 0;
571 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
572 mActiveFonts[i]->invalidateTextureCache(cacheLine);
573 }
574 }
575 }
576
577 deallocateTextureMemory(mCacheTexture128);
578 deallocateTextureMemory(mCacheTexture256);
579 deallocateTextureMemory(mCacheTexture512);
580}
581
Chet Haase2a47c142011-12-14 15:22:56 -0800582void FontRenderer::allocateTextureMemory(CacheTexture *cacheTexture) {
583 int width = cacheTexture->mWidth;
584 int height = cacheTexture->mHeight;
585 cacheTexture->mTexture = new uint8_t[width * height];
586 memset(cacheTexture->mTexture, 0, width * height * sizeof(uint8_t));
587 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
588 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
589 // Initialize texture dimensions
590 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
591 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800592
Chet Haase2a47c142011-12-14 15:22:56 -0800593 const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST;
594 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
595 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
596
597 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
598 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
Chet Haase7de0cb12011-12-05 16:35:38 -0800599}
600
601void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
602 uint32_t* retOriginX, uint32_t* retOriginY) {
603 cachedGlyph->mIsValid = false;
Romain Guy694b5192010-07-21 21:33:20 -0700604 // If the glyph is too tall, don't cache it
Chet Haase7de0cb12011-12-05 16:35:38 -0800605 if (glyph.fHeight + TEXTURE_BORDER_SIZE > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
Steve Block3762c312012-01-06 19:20:56 +0000606 ALOGE("Font size to large to fit in cache. width, height = %i, %i",
Chet Haase7de0cb12011-12-05 16:35:38 -0800607 (int) glyph.fWidth, (int) glyph.fHeight);
608 return;
Romain Guy694b5192010-07-21 21:33:20 -0700609 }
610
611 // Now copy the bitmap into the cache texture
612 uint32_t startX = 0;
613 uint32_t startY = 0;
614
615 bool bitmapFit = false;
Chet Haase7de0cb12011-12-05 16:35:38 -0800616 CacheTextureLine *cacheLine;
Romain Guy694b5192010-07-21 21:33:20 -0700617 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
618 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
619 if (bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800620 cacheLine = mCacheLines[i];
Romain Guy694b5192010-07-21 21:33:20 -0700621 break;
622 }
623 }
624
625 // If the new glyph didn't fit, flush the state so far and invalidate everything
626 if (!bitmapFit) {
627 flushAllAndInvalidate();
628
629 // Try to fit it again
630 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
631 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
632 if (bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800633 cacheLine = mCacheLines[i];
Romain Guy694b5192010-07-21 21:33:20 -0700634 break;
635 }
636 }
637
638 // if we still don't fit, something is wrong and we shouldn't draw
639 if (!bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800640 return;
Romain Guy694b5192010-07-21 21:33:20 -0700641 }
642 }
643
Chet Haase7de0cb12011-12-05 16:35:38 -0800644 cachedGlyph->mCachedTextureLine = cacheLine;
645
Romain Guy694b5192010-07-21 21:33:20 -0700646 *retOriginX = startX;
647 *retOriginY = startY;
648
649 uint32_t endX = startX + glyph.fWidth;
650 uint32_t endY = startY + glyph.fHeight;
651
Chet Haase7de0cb12011-12-05 16:35:38 -0800652 uint32_t cacheWidth = cacheLine->mMaxWidth;
Romain Guy694b5192010-07-21 21:33:20 -0700653
Chet Haase7de0cb12011-12-05 16:35:38 -0800654 CacheTexture *cacheTexture = cacheLine->mCacheTexture;
655 if (cacheTexture->mTexture == NULL) {
656 // Large-glyph texture memory is allocated only as needed
Chet Haase2a47c142011-12-14 15:22:56 -0800657 allocateTextureMemory(cacheTexture);
Chet Haase7de0cb12011-12-05 16:35:38 -0800658 }
659 uint8_t* cacheBuffer = cacheTexture->mTexture;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700660 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700661 unsigned int stride = glyph.rowBytes();
662
663 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
664 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
665 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700666 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
Romain Guyb45c0c92010-08-26 20:35:23 -0700667 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
Romain Guy694b5192010-07-21 21:33:20 -0700668 }
669 }
Romain Guy97771732012-02-28 18:17:02 -0800670
Chet Haase7de0cb12011-12-05 16:35:38 -0800671 cachedGlyph->mIsValid = true;
Romain Guy694b5192010-07-21 21:33:20 -0700672}
673
Chet Haase7de0cb12011-12-05 16:35:38 -0800674CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800675 GLuint textureId;
676 glGenTextures(1, &textureId);
Chet Haase2a47c142011-12-14 15:22:56 -0800677 uint8_t* textureMemory = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700678
Chet Haase2a47c142011-12-14 15:22:56 -0800679 CacheTexture* cacheTexture = new CacheTexture(textureMemory, textureId, width, height);
680 if (allocate) {
681 allocateTextureMemory(cacheTexture);
682 }
683 return cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800684}
685
686void FontRenderer::initTextTexture() {
687 mCacheLines.clear();
688
689 // Next, use other, separate caches for large glyphs.
690 uint16_t maxWidth = 0;
691 if (Caches::hasInstance()) {
692 maxWidth = Caches::getInstance().maxTextureSize;
Chet Haase44984ea2011-05-19 13:50:47 -0700693 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800694 if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) {
695 maxWidth = MAX_TEXT_CACHE_WIDTH;
696 }
697 if (mCacheTextureSmall != NULL) {
698 delete mCacheTextureSmall;
699 delete mCacheTexture128;
700 delete mCacheTexture256;
701 delete mCacheTexture512;
702 }
703 mCacheTextureSmall = createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true);
704 mCacheTexture128 = createCacheTexture(maxWidth, 256, false);
705 mCacheTexture256 = createCacheTexture(maxWidth, 256, false);
706 mCacheTexture512 = createCacheTexture(maxWidth, 512, false);
707 mCurrentCacheTexture = mCacheTextureSmall;
708
709 mUploadTexture = false;
710 // Split up our default cache texture into lines of certain widths
711 int nextLine = 0;
712 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 18, nextLine, 0, mCacheTextureSmall));
713 nextLine += mCacheLines.top()->mMaxHeight;
714 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
715 nextLine += mCacheLines.top()->mMaxHeight;
716 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
717 nextLine += mCacheLines.top()->mMaxHeight;
718 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
719 nextLine += mCacheLines.top()->mMaxHeight;
720 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
721 nextLine += mCacheLines.top()->mMaxHeight;
722 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 42, nextLine, 0, mCacheTextureSmall));
723 nextLine += mCacheLines.top()->mMaxHeight;
724 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, mSmallCacheHeight - nextLine,
725 nextLine, 0, mCacheTextureSmall));
726
727 // The first cache is split into 2 lines of height 128, the rest have just one cache line.
728 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 0, 0, mCacheTexture128));
729 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 128, 0, mCacheTexture128));
730 mCacheLines.push(new CacheTextureLine(maxWidth, 256, 0, 0, mCacheTexture256));
731 mCacheLines.push(new CacheTextureLine(maxWidth, 512, 0, 0, mCacheTexture512));
Romain Guy694b5192010-07-21 21:33:20 -0700732}
733
734// Avoid having to reallocate memory and render quad by quad
735void FontRenderer::initVertexArrayBuffers() {
Romain Guyd71dd362011-12-12 19:03:35 -0800736 uint32_t numIndices = mMaxNumberOfQuads * 6;
737 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700738 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700739
740 // Four verts, two triangles , six indices per quad
741 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
742 int i6 = i * 6;
743 int i4 = i * 4;
744
745 indexBufferData[i6 + 0] = i4 + 0;
746 indexBufferData[i6 + 1] = i4 + 1;
747 indexBufferData[i6 + 2] = i4 + 2;
748
749 indexBufferData[i6 + 3] = i4 + 0;
750 indexBufferData[i6 + 4] = i4 + 2;
751 indexBufferData[i6 + 5] = i4 + 3;
752 }
753
754 glGenBuffers(1, &mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800755 Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700756 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
Romain Guy694b5192010-07-21 21:33:20 -0700757
758 free(indexBufferData);
759
Romain Guyd71dd362011-12-12 19:03:35 -0800760 uint32_t coordSize = 2;
Romain Guy694b5192010-07-21 21:33:20 -0700761 uint32_t uvSize = 2;
762 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700763 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
764 mTextMeshPtr = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700765}
766
767// We don't want to allocate anything unless we actually draw text
768void FontRenderer::checkInit() {
769 if (mInitialized) {
770 return;
771 }
772
773 initTextTexture();
774 initVertexArrayBuffers();
775
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700776 // We store a string with letters in a rough frequency of occurrence
777 mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
778 mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
779 mLatinPrecache += String16(",.?!()-+@;:`'");
780 mLatinPrecache += String16("0123456789");
781
Romain Guy694b5192010-07-21 21:33:20 -0700782 mInitialized = true;
783}
784
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700785void FontRenderer::checkTextureUpdate() {
Chet Haase7de0cb12011-12-05 16:35:38 -0800786 if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700787 return;
Romain Guy694b5192010-07-21 21:33:20 -0700788 }
789
Romain Guy2d4fd362011-12-13 22:00:19 -0800790 Caches& caches = Caches::getInstance();
791 GLuint lastTextureId = 0;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700792 // Iterate over all the cache lines and see which ones need to be updated
793 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
794 CacheTextureLine* cl = mCacheLines[i];
Chet Haase7de0cb12011-12-05 16:35:38 -0800795 if (cl->mDirty && cl->mCacheTexture->mTexture != NULL) {
796 CacheTexture* cacheTexture = cl->mCacheTexture;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700797 uint32_t xOffset = 0;
798 uint32_t yOffset = cl->mCurrentRow;
Chet Haase7de0cb12011-12-05 16:35:38 -0800799 uint32_t width = cl->mMaxWidth;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700800 uint32_t height = cl->mMaxHeight;
Chet Haase7de0cb12011-12-05 16:35:38 -0800801 void* textureData = cacheTexture->mTexture + (yOffset * width);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700802
Romain Guy2d4fd362011-12-13 22:00:19 -0800803 if (cacheTexture->mTextureId != lastTextureId) {
804 caches.activeTexture(0);
805 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
806 lastTextureId = cacheTexture->mTextureId;
807 }
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700808 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700809 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700810
811 cl->mDirty = false;
812 }
813 }
814
Chet Haase7de0cb12011-12-05 16:35:38 -0800815 glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
Chet Haase2a47c142011-12-14 15:22:56 -0800816 if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) {
817 const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST;
818 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
819 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
820 mCurrentCacheTexture->mLinearFiltering = mLinearFiltering;
821 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800822 mLastCacheTexture = mCurrentCacheTexture;
823
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700824 mUploadTexture = false;
825}
826
827void FontRenderer::issueDrawCommand() {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700828 checkTextureUpdate();
829
Romain Guy15bc6432011-12-13 13:11:32 -0800830 Caches& caches = Caches::getInstance();
Romain Guy2d4fd362011-12-13 22:00:19 -0800831 caches.bindIndicesBuffer(mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800832 if (!mDrawn) {
833 float* buffer = mTextMeshPtr;
834 int offset = 2;
835
836 bool force = caches.unbindMeshBuffer();
837 caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
838 caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
839 buffer + offset);
840 }
841
Romain Guy694b5192010-07-21 21:33:20 -0700842 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
Romain Guy5b3b3522010-10-27 18:57:51 -0700843
844 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700845}
846
Romain Guy97771732012-02-28 18:17:02 -0800847void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
848 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800849 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800850 if (texture != mCurrentCacheTexture) {
851 if (mCurrentQuadIndex != 0) {
852 // First, draw everything stored already which uses the previous texture
853 issueDrawCommand();
854 mCurrentQuadIndex = 0;
855 }
856 // Now use the new texture id
857 mCurrentCacheTexture = texture;
858 }
Romain Guy09147fb2010-07-22 13:08:20 -0700859
Romain Guy694b5192010-07-21 21:33:20 -0700860 const uint32_t vertsPerQuad = 4;
Romain Guyd71dd362011-12-12 19:03:35 -0800861 const uint32_t floatsPerVert = 4;
Romain Guy51769a62010-07-23 00:28:00 -0700862 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700863
Romain Guy694b5192010-07-21 21:33:20 -0700864 (*currentPos++) = x1;
865 (*currentPos++) = y1;
Romain Guy694b5192010-07-21 21:33:20 -0700866 (*currentPos++) = u1;
867 (*currentPos++) = v1;
868
869 (*currentPos++) = x2;
870 (*currentPos++) = y2;
Romain Guy694b5192010-07-21 21:33:20 -0700871 (*currentPos++) = u2;
872 (*currentPos++) = v2;
873
874 (*currentPos++) = x3;
875 (*currentPos++) = y3;
Romain Guy694b5192010-07-21 21:33:20 -0700876 (*currentPos++) = u3;
877 (*currentPos++) = v3;
878
879 (*currentPos++) = x4;
880 (*currentPos++) = y4;
Romain Guy694b5192010-07-21 21:33:20 -0700881 (*currentPos++) = u4;
882 (*currentPos++) = v4;
883
884 mCurrentQuadIndex++;
Romain Guy97771732012-02-28 18:17:02 -0800885}
886
887void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
888 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
889 float x4, float y4, float u4, float v4, CacheTexture* texture) {
890
891 if (mClip &&
892 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
893 return;
894 }
895
896 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 -0700897
Romain Guy5b3b3522010-10-27 18:57:51 -0700898 if (mBounds) {
899 mBounds->left = fmin(mBounds->left, x1);
900 mBounds->top = fmin(mBounds->top, y3);
901 mBounds->right = fmax(mBounds->right, x3);
902 mBounds->bottom = fmax(mBounds->bottom, y1);
903 }
904
Romain Guy694b5192010-07-21 21:33:20 -0700905 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
906 issueDrawCommand();
907 mCurrentQuadIndex = 0;
908 }
909}
910
Romain Guy97771732012-02-28 18:17:02 -0800911void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
912 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
913 float x4, float y4, float u4, float v4, CacheTexture* texture) {
914
915 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
916
917 if (mBounds) {
918 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
919 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
920 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
921 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
922 }
923
924 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
925 issueDrawCommand();
926 mCurrentQuadIndex = 0;
927 }
928}
929
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700930uint32_t FontRenderer::getRemainingCacheCapacity() {
931 uint32_t remainingCapacity = 0;
932 float totalPixels = 0;
933 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
934 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
935 totalPixels += mCacheLines[i]->mMaxWidth;
936 }
937 remainingCapacity = (remainingCapacity * 100) / totalPixels;
938 return remainingCapacity;
939}
940
941void FontRenderer::precacheLatin(SkPaint* paint) {
942 // Remaining capacity is measured in %
943 uint32_t remainingCapacity = getRemainingCacheCapacity();
944 uint32_t precacheIdx = 0;
Romain Guy054dc182010-10-15 17:55:25 -0700945 while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
Romain Guy726aeba2011-06-01 14:52:00 -0700946 mCurrentFont->getCachedGlyph(paint, (int32_t) mLatinPrecache[precacheIdx]);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700947 remainingCapacity = getRemainingCacheCapacity();
948 precacheIdx ++;
949 }
950}
951
952void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
953 uint32_t currentNumFonts = mActiveFonts.size();
Romain Guy325a0f92011-01-05 15:26:55 -0800954 int flags = 0;
955 if (paint->isFakeBoldText()) {
956 flags |= Font::kFakeBold;
957 }
Romain Guy2577db12011-01-18 13:02:38 -0800958
959 const float skewX = paint->getTextSkewX();
960 uint32_t italicStyle = *(uint32_t*) &skewX;
Chet Haase8668f8a2011-03-02 13:51:36 -0800961 const float scaleXFloat = paint->getTextScaleX();
962 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
Romain Guybd496bc2011-08-02 17:32:41 -0700963 SkPaint::Style style = paint->getStyle();
964 const float strokeWidthFloat = paint->getStrokeWidth();
965 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
966 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
967 scaleX, style, strokeWidth);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700968
969 const float maxPrecacheFontSize = 40.0f;
970 bool isNewFont = currentNumFonts != mActiveFonts.size();
971
Romain Guy2bffd262010-09-12 17:40:02 -0700972 if (isNewFont && fontSize <= maxPrecacheFontSize) {
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700973 precacheLatin(paint);
974 }
Romain Guy694b5192010-07-21 21:33:20 -0700975}
Romain Guy7975fb62010-10-01 16:36:14 -0700976
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700977FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Romain Guy1e45aae2010-08-13 19:39:53 -0700978 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
979 checkInit();
980
981 if (!mCurrentFont) {
982 DropShadow image;
983 image.width = 0;
984 image.height = 0;
985 image.image = NULL;
986 image.penX = 0;
987 image.penY = 0;
988 return image;
989 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700990
Romain Guy2d4fd362011-12-13 22:00:19 -0800991 mDrawn = false;
Romain Guyff98fa52011-11-28 09:35:09 -0800992 mClip = NULL;
993 mBounds = NULL;
994
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700995 Rect bounds;
Romain Guy726aeba2011-06-01 14:52:00 -0700996 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds);
Romain Guyff98fa52011-11-28 09:35:09 -0800997
Romain Guy1e45aae2010-08-13 19:39:53 -0700998 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
999 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001000 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guyff98fa52011-11-28 09:35:09 -08001001
Romain Guy1e45aae2010-08-13 19:39:53 -07001002 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001003 dataBuffer[i] = 0;
1004 }
Romain Guy1e45aae2010-08-13 19:39:53 -07001005
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001006 int penX = radius - bounds.left;
1007 int penY = radius - bounds.bottom;
1008
Romain Guy726aeba2011-06-01 14:52:00 -07001009 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
Romain Guy1e45aae2010-08-13 19:39:53 -07001010 dataBuffer, paddedWidth, paddedHeight);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001011 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
1012
1013 DropShadow image;
1014 image.width = paddedWidth;
1015 image.height = paddedHeight;
1016 image.image = dataBuffer;
1017 image.penX = penX;
1018 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -08001019
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001020 return image;
1021}
Romain Guy694b5192010-07-21 21:33:20 -07001022
Romain Guy671d6cf2012-01-18 12:39:17 -08001023void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -07001024 checkInit();
1025
Romain Guy5b3b3522010-10-27 18:57:51 -07001026 mDrawn = false;
1027 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -07001028 mClip = clip;
Romain Guy671d6cf2012-01-18 12:39:17 -08001029}
Romain Guyff98fa52011-11-28 09:35:09 -08001030
Romain Guy671d6cf2012-01-18 12:39:17 -08001031void FontRenderer::finishRender() {
Romain Guy5b3b3522010-10-27 18:57:51 -07001032 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -08001033 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -07001034
1035 if (mCurrentQuadIndex != 0) {
1036 issueDrawCommand();
1037 mCurrentQuadIndex = 0;
1038 }
Romain Guy671d6cf2012-01-18 12:39:17 -08001039}
1040
1041bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
1042 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
1043 if (!mCurrentFont) {
1044 ALOGE("No font set");
1045 return false;
1046 }
1047
1048 initRender(clip, bounds);
1049 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
1050 finishRender();
1051
1052 return mDrawn;
1053}
1054
1055bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
1056 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
1057 const float* positions, Rect* bounds) {
1058 if (!mCurrentFont) {
1059 ALOGE("No font set");
1060 return false;
1061 }
1062
1063 initRender(clip, bounds);
1064 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
1065 finishRender();
Romain Guy5b3b3522010-10-27 18:57:51 -07001066
1067 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -07001068}
1069
Romain Guy97771732012-02-28 18:17:02 -08001070bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
1071 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
1072 float hOffset, float vOffset, Rect* bounds) {
1073 if (!mCurrentFont) {
1074 ALOGE("No font set");
1075 return false;
1076 }
1077
1078 initRender(clip, bounds);
1079 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
1080 finishRender();
1081
1082 return mDrawn;
1083}
1084
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001085void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
1086 // Compute gaussian weights for the blur
1087 // e is the euler's number
1088 float e = 2.718281828459045f;
1089 float pi = 3.1415926535897932f;
1090 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
1091 // x is of the form [-radius .. 0 .. radius]
1092 // and sigma varies with radius.
1093 // Based on some experimental radius values and sigma's
1094 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001095 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001096 // The larger the radius gets, the more our gaussian blur
1097 // will resemble a box blur since with large sigma
1098 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -08001099 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001100
1101 // Now compute the coefficints
1102 // We will store some redundant values to save some math during
1103 // the blur calculations
1104 // precompute some values
1105 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
1106 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
1107
1108 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -08001109 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -07001110 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001111 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
1112 normalizeFactor += weights[r + radius];
1113 }
1114
1115 //Now we need to normalize the weights because all our coefficients need to add up to one
1116 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -08001117 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001118 weights[r + radius] *= normalizeFactor;
1119 }
1120}
1121
1122void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -07001123 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001124 float blurredPixel = 0.0f;
1125 float currentPixel = 0.0f;
1126
Romain Guy325a0f92011-01-05 15:26:55 -08001127 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001128
1129 const uint8_t* input = source + y * width;
1130 uint8_t* output = dest + y * width;
1131
Romain Guy325a0f92011-01-05 15:26:55 -08001132 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001133 blurredPixel = 0.0f;
1134 const float* gPtr = weights;
1135 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -08001136 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001137 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -08001138 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -07001139 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001140 blurredPixel += currentPixel * gPtr[0];
1141 gPtr++;
1142 i++;
1143 }
1144 } else {
Romain Guy325a0f92011-01-05 15:26:55 -08001145 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001146 // Stepping left and right away from the pixel
1147 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -08001148 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001149 validW = 0;
1150 }
Romain Guy325a0f92011-01-05 15:26:55 -08001151 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001152 validW = width - 1;
1153 }
1154
Romain Guy325a0f92011-01-05 15:26:55 -08001155 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001156 blurredPixel += currentPixel * gPtr[0];
1157 gPtr++;
1158 }
1159 }
1160 *output = (uint8_t)blurredPixel;
1161 output ++;
1162 }
1163 }
1164}
1165
1166void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -07001167 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001168 float blurredPixel = 0.0f;
1169 float currentPixel = 0.0f;
1170
Romain Guy325a0f92011-01-05 15:26:55 -08001171 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001172
1173 uint8_t* output = dest + y * width;
1174
Romain Guy325a0f92011-01-05 15:26:55 -08001175 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001176 blurredPixel = 0.0f;
1177 const float* gPtr = weights;
1178 const uint8_t* input = source + x;
1179 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -08001180 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001181 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -08001182 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001183 currentPixel = (float)(*i);
1184 blurredPixel += currentPixel * gPtr[0];
1185 gPtr++;
1186 i += width;
1187 }
1188 } else {
Romain Guy325a0f92011-01-05 15:26:55 -08001189 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001190 int validH = y + r;
1191 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -08001192 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001193 validH = 0;
1194 }
Romain Guy325a0f92011-01-05 15:26:55 -08001195 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001196 validH = height - 1;
1197 }
1198
1199 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -08001200 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001201 blurredPixel += currentPixel * gPtr[0];
1202 gPtr++;
1203 }
1204 }
Romain Guy325a0f92011-01-05 15:26:55 -08001205 *output = (uint8_t) blurredPixel;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001206 output ++;
1207 }
1208 }
1209}
1210
1211
1212void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
1213 float *gaussian = new float[2 * radius + 1];
1214 computeGaussianWeights(gaussian, radius);
Romain Guyd71dd362011-12-12 19:03:35 -08001215
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001216 uint8_t* scratch = new uint8_t[width * height];
Romain Guyd71dd362011-12-12 19:03:35 -08001217
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001218 horizontalBlur(gaussian, radius, image, scratch, width, height);
1219 verticalBlur(gaussian, radius, scratch, image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -08001220
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001221 delete[] gaussian;
1222 delete[] scratch;
1223}
1224
Romain Guy694b5192010-07-21 21:33:20 -07001225}; // namespace uirenderer
1226}; // namespace android