blob: 3c6a952e09f08f4eb8fb3b589bcd19ce7e2fd79e [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
Romain Guy2d4fd362011-12-13 22:00:19 -0800618 Caches& caches = Caches::getInstance();
619 GLuint lastTextureId = 0;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700620 // Iterate over all the cache lines and see which ones need to be updated
621 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
622 CacheTextureLine* cl = mCacheLines[i];
Chet Haase7de0cb12011-12-05 16:35:38 -0800623 if (cl->mDirty && cl->mCacheTexture->mTexture != NULL) {
624 CacheTexture* cacheTexture = cl->mCacheTexture;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700625 uint32_t xOffset = 0;
626 uint32_t yOffset = cl->mCurrentRow;
Chet Haase7de0cb12011-12-05 16:35:38 -0800627 uint32_t width = cl->mMaxWidth;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700628 uint32_t height = cl->mMaxHeight;
Chet Haase7de0cb12011-12-05 16:35:38 -0800629 void* textureData = cacheTexture->mTexture + (yOffset * width);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700630
Romain Guy2d4fd362011-12-13 22:00:19 -0800631 if (cacheTexture->mTextureId != lastTextureId) {
632 caches.activeTexture(0);
633 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
634 lastTextureId = cacheTexture->mTextureId;
635 }
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700636 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700637 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700638
639 cl->mDirty = false;
640 }
641 }
642
Chet Haase7de0cb12011-12-05 16:35:38 -0800643 glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
644 mLastCacheTexture = mCurrentCacheTexture;
645
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700646 mUploadTexture = false;
647}
648
649void FontRenderer::issueDrawCommand() {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700650 checkTextureUpdate();
651
Romain Guy15bc6432011-12-13 13:11:32 -0800652 Caches& caches = Caches::getInstance();
Romain Guy2d4fd362011-12-13 22:00:19 -0800653 caches.bindIndicesBuffer(mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800654 if (!mDrawn) {
655 float* buffer = mTextMeshPtr;
656 int offset = 2;
657
658 bool force = caches.unbindMeshBuffer();
659 caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
660 caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
661 buffer + offset);
662 }
663
Romain Guy694b5192010-07-21 21:33:20 -0700664 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
Romain Guy5b3b3522010-10-27 18:57:51 -0700665
666 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700667}
668
Romain Guyd71dd362011-12-12 19:03:35 -0800669void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
670 float x2, float y2, float u2, float v2,
671 float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800672 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Romain Guyd71dd362011-12-12 19:03:35 -0800673
Romain Guyff98fa52011-11-28 09:35:09 -0800674 if (mClip &&
675 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
Romain Guy09147fb2010-07-22 13:08:20 -0700676 return;
677 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800678 if (texture != mCurrentCacheTexture) {
679 if (mCurrentQuadIndex != 0) {
680 // First, draw everything stored already which uses the previous texture
681 issueDrawCommand();
682 mCurrentQuadIndex = 0;
683 }
684 // Now use the new texture id
685 mCurrentCacheTexture = texture;
686 }
Romain Guy09147fb2010-07-22 13:08:20 -0700687
Romain Guy694b5192010-07-21 21:33:20 -0700688 const uint32_t vertsPerQuad = 4;
Romain Guyd71dd362011-12-12 19:03:35 -0800689 const uint32_t floatsPerVert = 4;
Romain Guy51769a62010-07-23 00:28:00 -0700690 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700691
Romain Guy694b5192010-07-21 21:33:20 -0700692 (*currentPos++) = x1;
693 (*currentPos++) = y1;
Romain Guy694b5192010-07-21 21:33:20 -0700694 (*currentPos++) = u1;
695 (*currentPos++) = v1;
696
697 (*currentPos++) = x2;
698 (*currentPos++) = y2;
Romain Guy694b5192010-07-21 21:33:20 -0700699 (*currentPos++) = u2;
700 (*currentPos++) = v2;
701
702 (*currentPos++) = x3;
703 (*currentPos++) = y3;
Romain Guy694b5192010-07-21 21:33:20 -0700704 (*currentPos++) = u3;
705 (*currentPos++) = v3;
706
707 (*currentPos++) = x4;
708 (*currentPos++) = y4;
Romain Guy694b5192010-07-21 21:33:20 -0700709 (*currentPos++) = u4;
710 (*currentPos++) = v4;
711
712 mCurrentQuadIndex++;
713
Romain Guy5b3b3522010-10-27 18:57:51 -0700714 if (mBounds) {
715 mBounds->left = fmin(mBounds->left, x1);
716 mBounds->top = fmin(mBounds->top, y3);
717 mBounds->right = fmax(mBounds->right, x3);
718 mBounds->bottom = fmax(mBounds->bottom, y1);
719 }
720
Romain Guy694b5192010-07-21 21:33:20 -0700721 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
722 issueDrawCommand();
723 mCurrentQuadIndex = 0;
724 }
725}
726
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700727uint32_t FontRenderer::getRemainingCacheCapacity() {
728 uint32_t remainingCapacity = 0;
729 float totalPixels = 0;
730 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
731 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
732 totalPixels += mCacheLines[i]->mMaxWidth;
733 }
734 remainingCapacity = (remainingCapacity * 100) / totalPixels;
735 return remainingCapacity;
736}
737
738void FontRenderer::precacheLatin(SkPaint* paint) {
739 // Remaining capacity is measured in %
740 uint32_t remainingCapacity = getRemainingCacheCapacity();
741 uint32_t precacheIdx = 0;
Romain Guy054dc182010-10-15 17:55:25 -0700742 while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
Romain Guy726aeba2011-06-01 14:52:00 -0700743 mCurrentFont->getCachedGlyph(paint, (int32_t) mLatinPrecache[precacheIdx]);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700744 remainingCapacity = getRemainingCacheCapacity();
745 precacheIdx ++;
746 }
747}
748
749void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
750 uint32_t currentNumFonts = mActiveFonts.size();
Romain Guy325a0f92011-01-05 15:26:55 -0800751 int flags = 0;
752 if (paint->isFakeBoldText()) {
753 flags |= Font::kFakeBold;
754 }
Romain Guy2577db12011-01-18 13:02:38 -0800755
756 const float skewX = paint->getTextSkewX();
757 uint32_t italicStyle = *(uint32_t*) &skewX;
Chet Haase8668f8a2011-03-02 13:51:36 -0800758 const float scaleXFloat = paint->getTextScaleX();
759 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
Romain Guybd496bc2011-08-02 17:32:41 -0700760 SkPaint::Style style = paint->getStyle();
761 const float strokeWidthFloat = paint->getStrokeWidth();
762 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
763 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
764 scaleX, style, strokeWidth);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700765
766 const float maxPrecacheFontSize = 40.0f;
767 bool isNewFont = currentNumFonts != mActiveFonts.size();
768
Romain Guy2bffd262010-09-12 17:40:02 -0700769 if (isNewFont && fontSize <= maxPrecacheFontSize) {
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700770 precacheLatin(paint);
771 }
Romain Guy694b5192010-07-21 21:33:20 -0700772}
Romain Guy7975fb62010-10-01 16:36:14 -0700773
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700774FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Romain Guy1e45aae2010-08-13 19:39:53 -0700775 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
776 checkInit();
777
778 if (!mCurrentFont) {
779 DropShadow image;
780 image.width = 0;
781 image.height = 0;
782 image.image = NULL;
783 image.penX = 0;
784 image.penY = 0;
785 return image;
786 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700787
Romain Guy2d4fd362011-12-13 22:00:19 -0800788 mDrawn = false;
Romain Guyff98fa52011-11-28 09:35:09 -0800789 mClip = NULL;
790 mBounds = NULL;
791
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700792 Rect bounds;
Romain Guy726aeba2011-06-01 14:52:00 -0700793 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds);
Romain Guyff98fa52011-11-28 09:35:09 -0800794
Romain Guy1e45aae2010-08-13 19:39:53 -0700795 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
796 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700797 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guyff98fa52011-11-28 09:35:09 -0800798
Romain Guy1e45aae2010-08-13 19:39:53 -0700799 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700800 dataBuffer[i] = 0;
801 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700802
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700803 int penX = radius - bounds.left;
804 int penY = radius - bounds.bottom;
805
Romain Guy726aeba2011-06-01 14:52:00 -0700806 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
Romain Guy1e45aae2010-08-13 19:39:53 -0700807 dataBuffer, paddedWidth, paddedHeight);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700808 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
809
810 DropShadow image;
811 image.width = paddedWidth;
812 image.height = paddedHeight;
813 image.image = dataBuffer;
814 image.penX = penX;
815 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -0800816
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700817 return image;
818}
Romain Guy694b5192010-07-21 21:33:20 -0700819
Romain Guy5b3b3522010-10-27 18:57:51 -0700820bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
821 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700822 checkInit();
823
Romain Guy09147fb2010-07-22 13:08:20 -0700824 if (!mCurrentFont) {
825 LOGE("No font set");
Romain Guy5b3b3522010-10-27 18:57:51 -0700826 return false;
Romain Guy694b5192010-07-21 21:33:20 -0700827 }
828
Romain Guy5b3b3522010-10-27 18:57:51 -0700829 mDrawn = false;
830 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -0700831 mClip = clip;
Romain Guyff98fa52011-11-28 09:35:09 -0800832
Romain Guy726aeba2011-06-01 14:52:00 -0700833 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
Romain Guyff98fa52011-11-28 09:35:09 -0800834
Romain Guy5b3b3522010-10-27 18:57:51 -0700835 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -0800836 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700837
838 if (mCurrentQuadIndex != 0) {
839 issueDrawCommand();
840 mCurrentQuadIndex = 0;
841 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700842
843 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700844}
845
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700846void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
847 // Compute gaussian weights for the blur
848 // e is the euler's number
849 float e = 2.718281828459045f;
850 float pi = 3.1415926535897932f;
851 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
852 // x is of the form [-radius .. 0 .. radius]
853 // and sigma varies with radius.
854 // Based on some experimental radius values and sigma's
855 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700856 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700857 // The larger the radius gets, the more our gaussian blur
858 // will resemble a box blur since with large sigma
859 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -0800860 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700861
862 // Now compute the coefficints
863 // We will store some redundant values to save some math during
864 // the blur calculations
865 // precompute some values
866 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
867 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
868
869 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -0800870 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700871 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700872 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
873 normalizeFactor += weights[r + radius];
874 }
875
876 //Now we need to normalize the weights because all our coefficients need to add up to one
877 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -0800878 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700879 weights[r + radius] *= normalizeFactor;
880 }
881}
882
883void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700884 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700885 float blurredPixel = 0.0f;
886 float currentPixel = 0.0f;
887
Romain Guy325a0f92011-01-05 15:26:55 -0800888 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700889
890 const uint8_t* input = source + y * width;
891 uint8_t* output = dest + y * width;
892
Romain Guy325a0f92011-01-05 15:26:55 -0800893 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700894 blurredPixel = 0.0f;
895 const float* gPtr = weights;
896 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800897 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700898 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -0800899 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700900 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700901 blurredPixel += currentPixel * gPtr[0];
902 gPtr++;
903 i++;
904 }
905 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800906 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700907 // Stepping left and right away from the pixel
908 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -0800909 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700910 validW = 0;
911 }
Romain Guy325a0f92011-01-05 15:26:55 -0800912 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700913 validW = width - 1;
914 }
915
Romain Guy325a0f92011-01-05 15:26:55 -0800916 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700917 blurredPixel += currentPixel * gPtr[0];
918 gPtr++;
919 }
920 }
921 *output = (uint8_t)blurredPixel;
922 output ++;
923 }
924 }
925}
926
927void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700928 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700929 float blurredPixel = 0.0f;
930 float currentPixel = 0.0f;
931
Romain Guy325a0f92011-01-05 15:26:55 -0800932 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700933
934 uint8_t* output = dest + y * width;
935
Romain Guy325a0f92011-01-05 15:26:55 -0800936 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700937 blurredPixel = 0.0f;
938 const float* gPtr = weights;
939 const uint8_t* input = source + x;
940 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800941 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700942 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -0800943 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700944 currentPixel = (float)(*i);
945 blurredPixel += currentPixel * gPtr[0];
946 gPtr++;
947 i += width;
948 }
949 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800950 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700951 int validH = y + r;
952 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -0800953 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700954 validH = 0;
955 }
Romain Guy325a0f92011-01-05 15:26:55 -0800956 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700957 validH = height - 1;
958 }
959
960 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -0800961 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700962 blurredPixel += currentPixel * gPtr[0];
963 gPtr++;
964 }
965 }
Romain Guy325a0f92011-01-05 15:26:55 -0800966 *output = (uint8_t) blurredPixel;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700967 output ++;
968 }
969 }
970}
971
972
973void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
974 float *gaussian = new float[2 * radius + 1];
975 computeGaussianWeights(gaussian, radius);
Romain Guyd71dd362011-12-12 19:03:35 -0800976
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700977 uint8_t* scratch = new uint8_t[width * height];
Romain Guyd71dd362011-12-12 19:03:35 -0800978
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700979 horizontalBlur(gaussian, radius, image, scratch, width, height);
980 verticalBlur(gaussian, radius, scratch, image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -0800981
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700982 delete[] gaussian;
983 delete[] scratch;
984}
985
Romain Guy694b5192010-07-21 21:33:20 -0700986}; // namespace uirenderer
987}; // namespace android