blob: 29dff04f76dc5424b0a48fe1d0fd2dee7c157268 [file] [log] [blame]
Romain Guy694b5192010-07-21 21:33:20 -07001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "OpenGLRenderer"
18
Romain Guy694b5192010-07-21 21:33:20 -070019#include <SkUtils.h>
20
Romain Guy51769a62010-07-23 00:28:00 -070021#include <cutils/properties.h>
Romain Guye2d345e2010-09-24 18:39:22 -070022
Romain Guy51769a62010-07-23 00:28:00 -070023#include <utils/Log.h>
24
Romain Guyc9855a52011-01-21 21:14:15 -080025#include "Debug.h"
Romain Guy51769a62010-07-23 00:28:00 -070026#include "FontRenderer.h"
Chet Haase7de0cb12011-12-05 16:35:38 -080027#include "Caches.h"
Romain Guy51769a62010-07-23 00:28:00 -070028
Romain Guy694b5192010-07-21 21:33:20 -070029namespace android {
30namespace uirenderer {
31
32///////////////////////////////////////////////////////////////////////////////
Romain Guy51769a62010-07-23 00:28:00 -070033// Defines
34///////////////////////////////////////////////////////////////////////////////
35
36#define DEFAULT_TEXT_CACHE_WIDTH 1024
37#define DEFAULT_TEXT_CACHE_HEIGHT 256
Chet Haase44984ea2011-05-19 13:50:47 -070038#define MAX_TEXT_CACHE_WIDTH 2048
Chet Haase7de0cb12011-12-05 16:35:38 -080039#define TEXTURE_BORDER_SIZE 2
40
41///////////////////////////////////////////////////////////////////////////////
42// CacheTextureLine
43///////////////////////////////////////////////////////////////////////////////
44
45bool CacheTextureLine::fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
46 if (glyph.fHeight + TEXTURE_BORDER_SIZE > mMaxHeight) {
47 return false;
48 }
49
50 if (mCurrentCol + glyph.fWidth + TEXTURE_BORDER_SIZE < mMaxWidth) {
51 *retOriginX = mCurrentCol + 1;
52 *retOriginY = mCurrentRow + 1;
53 mCurrentCol += glyph.fWidth + TEXTURE_BORDER_SIZE;
54 mDirty = true;
55 return true;
56 }
57
58 return false;
59}
Chet Haase44984ea2011-05-19 13:50:47 -070060
Romain Guy51769a62010-07-23 00:28:00 -070061///////////////////////////////////////////////////////////////////////////////
Romain Guy694b5192010-07-21 21:33:20 -070062// Font
63///////////////////////////////////////////////////////////////////////////////
64
Romain Guy2577db12011-01-18 13:02:38 -080065Font::Font(FontRenderer* state, uint32_t fontId, float fontSize,
Romain Guybd496bc2011-08-02 17:32:41 -070066 int flags, uint32_t italicStyle, uint32_t scaleX,
67 SkPaint::Style style, uint32_t strokeWidth) :
Romain Guy2577db12011-01-18 13:02:38 -080068 mState(state), mFontId(fontId), mFontSize(fontSize),
Romain Guybd496bc2011-08-02 17:32:41 -070069 mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX),
70 mStyle(style), mStrokeWidth(mStrokeWidth) {
Romain Guy694b5192010-07-21 21:33:20 -070071}
72
73
74Font::~Font() {
75 for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
76 if (mState->mActiveFonts[ct] == this) {
77 mState->mActiveFonts.removeAt(ct);
78 break;
79 }
80 }
81
82 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
Romain Guy726aeba2011-06-01 14:52:00 -070083 delete mCachedGlyphs.valueAt(i);
Romain Guy694b5192010-07-21 21:33:20 -070084 }
85}
86
87void Font::invalidateTextureCache() {
88 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
89 mCachedGlyphs.valueAt(i)->mIsValid = false;
90 }
91}
92
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070093void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds) {
94 int nPenX = x + glyph->mBitmapLeft;
95 int nPenY = y + glyph->mBitmapTop;
96
97 int width = (int) glyph->mBitmapWidth;
98 int height = (int) glyph->mBitmapHeight;
99
Romain Guy61c8c9c2010-08-09 20:48:09 -0700100 if (bounds->bottom > nPenY) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700101 bounds->bottom = nPenY;
102 }
Romain Guy61c8c9c2010-08-09 20:48:09 -0700103 if (bounds->left > nPenX) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700104 bounds->left = nPenX;
105 }
Romain Guy61c8c9c2010-08-09 20:48:09 -0700106 if (bounds->right < nPenX + width) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700107 bounds->right = nPenX + width;
108 }
Romain Guy61c8c9c2010-08-09 20:48:09 -0700109 if (bounds->top < nPenY + height) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700110 bounds->top = nPenY + height;
111 }
112}
113
Romain Guy694b5192010-07-21 21:33:20 -0700114void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
Romain Guy694b5192010-07-21 21:33:20 -0700115 int nPenX = x + glyph->mBitmapLeft;
116 int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
117
Romain Guy51769a62010-07-23 00:28:00 -0700118 float u1 = glyph->mBitmapMinU;
119 float u2 = glyph->mBitmapMaxU;
120 float v1 = glyph->mBitmapMinV;
121 float v2 = glyph->mBitmapMaxV;
122
123 int width = (int) glyph->mBitmapWidth;
124 int height = (int) glyph->mBitmapHeight;
125
Romain Guyd71dd362011-12-12 19:03:35 -0800126 mState->appendMeshQuad(nPenX, nPenY, u1, v2,
127 nPenX + width, nPenY, u2, v2,
128 nPenX + width, nPenY - height, u2, v1,
Chet Haase7de0cb12011-12-05 16:35:38 -0800129 nPenX, nPenY - height, u1, v1, glyph->mCachedTextureLine->mCacheTexture);
Romain Guy694b5192010-07-21 21:33:20 -0700130}
131
Romain Guyb45c0c92010-08-26 20:35:23 -0700132void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
133 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700134 int nPenX = x + glyph->mBitmapLeft;
135 int nPenY = y + glyph->mBitmapTop;
136
137 uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
138 uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
139
Chet Haase7de0cb12011-12-05 16:35:38 -0800140 CacheTexture *cacheTexture = glyph->mCachedTextureLine->mCacheTexture;
141 uint32_t cacheWidth = cacheTexture->mWidth;
142 const uint8_t* cacheBuffer = cacheTexture->mTexture;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700143
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700144 uint32_t cacheX = 0, cacheY = 0;
145 int32_t bX = 0, bY = 0;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700146 for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
147 for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
Romain Guyb45c0c92010-08-26 20:35:23 -0700148 if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700149 LOGE("Skipping invalid index");
150 continue;
151 }
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700152 uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
153 bitmap[bY * bitmapW + bX] = tempCol;
154 }
155 }
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700156}
157
Chet Haase7de0cb12011-12-05 16:35:38 -0800158CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700159 CachedGlyphInfo* cachedGlyph = NULL;
Romain Guy726aeba2011-06-01 14:52:00 -0700160 ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
Romain Guy1e45aae2010-08-13 19:39:53 -0700161 if (index >= 0) {
162 cachedGlyph = mCachedGlyphs.valueAt(index);
163 } else {
Romain Guy726aeba2011-06-01 14:52:00 -0700164 cachedGlyph = cacheGlyph(paint, textUnit);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700165 }
166
167 // Is the glyph still in texture cache?
168 if (!cachedGlyph->mIsValid) {
Romain Guy726aeba2011-06-01 14:52:00 -0700169 const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700170 updateGlyphCache(paint, skiaGlyph, cachedGlyph);
171 }
172
173 return cachedGlyph;
174}
175
Romain Guy726aeba2011-06-01 14:52:00 -0700176void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700177 int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
178 if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
Romain Guy726aeba2011-06-01 14:52:00 -0700179 render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700180 bitmapW, bitmapH, NULL);
181 } else {
Romain Guy726aeba2011-06-01 14:52:00 -0700182 render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
183 0, 0, NULL);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700184 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700185}
186
Romain Guy726aeba2011-06-01 14:52:00 -0700187void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700188 int numGlyphs, Rect *bounds) {
189 if (bounds == NULL) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700190 LOGE("No return rectangle provided to measure text");
191 return;
192 }
193 bounds->set(1e6, -1e6, -1e6, 1e6);
Romain Guy726aeba2011-06-01 14:52:00 -0700194 render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700195}
196
Romain Guy58ef7fb2010-09-13 12:52:37 -0700197#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16)
Romain Guy2bffd262010-09-12 17:40:02 -0700198
Romain Guy726aeba2011-06-01 14:52:00 -0700199void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700200 int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
201 uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700202 if (numGlyphs == 0 || text == NULL || len == 0) {
203 return;
204 }
205
Romain Guy5a6d3a42011-10-07 15:03:24 -0700206 float penX = x;
Romain Guy2bffd262010-09-12 17:40:02 -0700207 int penY = y;
Romain Guy694b5192010-07-21 21:33:20 -0700208 int glyphsLeft = 1;
209 if (numGlyphs > 0) {
210 glyphsLeft = numGlyphs;
211 }
212
Romain Guy2bffd262010-09-12 17:40:02 -0700213 SkFixed prevRsbDelta = 0;
Romain Guy5a6d3a42011-10-07 15:03:24 -0700214 penX += 0.5f;
Romain Guy2bffd262010-09-12 17:40:02 -0700215
Romain Guy694b5192010-07-21 21:33:20 -0700216 text += start;
217
218 while (glyphsLeft > 0) {
Romain Guy726aeba2011-06-01 14:52:00 -0700219 glyph_t glyph = GET_GLYPH(text);
Romain Guy694b5192010-07-21 21:33:20 -0700220
Romain Guy61c8c9c2010-08-09 20:48:09 -0700221 // Reached the end of the string
Romain Guy726aeba2011-06-01 14:52:00 -0700222 if (IS_END_OF_STRING(glyph)) {
Romain Guy694b5192010-07-21 21:33:20 -0700223 break;
224 }
225
Romain Guy726aeba2011-06-01 14:52:00 -0700226 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
Romain Guy5a6d3a42011-10-07 15:03:24 -0700227 penX += SkFixedToFloat(SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta));
Romain Guy2bffd262010-09-12 17:40:02 -0700228 prevRsbDelta = cachedGlyph->mRsbDelta;
Romain Guy694b5192010-07-21 21:33:20 -0700229
230 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
231 if (cachedGlyph->mIsValid) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700232 switch(mode) {
233 case FRAMEBUFFER:
Romain Guy5a6d3a42011-10-07 15:03:24 -0700234 drawCachedGlyph(cachedGlyph, (int) floorf(penX), penY);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700235 break;
236 case BITMAP:
Romain Guy5a6d3a42011-10-07 15:03:24 -0700237 drawCachedGlyph(cachedGlyph, (int) floorf(penX), penY, bitmap, bitmapW, bitmapH);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700238 break;
239 case MEASURE:
Romain Guy5a6d3a42011-10-07 15:03:24 -0700240 measureCachedGlyph(cachedGlyph, (int) floorf(penX), penY, bounds);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700241 break;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700242 }
Romain Guy694b5192010-07-21 21:33:20 -0700243 }
244
Romain Guy5a6d3a42011-10-07 15:03:24 -0700245 penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
Romain Guy694b5192010-07-21 21:33:20 -0700246
247 // If we were given a specific number of glyphs, decrement
248 if (numGlyphs > 0) {
249 glyphsLeft--;
250 }
251 }
252}
253
Romain Guy51769a62010-07-23 00:28:00 -0700254void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
Romain Guy694b5192010-07-21 21:33:20 -0700255 glyph->mAdvanceX = skiaGlyph.fAdvanceX;
256 glyph->mAdvanceY = skiaGlyph.fAdvanceY;
257 glyph->mBitmapLeft = skiaGlyph.fLeft;
258 glyph->mBitmapTop = skiaGlyph.fTop;
Romain Guy2bffd262010-09-12 17:40:02 -0700259 glyph->mLsbDelta = skiaGlyph.fLsbDelta;
260 glyph->mRsbDelta = skiaGlyph.fRsbDelta;
Romain Guy694b5192010-07-21 21:33:20 -0700261
262 uint32_t startX = 0;
263 uint32_t startY = 0;
264
Romain Guy694b5192010-07-21 21:33:20 -0700265 // Get the bitmap for the glyph
266 paint->findImage(skiaGlyph);
Chet Haase7de0cb12011-12-05 16:35:38 -0800267 mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700268
269 if (!glyph->mIsValid) {
270 return;
271 }
272
273 uint32_t endX = startX + skiaGlyph.fWidth;
274 uint32_t endY = startY + skiaGlyph.fHeight;
275
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700276 glyph->mStartX = startX;
277 glyph->mStartY = startY;
Romain Guy694b5192010-07-21 21:33:20 -0700278 glyph->mBitmapWidth = skiaGlyph.fWidth;
279 glyph->mBitmapHeight = skiaGlyph.fHeight;
280
Chet Haase7de0cb12011-12-05 16:35:38 -0800281 uint32_t cacheWidth = glyph->mCachedTextureLine->mCacheTexture->mWidth;
282 uint32_t cacheHeight = glyph->mCachedTextureLine->mCacheTexture->mHeight;
Romain Guy694b5192010-07-21 21:33:20 -0700283
284 glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
285 glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
286 glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
287 glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
288
Romain Guy51769a62010-07-23 00:28:00 -0700289 mState->mUploadTexture = true;
Romain Guy694b5192010-07-21 21:33:20 -0700290}
291
Chet Haase7de0cb12011-12-05 16:35:38 -0800292CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) {
Romain Guy51769a62010-07-23 00:28:00 -0700293 CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
Romain Guy694b5192010-07-21 21:33:20 -0700294 mCachedGlyphs.add(glyph, newGlyph);
295
Romain Guy726aeba2011-06-01 14:52:00 -0700296 const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
Romain Guy694b5192010-07-21 21:33:20 -0700297 newGlyph->mGlyphIndex = skiaGlyph.fID;
298 newGlyph->mIsValid = false;
299
300 updateGlyphCache(paint, skiaGlyph, newGlyph);
301
302 return newGlyph;
303}
304
Romain Guy2577db12011-01-18 13:02:38 -0800305Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
Romain Guybd496bc2011-08-02 17:32:41 -0700306 int flags, uint32_t italicStyle, uint32_t scaleX,
307 SkPaint::Style style, uint32_t strokeWidth) {
Romain Guy694b5192010-07-21 21:33:20 -0700308 Vector<Font*> &activeFonts = state->mActiveFonts;
309
310 for (uint32_t i = 0; i < activeFonts.size(); i++) {
Romain Guy51769a62010-07-23 00:28:00 -0700311 Font* font = activeFonts[i];
Romain Guy2577db12011-01-18 13:02:38 -0800312 if (font->mFontId == fontId && font->mFontSize == fontSize &&
Chet Haase8668f8a2011-03-02 13:51:36 -0800313 font->mFlags == flags && font->mItalicStyle == italicStyle &&
Romain Guybd496bc2011-08-02 17:32:41 -0700314 font->mScaleX == scaleX && font->mStyle == style &&
315 (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
Romain Guy51769a62010-07-23 00:28:00 -0700316 return font;
Romain Guy694b5192010-07-21 21:33:20 -0700317 }
318 }
319
Romain Guybd496bc2011-08-02 17:32:41 -0700320 Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
321 scaleX, style, strokeWidth);
Romain Guy694b5192010-07-21 21:33:20 -0700322 activeFonts.push(newFont);
323 return newFont;
324}
325
326///////////////////////////////////////////////////////////////////////////////
327// FontRenderer
328///////////////////////////////////////////////////////////////////////////////
329
Romain Guy514fb182011-01-19 14:38:29 -0800330static bool sLogFontRendererCreate = true;
331
Romain Guy694b5192010-07-21 21:33:20 -0700332FontRenderer::FontRenderer() {
Romain Guyc9855a52011-01-21 21:14:15 -0800333 if (sLogFontRendererCreate) {
334 INIT_LOGD("Creating FontRenderer");
335 }
Romain Guy51769a62010-07-23 00:28:00 -0700336
Romain Guyb45c0c92010-08-26 20:35:23 -0700337 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700338 mInitialized = false;
339 mMaxNumberOfQuads = 1024;
340 mCurrentQuadIndex = 0;
341
Romain Guy9cccc2b92010-08-07 23:46:15 -0700342 mTextMeshPtr = NULL;
Chet Haase7de0cb12011-12-05 16:35:38 -0800343 mCurrentCacheTexture = NULL;
344 mLastCacheTexture = NULL;
345 mCacheTextureSmall = NULL;
346 mCacheTexture128 = NULL;
347 mCacheTexture256 = NULL;
348 mCacheTexture512 = NULL;
Romain Guy9cccc2b92010-08-07 23:46:15 -0700349
Romain Guy694b5192010-07-21 21:33:20 -0700350 mIndexBufferID = 0;
351
Chet Haase7de0cb12011-12-05 16:35:38 -0800352 mSmallCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
353 mSmallCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -0700354
355 char property[PROPERTY_VALUE_MAX];
356 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800357 if (sLogFontRendererCreate) {
358 INIT_LOGD(" Setting text cache width to %s pixels", property);
359 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800360 mSmallCacheWidth = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -0700361 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800362 if (sLogFontRendererCreate) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800363 INIT_LOGD(" Using default text cache width of %i pixels", mSmallCacheWidth);
Romain Guy514fb182011-01-19 14:38:29 -0800364 }
Romain Guy51769a62010-07-23 00:28:00 -0700365 }
366
367 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800368 if (sLogFontRendererCreate) {
369 INIT_LOGD(" Setting text cache width to %s pixels", property);
370 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800371 mSmallCacheHeight = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -0700372 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800373 if (sLogFontRendererCreate) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800374 INIT_LOGD(" Using default text cache height of %i pixels", mSmallCacheHeight);
Romain Guy514fb182011-01-19 14:38:29 -0800375 }
Romain Guy51769a62010-07-23 00:28:00 -0700376 }
Romain Guy514fb182011-01-19 14:38:29 -0800377
378 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -0700379}
380
381FontRenderer::~FontRenderer() {
382 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
383 delete mCacheLines[i];
384 }
385 mCacheLines.clear();
386
Romain Guy9cccc2b92010-08-07 23:46:15 -0700387 if (mInitialized) {
388 delete[] mTextMeshPtr;
Chet Haase7de0cb12011-12-05 16:35:38 -0800389 delete mCacheTextureSmall;
390 delete mCacheTexture128;
391 delete mCacheTexture256;
392 delete mCacheTexture512;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700393 }
Romain Guy694b5192010-07-21 21:33:20 -0700394
395 Vector<Font*> fontsToDereference = mActiveFonts;
396 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
397 delete fontsToDereference[i];
398 }
399}
400
401void FontRenderer::flushAllAndInvalidate() {
402 if (mCurrentQuadIndex != 0) {
403 issueDrawCommand();
404 mCurrentQuadIndex = 0;
405 }
406 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
407 mActiveFonts[i]->invalidateTextureCache();
408 }
409 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
410 mCacheLines[i]->mCurrentCol = 0;
411 }
412}
413
Chet Haase7de0cb12011-12-05 16:35:38 -0800414uint8_t* FontRenderer::allocateTextureMemory(int width, int height) {
415 uint8_t* textureMemory = new uint8_t[width * height];
416 memset(textureMemory, 0, width * height * sizeof(uint8_t));
417
418 return textureMemory;
419}
420
421void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
422 uint32_t* retOriginX, uint32_t* retOriginY) {
423 cachedGlyph->mIsValid = false;
Romain Guy694b5192010-07-21 21:33:20 -0700424 // If the glyph is too tall, don't cache it
Chet Haase7de0cb12011-12-05 16:35:38 -0800425 if (glyph.fHeight + TEXTURE_BORDER_SIZE > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
426 LOGE("Font size to large to fit in cache. width, height = %i, %i",
427 (int) glyph.fWidth, (int) glyph.fHeight);
428 return;
Romain Guy694b5192010-07-21 21:33:20 -0700429 }
430
431 // Now copy the bitmap into the cache texture
432 uint32_t startX = 0;
433 uint32_t startY = 0;
434
435 bool bitmapFit = false;
Chet Haase7de0cb12011-12-05 16:35:38 -0800436 CacheTextureLine *cacheLine;
Romain Guy694b5192010-07-21 21:33:20 -0700437 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
438 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
439 if (bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800440 cacheLine = mCacheLines[i];
Romain Guy694b5192010-07-21 21:33:20 -0700441 break;
442 }
443 }
444
445 // If the new glyph didn't fit, flush the state so far and invalidate everything
446 if (!bitmapFit) {
447 flushAllAndInvalidate();
448
449 // Try to fit it again
450 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
451 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
452 if (bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800453 cacheLine = mCacheLines[i];
Romain Guy694b5192010-07-21 21:33:20 -0700454 break;
455 }
456 }
457
458 // if we still don't fit, something is wrong and we shouldn't draw
459 if (!bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800460 return;
Romain Guy694b5192010-07-21 21:33:20 -0700461 }
462 }
463
Chet Haase7de0cb12011-12-05 16:35:38 -0800464 cachedGlyph->mCachedTextureLine = cacheLine;
465
Romain Guy694b5192010-07-21 21:33:20 -0700466 *retOriginX = startX;
467 *retOriginY = startY;
468
469 uint32_t endX = startX + glyph.fWidth;
470 uint32_t endY = startY + glyph.fHeight;
471
Chet Haase7de0cb12011-12-05 16:35:38 -0800472 uint32_t cacheWidth = cacheLine->mMaxWidth;
Romain Guy694b5192010-07-21 21:33:20 -0700473
Chet Haase7de0cb12011-12-05 16:35:38 -0800474 CacheTexture *cacheTexture = cacheLine->mCacheTexture;
475 if (cacheTexture->mTexture == NULL) {
476 // Large-glyph texture memory is allocated only as needed
477 cacheTexture->mTexture = allocateTextureMemory(cacheTexture->mWidth, cacheTexture->mHeight);
478 }
479 uint8_t* cacheBuffer = cacheTexture->mTexture;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700480 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700481 unsigned int stride = glyph.rowBytes();
482
483 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
484 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
485 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700486 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
Romain Guyb45c0c92010-08-26 20:35:23 -0700487 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
Romain Guy694b5192010-07-21 21:33:20 -0700488 }
489 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800490 cachedGlyph->mIsValid = true;
Romain Guy694b5192010-07-21 21:33:20 -0700491}
492
Chet Haase7de0cb12011-12-05 16:35:38 -0800493CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
494 uint8_t* textureMemory = allocate ? allocateTextureMemory(width, height) : NULL;
495 GLuint textureId;
496 glGenTextures(1, &textureId);
497 glBindTexture(GL_TEXTURE_2D, textureId);
Romain Guy694b5192010-07-21 21:33:20 -0700498 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
Chet Haase44984ea2011-05-19 13:50:47 -0700499 // Initialize texture dimensions
Chet Haase7de0cb12011-12-05 16:35:38 -0800500 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700501 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Romain Guy694b5192010-07-21 21:33:20 -0700502
Romain Guye8cb9c142010-10-04 14:14:11 -0700503 mLinearFiltering = false;
504 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
505 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
Romain Guy694b5192010-07-21 21:33:20 -0700506
507 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
508 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
509
Chet Haase7de0cb12011-12-05 16:35:38 -0800510 return new CacheTexture(textureMemory, textureId, width, height);
511}
512
513void FontRenderer::initTextTexture() {
514 mCacheLines.clear();
515
516 // Next, use other, separate caches for large glyphs.
517 uint16_t maxWidth = 0;
518 if (Caches::hasInstance()) {
519 maxWidth = Caches::getInstance().maxTextureSize;
Chet Haase44984ea2011-05-19 13:50:47 -0700520 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800521 if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) {
522 maxWidth = MAX_TEXT_CACHE_WIDTH;
523 }
524 if (mCacheTextureSmall != NULL) {
525 delete mCacheTextureSmall;
526 delete mCacheTexture128;
527 delete mCacheTexture256;
528 delete mCacheTexture512;
529 }
530 mCacheTextureSmall = createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true);
531 mCacheTexture128 = createCacheTexture(maxWidth, 256, false);
532 mCacheTexture256 = createCacheTexture(maxWidth, 256, false);
533 mCacheTexture512 = createCacheTexture(maxWidth, 512, false);
534 mCurrentCacheTexture = mCacheTextureSmall;
535
536 mUploadTexture = false;
537 // Split up our default cache texture into lines of certain widths
538 int nextLine = 0;
539 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 18, nextLine, 0, mCacheTextureSmall));
540 nextLine += mCacheLines.top()->mMaxHeight;
541 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
542 nextLine += mCacheLines.top()->mMaxHeight;
543 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
544 nextLine += mCacheLines.top()->mMaxHeight;
545 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
546 nextLine += mCacheLines.top()->mMaxHeight;
547 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
548 nextLine += mCacheLines.top()->mMaxHeight;
549 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 42, nextLine, 0, mCacheTextureSmall));
550 nextLine += mCacheLines.top()->mMaxHeight;
551 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, mSmallCacheHeight - nextLine,
552 nextLine, 0, mCacheTextureSmall));
553
554 // The first cache is split into 2 lines of height 128, the rest have just one cache line.
555 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 0, 0, mCacheTexture128));
556 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 128, 0, mCacheTexture128));
557 mCacheLines.push(new CacheTextureLine(maxWidth, 256, 0, 0, mCacheTexture256));
558 mCacheLines.push(new CacheTextureLine(maxWidth, 512, 0, 0, mCacheTexture512));
Romain Guy694b5192010-07-21 21:33:20 -0700559}
560
561// Avoid having to reallocate memory and render quad by quad
562void FontRenderer::initVertexArrayBuffers() {
Romain Guyd71dd362011-12-12 19:03:35 -0800563 uint32_t numIndices = mMaxNumberOfQuads * 6;
564 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700565 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700566
567 // Four verts, two triangles , six indices per quad
568 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
569 int i6 = i * 6;
570 int i4 = i * 4;
571
572 indexBufferData[i6 + 0] = i4 + 0;
573 indexBufferData[i6 + 1] = i4 + 1;
574 indexBufferData[i6 + 2] = i4 + 2;
575
576 indexBufferData[i6 + 3] = i4 + 0;
577 indexBufferData[i6 + 4] = i4 + 2;
578 indexBufferData[i6 + 5] = i4 + 3;
579 }
580
581 glGenBuffers(1, &mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700582 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
583 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
584 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
Romain Guy694b5192010-07-21 21:33:20 -0700585
586 free(indexBufferData);
587
Romain Guyd71dd362011-12-12 19:03:35 -0800588 uint32_t coordSize = 2;
Romain Guy694b5192010-07-21 21:33:20 -0700589 uint32_t uvSize = 2;
590 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700591 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
592 mTextMeshPtr = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700593}
594
595// We don't want to allocate anything unless we actually draw text
596void FontRenderer::checkInit() {
597 if (mInitialized) {
598 return;
599 }
600
601 initTextTexture();
602 initVertexArrayBuffers();
603
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700604 // We store a string with letters in a rough frequency of occurrence
605 mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
606 mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
607 mLatinPrecache += String16(",.?!()-+@;:`'");
608 mLatinPrecache += String16("0123456789");
609
Romain Guy694b5192010-07-21 21:33:20 -0700610 mInitialized = true;
611}
612
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700613void FontRenderer::checkTextureUpdate() {
Chet Haase7de0cb12011-12-05 16:35:38 -0800614 if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700615 return;
Romain Guy694b5192010-07-21 21:33:20 -0700616 }
617
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700618 // Iterate over all the cache lines and see which ones need to be updated
619 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
620 CacheTextureLine* cl = mCacheLines[i];
Chet Haase7de0cb12011-12-05 16:35:38 -0800621 if (cl->mDirty && cl->mCacheTexture->mTexture != NULL) {
622 CacheTexture* cacheTexture = cl->mCacheTexture;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700623 uint32_t xOffset = 0;
624 uint32_t yOffset = cl->mCurrentRow;
Chet Haase7de0cb12011-12-05 16:35:38 -0800625 uint32_t width = cl->mMaxWidth;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700626 uint32_t height = cl->mMaxHeight;
Chet Haase7de0cb12011-12-05 16:35:38 -0800627 void* textureData = cacheTexture->mTexture + (yOffset * width);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700628
Chet Haase7de0cb12011-12-05 16:35:38 -0800629 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700630 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700631 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700632
633 cl->mDirty = false;
634 }
635 }
636
Chet Haase7de0cb12011-12-05 16:35:38 -0800637 glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
638 mLastCacheTexture = mCurrentCacheTexture;
639
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700640 mUploadTexture = false;
641}
642
643void FontRenderer::issueDrawCommand() {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700644 checkTextureUpdate();
645
Romain Guy694b5192010-07-21 21:33:20 -0700646 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
647 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
Romain Guy5b3b3522010-10-27 18:57:51 -0700648
649 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700650}
651
Romain Guyd71dd362011-12-12 19:03:35 -0800652void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
653 float x2, float y2, float u2, float v2,
654 float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800655 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Romain Guyd71dd362011-12-12 19:03:35 -0800656
Romain Guyff98fa52011-11-28 09:35:09 -0800657 if (mClip &&
658 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
Romain Guy09147fb2010-07-22 13:08:20 -0700659 return;
660 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800661 if (texture != mCurrentCacheTexture) {
662 if (mCurrentQuadIndex != 0) {
663 // First, draw everything stored already which uses the previous texture
664 issueDrawCommand();
665 mCurrentQuadIndex = 0;
666 }
667 // Now use the new texture id
668 mCurrentCacheTexture = texture;
669 }
Romain Guy09147fb2010-07-22 13:08:20 -0700670
Romain Guy694b5192010-07-21 21:33:20 -0700671 const uint32_t vertsPerQuad = 4;
Romain Guyd71dd362011-12-12 19:03:35 -0800672 const uint32_t floatsPerVert = 4;
Romain Guy51769a62010-07-23 00:28:00 -0700673 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700674
Romain Guy694b5192010-07-21 21:33:20 -0700675 (*currentPos++) = x1;
676 (*currentPos++) = y1;
Romain Guy694b5192010-07-21 21:33:20 -0700677 (*currentPos++) = u1;
678 (*currentPos++) = v1;
679
680 (*currentPos++) = x2;
681 (*currentPos++) = y2;
Romain Guy694b5192010-07-21 21:33:20 -0700682 (*currentPos++) = u2;
683 (*currentPos++) = v2;
684
685 (*currentPos++) = x3;
686 (*currentPos++) = y3;
Romain Guy694b5192010-07-21 21:33:20 -0700687 (*currentPos++) = u3;
688 (*currentPos++) = v3;
689
690 (*currentPos++) = x4;
691 (*currentPos++) = y4;
Romain Guy694b5192010-07-21 21:33:20 -0700692 (*currentPos++) = u4;
693 (*currentPos++) = v4;
694
695 mCurrentQuadIndex++;
696
Romain Guy5b3b3522010-10-27 18:57:51 -0700697 if (mBounds) {
698 mBounds->left = fmin(mBounds->left, x1);
699 mBounds->top = fmin(mBounds->top, y3);
700 mBounds->right = fmax(mBounds->right, x3);
701 mBounds->bottom = fmax(mBounds->bottom, y1);
702 }
703
Romain Guy694b5192010-07-21 21:33:20 -0700704 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
705 issueDrawCommand();
706 mCurrentQuadIndex = 0;
707 }
708}
709
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700710uint32_t FontRenderer::getRemainingCacheCapacity() {
711 uint32_t remainingCapacity = 0;
712 float totalPixels = 0;
713 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
714 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
715 totalPixels += mCacheLines[i]->mMaxWidth;
716 }
717 remainingCapacity = (remainingCapacity * 100) / totalPixels;
718 return remainingCapacity;
719}
720
721void FontRenderer::precacheLatin(SkPaint* paint) {
722 // Remaining capacity is measured in %
723 uint32_t remainingCapacity = getRemainingCacheCapacity();
724 uint32_t precacheIdx = 0;
Romain Guy054dc182010-10-15 17:55:25 -0700725 while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
Romain Guy726aeba2011-06-01 14:52:00 -0700726 mCurrentFont->getCachedGlyph(paint, (int32_t) mLatinPrecache[precacheIdx]);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700727 remainingCapacity = getRemainingCacheCapacity();
728 precacheIdx ++;
729 }
730}
731
732void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
733 uint32_t currentNumFonts = mActiveFonts.size();
Romain Guy325a0f92011-01-05 15:26:55 -0800734 int flags = 0;
735 if (paint->isFakeBoldText()) {
736 flags |= Font::kFakeBold;
737 }
Romain Guy2577db12011-01-18 13:02:38 -0800738
739 const float skewX = paint->getTextSkewX();
740 uint32_t italicStyle = *(uint32_t*) &skewX;
Chet Haase8668f8a2011-03-02 13:51:36 -0800741 const float scaleXFloat = paint->getTextScaleX();
742 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
Romain Guybd496bc2011-08-02 17:32:41 -0700743 SkPaint::Style style = paint->getStyle();
744 const float strokeWidthFloat = paint->getStrokeWidth();
745 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
746 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
747 scaleX, style, strokeWidth);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700748
749 const float maxPrecacheFontSize = 40.0f;
750 bool isNewFont = currentNumFonts != mActiveFonts.size();
751
Romain Guy2bffd262010-09-12 17:40:02 -0700752 if (isNewFont && fontSize <= maxPrecacheFontSize) {
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700753 precacheLatin(paint);
754 }
Romain Guy694b5192010-07-21 21:33:20 -0700755}
Romain Guy7975fb62010-10-01 16:36:14 -0700756
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700757FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Romain Guy1e45aae2010-08-13 19:39:53 -0700758 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
759 checkInit();
760
761 if (!mCurrentFont) {
762 DropShadow image;
763 image.width = 0;
764 image.height = 0;
765 image.image = NULL;
766 image.penX = 0;
767 image.penY = 0;
768 return image;
769 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700770
Romain Guyff98fa52011-11-28 09:35:09 -0800771 mClip = NULL;
772 mBounds = NULL;
773
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700774 Rect bounds;
Romain Guy726aeba2011-06-01 14:52:00 -0700775 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds);
Romain Guyff98fa52011-11-28 09:35:09 -0800776
Romain Guy1e45aae2010-08-13 19:39:53 -0700777 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
778 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700779 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guyff98fa52011-11-28 09:35:09 -0800780
Romain Guy1e45aae2010-08-13 19:39:53 -0700781 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700782 dataBuffer[i] = 0;
783 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700784
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700785 int penX = radius - bounds.left;
786 int penY = radius - bounds.bottom;
787
Romain Guy726aeba2011-06-01 14:52:00 -0700788 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
Romain Guy1e45aae2010-08-13 19:39:53 -0700789 dataBuffer, paddedWidth, paddedHeight);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700790 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
791
792 DropShadow image;
793 image.width = paddedWidth;
794 image.height = paddedHeight;
795 image.image = dataBuffer;
796 image.penX = penX;
797 image.penY = penY;
798 return image;
799}
Romain Guy694b5192010-07-21 21:33:20 -0700800
Romain Guy5b3b3522010-10-27 18:57:51 -0700801bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
802 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700803 checkInit();
804
Romain Guy09147fb2010-07-22 13:08:20 -0700805 if (!mCurrentFont) {
806 LOGE("No font set");
Romain Guy5b3b3522010-10-27 18:57:51 -0700807 return false;
Romain Guy694b5192010-07-21 21:33:20 -0700808 }
809
Romain Guy5b3b3522010-10-27 18:57:51 -0700810 mDrawn = false;
811 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -0700812 mClip = clip;
Romain Guyff98fa52011-11-28 09:35:09 -0800813
Romain Guy726aeba2011-06-01 14:52:00 -0700814 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
Romain Guyff98fa52011-11-28 09:35:09 -0800815
Romain Guy5b3b3522010-10-27 18:57:51 -0700816 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -0800817 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700818
819 if (mCurrentQuadIndex != 0) {
820 issueDrawCommand();
821 mCurrentQuadIndex = 0;
822 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700823
824 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700825}
826
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700827void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
828 // Compute gaussian weights for the blur
829 // e is the euler's number
830 float e = 2.718281828459045f;
831 float pi = 3.1415926535897932f;
832 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
833 // x is of the form [-radius .. 0 .. radius]
834 // and sigma varies with radius.
835 // Based on some experimental radius values and sigma's
836 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700837 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700838 // The larger the radius gets, the more our gaussian blur
839 // will resemble a box blur since with large sigma
840 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -0800841 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700842
843 // Now compute the coefficints
844 // We will store some redundant values to save some math during
845 // the blur calculations
846 // precompute some values
847 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
848 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
849
850 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -0800851 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700852 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700853 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
854 normalizeFactor += weights[r + radius];
855 }
856
857 //Now we need to normalize the weights because all our coefficients need to add up to one
858 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -0800859 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700860 weights[r + radius] *= normalizeFactor;
861 }
862}
863
864void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700865 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700866 float blurredPixel = 0.0f;
867 float currentPixel = 0.0f;
868
Romain Guy325a0f92011-01-05 15:26:55 -0800869 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700870
871 const uint8_t* input = source + y * width;
872 uint8_t* output = dest + y * width;
873
Romain Guy325a0f92011-01-05 15:26:55 -0800874 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700875 blurredPixel = 0.0f;
876 const float* gPtr = weights;
877 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800878 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700879 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -0800880 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700881 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700882 blurredPixel += currentPixel * gPtr[0];
883 gPtr++;
884 i++;
885 }
886 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800887 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700888 // Stepping left and right away from the pixel
889 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -0800890 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700891 validW = 0;
892 }
Romain Guy325a0f92011-01-05 15:26:55 -0800893 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700894 validW = width - 1;
895 }
896
Romain Guy325a0f92011-01-05 15:26:55 -0800897 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700898 blurredPixel += currentPixel * gPtr[0];
899 gPtr++;
900 }
901 }
902 *output = (uint8_t)blurredPixel;
903 output ++;
904 }
905 }
906}
907
908void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700909 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700910 float blurredPixel = 0.0f;
911 float currentPixel = 0.0f;
912
Romain Guy325a0f92011-01-05 15:26:55 -0800913 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700914
915 uint8_t* output = dest + y * width;
916
Romain Guy325a0f92011-01-05 15:26:55 -0800917 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700918 blurredPixel = 0.0f;
919 const float* gPtr = weights;
920 const uint8_t* input = source + x;
921 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800922 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700923 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -0800924 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700925 currentPixel = (float)(*i);
926 blurredPixel += currentPixel * gPtr[0];
927 gPtr++;
928 i += width;
929 }
930 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800931 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700932 int validH = y + r;
933 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -0800934 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700935 validH = 0;
936 }
Romain Guy325a0f92011-01-05 15:26:55 -0800937 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700938 validH = height - 1;
939 }
940
941 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -0800942 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700943 blurredPixel += currentPixel * gPtr[0];
944 gPtr++;
945 }
946 }
Romain Guy325a0f92011-01-05 15:26:55 -0800947 *output = (uint8_t) blurredPixel;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700948 output ++;
949 }
950 }
951}
952
953
954void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
955 float *gaussian = new float[2 * radius + 1];
956 computeGaussianWeights(gaussian, radius);
Romain Guyd71dd362011-12-12 19:03:35 -0800957
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700958 uint8_t* scratch = new uint8_t[width * height];
Romain Guyd71dd362011-12-12 19:03:35 -0800959
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700960 horizontalBlur(gaussian, radius, image, scratch, width, height);
961 verticalBlur(gaussian, radius, scratch, image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -0800962
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700963 delete[] gaussian;
964 delete[] scratch;
965}
966
Romain Guy694b5192010-07-21 21:33:20 -0700967}; // namespace uirenderer
968}; // namespace android