blob: 34245b0f9ef36001876aedc131e82c2e5a4f64bc [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
Chet Haase2a47c142011-12-14 15:22:56 -0800351 mLinearFiltering = false;
352
Romain Guy694b5192010-07-21 21:33:20 -0700353 mIndexBufferID = 0;
354
Chet Haase7de0cb12011-12-05 16:35:38 -0800355 mSmallCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
356 mSmallCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -0700357
358 char property[PROPERTY_VALUE_MAX];
359 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800360 if (sLogFontRendererCreate) {
361 INIT_LOGD(" Setting text cache width to %s pixels", property);
362 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800363 mSmallCacheWidth = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -0700364 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800365 if (sLogFontRendererCreate) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800366 INIT_LOGD(" Using default text cache width of %i pixels", mSmallCacheWidth);
Romain Guy514fb182011-01-19 14:38:29 -0800367 }
Romain Guy51769a62010-07-23 00:28:00 -0700368 }
369
370 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800371 if (sLogFontRendererCreate) {
372 INIT_LOGD(" Setting text cache width to %s pixels", property);
373 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800374 mSmallCacheHeight = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -0700375 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800376 if (sLogFontRendererCreate) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800377 INIT_LOGD(" Using default text cache height of %i pixels", mSmallCacheHeight);
Romain Guy514fb182011-01-19 14:38:29 -0800378 }
Romain Guy51769a62010-07-23 00:28:00 -0700379 }
Romain Guy514fb182011-01-19 14:38:29 -0800380
381 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -0700382}
383
384FontRenderer::~FontRenderer() {
385 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
386 delete mCacheLines[i];
387 }
388 mCacheLines.clear();
389
Romain Guy9cccc2b92010-08-07 23:46:15 -0700390 if (mInitialized) {
391 delete[] mTextMeshPtr;
Chet Haase7de0cb12011-12-05 16:35:38 -0800392 delete mCacheTextureSmall;
393 delete mCacheTexture128;
394 delete mCacheTexture256;
395 delete mCacheTexture512;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700396 }
Romain Guy694b5192010-07-21 21:33:20 -0700397
398 Vector<Font*> fontsToDereference = mActiveFonts;
399 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
400 delete fontsToDereference[i];
401 }
402}
403
404void FontRenderer::flushAllAndInvalidate() {
405 if (mCurrentQuadIndex != 0) {
406 issueDrawCommand();
407 mCurrentQuadIndex = 0;
408 }
409 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
410 mActiveFonts[i]->invalidateTextureCache();
411 }
412 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
413 mCacheLines[i]->mCurrentCol = 0;
414 }
415}
416
Chet Haase2a47c142011-12-14 15:22:56 -0800417void FontRenderer::allocateTextureMemory(CacheTexture *cacheTexture) {
418 int width = cacheTexture->mWidth;
419 int height = cacheTexture->mHeight;
420 cacheTexture->mTexture = new uint8_t[width * height];
421 memset(cacheTexture->mTexture, 0, width * height * sizeof(uint8_t));
422 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
423 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
424 // Initialize texture dimensions
425 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
426 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800427
Chet Haase2a47c142011-12-14 15:22:56 -0800428 const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST;
429 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
430 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
431
432 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
433 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
Chet Haase7de0cb12011-12-05 16:35:38 -0800434}
435
436void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
437 uint32_t* retOriginX, uint32_t* retOriginY) {
438 cachedGlyph->mIsValid = false;
Romain Guy694b5192010-07-21 21:33:20 -0700439 // If the glyph is too tall, don't cache it
Chet Haase7de0cb12011-12-05 16:35:38 -0800440 if (glyph.fHeight + TEXTURE_BORDER_SIZE > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
441 LOGE("Font size to large to fit in cache. width, height = %i, %i",
442 (int) glyph.fWidth, (int) glyph.fHeight);
443 return;
Romain Guy694b5192010-07-21 21:33:20 -0700444 }
445
446 // Now copy the bitmap into the cache texture
447 uint32_t startX = 0;
448 uint32_t startY = 0;
449
450 bool bitmapFit = false;
Chet Haase7de0cb12011-12-05 16:35:38 -0800451 CacheTextureLine *cacheLine;
Romain Guy694b5192010-07-21 21:33:20 -0700452 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
453 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
454 if (bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800455 cacheLine = mCacheLines[i];
Romain Guy694b5192010-07-21 21:33:20 -0700456 break;
457 }
458 }
459
460 // If the new glyph didn't fit, flush the state so far and invalidate everything
461 if (!bitmapFit) {
462 flushAllAndInvalidate();
463
464 // Try to fit it again
465 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
466 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
467 if (bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800468 cacheLine = mCacheLines[i];
Romain Guy694b5192010-07-21 21:33:20 -0700469 break;
470 }
471 }
472
473 // if we still don't fit, something is wrong and we shouldn't draw
474 if (!bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800475 return;
Romain Guy694b5192010-07-21 21:33:20 -0700476 }
477 }
478
Chet Haase7de0cb12011-12-05 16:35:38 -0800479 cachedGlyph->mCachedTextureLine = cacheLine;
480
Romain Guy694b5192010-07-21 21:33:20 -0700481 *retOriginX = startX;
482 *retOriginY = startY;
483
484 uint32_t endX = startX + glyph.fWidth;
485 uint32_t endY = startY + glyph.fHeight;
486
Chet Haase7de0cb12011-12-05 16:35:38 -0800487 uint32_t cacheWidth = cacheLine->mMaxWidth;
Romain Guy694b5192010-07-21 21:33:20 -0700488
Chet Haase7de0cb12011-12-05 16:35:38 -0800489 CacheTexture *cacheTexture = cacheLine->mCacheTexture;
490 if (cacheTexture->mTexture == NULL) {
491 // Large-glyph texture memory is allocated only as needed
Chet Haase2a47c142011-12-14 15:22:56 -0800492 allocateTextureMemory(cacheTexture);
Chet Haase7de0cb12011-12-05 16:35:38 -0800493 }
494 uint8_t* cacheBuffer = cacheTexture->mTexture;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700495 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700496 unsigned int stride = glyph.rowBytes();
497
498 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
499 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
500 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700501 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
Romain Guyb45c0c92010-08-26 20:35:23 -0700502 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
Romain Guy694b5192010-07-21 21:33:20 -0700503 }
504 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800505 cachedGlyph->mIsValid = true;
Romain Guy694b5192010-07-21 21:33:20 -0700506}
507
Chet Haase7de0cb12011-12-05 16:35:38 -0800508CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800509 GLuint textureId;
510 glGenTextures(1, &textureId);
Chet Haase2a47c142011-12-14 15:22:56 -0800511 uint8_t* textureMemory = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700512
Chet Haase2a47c142011-12-14 15:22:56 -0800513 CacheTexture* cacheTexture = new CacheTexture(textureMemory, textureId, width, height);
514 if (allocate) {
515 allocateTextureMemory(cacheTexture);
516 }
517 return cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800518}
519
520void FontRenderer::initTextTexture() {
521 mCacheLines.clear();
522
523 // Next, use other, separate caches for large glyphs.
524 uint16_t maxWidth = 0;
525 if (Caches::hasInstance()) {
526 maxWidth = Caches::getInstance().maxTextureSize;
Chet Haase44984ea2011-05-19 13:50:47 -0700527 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800528 if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) {
529 maxWidth = MAX_TEXT_CACHE_WIDTH;
530 }
531 if (mCacheTextureSmall != NULL) {
532 delete mCacheTextureSmall;
533 delete mCacheTexture128;
534 delete mCacheTexture256;
535 delete mCacheTexture512;
536 }
537 mCacheTextureSmall = createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true);
538 mCacheTexture128 = createCacheTexture(maxWidth, 256, false);
539 mCacheTexture256 = createCacheTexture(maxWidth, 256, false);
540 mCacheTexture512 = createCacheTexture(maxWidth, 512, false);
541 mCurrentCacheTexture = mCacheTextureSmall;
542
543 mUploadTexture = false;
544 // Split up our default cache texture into lines of certain widths
545 int nextLine = 0;
546 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 18, nextLine, 0, mCacheTextureSmall));
547 nextLine += mCacheLines.top()->mMaxHeight;
548 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
549 nextLine += mCacheLines.top()->mMaxHeight;
550 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
551 nextLine += mCacheLines.top()->mMaxHeight;
552 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
553 nextLine += mCacheLines.top()->mMaxHeight;
554 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
555 nextLine += mCacheLines.top()->mMaxHeight;
556 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 42, nextLine, 0, mCacheTextureSmall));
557 nextLine += mCacheLines.top()->mMaxHeight;
558 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, mSmallCacheHeight - nextLine,
559 nextLine, 0, mCacheTextureSmall));
560
561 // The first cache is split into 2 lines of height 128, the rest have just one cache line.
562 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 0, 0, mCacheTexture128));
563 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 128, 0, mCacheTexture128));
564 mCacheLines.push(new CacheTextureLine(maxWidth, 256, 0, 0, mCacheTexture256));
565 mCacheLines.push(new CacheTextureLine(maxWidth, 512, 0, 0, mCacheTexture512));
Romain Guy694b5192010-07-21 21:33:20 -0700566}
567
568// Avoid having to reallocate memory and render quad by quad
569void FontRenderer::initVertexArrayBuffers() {
Romain Guyd71dd362011-12-12 19:03:35 -0800570 uint32_t numIndices = mMaxNumberOfQuads * 6;
571 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700572 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700573
574 // Four verts, two triangles , six indices per quad
575 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
576 int i6 = i * 6;
577 int i4 = i * 4;
578
579 indexBufferData[i6 + 0] = i4 + 0;
580 indexBufferData[i6 + 1] = i4 + 1;
581 indexBufferData[i6 + 2] = i4 + 2;
582
583 indexBufferData[i6 + 3] = i4 + 0;
584 indexBufferData[i6 + 4] = i4 + 2;
585 indexBufferData[i6 + 5] = i4 + 3;
586 }
587
588 glGenBuffers(1, &mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800589 Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700590 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
Romain Guy694b5192010-07-21 21:33:20 -0700591
592 free(indexBufferData);
593
Romain Guyd71dd362011-12-12 19:03:35 -0800594 uint32_t coordSize = 2;
Romain Guy694b5192010-07-21 21:33:20 -0700595 uint32_t uvSize = 2;
596 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700597 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
598 mTextMeshPtr = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700599}
600
601// We don't want to allocate anything unless we actually draw text
602void FontRenderer::checkInit() {
603 if (mInitialized) {
604 return;
605 }
606
607 initTextTexture();
608 initVertexArrayBuffers();
609
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700610 // We store a string with letters in a rough frequency of occurrence
611 mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
612 mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
613 mLatinPrecache += String16(",.?!()-+@;:`'");
614 mLatinPrecache += String16("0123456789");
615
Romain Guy694b5192010-07-21 21:33:20 -0700616 mInitialized = true;
617}
618
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700619void FontRenderer::checkTextureUpdate() {
Chet Haase7de0cb12011-12-05 16:35:38 -0800620 if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700621 return;
Romain Guy694b5192010-07-21 21:33:20 -0700622 }
623
Romain Guy2d4fd362011-12-13 22:00:19 -0800624 Caches& caches = Caches::getInstance();
625 GLuint lastTextureId = 0;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700626 // Iterate over all the cache lines and see which ones need to be updated
627 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
628 CacheTextureLine* cl = mCacheLines[i];
Chet Haase7de0cb12011-12-05 16:35:38 -0800629 if (cl->mDirty && cl->mCacheTexture->mTexture != NULL) {
630 CacheTexture* cacheTexture = cl->mCacheTexture;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700631 uint32_t xOffset = 0;
632 uint32_t yOffset = cl->mCurrentRow;
Chet Haase7de0cb12011-12-05 16:35:38 -0800633 uint32_t width = cl->mMaxWidth;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700634 uint32_t height = cl->mMaxHeight;
Chet Haase7de0cb12011-12-05 16:35:38 -0800635 void* textureData = cacheTexture->mTexture + (yOffset * width);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700636
Romain Guy2d4fd362011-12-13 22:00:19 -0800637 if (cacheTexture->mTextureId != lastTextureId) {
638 caches.activeTexture(0);
639 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
640 lastTextureId = cacheTexture->mTextureId;
641 }
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700642 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700643 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700644
645 cl->mDirty = false;
646 }
647 }
648
Chet Haase7de0cb12011-12-05 16:35:38 -0800649 glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
Chet Haase2a47c142011-12-14 15:22:56 -0800650 if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) {
651 const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST;
652 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
653 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
654 mCurrentCacheTexture->mLinearFiltering = mLinearFiltering;
655 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800656 mLastCacheTexture = mCurrentCacheTexture;
657
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700658 mUploadTexture = false;
659}
660
661void FontRenderer::issueDrawCommand() {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700662 checkTextureUpdate();
663
Romain Guy15bc6432011-12-13 13:11:32 -0800664 Caches& caches = Caches::getInstance();
Romain Guy2d4fd362011-12-13 22:00:19 -0800665 caches.bindIndicesBuffer(mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800666 if (!mDrawn) {
667 float* buffer = mTextMeshPtr;
668 int offset = 2;
669
670 bool force = caches.unbindMeshBuffer();
671 caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
672 caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
673 buffer + offset);
674 }
675
Romain Guy694b5192010-07-21 21:33:20 -0700676 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
Romain Guy5b3b3522010-10-27 18:57:51 -0700677
678 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700679}
680
Romain Guyd71dd362011-12-12 19:03:35 -0800681void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
682 float x2, float y2, float u2, float v2,
683 float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800684 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Romain Guyd71dd362011-12-12 19:03:35 -0800685
Romain Guyff98fa52011-11-28 09:35:09 -0800686 if (mClip &&
687 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
Romain Guy09147fb2010-07-22 13:08:20 -0700688 return;
689 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800690 if (texture != mCurrentCacheTexture) {
691 if (mCurrentQuadIndex != 0) {
692 // First, draw everything stored already which uses the previous texture
693 issueDrawCommand();
694 mCurrentQuadIndex = 0;
695 }
696 // Now use the new texture id
697 mCurrentCacheTexture = texture;
698 }
Romain Guy09147fb2010-07-22 13:08:20 -0700699
Romain Guy694b5192010-07-21 21:33:20 -0700700 const uint32_t vertsPerQuad = 4;
Romain Guyd71dd362011-12-12 19:03:35 -0800701 const uint32_t floatsPerVert = 4;
Romain Guy51769a62010-07-23 00:28:00 -0700702 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700703
Romain Guy694b5192010-07-21 21:33:20 -0700704 (*currentPos++) = x1;
705 (*currentPos++) = y1;
Romain Guy694b5192010-07-21 21:33:20 -0700706 (*currentPos++) = u1;
707 (*currentPos++) = v1;
708
709 (*currentPos++) = x2;
710 (*currentPos++) = y2;
Romain Guy694b5192010-07-21 21:33:20 -0700711 (*currentPos++) = u2;
712 (*currentPos++) = v2;
713
714 (*currentPos++) = x3;
715 (*currentPos++) = y3;
Romain Guy694b5192010-07-21 21:33:20 -0700716 (*currentPos++) = u3;
717 (*currentPos++) = v3;
718
719 (*currentPos++) = x4;
720 (*currentPos++) = y4;
Romain Guy694b5192010-07-21 21:33:20 -0700721 (*currentPos++) = u4;
722 (*currentPos++) = v4;
723
724 mCurrentQuadIndex++;
725
Romain Guy5b3b3522010-10-27 18:57:51 -0700726 if (mBounds) {
727 mBounds->left = fmin(mBounds->left, x1);
728 mBounds->top = fmin(mBounds->top, y3);
729 mBounds->right = fmax(mBounds->right, x3);
730 mBounds->bottom = fmax(mBounds->bottom, y1);
731 }
732
Romain Guy694b5192010-07-21 21:33:20 -0700733 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
734 issueDrawCommand();
735 mCurrentQuadIndex = 0;
736 }
737}
738
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700739uint32_t FontRenderer::getRemainingCacheCapacity() {
740 uint32_t remainingCapacity = 0;
741 float totalPixels = 0;
742 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
743 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
744 totalPixels += mCacheLines[i]->mMaxWidth;
745 }
746 remainingCapacity = (remainingCapacity * 100) / totalPixels;
747 return remainingCapacity;
748}
749
750void FontRenderer::precacheLatin(SkPaint* paint) {
751 // Remaining capacity is measured in %
752 uint32_t remainingCapacity = getRemainingCacheCapacity();
753 uint32_t precacheIdx = 0;
Romain Guy054dc182010-10-15 17:55:25 -0700754 while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
Romain Guy726aeba2011-06-01 14:52:00 -0700755 mCurrentFont->getCachedGlyph(paint, (int32_t) mLatinPrecache[precacheIdx]);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700756 remainingCapacity = getRemainingCacheCapacity();
757 precacheIdx ++;
758 }
759}
760
761void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
762 uint32_t currentNumFonts = mActiveFonts.size();
Romain Guy325a0f92011-01-05 15:26:55 -0800763 int flags = 0;
764 if (paint->isFakeBoldText()) {
765 flags |= Font::kFakeBold;
766 }
Romain Guy2577db12011-01-18 13:02:38 -0800767
768 const float skewX = paint->getTextSkewX();
769 uint32_t italicStyle = *(uint32_t*) &skewX;
Chet Haase8668f8a2011-03-02 13:51:36 -0800770 const float scaleXFloat = paint->getTextScaleX();
771 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
Romain Guybd496bc2011-08-02 17:32:41 -0700772 SkPaint::Style style = paint->getStyle();
773 const float strokeWidthFloat = paint->getStrokeWidth();
774 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
775 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
776 scaleX, style, strokeWidth);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700777
778 const float maxPrecacheFontSize = 40.0f;
779 bool isNewFont = currentNumFonts != mActiveFonts.size();
780
Romain Guy2bffd262010-09-12 17:40:02 -0700781 if (isNewFont && fontSize <= maxPrecacheFontSize) {
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700782 precacheLatin(paint);
783 }
Romain Guy694b5192010-07-21 21:33:20 -0700784}
Romain Guy7975fb62010-10-01 16:36:14 -0700785
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700786FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Romain Guy1e45aae2010-08-13 19:39:53 -0700787 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
788 checkInit();
789
790 if (!mCurrentFont) {
791 DropShadow image;
792 image.width = 0;
793 image.height = 0;
794 image.image = NULL;
795 image.penX = 0;
796 image.penY = 0;
797 return image;
798 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700799
Romain Guy2d4fd362011-12-13 22:00:19 -0800800 mDrawn = false;
Romain Guyff98fa52011-11-28 09:35:09 -0800801 mClip = NULL;
802 mBounds = NULL;
803
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700804 Rect bounds;
Romain Guy726aeba2011-06-01 14:52:00 -0700805 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds);
Romain Guyff98fa52011-11-28 09:35:09 -0800806
Romain Guy1e45aae2010-08-13 19:39:53 -0700807 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
808 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700809 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guyff98fa52011-11-28 09:35:09 -0800810
Romain Guy1e45aae2010-08-13 19:39:53 -0700811 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700812 dataBuffer[i] = 0;
813 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700814
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700815 int penX = radius - bounds.left;
816 int penY = radius - bounds.bottom;
817
Romain Guy726aeba2011-06-01 14:52:00 -0700818 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
Romain Guy1e45aae2010-08-13 19:39:53 -0700819 dataBuffer, paddedWidth, paddedHeight);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700820 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
821
822 DropShadow image;
823 image.width = paddedWidth;
824 image.height = paddedHeight;
825 image.image = dataBuffer;
826 image.penX = penX;
827 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -0800828
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700829 return image;
830}
Romain Guy694b5192010-07-21 21:33:20 -0700831
Romain Guy5b3b3522010-10-27 18:57:51 -0700832bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
833 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700834 checkInit();
835
Romain Guy09147fb2010-07-22 13:08:20 -0700836 if (!mCurrentFont) {
837 LOGE("No font set");
Romain Guy5b3b3522010-10-27 18:57:51 -0700838 return false;
Romain Guy694b5192010-07-21 21:33:20 -0700839 }
840
Romain Guy5b3b3522010-10-27 18:57:51 -0700841 mDrawn = false;
842 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -0700843 mClip = clip;
Romain Guyff98fa52011-11-28 09:35:09 -0800844
Romain Guy726aeba2011-06-01 14:52:00 -0700845 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
Romain Guyff98fa52011-11-28 09:35:09 -0800846
Romain Guy5b3b3522010-10-27 18:57:51 -0700847 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -0800848 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700849
850 if (mCurrentQuadIndex != 0) {
851 issueDrawCommand();
852 mCurrentQuadIndex = 0;
853 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700854
855 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700856}
857
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700858void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
859 // Compute gaussian weights for the blur
860 // e is the euler's number
861 float e = 2.718281828459045f;
862 float pi = 3.1415926535897932f;
863 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
864 // x is of the form [-radius .. 0 .. radius]
865 // and sigma varies with radius.
866 // Based on some experimental radius values and sigma's
867 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700868 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700869 // The larger the radius gets, the more our gaussian blur
870 // will resemble a box blur since with large sigma
871 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -0800872 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700873
874 // Now compute the coefficints
875 // We will store some redundant values to save some math during
876 // the blur calculations
877 // precompute some values
878 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
879 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
880
881 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -0800882 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700883 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700884 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
885 normalizeFactor += weights[r + radius];
886 }
887
888 //Now we need to normalize the weights because all our coefficients need to add up to one
889 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -0800890 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700891 weights[r + radius] *= normalizeFactor;
892 }
893}
894
895void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700896 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700897 float blurredPixel = 0.0f;
898 float currentPixel = 0.0f;
899
Romain Guy325a0f92011-01-05 15:26:55 -0800900 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700901
902 const uint8_t* input = source + y * width;
903 uint8_t* output = dest + y * width;
904
Romain Guy325a0f92011-01-05 15:26:55 -0800905 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700906 blurredPixel = 0.0f;
907 const float* gPtr = weights;
908 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800909 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700910 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -0800911 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700912 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700913 blurredPixel += currentPixel * gPtr[0];
914 gPtr++;
915 i++;
916 }
917 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800918 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700919 // Stepping left and right away from the pixel
920 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -0800921 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700922 validW = 0;
923 }
Romain Guy325a0f92011-01-05 15:26:55 -0800924 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700925 validW = width - 1;
926 }
927
Romain Guy325a0f92011-01-05 15:26:55 -0800928 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700929 blurredPixel += currentPixel * gPtr[0];
930 gPtr++;
931 }
932 }
933 *output = (uint8_t)blurredPixel;
934 output ++;
935 }
936 }
937}
938
939void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700940 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700941 float blurredPixel = 0.0f;
942 float currentPixel = 0.0f;
943
Romain Guy325a0f92011-01-05 15:26:55 -0800944 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700945
946 uint8_t* output = dest + y * width;
947
Romain Guy325a0f92011-01-05 15:26:55 -0800948 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700949 blurredPixel = 0.0f;
950 const float* gPtr = weights;
951 const uint8_t* input = source + x;
952 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800953 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700954 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -0800955 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700956 currentPixel = (float)(*i);
957 blurredPixel += currentPixel * gPtr[0];
958 gPtr++;
959 i += width;
960 }
961 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800962 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700963 int validH = y + r;
964 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -0800965 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700966 validH = 0;
967 }
Romain Guy325a0f92011-01-05 15:26:55 -0800968 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700969 validH = height - 1;
970 }
971
972 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -0800973 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700974 blurredPixel += currentPixel * gPtr[0];
975 gPtr++;
976 }
977 }
Romain Guy325a0f92011-01-05 15:26:55 -0800978 *output = (uint8_t) blurredPixel;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700979 output ++;
980 }
981 }
982}
983
984
985void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
986 float *gaussian = new float[2 * radius + 1];
987 computeGaussianWeights(gaussian, radius);
Romain Guyd71dd362011-12-12 19:03:35 -0800988
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700989 uint8_t* scratch = new uint8_t[width * height];
Romain Guyd71dd362011-12-12 19:03:35 -0800990
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700991 horizontalBlur(gaussian, radius, image, scratch, width, height);
992 verticalBlur(gaussian, radius, scratch, image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -0800993
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700994 delete[] gaussian;
995 delete[] scratch;
996}
997
Romain Guy694b5192010-07-21 21:33:20 -0700998}; // namespace uirenderer
999}; // namespace android