blob: 27556d9846a61b5bbd41204c05fc6e46d7f90324 [file] [log] [blame]
Romain Guy694b5192010-07-21 21:33:20 -07001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "OpenGLRenderer"
18
Romain Guy694b5192010-07-21 21:33:20 -070019#include <SkUtils.h>
20
Romain Guy51769a62010-07-23 00:28:00 -070021#include <cutils/properties.h>
Romain Guye2d345e2010-09-24 18:39:22 -070022
Romain Guy51769a62010-07-23 00:28:00 -070023#include <utils/Log.h>
24
Romain Guy15bc6432011-12-13 13:11:32 -080025#include "Caches.h"
Romain Guyc9855a52011-01-21 21:14:15 -080026#include "Debug.h"
Romain Guy51769a62010-07-23 00:28:00 -070027#include "FontRenderer.h"
Chet Haase7de0cb12011-12-05 16:35:38 -080028#include "Caches.h"
Romain Guy51769a62010-07-23 00:28:00 -070029
Romain Guy694b5192010-07-21 21:33:20 -070030namespace android {
31namespace uirenderer {
32
33///////////////////////////////////////////////////////////////////////////////
Romain Guy51769a62010-07-23 00:28:00 -070034// Defines
35///////////////////////////////////////////////////////////////////////////////
36
37#define DEFAULT_TEXT_CACHE_WIDTH 1024
38#define DEFAULT_TEXT_CACHE_HEIGHT 256
Chet Haase44984ea2011-05-19 13:50:47 -070039#define MAX_TEXT_CACHE_WIDTH 2048
Chet Haase7de0cb12011-12-05 16:35:38 -080040#define TEXTURE_BORDER_SIZE 2
41
42///////////////////////////////////////////////////////////////////////////////
43// CacheTextureLine
44///////////////////////////////////////////////////////////////////////////////
45
46bool CacheTextureLine::fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
47 if (glyph.fHeight + TEXTURE_BORDER_SIZE > mMaxHeight) {
48 return false;
49 }
50
51 if (mCurrentCol + glyph.fWidth + TEXTURE_BORDER_SIZE < mMaxWidth) {
52 *retOriginX = mCurrentCol + 1;
53 *retOriginY = mCurrentRow + 1;
54 mCurrentCol += glyph.fWidth + TEXTURE_BORDER_SIZE;
55 mDirty = true;
56 return true;
57 }
58
59 return false;
60}
Chet Haase44984ea2011-05-19 13:50:47 -070061
Romain Guy51769a62010-07-23 00:28:00 -070062///////////////////////////////////////////////////////////////////////////////
Romain Guy694b5192010-07-21 21:33:20 -070063// Font
64///////////////////////////////////////////////////////////////////////////////
65
Romain Guy2577db12011-01-18 13:02:38 -080066Font::Font(FontRenderer* state, uint32_t fontId, float fontSize,
Romain Guybd496bc2011-08-02 17:32:41 -070067 int flags, uint32_t italicStyle, uint32_t scaleX,
68 SkPaint::Style style, uint32_t strokeWidth) :
Romain Guy2577db12011-01-18 13:02:38 -080069 mState(state), mFontId(fontId), mFontSize(fontSize),
Romain Guybd496bc2011-08-02 17:32:41 -070070 mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX),
71 mStyle(style), mStrokeWidth(mStrokeWidth) {
Romain Guy694b5192010-07-21 21:33:20 -070072}
73
74
75Font::~Font() {
76 for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
77 if (mState->mActiveFonts[ct] == this) {
78 mState->mActiveFonts.removeAt(ct);
79 break;
80 }
81 }
82
83 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
Romain Guy726aeba2011-06-01 14:52:00 -070084 delete mCachedGlyphs.valueAt(i);
Romain Guy694b5192010-07-21 21:33:20 -070085 }
86}
87
88void Font::invalidateTextureCache() {
89 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
90 mCachedGlyphs.valueAt(i)->mIsValid = false;
91 }
92}
93
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070094void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds) {
95 int nPenX = x + glyph->mBitmapLeft;
96 int nPenY = y + glyph->mBitmapTop;
97
98 int width = (int) glyph->mBitmapWidth;
99 int height = (int) glyph->mBitmapHeight;
100
Romain Guy61c8c9c2010-08-09 20:48:09 -0700101 if (bounds->bottom > nPenY) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700102 bounds->bottom = nPenY;
103 }
Romain Guy61c8c9c2010-08-09 20:48:09 -0700104 if (bounds->left > nPenX) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700105 bounds->left = nPenX;
106 }
Romain Guy61c8c9c2010-08-09 20:48:09 -0700107 if (bounds->right < nPenX + width) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700108 bounds->right = nPenX + width;
109 }
Romain Guy61c8c9c2010-08-09 20:48:09 -0700110 if (bounds->top < nPenY + height) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700111 bounds->top = nPenY + height;
112 }
113}
114
Romain Guy694b5192010-07-21 21:33:20 -0700115void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
Romain Guy694b5192010-07-21 21:33:20 -0700116 int nPenX = x + glyph->mBitmapLeft;
117 int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
118
Romain Guy51769a62010-07-23 00:28:00 -0700119 float u1 = glyph->mBitmapMinU;
120 float u2 = glyph->mBitmapMaxU;
121 float v1 = glyph->mBitmapMinV;
122 float v2 = glyph->mBitmapMaxV;
123
124 int width = (int) glyph->mBitmapWidth;
125 int height = (int) glyph->mBitmapHeight;
126
Romain Guyd71dd362011-12-12 19:03:35 -0800127 mState->appendMeshQuad(nPenX, nPenY, u1, v2,
128 nPenX + width, nPenY, u2, v2,
129 nPenX + width, nPenY - height, u2, v1,
Chet Haase7de0cb12011-12-05 16:35:38 -0800130 nPenX, nPenY - height, u1, v1, glyph->mCachedTextureLine->mCacheTexture);
Romain Guy694b5192010-07-21 21:33:20 -0700131}
132
Romain Guyb45c0c92010-08-26 20:35:23 -0700133void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
134 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700135 int nPenX = x + glyph->mBitmapLeft;
136 int nPenY = y + glyph->mBitmapTop;
137
138 uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
139 uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
140
Chet Haase7de0cb12011-12-05 16:35:38 -0800141 CacheTexture *cacheTexture = glyph->mCachedTextureLine->mCacheTexture;
142 uint32_t cacheWidth = cacheTexture->mWidth;
143 const uint8_t* cacheBuffer = cacheTexture->mTexture;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700144
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700145 uint32_t cacheX = 0, cacheY = 0;
146 int32_t bX = 0, bY = 0;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700147 for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
148 for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
Romain Guyb45c0c92010-08-26 20:35:23 -0700149 if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700150 LOGE("Skipping invalid index");
151 continue;
152 }
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700153 uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
154 bitmap[bY * bitmapW + bX] = tempCol;
155 }
156 }
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700157}
158
Chet Haase7de0cb12011-12-05 16:35:38 -0800159CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700160 CachedGlyphInfo* cachedGlyph = NULL;
Romain Guy726aeba2011-06-01 14:52:00 -0700161 ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
Romain Guy1e45aae2010-08-13 19:39:53 -0700162 if (index >= 0) {
163 cachedGlyph = mCachedGlyphs.valueAt(index);
164 } else {
Romain Guy726aeba2011-06-01 14:52:00 -0700165 cachedGlyph = cacheGlyph(paint, textUnit);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700166 }
167
168 // Is the glyph still in texture cache?
169 if (!cachedGlyph->mIsValid) {
Romain Guy726aeba2011-06-01 14:52:00 -0700170 const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700171 updateGlyphCache(paint, skiaGlyph, cachedGlyph);
172 }
173
174 return cachedGlyph;
175}
176
Romain Guy726aeba2011-06-01 14:52:00 -0700177void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700178 int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
179 if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
Romain Guy726aeba2011-06-01 14:52:00 -0700180 render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700181 bitmapW, bitmapH, NULL);
182 } else {
Romain Guy726aeba2011-06-01 14:52:00 -0700183 render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
184 0, 0, NULL);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700185 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700186}
187
Romain Guy726aeba2011-06-01 14:52:00 -0700188void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700189 int numGlyphs, Rect *bounds) {
190 if (bounds == NULL) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700191 LOGE("No return rectangle provided to measure text");
192 return;
193 }
194 bounds->set(1e6, -1e6, -1e6, 1e6);
Romain Guy726aeba2011-06-01 14:52:00 -0700195 render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700196}
197
Romain Guy58ef7fb2010-09-13 12:52:37 -0700198#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16)
Romain Guy2bffd262010-09-12 17:40:02 -0700199
Romain Guy726aeba2011-06-01 14:52:00 -0700200void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700201 int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
202 uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700203 if (numGlyphs == 0 || text == NULL || len == 0) {
204 return;
205 }
206
Romain Guy5a6d3a42011-10-07 15:03:24 -0700207 float penX = x;
Romain Guy2bffd262010-09-12 17:40:02 -0700208 int penY = y;
Romain Guy694b5192010-07-21 21:33:20 -0700209 int glyphsLeft = 1;
210 if (numGlyphs > 0) {
211 glyphsLeft = numGlyphs;
212 }
213
Romain Guy2bffd262010-09-12 17:40:02 -0700214 SkFixed prevRsbDelta = 0;
Romain Guy5a6d3a42011-10-07 15:03:24 -0700215 penX += 0.5f;
Romain Guy2bffd262010-09-12 17:40:02 -0700216
Romain Guy694b5192010-07-21 21:33:20 -0700217 text += start;
218
219 while (glyphsLeft > 0) {
Romain Guy726aeba2011-06-01 14:52:00 -0700220 glyph_t glyph = GET_GLYPH(text);
Romain Guy694b5192010-07-21 21:33:20 -0700221
Romain Guy61c8c9c2010-08-09 20:48:09 -0700222 // Reached the end of the string
Romain Guy726aeba2011-06-01 14:52:00 -0700223 if (IS_END_OF_STRING(glyph)) {
Romain Guy694b5192010-07-21 21:33:20 -0700224 break;
225 }
226
Romain Guy726aeba2011-06-01 14:52:00 -0700227 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
Romain Guy5a6d3a42011-10-07 15:03:24 -0700228 penX += SkFixedToFloat(SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta));
Romain Guy2bffd262010-09-12 17:40:02 -0700229 prevRsbDelta = cachedGlyph->mRsbDelta;
Romain Guy694b5192010-07-21 21:33:20 -0700230
231 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
232 if (cachedGlyph->mIsValid) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700233 switch(mode) {
234 case FRAMEBUFFER:
Romain Guy5a6d3a42011-10-07 15:03:24 -0700235 drawCachedGlyph(cachedGlyph, (int) floorf(penX), penY);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700236 break;
237 case BITMAP:
Romain Guy5a6d3a42011-10-07 15:03:24 -0700238 drawCachedGlyph(cachedGlyph, (int) floorf(penX), penY, bitmap, bitmapW, bitmapH);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700239 break;
240 case MEASURE:
Romain Guy5a6d3a42011-10-07 15:03:24 -0700241 measureCachedGlyph(cachedGlyph, (int) floorf(penX), penY, bounds);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700242 break;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700243 }
Romain Guy694b5192010-07-21 21:33:20 -0700244 }
245
Romain Guy5a6d3a42011-10-07 15:03:24 -0700246 penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
Romain Guy694b5192010-07-21 21:33:20 -0700247
248 // If we were given a specific number of glyphs, decrement
249 if (numGlyphs > 0) {
250 glyphsLeft--;
251 }
252 }
253}
254
Romain Guy51769a62010-07-23 00:28:00 -0700255void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
Romain Guy694b5192010-07-21 21:33:20 -0700256 glyph->mAdvanceX = skiaGlyph.fAdvanceX;
257 glyph->mAdvanceY = skiaGlyph.fAdvanceY;
258 glyph->mBitmapLeft = skiaGlyph.fLeft;
259 glyph->mBitmapTop = skiaGlyph.fTop;
Romain Guy2bffd262010-09-12 17:40:02 -0700260 glyph->mLsbDelta = skiaGlyph.fLsbDelta;
261 glyph->mRsbDelta = skiaGlyph.fRsbDelta;
Romain Guy694b5192010-07-21 21:33:20 -0700262
263 uint32_t startX = 0;
264 uint32_t startY = 0;
265
Romain Guy694b5192010-07-21 21:33:20 -0700266 // Get the bitmap for the glyph
267 paint->findImage(skiaGlyph);
Chet Haase7de0cb12011-12-05 16:35:38 -0800268 mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700269
270 if (!glyph->mIsValid) {
271 return;
272 }
273
274 uint32_t endX = startX + skiaGlyph.fWidth;
275 uint32_t endY = startY + skiaGlyph.fHeight;
276
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700277 glyph->mStartX = startX;
278 glyph->mStartY = startY;
Romain Guy694b5192010-07-21 21:33:20 -0700279 glyph->mBitmapWidth = skiaGlyph.fWidth;
280 glyph->mBitmapHeight = skiaGlyph.fHeight;
281
Chet Haase7de0cb12011-12-05 16:35:38 -0800282 uint32_t cacheWidth = glyph->mCachedTextureLine->mCacheTexture->mWidth;
283 uint32_t cacheHeight = glyph->mCachedTextureLine->mCacheTexture->mHeight;
Romain Guy694b5192010-07-21 21:33:20 -0700284
285 glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
286 glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
287 glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
288 glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
289
Romain Guy51769a62010-07-23 00:28:00 -0700290 mState->mUploadTexture = true;
Romain Guy694b5192010-07-21 21:33:20 -0700291}
292
Chet Haase7de0cb12011-12-05 16:35:38 -0800293CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) {
Romain Guy51769a62010-07-23 00:28:00 -0700294 CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
Romain Guy694b5192010-07-21 21:33:20 -0700295 mCachedGlyphs.add(glyph, newGlyph);
296
Romain Guy726aeba2011-06-01 14:52:00 -0700297 const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
Romain Guy694b5192010-07-21 21:33:20 -0700298 newGlyph->mGlyphIndex = skiaGlyph.fID;
299 newGlyph->mIsValid = false;
300
301 updateGlyphCache(paint, skiaGlyph, newGlyph);
302
303 return newGlyph;
304}
305
Romain Guy2577db12011-01-18 13:02:38 -0800306Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
Romain Guybd496bc2011-08-02 17:32:41 -0700307 int flags, uint32_t italicStyle, uint32_t scaleX,
308 SkPaint::Style style, uint32_t strokeWidth) {
Romain Guy694b5192010-07-21 21:33:20 -0700309 Vector<Font*> &activeFonts = state->mActiveFonts;
310
311 for (uint32_t i = 0; i < activeFonts.size(); i++) {
Romain Guy51769a62010-07-23 00:28:00 -0700312 Font* font = activeFonts[i];
Romain Guy2577db12011-01-18 13:02:38 -0800313 if (font->mFontId == fontId && font->mFontSize == fontSize &&
Chet Haase8668f8a2011-03-02 13:51:36 -0800314 font->mFlags == flags && font->mItalicStyle == italicStyle &&
Romain Guybd496bc2011-08-02 17:32:41 -0700315 font->mScaleX == scaleX && font->mStyle == style &&
316 (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
Romain Guy51769a62010-07-23 00:28:00 -0700317 return font;
Romain Guy694b5192010-07-21 21:33:20 -0700318 }
319 }
320
Romain Guybd496bc2011-08-02 17:32:41 -0700321 Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
322 scaleX, style, strokeWidth);
Romain Guy694b5192010-07-21 21:33:20 -0700323 activeFonts.push(newFont);
324 return newFont;
325}
326
327///////////////////////////////////////////////////////////////////////////////
328// FontRenderer
329///////////////////////////////////////////////////////////////////////////////
330
Romain Guy514fb182011-01-19 14:38:29 -0800331static bool sLogFontRendererCreate = true;
332
Romain Guy694b5192010-07-21 21:33:20 -0700333FontRenderer::FontRenderer() {
Romain Guyc9855a52011-01-21 21:14:15 -0800334 if (sLogFontRendererCreate) {
335 INIT_LOGD("Creating FontRenderer");
336 }
Romain Guy51769a62010-07-23 00:28:00 -0700337
Romain Guyb45c0c92010-08-26 20:35:23 -0700338 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700339 mInitialized = false;
340 mMaxNumberOfQuads = 1024;
341 mCurrentQuadIndex = 0;
342
Romain Guy9cccc2b92010-08-07 23:46:15 -0700343 mTextMeshPtr = NULL;
Chet Haase7de0cb12011-12-05 16:35:38 -0800344 mCurrentCacheTexture = NULL;
345 mLastCacheTexture = NULL;
346 mCacheTextureSmall = NULL;
347 mCacheTexture128 = NULL;
348 mCacheTexture256 = NULL;
349 mCacheTexture512 = NULL;
Romain Guy9cccc2b92010-08-07 23:46:15 -0700350
Romain Guy694b5192010-07-21 21:33:20 -0700351 mIndexBufferID = 0;
352
Chet Haase7de0cb12011-12-05 16:35:38 -0800353 mSmallCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
354 mSmallCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -0700355
356 char property[PROPERTY_VALUE_MAX];
357 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800358 if (sLogFontRendererCreate) {
359 INIT_LOGD(" Setting text cache width to %s pixels", property);
360 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800361 mSmallCacheWidth = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -0700362 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800363 if (sLogFontRendererCreate) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800364 INIT_LOGD(" Using default text cache width of %i pixels", mSmallCacheWidth);
Romain Guy514fb182011-01-19 14:38:29 -0800365 }
Romain Guy51769a62010-07-23 00:28:00 -0700366 }
367
368 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800369 if (sLogFontRendererCreate) {
370 INIT_LOGD(" Setting text cache width to %s pixels", property);
371 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800372 mSmallCacheHeight = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -0700373 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800374 if (sLogFontRendererCreate) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800375 INIT_LOGD(" Using default text cache height of %i pixels", mSmallCacheHeight);
Romain Guy514fb182011-01-19 14:38:29 -0800376 }
Romain Guy51769a62010-07-23 00:28:00 -0700377 }
Romain Guy514fb182011-01-19 14:38:29 -0800378
379 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -0700380}
381
382FontRenderer::~FontRenderer() {
383 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
384 delete mCacheLines[i];
385 }
386 mCacheLines.clear();
387
Romain Guy9cccc2b92010-08-07 23:46:15 -0700388 if (mInitialized) {
389 delete[] mTextMeshPtr;
Chet Haase7de0cb12011-12-05 16:35:38 -0800390 delete mCacheTextureSmall;
391 delete mCacheTexture128;
392 delete mCacheTexture256;
393 delete mCacheTexture512;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700394 }
Romain Guy694b5192010-07-21 21:33:20 -0700395
396 Vector<Font*> fontsToDereference = mActiveFonts;
397 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
398 delete fontsToDereference[i];
399 }
400}
401
402void FontRenderer::flushAllAndInvalidate() {
403 if (mCurrentQuadIndex != 0) {
404 issueDrawCommand();
405 mCurrentQuadIndex = 0;
406 }
407 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
408 mActiveFonts[i]->invalidateTextureCache();
409 }
410 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
411 mCacheLines[i]->mCurrentCol = 0;
412 }
413}
414
Chet Haase7de0cb12011-12-05 16:35:38 -0800415uint8_t* FontRenderer::allocateTextureMemory(int width, int height) {
416 uint8_t* textureMemory = new uint8_t[width * height];
417 memset(textureMemory, 0, width * height * sizeof(uint8_t));
418
419 return textureMemory;
420}
421
422void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
423 uint32_t* retOriginX, uint32_t* retOriginY) {
424 cachedGlyph->mIsValid = false;
Romain Guy694b5192010-07-21 21:33:20 -0700425 // If the glyph is too tall, don't cache it
Chet Haase7de0cb12011-12-05 16:35:38 -0800426 if (glyph.fHeight + TEXTURE_BORDER_SIZE > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
427 LOGE("Font size to large to fit in cache. width, height = %i, %i",
428 (int) glyph.fWidth, (int) glyph.fHeight);
429 return;
Romain Guy694b5192010-07-21 21:33:20 -0700430 }
431
432 // Now copy the bitmap into the cache texture
433 uint32_t startX = 0;
434 uint32_t startY = 0;
435
436 bool bitmapFit = false;
Chet Haase7de0cb12011-12-05 16:35:38 -0800437 CacheTextureLine *cacheLine;
Romain Guy694b5192010-07-21 21:33:20 -0700438 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
439 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
440 if (bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800441 cacheLine = mCacheLines[i];
Romain Guy694b5192010-07-21 21:33:20 -0700442 break;
443 }
444 }
445
446 // If the new glyph didn't fit, flush the state so far and invalidate everything
447 if (!bitmapFit) {
448 flushAllAndInvalidate();
449
450 // Try to fit it again
451 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
452 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
453 if (bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800454 cacheLine = mCacheLines[i];
Romain Guy694b5192010-07-21 21:33:20 -0700455 break;
456 }
457 }
458
459 // if we still don't fit, something is wrong and we shouldn't draw
460 if (!bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800461 return;
Romain Guy694b5192010-07-21 21:33:20 -0700462 }
463 }
464
Chet Haase7de0cb12011-12-05 16:35:38 -0800465 cachedGlyph->mCachedTextureLine = cacheLine;
466
Romain Guy694b5192010-07-21 21:33:20 -0700467 *retOriginX = startX;
468 *retOriginY = startY;
469
470 uint32_t endX = startX + glyph.fWidth;
471 uint32_t endY = startY + glyph.fHeight;
472
Chet Haase7de0cb12011-12-05 16:35:38 -0800473 uint32_t cacheWidth = cacheLine->mMaxWidth;
Romain Guy694b5192010-07-21 21:33:20 -0700474
Chet Haase7de0cb12011-12-05 16:35:38 -0800475 CacheTexture *cacheTexture = cacheLine->mCacheTexture;
476 if (cacheTexture->mTexture == NULL) {
477 // Large-glyph texture memory is allocated only as needed
478 cacheTexture->mTexture = allocateTextureMemory(cacheTexture->mWidth, cacheTexture->mHeight);
479 }
480 uint8_t* cacheBuffer = cacheTexture->mTexture;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700481 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700482 unsigned int stride = glyph.rowBytes();
483
484 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
485 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
486 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700487 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
Romain Guyb45c0c92010-08-26 20:35:23 -0700488 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
Romain Guy694b5192010-07-21 21:33:20 -0700489 }
490 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800491 cachedGlyph->mIsValid = true;
Romain Guy694b5192010-07-21 21:33:20 -0700492}
493
Chet Haase7de0cb12011-12-05 16:35:38 -0800494CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
495 uint8_t* textureMemory = allocate ? allocateTextureMemory(width, height) : NULL;
496 GLuint textureId;
497 glGenTextures(1, &textureId);
498 glBindTexture(GL_TEXTURE_2D, textureId);
Romain Guy694b5192010-07-21 21:33:20 -0700499 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
Chet Haase44984ea2011-05-19 13:50:47 -0700500 // Initialize texture dimensions
Chet Haase7de0cb12011-12-05 16:35:38 -0800501 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700502 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Romain Guy694b5192010-07-21 21:33:20 -0700503
Romain Guye8cb9c142010-10-04 14:14:11 -0700504 mLinearFiltering = false;
505 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
506 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
Romain Guy694b5192010-07-21 21:33:20 -0700507
508 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
509 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
510
Chet Haase7de0cb12011-12-05 16:35:38 -0800511 return new CacheTexture(textureMemory, textureId, width, height);
512}
513
514void FontRenderer::initTextTexture() {
515 mCacheLines.clear();
516
517 // Next, use other, separate caches for large glyphs.
518 uint16_t maxWidth = 0;
519 if (Caches::hasInstance()) {
520 maxWidth = Caches::getInstance().maxTextureSize;
Chet Haase44984ea2011-05-19 13:50:47 -0700521 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800522 if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) {
523 maxWidth = MAX_TEXT_CACHE_WIDTH;
524 }
525 if (mCacheTextureSmall != NULL) {
526 delete mCacheTextureSmall;
527 delete mCacheTexture128;
528 delete mCacheTexture256;
529 delete mCacheTexture512;
530 }
531 mCacheTextureSmall = createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true);
532 mCacheTexture128 = createCacheTexture(maxWidth, 256, false);
533 mCacheTexture256 = createCacheTexture(maxWidth, 256, false);
534 mCacheTexture512 = createCacheTexture(maxWidth, 512, false);
535 mCurrentCacheTexture = mCacheTextureSmall;
536
537 mUploadTexture = false;
538 // Split up our default cache texture into lines of certain widths
539 int nextLine = 0;
540 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 18, nextLine, 0, mCacheTextureSmall));
541 nextLine += mCacheLines.top()->mMaxHeight;
542 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
543 nextLine += mCacheLines.top()->mMaxHeight;
544 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
545 nextLine += mCacheLines.top()->mMaxHeight;
546 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
547 nextLine += mCacheLines.top()->mMaxHeight;
548 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
549 nextLine += mCacheLines.top()->mMaxHeight;
550 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 42, nextLine, 0, mCacheTextureSmall));
551 nextLine += mCacheLines.top()->mMaxHeight;
552 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, mSmallCacheHeight - nextLine,
553 nextLine, 0, mCacheTextureSmall));
554
555 // The first cache is split into 2 lines of height 128, the rest have just one cache line.
556 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 0, 0, mCacheTexture128));
557 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 128, 0, mCacheTexture128));
558 mCacheLines.push(new CacheTextureLine(maxWidth, 256, 0, 0, mCacheTexture256));
559 mCacheLines.push(new CacheTextureLine(maxWidth, 512, 0, 0, mCacheTexture512));
Romain Guy694b5192010-07-21 21:33:20 -0700560}
561
562// Avoid having to reallocate memory and render quad by quad
563void FontRenderer::initVertexArrayBuffers() {
Romain Guyd71dd362011-12-12 19:03:35 -0800564 uint32_t numIndices = mMaxNumberOfQuads * 6;
565 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700566 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700567
568 // Four verts, two triangles , six indices per quad
569 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
570 int i6 = i * 6;
571 int i4 = i * 4;
572
573 indexBufferData[i6 + 0] = i4 + 0;
574 indexBufferData[i6 + 1] = i4 + 1;
575 indexBufferData[i6 + 2] = i4 + 2;
576
577 indexBufferData[i6 + 3] = i4 + 0;
578 indexBufferData[i6 + 4] = i4 + 2;
579 indexBufferData[i6 + 5] = i4 + 3;
580 }
581
582 glGenBuffers(1, &mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800583 Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700584 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
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 Guy15bc6432011-12-13 13:11:32 -0800646 Caches& caches = Caches::getInstance();
647 if (!mDrawn) {
648 float* buffer = mTextMeshPtr;
649 int offset = 2;
650
651 bool force = caches.unbindMeshBuffer();
652 caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
653 caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
654 buffer + offset);
655 }
656
657 caches.bindIndicesBuffer(mIndexBufferID);
Romain Guy694b5192010-07-21 21:33:20 -0700658 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
Romain Guy5b3b3522010-10-27 18:57:51 -0700659
660 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700661}
662
Romain Guyd71dd362011-12-12 19:03:35 -0800663void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
664 float x2, float y2, float u2, float v2,
665 float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800666 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Romain Guyd71dd362011-12-12 19:03:35 -0800667
Romain Guyff98fa52011-11-28 09:35:09 -0800668 if (mClip &&
669 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
Romain Guy09147fb2010-07-22 13:08:20 -0700670 return;
671 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800672 if (texture != mCurrentCacheTexture) {
673 if (mCurrentQuadIndex != 0) {
674 // First, draw everything stored already which uses the previous texture
675 issueDrawCommand();
676 mCurrentQuadIndex = 0;
677 }
678 // Now use the new texture id
679 mCurrentCacheTexture = texture;
680 }
Romain Guy09147fb2010-07-22 13:08:20 -0700681
Romain Guy694b5192010-07-21 21:33:20 -0700682 const uint32_t vertsPerQuad = 4;
Romain Guyd71dd362011-12-12 19:03:35 -0800683 const uint32_t floatsPerVert = 4;
Romain Guy51769a62010-07-23 00:28:00 -0700684 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700685
Romain Guy694b5192010-07-21 21:33:20 -0700686 (*currentPos++) = x1;
687 (*currentPos++) = y1;
Romain Guy694b5192010-07-21 21:33:20 -0700688 (*currentPos++) = u1;
689 (*currentPos++) = v1;
690
691 (*currentPos++) = x2;
692 (*currentPos++) = y2;
Romain Guy694b5192010-07-21 21:33:20 -0700693 (*currentPos++) = u2;
694 (*currentPos++) = v2;
695
696 (*currentPos++) = x3;
697 (*currentPos++) = y3;
Romain Guy694b5192010-07-21 21:33:20 -0700698 (*currentPos++) = u3;
699 (*currentPos++) = v3;
700
701 (*currentPos++) = x4;
702 (*currentPos++) = y4;
Romain Guy694b5192010-07-21 21:33:20 -0700703 (*currentPos++) = u4;
704 (*currentPos++) = v4;
705
706 mCurrentQuadIndex++;
707
Romain Guy5b3b3522010-10-27 18:57:51 -0700708 if (mBounds) {
709 mBounds->left = fmin(mBounds->left, x1);
710 mBounds->top = fmin(mBounds->top, y3);
711 mBounds->right = fmax(mBounds->right, x3);
712 mBounds->bottom = fmax(mBounds->bottom, y1);
713 }
714
Romain Guy694b5192010-07-21 21:33:20 -0700715 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
716 issueDrawCommand();
717 mCurrentQuadIndex = 0;
718 }
719}
720
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700721uint32_t FontRenderer::getRemainingCacheCapacity() {
722 uint32_t remainingCapacity = 0;
723 float totalPixels = 0;
724 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
725 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
726 totalPixels += mCacheLines[i]->mMaxWidth;
727 }
728 remainingCapacity = (remainingCapacity * 100) / totalPixels;
729 return remainingCapacity;
730}
731
732void FontRenderer::precacheLatin(SkPaint* paint) {
733 // Remaining capacity is measured in %
734 uint32_t remainingCapacity = getRemainingCacheCapacity();
735 uint32_t precacheIdx = 0;
Romain Guy054dc182010-10-15 17:55:25 -0700736 while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
Romain Guy726aeba2011-06-01 14:52:00 -0700737 mCurrentFont->getCachedGlyph(paint, (int32_t) mLatinPrecache[precacheIdx]);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700738 remainingCapacity = getRemainingCacheCapacity();
739 precacheIdx ++;
740 }
741}
742
743void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
744 uint32_t currentNumFonts = mActiveFonts.size();
Romain Guy325a0f92011-01-05 15:26:55 -0800745 int flags = 0;
746 if (paint->isFakeBoldText()) {
747 flags |= Font::kFakeBold;
748 }
Romain Guy2577db12011-01-18 13:02:38 -0800749
750 const float skewX = paint->getTextSkewX();
751 uint32_t italicStyle = *(uint32_t*) &skewX;
Chet Haase8668f8a2011-03-02 13:51:36 -0800752 const float scaleXFloat = paint->getTextScaleX();
753 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
Romain Guybd496bc2011-08-02 17:32:41 -0700754 SkPaint::Style style = paint->getStyle();
755 const float strokeWidthFloat = paint->getStrokeWidth();
756 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
757 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
758 scaleX, style, strokeWidth);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700759
760 const float maxPrecacheFontSize = 40.0f;
761 bool isNewFont = currentNumFonts != mActiveFonts.size();
762
Romain Guy2bffd262010-09-12 17:40:02 -0700763 if (isNewFont && fontSize <= maxPrecacheFontSize) {
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700764 precacheLatin(paint);
765 }
Romain Guy694b5192010-07-21 21:33:20 -0700766}
Romain Guy7975fb62010-10-01 16:36:14 -0700767
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700768FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Romain Guy1e45aae2010-08-13 19:39:53 -0700769 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
770 checkInit();
771
772 if (!mCurrentFont) {
773 DropShadow image;
774 image.width = 0;
775 image.height = 0;
776 image.image = NULL;
777 image.penX = 0;
778 image.penY = 0;
779 return image;
780 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700781
Romain Guyff98fa52011-11-28 09:35:09 -0800782 mClip = NULL;
783 mBounds = NULL;
784
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700785 Rect bounds;
Romain Guy726aeba2011-06-01 14:52:00 -0700786 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds);
Romain Guyff98fa52011-11-28 09:35:09 -0800787
Romain Guy1e45aae2010-08-13 19:39:53 -0700788 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
789 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700790 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guyff98fa52011-11-28 09:35:09 -0800791
Romain Guy1e45aae2010-08-13 19:39:53 -0700792 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700793 dataBuffer[i] = 0;
794 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700795
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700796 int penX = radius - bounds.left;
797 int penY = radius - bounds.bottom;
798
Romain Guy726aeba2011-06-01 14:52:00 -0700799 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
Romain Guy1e45aae2010-08-13 19:39:53 -0700800 dataBuffer, paddedWidth, paddedHeight);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700801 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
802
803 DropShadow image;
804 image.width = paddedWidth;
805 image.height = paddedHeight;
806 image.image = dataBuffer;
807 image.penX = penX;
808 image.penY = penY;
809 return image;
810}
Romain Guy694b5192010-07-21 21:33:20 -0700811
Romain Guy5b3b3522010-10-27 18:57:51 -0700812bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
813 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700814 checkInit();
815
Romain Guy09147fb2010-07-22 13:08:20 -0700816 if (!mCurrentFont) {
817 LOGE("No font set");
Romain Guy5b3b3522010-10-27 18:57:51 -0700818 return false;
Romain Guy694b5192010-07-21 21:33:20 -0700819 }
820
Romain Guy5b3b3522010-10-27 18:57:51 -0700821 mDrawn = false;
822 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -0700823 mClip = clip;
Romain Guyff98fa52011-11-28 09:35:09 -0800824
Romain Guy726aeba2011-06-01 14:52:00 -0700825 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
Romain Guyff98fa52011-11-28 09:35:09 -0800826
Romain Guy5b3b3522010-10-27 18:57:51 -0700827 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -0800828 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700829
830 if (mCurrentQuadIndex != 0) {
831 issueDrawCommand();
832 mCurrentQuadIndex = 0;
833 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700834
835 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700836}
837
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700838void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
839 // Compute gaussian weights for the blur
840 // e is the euler's number
841 float e = 2.718281828459045f;
842 float pi = 3.1415926535897932f;
843 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
844 // x is of the form [-radius .. 0 .. radius]
845 // and sigma varies with radius.
846 // Based on some experimental radius values and sigma's
847 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700848 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700849 // The larger the radius gets, the more our gaussian blur
850 // will resemble a box blur since with large sigma
851 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -0800852 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700853
854 // Now compute the coefficints
855 // We will store some redundant values to save some math during
856 // the blur calculations
857 // precompute some values
858 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
859 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
860
861 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -0800862 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700863 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700864 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
865 normalizeFactor += weights[r + radius];
866 }
867
868 //Now we need to normalize the weights because all our coefficients need to add up to one
869 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -0800870 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700871 weights[r + radius] *= normalizeFactor;
872 }
873}
874
875void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700876 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700877 float blurredPixel = 0.0f;
878 float currentPixel = 0.0f;
879
Romain Guy325a0f92011-01-05 15:26:55 -0800880 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700881
882 const uint8_t* input = source + y * width;
883 uint8_t* output = dest + y * width;
884
Romain Guy325a0f92011-01-05 15:26:55 -0800885 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700886 blurredPixel = 0.0f;
887 const float* gPtr = weights;
888 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800889 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700890 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -0800891 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700892 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700893 blurredPixel += currentPixel * gPtr[0];
894 gPtr++;
895 i++;
896 }
897 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800898 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700899 // Stepping left and right away from the pixel
900 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -0800901 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700902 validW = 0;
903 }
Romain Guy325a0f92011-01-05 15:26:55 -0800904 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700905 validW = width - 1;
906 }
907
Romain Guy325a0f92011-01-05 15:26:55 -0800908 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700909 blurredPixel += currentPixel * gPtr[0];
910 gPtr++;
911 }
912 }
913 *output = (uint8_t)blurredPixel;
914 output ++;
915 }
916 }
917}
918
919void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700920 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700921 float blurredPixel = 0.0f;
922 float currentPixel = 0.0f;
923
Romain Guy325a0f92011-01-05 15:26:55 -0800924 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700925
926 uint8_t* output = dest + y * width;
927
Romain Guy325a0f92011-01-05 15:26:55 -0800928 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700929 blurredPixel = 0.0f;
930 const float* gPtr = weights;
931 const uint8_t* input = source + x;
932 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800933 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700934 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -0800935 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700936 currentPixel = (float)(*i);
937 blurredPixel += currentPixel * gPtr[0];
938 gPtr++;
939 i += width;
940 }
941 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800942 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700943 int validH = y + r;
944 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -0800945 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700946 validH = 0;
947 }
Romain Guy325a0f92011-01-05 15:26:55 -0800948 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700949 validH = height - 1;
950 }
951
952 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -0800953 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700954 blurredPixel += currentPixel * gPtr[0];
955 gPtr++;
956 }
957 }
Romain Guy325a0f92011-01-05 15:26:55 -0800958 *output = (uint8_t) blurredPixel;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700959 output ++;
960 }
961 }
962}
963
964
965void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
966 float *gaussian = new float[2 * radius + 1];
967 computeGaussianWeights(gaussian, radius);
Romain Guyd71dd362011-12-12 19:03:35 -0800968
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700969 uint8_t* scratch = new uint8_t[width * height];
Romain Guyd71dd362011-12-12 19:03:35 -0800970
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700971 horizontalBlur(gaussian, radius, image, scratch, width, height);
972 verticalBlur(gaussian, radius, scratch, image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -0800973
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700974 delete[] gaussian;
975 delete[] scratch;
976}
977
Romain Guy694b5192010-07-21 21:33:20 -0700978}; // namespace uirenderer
979}; // namespace android