blob: 8462307ae8a4410964bd17e992a37100b43fc4ea [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
Chet Haase9a824562011-12-16 15:44:59 -080088void Font::invalidateTextureCache(CacheTextureLine *cacheLine) {
Romain Guy694b5192010-07-21 21:33:20 -070089 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
Chet Haase9a824562011-12-16 15:44:59 -080090 CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i);
91 if (cacheLine == NULL || cachedGlyph->mCachedTextureLine == cacheLine) {
92 cachedGlyph->mIsValid = false;
93 }
Romain Guy694b5192010-07-21 21:33:20 -070094 }
95}
96
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070097void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds) {
98 int nPenX = x + glyph->mBitmapLeft;
99 int nPenY = y + glyph->mBitmapTop;
100
101 int width = (int) glyph->mBitmapWidth;
102 int height = (int) glyph->mBitmapHeight;
103
Romain Guy61c8c9c2010-08-09 20:48:09 -0700104 if (bounds->bottom > nPenY) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700105 bounds->bottom = nPenY;
106 }
Romain Guy61c8c9c2010-08-09 20:48:09 -0700107 if (bounds->left > nPenX) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700108 bounds->left = nPenX;
109 }
Romain Guy61c8c9c2010-08-09 20:48:09 -0700110 if (bounds->right < nPenX + width) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700111 bounds->right = nPenX + width;
112 }
Romain Guy61c8c9c2010-08-09 20:48:09 -0700113 if (bounds->top < nPenY + height) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700114 bounds->top = nPenY + height;
115 }
116}
117
Romain Guy694b5192010-07-21 21:33:20 -0700118void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
Romain Guy694b5192010-07-21 21:33:20 -0700119 int nPenX = x + glyph->mBitmapLeft;
120 int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
121
Romain Guy51769a62010-07-23 00:28:00 -0700122 float u1 = glyph->mBitmapMinU;
123 float u2 = glyph->mBitmapMaxU;
124 float v1 = glyph->mBitmapMinV;
125 float v2 = glyph->mBitmapMaxV;
126
127 int width = (int) glyph->mBitmapWidth;
128 int height = (int) glyph->mBitmapHeight;
129
Romain Guyd71dd362011-12-12 19:03:35 -0800130 mState->appendMeshQuad(nPenX, nPenY, u1, v2,
131 nPenX + width, nPenY, u2, v2,
132 nPenX + width, nPenY - height, u2, v1,
Chet Haase7de0cb12011-12-05 16:35:38 -0800133 nPenX, nPenY - height, u1, v1, glyph->mCachedTextureLine->mCacheTexture);
Romain Guy694b5192010-07-21 21:33:20 -0700134}
135
Romain Guyb45c0c92010-08-26 20:35:23 -0700136void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
137 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700138 int nPenX = x + glyph->mBitmapLeft;
139 int nPenY = y + glyph->mBitmapTop;
140
141 uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
142 uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
143
Chet Haase7de0cb12011-12-05 16:35:38 -0800144 CacheTexture *cacheTexture = glyph->mCachedTextureLine->mCacheTexture;
145 uint32_t cacheWidth = cacheTexture->mWidth;
146 const uint8_t* cacheBuffer = cacheTexture->mTexture;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700147
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700148 uint32_t cacheX = 0, cacheY = 0;
149 int32_t bX = 0, bY = 0;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700150 for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
151 for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
Romain Guyb45c0c92010-08-26 20:35:23 -0700152 if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700153 LOGE("Skipping invalid index");
154 continue;
155 }
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700156 uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
157 bitmap[bY * bitmapW + bX] = tempCol;
158 }
159 }
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700160}
161
Chet Haase7de0cb12011-12-05 16:35:38 -0800162CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700163 CachedGlyphInfo* cachedGlyph = NULL;
Romain Guy726aeba2011-06-01 14:52:00 -0700164 ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
Romain Guy1e45aae2010-08-13 19:39:53 -0700165 if (index >= 0) {
166 cachedGlyph = mCachedGlyphs.valueAt(index);
167 } else {
Romain Guy726aeba2011-06-01 14:52:00 -0700168 cachedGlyph = cacheGlyph(paint, textUnit);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700169 }
170
171 // Is the glyph still in texture cache?
172 if (!cachedGlyph->mIsValid) {
Romain Guy726aeba2011-06-01 14:52:00 -0700173 const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700174 updateGlyphCache(paint, skiaGlyph, cachedGlyph);
175 }
176
177 return cachedGlyph;
178}
179
Romain Guy726aeba2011-06-01 14:52:00 -0700180void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700181 int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
182 if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
Romain Guy726aeba2011-06-01 14:52:00 -0700183 render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700184 bitmapW, bitmapH, NULL);
185 } else {
Romain Guy726aeba2011-06-01 14:52:00 -0700186 render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
187 0, 0, NULL);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700188 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700189}
190
Romain Guy726aeba2011-06-01 14:52:00 -0700191void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700192 int numGlyphs, Rect *bounds) {
193 if (bounds == NULL) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700194 LOGE("No return rectangle provided to measure text");
195 return;
196 }
197 bounds->set(1e6, -1e6, -1e6, 1e6);
Romain Guy726aeba2011-06-01 14:52:00 -0700198 render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700199}
200
Romain Guy58ef7fb2010-09-13 12:52:37 -0700201#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16)
Romain Guy2bffd262010-09-12 17:40:02 -0700202
Romain Guy726aeba2011-06-01 14:52:00 -0700203void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700204 int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
205 uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700206 if (numGlyphs == 0 || text == NULL || len == 0) {
207 return;
208 }
209
Romain Guy5a6d3a42011-10-07 15:03:24 -0700210 float penX = x;
Romain Guy2bffd262010-09-12 17:40:02 -0700211 int penY = y;
Romain Guy694b5192010-07-21 21:33:20 -0700212 int glyphsLeft = 1;
213 if (numGlyphs > 0) {
214 glyphsLeft = numGlyphs;
215 }
216
Romain Guy2bffd262010-09-12 17:40:02 -0700217 SkFixed prevRsbDelta = 0;
Romain Guy5a6d3a42011-10-07 15:03:24 -0700218 penX += 0.5f;
Romain Guy2bffd262010-09-12 17:40:02 -0700219
Romain Guy694b5192010-07-21 21:33:20 -0700220 text += start;
221
222 while (glyphsLeft > 0) {
Romain Guy726aeba2011-06-01 14:52:00 -0700223 glyph_t glyph = GET_GLYPH(text);
Romain Guy694b5192010-07-21 21:33:20 -0700224
Romain Guy61c8c9c2010-08-09 20:48:09 -0700225 // Reached the end of the string
Romain Guy726aeba2011-06-01 14:52:00 -0700226 if (IS_END_OF_STRING(glyph)) {
Romain Guy694b5192010-07-21 21:33:20 -0700227 break;
228 }
229
Romain Guy726aeba2011-06-01 14:52:00 -0700230 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
Romain Guy5a6d3a42011-10-07 15:03:24 -0700231 penX += SkFixedToFloat(SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta));
Romain Guy2bffd262010-09-12 17:40:02 -0700232 prevRsbDelta = cachedGlyph->mRsbDelta;
Romain Guy694b5192010-07-21 21:33:20 -0700233
234 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
235 if (cachedGlyph->mIsValid) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700236 switch(mode) {
237 case FRAMEBUFFER:
Romain Guy5a6d3a42011-10-07 15:03:24 -0700238 drawCachedGlyph(cachedGlyph, (int) floorf(penX), penY);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700239 break;
240 case BITMAP:
Romain Guy5a6d3a42011-10-07 15:03:24 -0700241 drawCachedGlyph(cachedGlyph, (int) floorf(penX), penY, bitmap, bitmapW, bitmapH);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700242 break;
243 case MEASURE:
Romain Guy5a6d3a42011-10-07 15:03:24 -0700244 measureCachedGlyph(cachedGlyph, (int) floorf(penX), penY, bounds);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700245 break;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700246 }
Romain Guy694b5192010-07-21 21:33:20 -0700247 }
248
Romain Guy5a6d3a42011-10-07 15:03:24 -0700249 penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
Romain Guy694b5192010-07-21 21:33:20 -0700250
251 // If we were given a specific number of glyphs, decrement
252 if (numGlyphs > 0) {
253 glyphsLeft--;
254 }
255 }
256}
257
Romain Guy51769a62010-07-23 00:28:00 -0700258void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
Romain Guy694b5192010-07-21 21:33:20 -0700259 glyph->mAdvanceX = skiaGlyph.fAdvanceX;
260 glyph->mAdvanceY = skiaGlyph.fAdvanceY;
261 glyph->mBitmapLeft = skiaGlyph.fLeft;
262 glyph->mBitmapTop = skiaGlyph.fTop;
Romain Guy2bffd262010-09-12 17:40:02 -0700263 glyph->mLsbDelta = skiaGlyph.fLsbDelta;
264 glyph->mRsbDelta = skiaGlyph.fRsbDelta;
Romain Guy694b5192010-07-21 21:33:20 -0700265
266 uint32_t startX = 0;
267 uint32_t startY = 0;
268
Romain Guy694b5192010-07-21 21:33:20 -0700269 // Get the bitmap for the glyph
270 paint->findImage(skiaGlyph);
Chet Haase7de0cb12011-12-05 16:35:38 -0800271 mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700272
273 if (!glyph->mIsValid) {
274 return;
275 }
276
277 uint32_t endX = startX + skiaGlyph.fWidth;
278 uint32_t endY = startY + skiaGlyph.fHeight;
279
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700280 glyph->mStartX = startX;
281 glyph->mStartY = startY;
Romain Guy694b5192010-07-21 21:33:20 -0700282 glyph->mBitmapWidth = skiaGlyph.fWidth;
283 glyph->mBitmapHeight = skiaGlyph.fHeight;
284
Chet Haase7de0cb12011-12-05 16:35:38 -0800285 uint32_t cacheWidth = glyph->mCachedTextureLine->mCacheTexture->mWidth;
286 uint32_t cacheHeight = glyph->mCachedTextureLine->mCacheTexture->mHeight;
Romain Guy694b5192010-07-21 21:33:20 -0700287
288 glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
289 glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
290 glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
291 glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
292
Romain Guy51769a62010-07-23 00:28:00 -0700293 mState->mUploadTexture = true;
Romain Guy694b5192010-07-21 21:33:20 -0700294}
295
Chet Haase7de0cb12011-12-05 16:35:38 -0800296CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) {
Romain Guy51769a62010-07-23 00:28:00 -0700297 CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
Romain Guy694b5192010-07-21 21:33:20 -0700298 mCachedGlyphs.add(glyph, newGlyph);
299
Romain Guy726aeba2011-06-01 14:52:00 -0700300 const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
Romain Guy694b5192010-07-21 21:33:20 -0700301 newGlyph->mGlyphIndex = skiaGlyph.fID;
302 newGlyph->mIsValid = false;
303
304 updateGlyphCache(paint, skiaGlyph, newGlyph);
305
306 return newGlyph;
307}
308
Romain Guy2577db12011-01-18 13:02:38 -0800309Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
Romain Guybd496bc2011-08-02 17:32:41 -0700310 int flags, uint32_t italicStyle, uint32_t scaleX,
311 SkPaint::Style style, uint32_t strokeWidth) {
Romain Guy694b5192010-07-21 21:33:20 -0700312 Vector<Font*> &activeFonts = state->mActiveFonts;
313
314 for (uint32_t i = 0; i < activeFonts.size(); i++) {
Romain Guy51769a62010-07-23 00:28:00 -0700315 Font* font = activeFonts[i];
Romain Guy2577db12011-01-18 13:02:38 -0800316 if (font->mFontId == fontId && font->mFontSize == fontSize &&
Chet Haase8668f8a2011-03-02 13:51:36 -0800317 font->mFlags == flags && font->mItalicStyle == italicStyle &&
Romain Guybd496bc2011-08-02 17:32:41 -0700318 font->mScaleX == scaleX && font->mStyle == style &&
319 (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
Romain Guy51769a62010-07-23 00:28:00 -0700320 return font;
Romain Guy694b5192010-07-21 21:33:20 -0700321 }
322 }
323
Romain Guybd496bc2011-08-02 17:32:41 -0700324 Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
325 scaleX, style, strokeWidth);
Romain Guy694b5192010-07-21 21:33:20 -0700326 activeFonts.push(newFont);
327 return newFont;
328}
329
330///////////////////////////////////////////////////////////////////////////////
331// FontRenderer
332///////////////////////////////////////////////////////////////////////////////
333
Romain Guy514fb182011-01-19 14:38:29 -0800334static bool sLogFontRendererCreate = true;
335
Romain Guy694b5192010-07-21 21:33:20 -0700336FontRenderer::FontRenderer() {
Romain Guyc9855a52011-01-21 21:14:15 -0800337 if (sLogFontRendererCreate) {
338 INIT_LOGD("Creating FontRenderer");
339 }
Romain Guy51769a62010-07-23 00:28:00 -0700340
Romain Guyb45c0c92010-08-26 20:35:23 -0700341 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700342 mInitialized = false;
343 mMaxNumberOfQuads = 1024;
344 mCurrentQuadIndex = 0;
345
Romain Guy9cccc2b2010-08-07 23:46:15 -0700346 mTextMeshPtr = NULL;
Chet Haase7de0cb12011-12-05 16:35:38 -0800347 mCurrentCacheTexture = NULL;
348 mLastCacheTexture = NULL;
349 mCacheTextureSmall = NULL;
350 mCacheTexture128 = NULL;
351 mCacheTexture256 = NULL;
352 mCacheTexture512 = NULL;
Romain Guy9cccc2b2010-08-07 23:46:15 -0700353
Chet Haase2a47c142011-12-14 15:22:56 -0800354 mLinearFiltering = false;
355
Romain Guy694b5192010-07-21 21:33:20 -0700356 mIndexBufferID = 0;
357
Chet Haase7de0cb12011-12-05 16:35:38 -0800358 mSmallCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
359 mSmallCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -0700360
361 char property[PROPERTY_VALUE_MAX];
362 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800363 if (sLogFontRendererCreate) {
364 INIT_LOGD(" Setting text cache width to %s pixels", property);
365 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800366 mSmallCacheWidth = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -0700367 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800368 if (sLogFontRendererCreate) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800369 INIT_LOGD(" Using default text cache width of %i pixels", mSmallCacheWidth);
Romain Guy514fb182011-01-19 14:38:29 -0800370 }
Romain Guy51769a62010-07-23 00:28:00 -0700371 }
372
373 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800374 if (sLogFontRendererCreate) {
375 INIT_LOGD(" Setting text cache width to %s pixels", property);
376 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800377 mSmallCacheHeight = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -0700378 } else {
Romain Guy514fb182011-01-19 14:38:29 -0800379 if (sLogFontRendererCreate) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800380 INIT_LOGD(" Using default text cache height of %i pixels", mSmallCacheHeight);
Romain Guy514fb182011-01-19 14:38:29 -0800381 }
Romain Guy51769a62010-07-23 00:28:00 -0700382 }
Romain Guy514fb182011-01-19 14:38:29 -0800383
384 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -0700385}
386
387FontRenderer::~FontRenderer() {
388 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
389 delete mCacheLines[i];
390 }
391 mCacheLines.clear();
392
Romain Guy9cccc2b2010-08-07 23:46:15 -0700393 if (mInitialized) {
394 delete[] mTextMeshPtr;
Chet Haase7de0cb12011-12-05 16:35:38 -0800395 delete mCacheTextureSmall;
396 delete mCacheTexture128;
397 delete mCacheTexture256;
398 delete mCacheTexture512;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700399 }
Romain Guy694b5192010-07-21 21:33:20 -0700400
401 Vector<Font*> fontsToDereference = mActiveFonts;
402 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
403 delete fontsToDereference[i];
404 }
405}
406
407void FontRenderer::flushAllAndInvalidate() {
408 if (mCurrentQuadIndex != 0) {
409 issueDrawCommand();
410 mCurrentQuadIndex = 0;
411 }
412 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
413 mActiveFonts[i]->invalidateTextureCache();
414 }
415 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
416 mCacheLines[i]->mCurrentCol = 0;
417 }
418}
419
Chet Haase9a824562011-12-16 15:44:59 -0800420void FontRenderer::deallocateTextureMemory(CacheTexture *cacheTexture) {
421 if (cacheTexture && cacheTexture->mTexture) {
422 glDeleteTextures(1, &cacheTexture->mTextureId);
423 delete cacheTexture->mTexture;
424 cacheTexture->mTexture = NULL;
425 }
426}
427
428void FontRenderer::flushLargeCaches() {
429 if ((!mCacheTexture128 || !mCacheTexture128->mTexture) &&
430 (!mCacheTexture256 || !mCacheTexture256->mTexture) &&
431 (!mCacheTexture512 || !mCacheTexture512->mTexture)) {
432 // Typical case; no large glyph caches allocated
433 return;
434 }
435
436 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
437 CacheTextureLine* cacheLine = mCacheLines[i];
438 if ((cacheLine->mCacheTexture == mCacheTexture128 ||
439 cacheLine->mCacheTexture == mCacheTexture256 ||
440 cacheLine->mCacheTexture == mCacheTexture512) &&
441 cacheLine->mCacheTexture->mTexture != NULL) {
442 cacheLine->mCurrentCol = 0;
443 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
444 mActiveFonts[i]->invalidateTextureCache(cacheLine);
445 }
446 }
447 }
448
449 deallocateTextureMemory(mCacheTexture128);
450 deallocateTextureMemory(mCacheTexture256);
451 deallocateTextureMemory(mCacheTexture512);
452}
453
Chet Haase2a47c142011-12-14 15:22:56 -0800454void FontRenderer::allocateTextureMemory(CacheTexture *cacheTexture) {
455 int width = cacheTexture->mWidth;
456 int height = cacheTexture->mHeight;
457 cacheTexture->mTexture = new uint8_t[width * height];
458 memset(cacheTexture->mTexture, 0, width * height * sizeof(uint8_t));
459 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
460 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
461 // Initialize texture dimensions
462 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
463 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800464
Chet Haase2a47c142011-12-14 15:22:56 -0800465 const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST;
466 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
467 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
468
469 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
470 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
Chet Haase7de0cb12011-12-05 16:35:38 -0800471}
472
473void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
474 uint32_t* retOriginX, uint32_t* retOriginY) {
475 cachedGlyph->mIsValid = false;
Romain Guy694b5192010-07-21 21:33:20 -0700476 // If the glyph is too tall, don't cache it
Chet Haase7de0cb12011-12-05 16:35:38 -0800477 if (glyph.fHeight + TEXTURE_BORDER_SIZE > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
478 LOGE("Font size to large to fit in cache. width, height = %i, %i",
479 (int) glyph.fWidth, (int) glyph.fHeight);
480 return;
Romain Guy694b5192010-07-21 21:33:20 -0700481 }
482
483 // Now copy the bitmap into the cache texture
484 uint32_t startX = 0;
485 uint32_t startY = 0;
486
487 bool bitmapFit = false;
Chet Haase7de0cb12011-12-05 16:35:38 -0800488 CacheTextureLine *cacheLine;
Romain Guy694b5192010-07-21 21:33:20 -0700489 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
490 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
491 if (bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800492 cacheLine = mCacheLines[i];
Romain Guy694b5192010-07-21 21:33:20 -0700493 break;
494 }
495 }
496
497 // If the new glyph didn't fit, flush the state so far and invalidate everything
498 if (!bitmapFit) {
499 flushAllAndInvalidate();
500
501 // Try to fit it again
502 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
503 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
504 if (bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800505 cacheLine = mCacheLines[i];
Romain Guy694b5192010-07-21 21:33:20 -0700506 break;
507 }
508 }
509
510 // if we still don't fit, something is wrong and we shouldn't draw
511 if (!bitmapFit) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800512 return;
Romain Guy694b5192010-07-21 21:33:20 -0700513 }
514 }
515
Chet Haase7de0cb12011-12-05 16:35:38 -0800516 cachedGlyph->mCachedTextureLine = cacheLine;
517
Romain Guy694b5192010-07-21 21:33:20 -0700518 *retOriginX = startX;
519 *retOriginY = startY;
520
521 uint32_t endX = startX + glyph.fWidth;
522 uint32_t endY = startY + glyph.fHeight;
523
Chet Haase7de0cb12011-12-05 16:35:38 -0800524 uint32_t cacheWidth = cacheLine->mMaxWidth;
Romain Guy694b5192010-07-21 21:33:20 -0700525
Chet Haase7de0cb12011-12-05 16:35:38 -0800526 CacheTexture *cacheTexture = cacheLine->mCacheTexture;
527 if (cacheTexture->mTexture == NULL) {
528 // Large-glyph texture memory is allocated only as needed
Chet Haase2a47c142011-12-14 15:22:56 -0800529 allocateTextureMemory(cacheTexture);
Chet Haase7de0cb12011-12-05 16:35:38 -0800530 }
531 uint8_t* cacheBuffer = cacheTexture->mTexture;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700532 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700533 unsigned int stride = glyph.rowBytes();
534
535 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
536 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
537 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700538 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
Romain Guyb45c0c92010-08-26 20:35:23 -0700539 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
Romain Guy694b5192010-07-21 21:33:20 -0700540 }
541 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800542 cachedGlyph->mIsValid = true;
Romain Guy694b5192010-07-21 21:33:20 -0700543}
544
Chet Haase7de0cb12011-12-05 16:35:38 -0800545CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800546 GLuint textureId;
547 glGenTextures(1, &textureId);
Chet Haase2a47c142011-12-14 15:22:56 -0800548 uint8_t* textureMemory = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700549
Chet Haase2a47c142011-12-14 15:22:56 -0800550 CacheTexture* cacheTexture = new CacheTexture(textureMemory, textureId, width, height);
551 if (allocate) {
552 allocateTextureMemory(cacheTexture);
553 }
554 return cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800555}
556
557void FontRenderer::initTextTexture() {
558 mCacheLines.clear();
559
560 // Next, use other, separate caches for large glyphs.
561 uint16_t maxWidth = 0;
562 if (Caches::hasInstance()) {
563 maxWidth = Caches::getInstance().maxTextureSize;
Chet Haase44984ea2011-05-19 13:50:47 -0700564 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800565 if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) {
566 maxWidth = MAX_TEXT_CACHE_WIDTH;
567 }
568 if (mCacheTextureSmall != NULL) {
569 delete mCacheTextureSmall;
570 delete mCacheTexture128;
571 delete mCacheTexture256;
572 delete mCacheTexture512;
573 }
574 mCacheTextureSmall = createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true);
575 mCacheTexture128 = createCacheTexture(maxWidth, 256, false);
576 mCacheTexture256 = createCacheTexture(maxWidth, 256, false);
577 mCacheTexture512 = createCacheTexture(maxWidth, 512, false);
578 mCurrentCacheTexture = mCacheTextureSmall;
579
580 mUploadTexture = false;
581 // Split up our default cache texture into lines of certain widths
582 int nextLine = 0;
583 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 18, nextLine, 0, mCacheTextureSmall));
584 nextLine += mCacheLines.top()->mMaxHeight;
585 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
586 nextLine += mCacheLines.top()->mMaxHeight;
587 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
588 nextLine += mCacheLines.top()->mMaxHeight;
589 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
590 nextLine += mCacheLines.top()->mMaxHeight;
591 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
592 nextLine += mCacheLines.top()->mMaxHeight;
593 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 42, nextLine, 0, mCacheTextureSmall));
594 nextLine += mCacheLines.top()->mMaxHeight;
595 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, mSmallCacheHeight - nextLine,
596 nextLine, 0, mCacheTextureSmall));
597
598 // The first cache is split into 2 lines of height 128, the rest have just one cache line.
599 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 0, 0, mCacheTexture128));
600 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 128, 0, mCacheTexture128));
601 mCacheLines.push(new CacheTextureLine(maxWidth, 256, 0, 0, mCacheTexture256));
602 mCacheLines.push(new CacheTextureLine(maxWidth, 512, 0, 0, mCacheTexture512));
Romain Guy694b5192010-07-21 21:33:20 -0700603}
604
605// Avoid having to reallocate memory and render quad by quad
606void FontRenderer::initVertexArrayBuffers() {
Romain Guyd71dd362011-12-12 19:03:35 -0800607 uint32_t numIndices = mMaxNumberOfQuads * 6;
608 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700609 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700610
611 // Four verts, two triangles , six indices per quad
612 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
613 int i6 = i * 6;
614 int i4 = i * 4;
615
616 indexBufferData[i6 + 0] = i4 + 0;
617 indexBufferData[i6 + 1] = i4 + 1;
618 indexBufferData[i6 + 2] = i4 + 2;
619
620 indexBufferData[i6 + 3] = i4 + 0;
621 indexBufferData[i6 + 4] = i4 + 2;
622 indexBufferData[i6 + 5] = i4 + 3;
623 }
624
625 glGenBuffers(1, &mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800626 Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700627 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
Romain Guy694b5192010-07-21 21:33:20 -0700628
629 free(indexBufferData);
630
Romain Guyd71dd362011-12-12 19:03:35 -0800631 uint32_t coordSize = 2;
Romain Guy694b5192010-07-21 21:33:20 -0700632 uint32_t uvSize = 2;
633 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700634 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
635 mTextMeshPtr = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700636}
637
638// We don't want to allocate anything unless we actually draw text
639void FontRenderer::checkInit() {
640 if (mInitialized) {
641 return;
642 }
643
644 initTextTexture();
645 initVertexArrayBuffers();
646
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700647 // We store a string with letters in a rough frequency of occurrence
648 mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
649 mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
650 mLatinPrecache += String16(",.?!()-+@;:`'");
651 mLatinPrecache += String16("0123456789");
652
Romain Guy694b5192010-07-21 21:33:20 -0700653 mInitialized = true;
654}
655
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700656void FontRenderer::checkTextureUpdate() {
Chet Haase7de0cb12011-12-05 16:35:38 -0800657 if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700658 return;
Romain Guy694b5192010-07-21 21:33:20 -0700659 }
660
Romain Guy2d4fd362011-12-13 22:00:19 -0800661 Caches& caches = Caches::getInstance();
662 GLuint lastTextureId = 0;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700663 // Iterate over all the cache lines and see which ones need to be updated
664 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
665 CacheTextureLine* cl = mCacheLines[i];
Chet Haase7de0cb12011-12-05 16:35:38 -0800666 if (cl->mDirty && cl->mCacheTexture->mTexture != NULL) {
667 CacheTexture* cacheTexture = cl->mCacheTexture;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700668 uint32_t xOffset = 0;
669 uint32_t yOffset = cl->mCurrentRow;
Chet Haase7de0cb12011-12-05 16:35:38 -0800670 uint32_t width = cl->mMaxWidth;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700671 uint32_t height = cl->mMaxHeight;
Chet Haase7de0cb12011-12-05 16:35:38 -0800672 void* textureData = cacheTexture->mTexture + (yOffset * width);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700673
Romain Guy2d4fd362011-12-13 22:00:19 -0800674 if (cacheTexture->mTextureId != lastTextureId) {
675 caches.activeTexture(0);
676 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
677 lastTextureId = cacheTexture->mTextureId;
678 }
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700679 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700680 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700681
682 cl->mDirty = false;
683 }
684 }
685
Chet Haase7de0cb12011-12-05 16:35:38 -0800686 glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
Chet Haase2a47c142011-12-14 15:22:56 -0800687 if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) {
688 const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST;
689 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
690 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
691 mCurrentCacheTexture->mLinearFiltering = mLinearFiltering;
692 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800693 mLastCacheTexture = mCurrentCacheTexture;
694
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700695 mUploadTexture = false;
696}
697
698void FontRenderer::issueDrawCommand() {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700699 checkTextureUpdate();
700
Romain Guy15bc6432011-12-13 13:11:32 -0800701 Caches& caches = Caches::getInstance();
Romain Guy2d4fd362011-12-13 22:00:19 -0800702 caches.bindIndicesBuffer(mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800703 if (!mDrawn) {
704 float* buffer = mTextMeshPtr;
705 int offset = 2;
706
707 bool force = caches.unbindMeshBuffer();
708 caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
709 caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
710 buffer + offset);
711 }
712
Romain Guy694b5192010-07-21 21:33:20 -0700713 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
Romain Guy5b3b3522010-10-27 18:57:51 -0700714
715 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700716}
717
Romain Guyd71dd362011-12-12 19:03:35 -0800718void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
719 float x2, float y2, float u2, float v2,
720 float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800721 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Romain Guyd71dd362011-12-12 19:03:35 -0800722
Romain Guyff98fa52011-11-28 09:35:09 -0800723 if (mClip &&
724 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
Romain Guy09147fb2010-07-22 13:08:20 -0700725 return;
726 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800727 if (texture != mCurrentCacheTexture) {
728 if (mCurrentQuadIndex != 0) {
729 // First, draw everything stored already which uses the previous texture
730 issueDrawCommand();
731 mCurrentQuadIndex = 0;
732 }
733 // Now use the new texture id
734 mCurrentCacheTexture = texture;
735 }
Romain Guy09147fb2010-07-22 13:08:20 -0700736
Romain Guy694b5192010-07-21 21:33:20 -0700737 const uint32_t vertsPerQuad = 4;
Romain Guyd71dd362011-12-12 19:03:35 -0800738 const uint32_t floatsPerVert = 4;
Romain Guy51769a62010-07-23 00:28:00 -0700739 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700740
Romain Guy694b5192010-07-21 21:33:20 -0700741 (*currentPos++) = x1;
742 (*currentPos++) = y1;
Romain Guy694b5192010-07-21 21:33:20 -0700743 (*currentPos++) = u1;
744 (*currentPos++) = v1;
745
746 (*currentPos++) = x2;
747 (*currentPos++) = y2;
Romain Guy694b5192010-07-21 21:33:20 -0700748 (*currentPos++) = u2;
749 (*currentPos++) = v2;
750
751 (*currentPos++) = x3;
752 (*currentPos++) = y3;
Romain Guy694b5192010-07-21 21:33:20 -0700753 (*currentPos++) = u3;
754 (*currentPos++) = v3;
755
756 (*currentPos++) = x4;
757 (*currentPos++) = y4;
Romain Guy694b5192010-07-21 21:33:20 -0700758 (*currentPos++) = u4;
759 (*currentPos++) = v4;
760
761 mCurrentQuadIndex++;
762
Romain Guy5b3b3522010-10-27 18:57:51 -0700763 if (mBounds) {
764 mBounds->left = fmin(mBounds->left, x1);
765 mBounds->top = fmin(mBounds->top, y3);
766 mBounds->right = fmax(mBounds->right, x3);
767 mBounds->bottom = fmax(mBounds->bottom, y1);
768 }
769
Romain Guy694b5192010-07-21 21:33:20 -0700770 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
771 issueDrawCommand();
772 mCurrentQuadIndex = 0;
773 }
774}
775
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700776uint32_t FontRenderer::getRemainingCacheCapacity() {
777 uint32_t remainingCapacity = 0;
778 float totalPixels = 0;
779 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
780 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
781 totalPixels += mCacheLines[i]->mMaxWidth;
782 }
783 remainingCapacity = (remainingCapacity * 100) / totalPixels;
784 return remainingCapacity;
785}
786
787void FontRenderer::precacheLatin(SkPaint* paint) {
788 // Remaining capacity is measured in %
789 uint32_t remainingCapacity = getRemainingCacheCapacity();
790 uint32_t precacheIdx = 0;
Romain Guy054dc182010-10-15 17:55:25 -0700791 while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
Romain Guy726aeba2011-06-01 14:52:00 -0700792 mCurrentFont->getCachedGlyph(paint, (int32_t) mLatinPrecache[precacheIdx]);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700793 remainingCapacity = getRemainingCacheCapacity();
794 precacheIdx ++;
795 }
796}
797
798void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
799 uint32_t currentNumFonts = mActiveFonts.size();
Romain Guy325a0f92011-01-05 15:26:55 -0800800 int flags = 0;
801 if (paint->isFakeBoldText()) {
802 flags |= Font::kFakeBold;
803 }
Romain Guy2577db12011-01-18 13:02:38 -0800804
805 const float skewX = paint->getTextSkewX();
806 uint32_t italicStyle = *(uint32_t*) &skewX;
Chet Haase8668f8a2011-03-02 13:51:36 -0800807 const float scaleXFloat = paint->getTextScaleX();
808 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
Romain Guybd496bc2011-08-02 17:32:41 -0700809 SkPaint::Style style = paint->getStyle();
810 const float strokeWidthFloat = paint->getStrokeWidth();
811 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
812 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
813 scaleX, style, strokeWidth);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700814
815 const float maxPrecacheFontSize = 40.0f;
816 bool isNewFont = currentNumFonts != mActiveFonts.size();
817
Romain Guy2bffd262010-09-12 17:40:02 -0700818 if (isNewFont && fontSize <= maxPrecacheFontSize) {
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700819 precacheLatin(paint);
820 }
Romain Guy694b5192010-07-21 21:33:20 -0700821}
Romain Guy7975fb62010-10-01 16:36:14 -0700822
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700823FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Romain Guy1e45aae2010-08-13 19:39:53 -0700824 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
825 checkInit();
826
827 if (!mCurrentFont) {
828 DropShadow image;
829 image.width = 0;
830 image.height = 0;
831 image.image = NULL;
832 image.penX = 0;
833 image.penY = 0;
834 return image;
835 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700836
Romain Guy2d4fd362011-12-13 22:00:19 -0800837 mDrawn = false;
Romain Guyff98fa52011-11-28 09:35:09 -0800838 mClip = NULL;
839 mBounds = NULL;
840
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700841 Rect bounds;
Romain Guy726aeba2011-06-01 14:52:00 -0700842 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds);
Romain Guyff98fa52011-11-28 09:35:09 -0800843
Romain Guy1e45aae2010-08-13 19:39:53 -0700844 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
845 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700846 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guyff98fa52011-11-28 09:35:09 -0800847
Romain Guy1e45aae2010-08-13 19:39:53 -0700848 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700849 dataBuffer[i] = 0;
850 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700851
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700852 int penX = radius - bounds.left;
853 int penY = radius - bounds.bottom;
854
Romain Guy726aeba2011-06-01 14:52:00 -0700855 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
Romain Guy1e45aae2010-08-13 19:39:53 -0700856 dataBuffer, paddedWidth, paddedHeight);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700857 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
858
859 DropShadow image;
860 image.width = paddedWidth;
861 image.height = paddedHeight;
862 image.image = dataBuffer;
863 image.penX = penX;
864 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -0800865
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700866 return image;
867}
Romain Guy694b5192010-07-21 21:33:20 -0700868
Romain Guy5b3b3522010-10-27 18:57:51 -0700869bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
870 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700871 checkInit();
872
Romain Guy09147fb2010-07-22 13:08:20 -0700873 if (!mCurrentFont) {
874 LOGE("No font set");
Romain Guy5b3b3522010-10-27 18:57:51 -0700875 return false;
Romain Guy694b5192010-07-21 21:33:20 -0700876 }
877
Romain Guy5b3b3522010-10-27 18:57:51 -0700878 mDrawn = false;
879 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -0700880 mClip = clip;
Romain Guyff98fa52011-11-28 09:35:09 -0800881
Romain Guy726aeba2011-06-01 14:52:00 -0700882 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
Romain Guyff98fa52011-11-28 09:35:09 -0800883
Romain Guy5b3b3522010-10-27 18:57:51 -0700884 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -0800885 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700886
887 if (mCurrentQuadIndex != 0) {
888 issueDrawCommand();
889 mCurrentQuadIndex = 0;
890 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700891
892 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700893}
894
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700895void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
896 // Compute gaussian weights for the blur
897 // e is the euler's number
898 float e = 2.718281828459045f;
899 float pi = 3.1415926535897932f;
900 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
901 // x is of the form [-radius .. 0 .. radius]
902 // and sigma varies with radius.
903 // Based on some experimental radius values and sigma's
904 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700905 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700906 // The larger the radius gets, the more our gaussian blur
907 // will resemble a box blur since with large sigma
908 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -0800909 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700910
911 // Now compute the coefficints
912 // We will store some redundant values to save some math during
913 // the blur calculations
914 // precompute some values
915 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
916 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
917
918 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -0800919 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700920 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700921 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
922 normalizeFactor += weights[r + radius];
923 }
924
925 //Now we need to normalize the weights because all our coefficients need to add up to one
926 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -0800927 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700928 weights[r + radius] *= normalizeFactor;
929 }
930}
931
932void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700933 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700934 float blurredPixel = 0.0f;
935 float currentPixel = 0.0f;
936
Romain Guy325a0f92011-01-05 15:26:55 -0800937 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700938
939 const uint8_t* input = source + y * width;
940 uint8_t* output = dest + y * width;
941
Romain Guy325a0f92011-01-05 15:26:55 -0800942 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700943 blurredPixel = 0.0f;
944 const float* gPtr = weights;
945 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800946 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700947 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -0800948 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700949 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700950 blurredPixel += currentPixel * gPtr[0];
951 gPtr++;
952 i++;
953 }
954 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800955 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700956 // Stepping left and right away from the pixel
957 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -0800958 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700959 validW = 0;
960 }
Romain Guy325a0f92011-01-05 15:26:55 -0800961 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700962 validW = width - 1;
963 }
964
Romain Guy325a0f92011-01-05 15:26:55 -0800965 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700966 blurredPixel += currentPixel * gPtr[0];
967 gPtr++;
968 }
969 }
970 *output = (uint8_t)blurredPixel;
971 output ++;
972 }
973 }
974}
975
976void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700977 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700978 float blurredPixel = 0.0f;
979 float currentPixel = 0.0f;
980
Romain Guy325a0f92011-01-05 15:26:55 -0800981 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700982
983 uint8_t* output = dest + y * width;
984
Romain Guy325a0f92011-01-05 15:26:55 -0800985 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700986 blurredPixel = 0.0f;
987 const float* gPtr = weights;
988 const uint8_t* input = source + x;
989 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800990 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700991 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -0800992 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700993 currentPixel = (float)(*i);
994 blurredPixel += currentPixel * gPtr[0];
995 gPtr++;
996 i += width;
997 }
998 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800999 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001000 int validH = y + r;
1001 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -08001002 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001003 validH = 0;
1004 }
Romain Guy325a0f92011-01-05 15:26:55 -08001005 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001006 validH = height - 1;
1007 }
1008
1009 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -08001010 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001011 blurredPixel += currentPixel * gPtr[0];
1012 gPtr++;
1013 }
1014 }
Romain Guy325a0f92011-01-05 15:26:55 -08001015 *output = (uint8_t) blurredPixel;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001016 output ++;
1017 }
1018 }
1019}
1020
1021
1022void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
1023 float *gaussian = new float[2 * radius + 1];
1024 computeGaussianWeights(gaussian, radius);
Romain Guyd71dd362011-12-12 19:03:35 -08001025
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001026 uint8_t* scratch = new uint8_t[width * height];
Romain Guyd71dd362011-12-12 19:03:35 -08001027
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001028 horizontalBlur(gaussian, radius, image, scratch, width, height);
1029 verticalBlur(gaussian, radius, scratch, image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -08001030
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -07001031 delete[] gaussian;
1032 delete[] scratch;
1033}
1034
Romain Guy694b5192010-07-21 21:33:20 -07001035}; // namespace uirenderer
1036}; // namespace android