blob: ccddd9155975ebb42e988e1820c80ba1990ff007 [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
Romain Guy33fa1f72012-08-07 19:09:57 -070040#define TEXTURE_BORDER_SIZE 1
Chet Haase7de0cb12011-12-05 16:35:38 -080041
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
Romain Guy33fa1f72012-08-07 19:09:57 -070053 if (mCurrentCol + glyph.fWidth + TEXTURE_BORDER_SIZE * 2 < mMaxWidth) {
54 *retOriginX = mCurrentCol + TEXTURE_BORDER_SIZE;
55 *retOriginY = mCurrentRow + TEXTURE_BORDER_SIZE;
56 mCurrentCol += glyph.fWidth + TEXTURE_BORDER_SIZE * 2;
Chet Haase7de0cb12011-12-05 16:35:38 -080057 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,
Raph Levien416a8472012-07-19 22:48:17 -0700291 int numGlyphs, Rect *bounds, const float* positions) {
Romain Guy61c8c9c2010-08-09 20:48:09 -0700292 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);
Raph Levien416a8472012-07-19 22:48:17 -0700297 render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, positions);
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
Romain Guy33fa1f72012-08-07 19:09:57 -0700415 glyph->mBitmapMinU = startX / (float) cacheWidth;
416 glyph->mBitmapMinV = startY / (float) cacheHeight;
417 glyph->mBitmapMaxU = endX / (float) cacheWidth;
418 glyph->mBitmapMaxV = endY / (float) cacheHeight;
Romain Guy694b5192010-07-21 21:33:20 -0700419
Romain Guy51769a62010-07-23 00:28:00 -0700420 mState->mUploadTexture = true;
Romain Guy694b5192010-07-21 21:33:20 -0700421}
422
Chet Haase7de0cb12011-12-05 16:35:38 -0800423CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) {
Romain Guy51769a62010-07-23 00:28:00 -0700424 CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
Romain Guy694b5192010-07-21 21:33:20 -0700425 mCachedGlyphs.add(glyph, newGlyph);
426
Romain Guy726aeba2011-06-01 14:52:00 -0700427 const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
Romain Guy694b5192010-07-21 21:33:20 -0700428 newGlyph->mGlyphIndex = skiaGlyph.fID;
429 newGlyph->mIsValid = false;
430
431 updateGlyphCache(paint, skiaGlyph, newGlyph);
432
433 return newGlyph;
434}
435
Romain Guy2577db12011-01-18 13:02:38 -0800436Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
Romain Guybd496bc2011-08-02 17:32:41 -0700437 int flags, uint32_t italicStyle, uint32_t scaleX,
438 SkPaint::Style style, uint32_t strokeWidth) {
Romain Guy694b5192010-07-21 21:33:20 -0700439 Vector<Font*> &activeFonts = state->mActiveFonts;
440
441 for (uint32_t i = 0; i < activeFonts.size(); i++) {
Romain Guy51769a62010-07-23 00:28:00 -0700442 Font* font = activeFonts[i];
Romain Guy2577db12011-01-18 13:02:38 -0800443 if (font->mFontId == fontId && font->mFontSize == fontSize &&
Chet Haase8668f8a2011-03-02 13:51:36 -0800444 font->mFlags == flags && font->mItalicStyle == italicStyle &&
Romain Guybd496bc2011-08-02 17:32:41 -0700445 font->mScaleX == scaleX && font->mStyle == style &&
446 (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
Romain Guy51769a62010-07-23 00:28:00 -0700447 return font;
Romain Guy694b5192010-07-21 21:33:20 -0700448 }
449 }
450
Romain Guybd496bc2011-08-02 17:32:41 -0700451 Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
452 scaleX, style, strokeWidth);
Romain Guy694b5192010-07-21 21:33:20 -0700453 activeFonts.push(newFont);
454 return newFont;
455}
456
457///////////////////////////////////////////////////////////////////////////////
458// FontRenderer
459///////////////////////////////////////////////////////////////////////////////
460
Romain Guy514fb182011-01-19 14:38:29 -0800461static bool sLogFontRendererCreate = true;
462
Romain Guy694b5192010-07-21 21:33:20 -0700463FontRenderer::FontRenderer() {
Romain Guyc9855a52011-01-21 21:14:15 -0800464 if (sLogFontRendererCreate) {
465 INIT_LOGD("Creating FontRenderer");
466 }
Romain Guy51769a62010-07-23 00:28:00 -0700467
Romain Guyb45c0c92010-08-26 20:35:23 -0700468 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700469 mInitialized = false;
470 mMaxNumberOfQuads = 1024;
471 mCurrentQuadIndex = 0;
472
Romain Guy9cccc2b92010-08-07 23:46:15 -0700473 mTextMeshPtr = NULL;
Chet Haase7de0cb12011-12-05 16:35:38 -0800474 mCurrentCacheTexture = NULL;
475 mLastCacheTexture = NULL;
476 mCacheTextureSmall = NULL;
477 mCacheTexture128 = NULL;
478 mCacheTexture256 = NULL;
479 mCacheTexture512 = NULL;
Romain Guy9cccc2b92010-08-07 23:46:15 -0700480
Chet Haase2a47c142011-12-14 15:22:56 -0800481 mLinearFiltering = false;
482
Romain Guy694b5192010-07-21 21:33:20 -0700483 mIndexBufferID = 0;
484
Chet Haase7de0cb12011-12-05 16:35:38 -0800485 mSmallCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
486 mSmallCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -0700487
488 char property[PROPERTY_VALUE_MAX];
489 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800490 if (sLogFontRendererCreate) {
491 INIT_LOGD(" Setting text cache width to %s pixels", property);
492 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800493 mSmallCacheWidth = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -0700494 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800495 if (sLogFontRendererCreate) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800496 INIT_LOGD(" Using default text cache width of %i pixels", mSmallCacheWidth);
Romain Guy514fb182011-01-19 14:38:29 -0800497 }
Romain Guy51769a62010-07-23 00:28:00 -0700498 }
499
500 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800501 if (sLogFontRendererCreate) {
502 INIT_LOGD(" Setting text cache width to %s pixels", property);
503 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800504 mSmallCacheHeight = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -0700505 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800506 if (sLogFontRendererCreate) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800507 INIT_LOGD(" Using default text cache height of %i pixels", mSmallCacheHeight);
Romain Guy514fb182011-01-19 14:38:29 -0800508 }
Romain Guy51769a62010-07-23 00:28:00 -0700509 }
Romain Guy514fb182011-01-19 14:38:29 -0800510
511 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -0700512}
513
514FontRenderer::~FontRenderer() {
515 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
516 delete mCacheLines[i];
517 }
518 mCacheLines.clear();
519
Romain Guy9cccc2b92010-08-07 23:46:15 -0700520 if (mInitialized) {
Romain Guya9dd8202012-03-26 14:52:00 -0700521 // Unbinding the buffer shouldn't be necessary but it crashes with some drivers
522 Caches::getInstance().unbindIndicesBuffer();
Romain Guyb0317982012-03-21 11:52:52 -0700523 glDeleteBuffers(1, &mIndexBufferID);
524
Romain Guy9cccc2b92010-08-07 23:46:15 -0700525 delete[] mTextMeshPtr;
Chet Haase7de0cb12011-12-05 16:35:38 -0800526 delete mCacheTextureSmall;
527 delete mCacheTexture128;
528 delete mCacheTexture256;
529 delete mCacheTexture512;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700530 }
Romain Guy694b5192010-07-21 21:33:20 -0700531
532 Vector<Font*> fontsToDereference = mActiveFonts;
533 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
534 delete fontsToDereference[i];
535 }
536}
537
538void FontRenderer::flushAllAndInvalidate() {
539 if (mCurrentQuadIndex != 0) {
540 issueDrawCommand();
541 mCurrentQuadIndex = 0;
542 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700543
Romain Guy694b5192010-07-21 21:33:20 -0700544 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
545 mActiveFonts[i]->invalidateTextureCache();
546 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700547
Romain Guy694b5192010-07-21 21:33:20 -0700548 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
549 mCacheLines[i]->mCurrentCol = 0;
550 }
551}
552
Chet Haase9a824562011-12-16 15:44:59 -0800553void FontRenderer::deallocateTextureMemory(CacheTexture *cacheTexture) {
554 if (cacheTexture && cacheTexture->mTexture) {
555 glDeleteTextures(1, &cacheTexture->mTextureId);
Romain Guy9d9758a2012-05-14 15:19:58 -0700556 delete[] cacheTexture->mTexture;
Chet Haase9a824562011-12-16 15:44:59 -0800557 cacheTexture->mTexture = NULL;
Romain Guy99a6ddd2012-05-14 15:32:18 -0700558 cacheTexture->mTextureId = 0;
Chet Haase9a824562011-12-16 15:44:59 -0800559 }
560}
561
562void FontRenderer::flushLargeCaches() {
563 if ((!mCacheTexture128 || !mCacheTexture128->mTexture) &&
564 (!mCacheTexture256 || !mCacheTexture256->mTexture) &&
565 (!mCacheTexture512 || !mCacheTexture512->mTexture)) {
566 // Typical case; no large glyph caches allocated
567 return;
568 }
569
570 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
571 CacheTextureLine* cacheLine = mCacheLines[i];
572 if ((cacheLine->mCacheTexture == mCacheTexture128 ||
573 cacheLine->mCacheTexture == mCacheTexture256 ||
574 cacheLine->mCacheTexture == mCacheTexture512) &&
575 cacheLine->mCacheTexture->mTexture != NULL) {
576 cacheLine->mCurrentCol = 0;
577 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
578 mActiveFonts[i]->invalidateTextureCache(cacheLine);
579 }
580 }
581 }
582
583 deallocateTextureMemory(mCacheTexture128);
584 deallocateTextureMemory(mCacheTexture256);
585 deallocateTextureMemory(mCacheTexture512);
586}
587
Romain Guy9d9758a2012-05-14 15:19:58 -0700588void FontRenderer::allocateTextureMemory(CacheTexture* cacheTexture) {
Chet Haase2a47c142011-12-14 15:22:56 -0800589 int width = cacheTexture->mWidth;
590 int height = cacheTexture->mHeight;
Romain Guy9d9758a2012-05-14 15:19:58 -0700591
Chet Haase2a47c142011-12-14 15:22:56 -0800592 cacheTexture->mTexture = new uint8_t[width * height];
Romain Guy99a6ddd2012-05-14 15:32:18 -0700593
594 if (!cacheTexture->mTextureId) {
595 glGenTextures(1, &cacheTexture->mTextureId);
596 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700597
Romain Guy16c88082012-06-11 16:03:47 -0700598 Caches::getInstance().activeTexture(0);
Chet Haase2a47c142011-12-14 15:22:56 -0800599 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
600 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
601 // Initialize texture dimensions
602 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
603 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800604
Chet Haase2a47c142011-12-14 15:22:56 -0800605 const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST;
606 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
607 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
608
609 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
610 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
Chet Haase7de0cb12011-12-05 16:35:38 -0800611}
612
613void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
614 uint32_t* retOriginX, uint32_t* retOriginY) {
615 cachedGlyph->mIsValid = false;
Romain Guy694b5192010-07-21 21:33:20 -0700616 // If the glyph is too tall, don't cache it
Romain Guy33fa1f72012-08-07 19:09:57 -0700617 if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
Steve Block3762c312012-01-06 19:20:56 +0000618 ALOGE("Font size to large to fit in cache. width, height = %i, %i",
Chet Haase7de0cb12011-12-05 16:35:38 -0800619 (int) glyph.fWidth, (int) glyph.fHeight);
620 return;
Romain Guy694b5192010-07-21 21:33:20 -0700621 }
622
623 // Now copy the bitmap into the cache texture
624 uint32_t startX = 0;
625 uint32_t startY = 0;
626
627 bool bitmapFit = false;
Chet Haase7de0cb12011-12-05 16:35:38 -0800628 CacheTextureLine *cacheLine;
Romain Guy694b5192010-07-21 21:33:20 -0700629 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 the new glyph didn't fit, flush the state so far and invalidate everything
638 if (!bitmapFit) {
639 flushAllAndInvalidate();
640
641 // Try to fit it again
642 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
643 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
644 if (bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800645 cacheLine = mCacheLines[i];
Romain Guy694b5192010-07-21 21:33:20 -0700646 break;
647 }
648 }
649
650 // if we still don't fit, something is wrong and we shouldn't draw
651 if (!bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800652 return;
Romain Guy694b5192010-07-21 21:33:20 -0700653 }
654 }
655
Chet Haase7de0cb12011-12-05 16:35:38 -0800656 cachedGlyph->mCachedTextureLine = cacheLine;
657
Romain Guy694b5192010-07-21 21:33:20 -0700658 *retOriginX = startX;
659 *retOriginY = startY;
660
661 uint32_t endX = startX + glyph.fWidth;
662 uint32_t endY = startY + glyph.fHeight;
663
Chet Haase7de0cb12011-12-05 16:35:38 -0800664 uint32_t cacheWidth = cacheLine->mMaxWidth;
Romain Guy694b5192010-07-21 21:33:20 -0700665
Romain Guy9d9758a2012-05-14 15:19:58 -0700666 CacheTexture* cacheTexture = cacheLine->mCacheTexture;
667 if (!cacheTexture->mTexture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800668 // Large-glyph texture memory is allocated only as needed
Chet Haase2a47c142011-12-14 15:22:56 -0800669 allocateTextureMemory(cacheTexture);
Chet Haase7de0cb12011-12-05 16:35:38 -0800670 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700671
Chet Haase7de0cb12011-12-05 16:35:38 -0800672 uint8_t* cacheBuffer = cacheTexture->mTexture;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700673 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700674 unsigned int stride = glyph.rowBytes();
675
676 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
Romain Guy33fa1f72012-08-07 19:09:57 -0700677
678 for (cacheX = startX - TEXTURE_BORDER_SIZE; cacheX < endX + TEXTURE_BORDER_SIZE; cacheX++) {
679 cacheBuffer[(startY - TEXTURE_BORDER_SIZE) * cacheWidth + cacheX] = 0;
680 cacheBuffer[(endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + cacheX] = 0;
681 }
682
683 for (cacheY = startY - TEXTURE_BORDER_SIZE + 1;
684 cacheY < endY + TEXTURE_BORDER_SIZE - 1; cacheY++) {
685 cacheBuffer[cacheY * cacheWidth + startX - TEXTURE_BORDER_SIZE] = 0;
686 cacheBuffer[cacheY * cacheWidth + endX + TEXTURE_BORDER_SIZE - 1] = 0;
687 }
688
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700689 if (mGammaTable) {
690 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
691 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
692 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
693 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
694 }
695 }
696 } else {
697 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
698 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
699 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
700 cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
701 }
Romain Guy694b5192010-07-21 21:33:20 -0700702 }
703 }
Romain Guy97771732012-02-28 18:17:02 -0800704
Chet Haase7de0cb12011-12-05 16:35:38 -0800705 cachedGlyph->mIsValid = true;
Romain Guy694b5192010-07-21 21:33:20 -0700706}
707
Chet Haase7de0cb12011-12-05 16:35:38 -0800708CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
Romain Guy0aa87bb2012-07-20 11:14:32 -0700709 CacheTexture* cacheTexture = new CacheTexture(width, height);
Romain Guy9d9758a2012-05-14 15:19:58 -0700710
Chet Haase2a47c142011-12-14 15:22:56 -0800711 if (allocate) {
712 allocateTextureMemory(cacheTexture);
713 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700714
Chet Haase2a47c142011-12-14 15:22:56 -0800715 return cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800716}
717
718void FontRenderer::initTextTexture() {
Romain Guy9d9758a2012-05-14 15:19:58 -0700719 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
720 delete mCacheLines[i];
721 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800722 mCacheLines.clear();
723
Romain Guy9d9758a2012-05-14 15:19:58 -0700724 if (mCacheTextureSmall) {
725 delete mCacheTextureSmall;
726 delete mCacheTexture128;
727 delete mCacheTexture256;
728 delete mCacheTexture512;
729 }
730
Chet Haase7de0cb12011-12-05 16:35:38 -0800731 // Next, use other, separate caches for large glyphs.
732 uint16_t maxWidth = 0;
733 if (Caches::hasInstance()) {
734 maxWidth = Caches::getInstance().maxTextureSize;
Chet Haase44984ea2011-05-19 13:50:47 -0700735 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700736
Chet Haase7de0cb12011-12-05 16:35:38 -0800737 if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) {
738 maxWidth = MAX_TEXT_CACHE_WIDTH;
739 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700740
Chet Haase7de0cb12011-12-05 16:35:38 -0800741 mCacheTextureSmall = createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true);
742 mCacheTexture128 = createCacheTexture(maxWidth, 256, false);
743 mCacheTexture256 = createCacheTexture(maxWidth, 256, false);
744 mCacheTexture512 = createCacheTexture(maxWidth, 512, false);
745 mCurrentCacheTexture = mCacheTextureSmall;
746
747 mUploadTexture = false;
748 // Split up our default cache texture into lines of certain widths
749 int nextLine = 0;
750 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 18, nextLine, 0, mCacheTextureSmall));
751 nextLine += mCacheLines.top()->mMaxHeight;
752 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
753 nextLine += mCacheLines.top()->mMaxHeight;
754 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
755 nextLine += mCacheLines.top()->mMaxHeight;
756 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
757 nextLine += mCacheLines.top()->mMaxHeight;
758 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
759 nextLine += mCacheLines.top()->mMaxHeight;
760 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 42, nextLine, 0, mCacheTextureSmall));
761 nextLine += mCacheLines.top()->mMaxHeight;
762 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, mSmallCacheHeight - nextLine,
763 nextLine, 0, mCacheTextureSmall));
764
765 // The first cache is split into 2 lines of height 128, the rest have just one cache line.
766 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 0, 0, mCacheTexture128));
767 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 128, 0, mCacheTexture128));
768 mCacheLines.push(new CacheTextureLine(maxWidth, 256, 0, 0, mCacheTexture256));
769 mCacheLines.push(new CacheTextureLine(maxWidth, 512, 0, 0, mCacheTexture512));
Romain Guy694b5192010-07-21 21:33:20 -0700770}
771
772// Avoid having to reallocate memory and render quad by quad
773void FontRenderer::initVertexArrayBuffers() {
Romain Guyd71dd362011-12-12 19:03:35 -0800774 uint32_t numIndices = mMaxNumberOfQuads * 6;
775 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700776 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700777
778 // Four verts, two triangles , six indices per quad
779 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
780 int i6 = i * 6;
781 int i4 = i * 4;
782
783 indexBufferData[i6 + 0] = i4 + 0;
784 indexBufferData[i6 + 1] = i4 + 1;
785 indexBufferData[i6 + 2] = i4 + 2;
786
787 indexBufferData[i6 + 3] = i4 + 0;
788 indexBufferData[i6 + 4] = i4 + 2;
789 indexBufferData[i6 + 5] = i4 + 3;
790 }
791
792 glGenBuffers(1, &mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800793 Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700794 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
Romain Guy694b5192010-07-21 21:33:20 -0700795
796 free(indexBufferData);
797
Romain Guyd71dd362011-12-12 19:03:35 -0800798 uint32_t coordSize = 2;
Romain Guy694b5192010-07-21 21:33:20 -0700799 uint32_t uvSize = 2;
800 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700801 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
802 mTextMeshPtr = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700803}
804
805// We don't want to allocate anything unless we actually draw text
806void FontRenderer::checkInit() {
807 if (mInitialized) {
808 return;
809 }
810
811 initTextTexture();
812 initVertexArrayBuffers();
813
814 mInitialized = true;
815}
816
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700817void FontRenderer::checkTextureUpdate() {
Chet Haase7de0cb12011-12-05 16:35:38 -0800818 if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700819 return;
Romain Guy694b5192010-07-21 21:33:20 -0700820 }
821
Romain Guy2d4fd362011-12-13 22:00:19 -0800822 Caches& caches = Caches::getInstance();
823 GLuint lastTextureId = 0;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700824 // Iterate over all the cache lines and see which ones need to be updated
825 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
826 CacheTextureLine* cl = mCacheLines[i];
Chet Haase7de0cb12011-12-05 16:35:38 -0800827 if (cl->mDirty && cl->mCacheTexture->mTexture != NULL) {
828 CacheTexture* cacheTexture = cl->mCacheTexture;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700829 uint32_t xOffset = 0;
830 uint32_t yOffset = cl->mCurrentRow;
Chet Haase7de0cb12011-12-05 16:35:38 -0800831 uint32_t width = cl->mMaxWidth;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700832 uint32_t height = cl->mMaxHeight;
Chet Haase7de0cb12011-12-05 16:35:38 -0800833 void* textureData = cacheTexture->mTexture + (yOffset * width);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700834
Romain Guy2d4fd362011-12-13 22:00:19 -0800835 if (cacheTexture->mTextureId != lastTextureId) {
836 caches.activeTexture(0);
837 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
838 lastTextureId = cacheTexture->mTextureId;
839 }
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700840 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700841 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700842
843 cl->mDirty = false;
844 }
845 }
846
Romain Guy16c88082012-06-11 16:03:47 -0700847 caches.activeTexture(0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800848 glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
Chet Haase2a47c142011-12-14 15:22:56 -0800849 if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) {
850 const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST;
851 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
852 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
853 mCurrentCacheTexture->mLinearFiltering = mLinearFiltering;
854 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800855 mLastCacheTexture = mCurrentCacheTexture;
856
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700857 mUploadTexture = false;
858}
859
860void FontRenderer::issueDrawCommand() {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700861 checkTextureUpdate();
862
Romain Guy15bc6432011-12-13 13:11:32 -0800863 Caches& caches = Caches::getInstance();
Romain Guy2d4fd362011-12-13 22:00:19 -0800864 caches.bindIndicesBuffer(mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800865 if (!mDrawn) {
866 float* buffer = mTextMeshPtr;
867 int offset = 2;
868
869 bool force = caches.unbindMeshBuffer();
870 caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
871 caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
872 buffer + offset);
873 }
874
Romain Guy694b5192010-07-21 21:33:20 -0700875 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
Romain Guy5b3b3522010-10-27 18:57:51 -0700876
877 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700878}
879
Romain Guy97771732012-02-28 18:17:02 -0800880void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
881 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800882 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800883 if (texture != mCurrentCacheTexture) {
884 if (mCurrentQuadIndex != 0) {
885 // First, draw everything stored already which uses the previous texture
886 issueDrawCommand();
887 mCurrentQuadIndex = 0;
888 }
889 // Now use the new texture id
890 mCurrentCacheTexture = texture;
891 }
Romain Guy09147fb2010-07-22 13:08:20 -0700892
Romain Guy694b5192010-07-21 21:33:20 -0700893 const uint32_t vertsPerQuad = 4;
Romain Guyd71dd362011-12-12 19:03:35 -0800894 const uint32_t floatsPerVert = 4;
Romain Guy51769a62010-07-23 00:28:00 -0700895 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700896
Romain Guy694b5192010-07-21 21:33:20 -0700897 (*currentPos++) = x1;
898 (*currentPos++) = y1;
Romain Guy694b5192010-07-21 21:33:20 -0700899 (*currentPos++) = u1;
900 (*currentPos++) = v1;
901
902 (*currentPos++) = x2;
903 (*currentPos++) = y2;
Romain Guy694b5192010-07-21 21:33:20 -0700904 (*currentPos++) = u2;
905 (*currentPos++) = v2;
906
907 (*currentPos++) = x3;
908 (*currentPos++) = y3;
Romain Guy694b5192010-07-21 21:33:20 -0700909 (*currentPos++) = u3;
910 (*currentPos++) = v3;
911
912 (*currentPos++) = x4;
913 (*currentPos++) = y4;
Romain Guy694b5192010-07-21 21:33:20 -0700914 (*currentPos++) = u4;
915 (*currentPos++) = v4;
916
917 mCurrentQuadIndex++;
Romain Guy97771732012-02-28 18:17:02 -0800918}
919
920void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
921 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
922 float x4, float y4, float u4, float v4, CacheTexture* texture) {
923
924 if (mClip &&
925 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
926 return;
927 }
928
929 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 -0700930
Romain Guy5b3b3522010-10-27 18:57:51 -0700931 if (mBounds) {
932 mBounds->left = fmin(mBounds->left, x1);
933 mBounds->top = fmin(mBounds->top, y3);
934 mBounds->right = fmax(mBounds->right, x3);
935 mBounds->bottom = fmax(mBounds->bottom, y1);
936 }
937
Romain Guy694b5192010-07-21 21:33:20 -0700938 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
939 issueDrawCommand();
940 mCurrentQuadIndex = 0;
941 }
942}
943
Romain Guy97771732012-02-28 18:17:02 -0800944void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
945 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
946 float x4, float y4, float u4, float v4, CacheTexture* texture) {
947
948 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
949
950 if (mBounds) {
951 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
952 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
953 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
954 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
955 }
956
957 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
958 issueDrawCommand();
959 mCurrentQuadIndex = 0;
960 }
961}
962
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700963uint32_t FontRenderer::getRemainingCacheCapacity() {
964 uint32_t remainingCapacity = 0;
965 float totalPixels = 0;
Chen YANG0412cfc2012-07-25 13:27:40 +0800966
967 //avoid divide by zero if the size is 0
968 if (mCacheLines.size() == 0) {
969 return 0;
970 }
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700971 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
972 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
973 totalPixels += mCacheLines[i]->mMaxWidth;
974 }
975 remainingCapacity = (remainingCapacity * 100) / totalPixels;
976 return remainingCapacity;
977}
978
979void FontRenderer::precacheLatin(SkPaint* paint) {
980 // Remaining capacity is measured in %
981 uint32_t remainingCapacity = getRemainingCacheCapacity();
Romain Guyae91c4c2012-05-14 14:00:27 -0700982 uint32_t precacheIndex = 0;
983
984 // We store a string with letters in a rough frequency of occurrence
985 String16 l("eisarntolcdugpmhbyfvkwzxjq EISARNTOLCDUGPMHBYFVKWZXJQ,.?!()-+@;:'0123456789");
986
987 size_t size = l.size();
988 uint16_t latin[size];
989 paint->utfToGlyphs(l.string(), SkPaint::kUTF16_TextEncoding, size * sizeof(char16_t), latin);
990
991 while (remainingCapacity > 25 && precacheIndex < size) {
992 mCurrentFont->getCachedGlyph(paint, TO_GLYPH(latin[precacheIndex]));
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700993 remainingCapacity = getRemainingCacheCapacity();
Romain Guyae91c4c2012-05-14 14:00:27 -0700994 precacheIndex++;
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700995 }
996}
997
998void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
999 uint32_t currentNumFonts = mActiveFonts.size();
Romain Guy325a0f92011-01-05 15:26:55 -08001000 int flags = 0;
1001 if (paint->isFakeBoldText()) {
1002 flags |= Font::kFakeBold;
1003 }
Romain Guy2577db12011-01-18 13:02:38 -08001004
1005 const float skewX = paint->getTextSkewX();
1006 uint32_t italicStyle = *(uint32_t*) &skewX;
Chet Haase8668f8a2011-03-02 13:51:36 -08001007 const float scaleXFloat = paint->getTextScaleX();
1008 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
Romain Guybd496bc2011-08-02 17:32:41 -07001009 SkPaint::Style style = paint->getStyle();
1010 const float strokeWidthFloat = paint->getStrokeWidth();
1011 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
1012 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
1013 scaleX, style, strokeWidth);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -07001014
1015 const float maxPrecacheFontSize = 40.0f;
1016 bool isNewFont = currentNumFonts != mActiveFonts.size();
1017
Romain Guy2bffd262010-09-12 17:40:02 -07001018 if (isNewFont && fontSize <= maxPrecacheFontSize) {
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -07001019 precacheLatin(paint);
1020 }
Romain Guy694b5192010-07-21 21:33:20 -07001021}
Romain Guy7975fb62010-10-01 16:36:14 -07001022
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001023FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Raph Levien416a8472012-07-19 22:48:17 -07001024 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
Romain Guy1e45aae2010-08-13 19:39:53 -07001025 checkInit();
1026
1027 if (!mCurrentFont) {
1028 DropShadow image;
1029 image.width = 0;
1030 image.height = 0;
1031 image.image = NULL;
1032 image.penX = 0;
1033 image.penY = 0;
1034 return image;
1035 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001036
Romain Guy2d4fd362011-12-13 22:00:19 -08001037 mDrawn = false;
Romain Guyff98fa52011-11-28 09:35:09 -08001038 mClip = NULL;
1039 mBounds = NULL;
1040
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001041 Rect bounds;
Raph Levien416a8472012-07-19 22:48:17 -07001042 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
Romain Guyff98fa52011-11-28 09:35:09 -08001043
Romain Guy1e45aae2010-08-13 19:39:53 -07001044 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
1045 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001046 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guyff98fa52011-11-28 09:35:09 -08001047
Romain Guy1e45aae2010-08-13 19:39:53 -07001048 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001049 dataBuffer[i] = 0;
1050 }
Romain Guy1e45aae2010-08-13 19:39:53 -07001051
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001052 int penX = radius - bounds.left;
1053 int penY = radius - bounds.bottom;
1054
Romain Guy726aeba2011-06-01 14:52:00 -07001055 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
Raph Levien416a8472012-07-19 22:48:17 -07001056 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001057 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
1058
1059 DropShadow image;
1060 image.width = paddedWidth;
1061 image.height = paddedHeight;
1062 image.image = dataBuffer;
1063 image.penX = penX;
1064 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -08001065
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001066 return image;
1067}
Romain Guy694b5192010-07-21 21:33:20 -07001068
Romain Guy671d6cf2012-01-18 12:39:17 -08001069void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -07001070 checkInit();
1071
Romain Guy5b3b3522010-10-27 18:57:51 -07001072 mDrawn = false;
1073 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -07001074 mClip = clip;
Romain Guy671d6cf2012-01-18 12:39:17 -08001075}
Romain Guyff98fa52011-11-28 09:35:09 -08001076
Romain Guy671d6cf2012-01-18 12:39:17 -08001077void FontRenderer::finishRender() {
Romain Guy5b3b3522010-10-27 18:57:51 -07001078 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -08001079 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -07001080
1081 if (mCurrentQuadIndex != 0) {
1082 issueDrawCommand();
1083 mCurrentQuadIndex = 0;
1084 }
Romain Guy671d6cf2012-01-18 12:39:17 -08001085}
1086
1087bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
1088 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
1089 if (!mCurrentFont) {
1090 ALOGE("No font set");
1091 return false;
1092 }
1093
1094 initRender(clip, bounds);
1095 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
1096 finishRender();
1097
1098 return mDrawn;
1099}
1100
1101bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
1102 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
1103 const float* positions, Rect* bounds) {
1104 if (!mCurrentFont) {
1105 ALOGE("No font set");
1106 return false;
1107 }
1108
1109 initRender(clip, bounds);
1110 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
1111 finishRender();
Romain Guy5b3b3522010-10-27 18:57:51 -07001112
1113 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -07001114}
1115
Romain Guy97771732012-02-28 18:17:02 -08001116bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
1117 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
1118 float hOffset, float vOffset, Rect* bounds) {
1119 if (!mCurrentFont) {
1120 ALOGE("No font set");
1121 return false;
1122 }
1123
1124 initRender(clip, bounds);
1125 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
1126 finishRender();
1127
1128 return mDrawn;
1129}
1130
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001131void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
1132 // Compute gaussian weights for the blur
1133 // e is the euler's number
1134 float e = 2.718281828459045f;
1135 float pi = 3.1415926535897932f;
1136 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
1137 // x is of the form [-radius .. 0 .. radius]
1138 // and sigma varies with radius.
1139 // Based on some experimental radius values and sigma's
1140 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -07001141 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001142 // The larger the radius gets, the more our gaussian blur
1143 // will resemble a box blur since with large sigma
1144 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -08001145 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001146
1147 // Now compute the coefficints
1148 // We will store some redundant values to save some math during
1149 // the blur calculations
1150 // precompute some values
1151 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
1152 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
1153
1154 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -08001155 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -07001156 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001157 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
1158 normalizeFactor += weights[r + radius];
1159 }
1160
1161 //Now we need to normalize the weights because all our coefficients need to add up to one
1162 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -08001163 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001164 weights[r + radius] *= normalizeFactor;
1165 }
1166}
1167
1168void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -07001169 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001170 float blurredPixel = 0.0f;
1171 float currentPixel = 0.0f;
1172
Romain Guy325a0f92011-01-05 15:26:55 -08001173 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001174
1175 const uint8_t* input = source + y * width;
1176 uint8_t* output = dest + y * width;
1177
Romain Guy325a0f92011-01-05 15:26:55 -08001178 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001179 blurredPixel = 0.0f;
1180 const float* gPtr = weights;
1181 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -08001182 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001183 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -08001184 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -07001185 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001186 blurredPixel += currentPixel * gPtr[0];
1187 gPtr++;
1188 i++;
1189 }
1190 } else {
Romain Guy325a0f92011-01-05 15:26:55 -08001191 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001192 // Stepping left and right away from the pixel
1193 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -08001194 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001195 validW = 0;
1196 }
Romain Guy325a0f92011-01-05 15:26:55 -08001197 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001198 validW = width - 1;
1199 }
1200
Romain Guy325a0f92011-01-05 15:26:55 -08001201 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001202 blurredPixel += currentPixel * gPtr[0];
1203 gPtr++;
1204 }
1205 }
1206 *output = (uint8_t)blurredPixel;
1207 output ++;
1208 }
1209 }
1210}
1211
1212void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -07001213 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001214 float blurredPixel = 0.0f;
1215 float currentPixel = 0.0f;
1216
Romain Guy325a0f92011-01-05 15:26:55 -08001217 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001218
1219 uint8_t* output = dest + y * width;
1220
Romain Guy325a0f92011-01-05 15:26:55 -08001221 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001222 blurredPixel = 0.0f;
1223 const float* gPtr = weights;
1224 const uint8_t* input = source + x;
1225 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -08001226 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001227 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -08001228 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001229 currentPixel = (float)(*i);
1230 blurredPixel += currentPixel * gPtr[0];
1231 gPtr++;
1232 i += width;
1233 }
1234 } else {
Romain Guy325a0f92011-01-05 15:26:55 -08001235 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001236 int validH = y + r;
1237 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -08001238 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001239 validH = 0;
1240 }
Romain Guy325a0f92011-01-05 15:26:55 -08001241 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001242 validH = height - 1;
1243 }
1244
1245 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -08001246 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001247 blurredPixel += currentPixel * gPtr[0];
1248 gPtr++;
1249 }
1250 }
Romain Guy325a0f92011-01-05 15:26:55 -08001251 *output = (uint8_t) blurredPixel;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001252 output ++;
1253 }
1254 }
1255}
1256
1257
1258void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
1259 float *gaussian = new float[2 * radius + 1];
1260 computeGaussianWeights(gaussian, radius);
Romain Guyd71dd362011-12-12 19:03:35 -08001261
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001262 uint8_t* scratch = new uint8_t[width * height];
Romain Guyd71dd362011-12-12 19:03:35 -08001263
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001264 horizontalBlur(gaussian, radius, image, scratch, width, height);
1265 verticalBlur(gaussian, radius, scratch, image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -08001266
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001267 delete[] gaussian;
1268 delete[] scratch;
1269}
1270
Romain Guy694b5192010-07-21 21:33:20 -07001271}; // namespace uirenderer
1272}; // namespace android