blob: 2959814984dfa71a261567d74788c1be132124e6 [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>
22#include <utils/Log.h>
23
24#include "FontRenderer.h"
25
Romain Guy694b5192010-07-21 21:33:20 -070026namespace android {
27namespace uirenderer {
28
29///////////////////////////////////////////////////////////////////////////////
Romain Guy51769a62010-07-23 00:28:00 -070030// Defines
31///////////////////////////////////////////////////////////////////////////////
32
33#define DEFAULT_TEXT_CACHE_WIDTH 1024
34#define DEFAULT_TEXT_CACHE_HEIGHT 256
35
36///////////////////////////////////////////////////////////////////////////////
Romain Guy694b5192010-07-21 21:33:20 -070037// Font
38///////////////////////////////////////////////////////////////////////////////
39
40Font::Font(FontRenderer* state, uint32_t fontId, float fontSize) :
41 mState(state), mFontId(fontId), mFontSize(fontSize) {
42}
43
44
45Font::~Font() {
46 for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
47 if (mState->mActiveFonts[ct] == this) {
48 mState->mActiveFonts.removeAt(ct);
49 break;
50 }
51 }
52
53 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
Romain Guy51769a62010-07-23 00:28:00 -070054 CachedGlyphInfo* glyph = mCachedGlyphs.valueAt(i);
Romain Guy694b5192010-07-21 21:33:20 -070055 delete glyph;
56 }
57}
58
59void Font::invalidateTextureCache() {
60 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
61 mCachedGlyphs.valueAt(i)->mIsValid = false;
62 }
63}
64
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070065void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds) {
66 int nPenX = x + glyph->mBitmapLeft;
67 int nPenY = y + glyph->mBitmapTop;
68
69 int width = (int) glyph->mBitmapWidth;
70 int height = (int) glyph->mBitmapHeight;
71
Romain Guy61c8c9c2010-08-09 20:48:09 -070072 if (bounds->bottom > nPenY) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070073 bounds->bottom = nPenY;
74 }
Romain Guy61c8c9c2010-08-09 20:48:09 -070075 if (bounds->left > nPenX) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070076 bounds->left = nPenX;
77 }
Romain Guy61c8c9c2010-08-09 20:48:09 -070078 if (bounds->right < nPenX + width) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070079 bounds->right = nPenX + width;
80 }
Romain Guy61c8c9c2010-08-09 20:48:09 -070081 if (bounds->top < nPenY + height) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -070082 bounds->top = nPenY + height;
83 }
84}
85
Romain Guy694b5192010-07-21 21:33:20 -070086void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
Romain Guy694b5192010-07-21 21:33:20 -070087 int nPenX = x + glyph->mBitmapLeft;
88 int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
89
Romain Guy51769a62010-07-23 00:28:00 -070090 float u1 = glyph->mBitmapMinU;
91 float u2 = glyph->mBitmapMaxU;
92 float v1 = glyph->mBitmapMinV;
93 float v2 = glyph->mBitmapMaxV;
94
95 int width = (int) glyph->mBitmapWidth;
96 int height = (int) glyph->mBitmapHeight;
97
98 mState->appendMeshQuad(nPenX, nPenY, 0, u1, v2,
99 nPenX + width, nPenY, 0, u2, v2,
100 nPenX + width, nPenY - height, 0, u2, v1,
101 nPenX, nPenY - height, 0, u1, v1);
Romain Guy694b5192010-07-21 21:33:20 -0700102}
103
Romain Guyb45c0c92010-08-26 20:35:23 -0700104void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
105 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700106 int nPenX = x + glyph->mBitmapLeft;
107 int nPenY = y + glyph->mBitmapTop;
108
109 uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
110 uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
111
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700112 uint32_t cacheWidth = mState->getCacheWidth();
113 const uint8_t* cacheBuffer = mState->getTextTextureData();
114
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700115 uint32_t cacheX = 0, cacheY = 0;
116 int32_t bX = 0, bY = 0;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700117 for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
118 for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
Romain Guyb45c0c92010-08-26 20:35:23 -0700119 if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700120 LOGE("Skipping invalid index");
121 continue;
122 }
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700123 uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
124 bitmap[bY * bitmapW + bX] = tempCol;
125 }
126 }
127
128}
129
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700130Font::CachedGlyphInfo* Font::getCachedUTFChar(SkPaint* paint, int32_t utfChar) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700131 CachedGlyphInfo* cachedGlyph = NULL;
132 ssize_t index = mCachedGlyphs.indexOfKey(utfChar);
133 if (index >= 0) {
134 cachedGlyph = mCachedGlyphs.valueAt(index);
135 } else {
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700136 cachedGlyph = cacheGlyph(paint, utfChar);
137 }
138
139 // Is the glyph still in texture cache?
140 if (!cachedGlyph->mIsValid) {
141 const SkGlyph& skiaGlyph = paint->getUnicharMetrics(utfChar);
142 updateGlyphCache(paint, skiaGlyph, cachedGlyph);
143 }
144
145 return cachedGlyph;
146}
147
Romain Guy51769a62010-07-23 00:28:00 -0700148void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700149 int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
150 if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
151 renderUTF(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
152 bitmapW, bitmapH, NULL);
153 } else {
154 renderUTF(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, 0, 0, NULL);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700155 }
156
157}
158
159void Font::measureUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700160 int numGlyphs, Rect *bounds) {
161 if (bounds == NULL) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700162 LOGE("No return rectangle provided to measure text");
163 return;
164 }
165 bounds->set(1e6, -1e6, -1e6, 1e6);
166 renderUTF(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds);
167}
168
Romain Guy58ef7fb2010-09-13 12:52:37 -0700169#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16)
Romain Guy2bffd262010-09-12 17:40:02 -0700170
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700171void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700172 int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
173 uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700174 if (numGlyphs == 0 || text == NULL || len == 0) {
175 return;
176 }
177
Romain Guy2bffd262010-09-12 17:40:02 -0700178 SkFixed penX = SkIntToFixed(x);
179 int penY = y;
Romain Guy694b5192010-07-21 21:33:20 -0700180 int glyphsLeft = 1;
181 if (numGlyphs > 0) {
182 glyphsLeft = numGlyphs;
183 }
184
Romain Guy2bffd262010-09-12 17:40:02 -0700185 SkFixed prevRsbDelta = 0;
186 penX += SK_Fixed1 / 2;
187
Romain Guy694b5192010-07-21 21:33:20 -0700188 text += start;
189
190 while (glyphsLeft > 0) {
Romain Guy694b5192010-07-21 21:33:20 -0700191 int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text);
192
Romain Guy61c8c9c2010-08-09 20:48:09 -0700193 // Reached the end of the string
Romain Guy694b5192010-07-21 21:33:20 -0700194 if (utfChar < 0) {
195 break;
196 }
197
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700198 CachedGlyphInfo* cachedGlyph = getCachedUTFChar(paint, utfChar);
Romain Guy2bffd262010-09-12 17:40:02 -0700199 penX += SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta);
200 prevRsbDelta = cachedGlyph->mRsbDelta;
Romain Guy694b5192010-07-21 21:33:20 -0700201
202 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
203 if (cachedGlyph->mIsValid) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700204 switch(mode) {
205 case FRAMEBUFFER:
Romain Guy2bffd262010-09-12 17:40:02 -0700206 drawCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700207 break;
208 case BITMAP:
Romain Guy2bffd262010-09-12 17:40:02 -0700209 drawCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY, bitmap, bitmapW, bitmapH);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700210 break;
211 case MEASURE:
Romain Guy2bffd262010-09-12 17:40:02 -0700212 measureCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY, bounds);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700213 break;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700214 }
Romain Guy694b5192010-07-21 21:33:20 -0700215 }
216
Romain Guy2bffd262010-09-12 17:40:02 -0700217 penX += cachedGlyph->mAdvanceX;
Romain Guy694b5192010-07-21 21:33:20 -0700218
219 // If we were given a specific number of glyphs, decrement
220 if (numGlyphs > 0) {
221 glyphsLeft--;
222 }
223 }
224}
225
Romain Guy51769a62010-07-23 00:28:00 -0700226void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
Romain Guy694b5192010-07-21 21:33:20 -0700227 glyph->mAdvanceX = skiaGlyph.fAdvanceX;
228 glyph->mAdvanceY = skiaGlyph.fAdvanceY;
229 glyph->mBitmapLeft = skiaGlyph.fLeft;
230 glyph->mBitmapTop = skiaGlyph.fTop;
Romain Guy2bffd262010-09-12 17:40:02 -0700231 glyph->mLsbDelta = skiaGlyph.fLsbDelta;
232 glyph->mRsbDelta = skiaGlyph.fRsbDelta;
Romain Guy694b5192010-07-21 21:33:20 -0700233
234 uint32_t startX = 0;
235 uint32_t startY = 0;
236
Romain Guy694b5192010-07-21 21:33:20 -0700237 // Get the bitmap for the glyph
238 paint->findImage(skiaGlyph);
Romain Guy51769a62010-07-23 00:28:00 -0700239 glyph->mIsValid = mState->cacheBitmap(skiaGlyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700240
241 if (!glyph->mIsValid) {
242 return;
243 }
244
245 uint32_t endX = startX + skiaGlyph.fWidth;
246 uint32_t endY = startY + skiaGlyph.fHeight;
247
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700248 glyph->mStartX = startX;
249 glyph->mStartY = startY;
Romain Guy694b5192010-07-21 21:33:20 -0700250 glyph->mBitmapWidth = skiaGlyph.fWidth;
251 glyph->mBitmapHeight = skiaGlyph.fHeight;
252
Romain Guy51769a62010-07-23 00:28:00 -0700253 uint32_t cacheWidth = mState->getCacheWidth();
254 uint32_t cacheHeight = mState->getCacheHeight();
Romain Guy694b5192010-07-21 21:33:20 -0700255
256 glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
257 glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
258 glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
259 glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
260
Romain Guy51769a62010-07-23 00:28:00 -0700261 mState->mUploadTexture = true;
Romain Guy694b5192010-07-21 21:33:20 -0700262}
263
Romain Guy51769a62010-07-23 00:28:00 -0700264Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, int32_t glyph) {
265 CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
Romain Guy694b5192010-07-21 21:33:20 -0700266 mCachedGlyphs.add(glyph, newGlyph);
267
268 const SkGlyph& skiaGlyph = paint->getUnicharMetrics(glyph);
269 newGlyph->mGlyphIndex = skiaGlyph.fID;
270 newGlyph->mIsValid = false;
271
272 updateGlyphCache(paint, skiaGlyph, newGlyph);
273
274 return newGlyph;
275}
276
277Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize) {
278 Vector<Font*> &activeFonts = state->mActiveFonts;
279
280 for (uint32_t i = 0; i < activeFonts.size(); i++) {
Romain Guy51769a62010-07-23 00:28:00 -0700281 Font* font = activeFonts[i];
282 if (font->mFontId == fontId && font->mFontSize == fontSize) {
283 return font;
Romain Guy694b5192010-07-21 21:33:20 -0700284 }
285 }
286
287 Font* newFont = new Font(state, fontId, fontSize);
288 activeFonts.push(newFont);
289 return newFont;
290}
291
292///////////////////////////////////////////////////////////////////////////////
293// FontRenderer
294///////////////////////////////////////////////////////////////////////////////
295
296FontRenderer::FontRenderer() {
Romain Guy51769a62010-07-23 00:28:00 -0700297 LOGD("Creating FontRenderer");
298
Romain Guyb45c0c92010-08-26 20:35:23 -0700299 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700300 mInitialized = false;
301 mMaxNumberOfQuads = 1024;
302 mCurrentQuadIndex = 0;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700303 mTextureId = 0;
Romain Guy694b5192010-07-21 21:33:20 -0700304
Romain Guy9cccc2b2010-08-07 23:46:15 -0700305 mTextMeshPtr = NULL;
306 mTextTexture = NULL;
307
Romain Guy694b5192010-07-21 21:33:20 -0700308 mIndexBufferID = 0;
309
Romain Guy51769a62010-07-23 00:28:00 -0700310 mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700311 mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -0700312
313 char property[PROPERTY_VALUE_MAX];
314 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
315 LOGD(" Setting text cache width to %s pixels", property);
316 mCacheWidth = atoi(property);
317 } else {
318 LOGD(" Using default text cache width of %i pixels", mCacheWidth);
319 }
320
321 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
322 LOGD(" Setting text cache width to %s pixels", property);
323 mCacheHeight = atoi(property);
324 } else {
325 LOGD(" Using default text cache height of %i pixels", mCacheHeight);
326 }
Romain Guy694b5192010-07-21 21:33:20 -0700327}
328
329FontRenderer::~FontRenderer() {
330 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
331 delete mCacheLines[i];
332 }
333 mCacheLines.clear();
334
Romain Guy9cccc2b2010-08-07 23:46:15 -0700335 if (mInitialized) {
336 delete[] mTextMeshPtr;
337 delete[] mTextTexture;
338 }
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700339
Romain Guy9cccc2b2010-08-07 23:46:15 -0700340 if (mTextureId) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700341 glDeleteTextures(1, &mTextureId);
342 }
Romain Guy694b5192010-07-21 21:33:20 -0700343
344 Vector<Font*> fontsToDereference = mActiveFonts;
345 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
346 delete fontsToDereference[i];
347 }
348}
349
350void FontRenderer::flushAllAndInvalidate() {
351 if (mCurrentQuadIndex != 0) {
352 issueDrawCommand();
353 mCurrentQuadIndex = 0;
354 }
355 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
356 mActiveFonts[i]->invalidateTextureCache();
357 }
358 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
359 mCacheLines[i]->mCurrentCol = 0;
360 }
361}
362
Romain Guy51769a62010-07-23 00:28:00 -0700363bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
Romain Guy694b5192010-07-21 21:33:20 -0700364 // If the glyph is too tall, don't cache it
Romain Guy2bffd262010-09-12 17:40:02 -0700365 if (glyph.fHeight > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
Romain Guy694b5192010-07-21 21:33:20 -0700366 LOGE("Font size to large to fit in cache. width, height = %i, %i",
367 (int) glyph.fWidth, (int) glyph.fHeight);
368 return false;
369 }
370
371 // Now copy the bitmap into the cache texture
372 uint32_t startX = 0;
373 uint32_t startY = 0;
374
375 bool bitmapFit = false;
376 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
377 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
378 if (bitmapFit) {
379 break;
380 }
381 }
382
383 // If the new glyph didn't fit, flush the state so far and invalidate everything
384 if (!bitmapFit) {
385 flushAllAndInvalidate();
386
387 // Try to fit it again
388 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
389 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
390 if (bitmapFit) {
391 break;
392 }
393 }
394
395 // if we still don't fit, something is wrong and we shouldn't draw
396 if (!bitmapFit) {
397 LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
398 (int) glyph.fWidth, (int) glyph.fHeight);
399 return false;
400 }
401 }
402
403 *retOriginX = startX;
404 *retOriginY = startY;
405
406 uint32_t endX = startX + glyph.fWidth;
407 uint32_t endY = startY + glyph.fHeight;
408
409 uint32_t cacheWidth = mCacheWidth;
410
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700411 uint8_t* cacheBuffer = mTextTexture;
412 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700413 unsigned int stride = glyph.rowBytes();
414
415 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
416 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
417 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700418 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
Romain Guyb45c0c92010-08-26 20:35:23 -0700419 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
Romain Guy694b5192010-07-21 21:33:20 -0700420 }
421 }
422
423 return true;
424}
425
426void FontRenderer::initTextTexture() {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700427 mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
Romain Guy694b5192010-07-21 21:33:20 -0700428 mUploadTexture = false;
429
430 glGenTextures(1, &mTextureId);
431 glBindTexture(GL_TEXTURE_2D, mTextureId);
432 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700433 // Initialize texture dimentions
434 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700435 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Romain Guy694b5192010-07-21 21:33:20 -0700436
437 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
438 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
439
440 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
441 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
442
443 // Split up our cache texture into lines of certain widths
444 int nextLine = 0;
Romain Guy51769a62010-07-23 00:28:00 -0700445 mCacheLines.push(new CacheTextureLine(mCacheWidth, 16, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700446 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700447 mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700448 nextLine += mCacheLines.top()->mMaxHeight;
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700449 mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0));
450 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700451 mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700452 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700453 mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700454 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700455 mCacheLines.push(new CacheTextureLine(mCacheWidth, 40, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700456 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700457 mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700458}
459
460// Avoid having to reallocate memory and render quad by quad
461void FontRenderer::initVertexArrayBuffers() {
462 uint32_t numIndicies = mMaxNumberOfQuads * 6;
463 uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700464 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700465
466 // Four verts, two triangles , six indices per quad
467 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
468 int i6 = i * 6;
469 int i4 = i * 4;
470
471 indexBufferData[i6 + 0] = i4 + 0;
472 indexBufferData[i6 + 1] = i4 + 1;
473 indexBufferData[i6 + 2] = i4 + 2;
474
475 indexBufferData[i6 + 3] = i4 + 0;
476 indexBufferData[i6 + 4] = i4 + 2;
477 indexBufferData[i6 + 5] = i4 + 3;
478 }
479
480 glGenBuffers(1, &mIndexBufferID);
481 glBindBuffer(GL_ARRAY_BUFFER, mIndexBufferID);
482 glBufferData(GL_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_DYNAMIC_DRAW);
483 glBindBuffer(GL_ARRAY_BUFFER, 0);
484
485 free(indexBufferData);
486
487 uint32_t coordSize = 3;
488 uint32_t uvSize = 2;
489 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700490 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
491 mTextMeshPtr = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700492}
493
494// We don't want to allocate anything unless we actually draw text
495void FontRenderer::checkInit() {
496 if (mInitialized) {
497 return;
498 }
499
500 initTextTexture();
501 initVertexArrayBuffers();
502
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700503 // We store a string with letters in a rough frequency of occurrence
504 mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
505 mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
506 mLatinPrecache += String16(",.?!()-+@;:`'");
507 mLatinPrecache += String16("0123456789");
508
Romain Guy694b5192010-07-21 21:33:20 -0700509 mInitialized = true;
510}
511
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700512void FontRenderer::checkTextureUpdate() {
513 if (!mUploadTexture) {
514 return;
Romain Guy694b5192010-07-21 21:33:20 -0700515 }
516
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700517 glBindTexture(GL_TEXTURE_2D, mTextureId);
518
519 // Iterate over all the cache lines and see which ones need to be updated
520 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
521 CacheTextureLine* cl = mCacheLines[i];
522 if(cl->mDirty) {
523 uint32_t xOffset = 0;
524 uint32_t yOffset = cl->mCurrentRow;
525 uint32_t width = mCacheWidth;
526 uint32_t height = cl->mMaxHeight;
Romain Guy1e45aae2010-08-13 19:39:53 -0700527 void* textureData = mTextTexture + yOffset*width;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700528
529 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700530 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700531
532 cl->mDirty = false;
533 }
534 }
535
536 mUploadTexture = false;
537}
538
539void FontRenderer::issueDrawCommand() {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700540 checkTextureUpdate();
541
Romain Guy51769a62010-07-23 00:28:00 -0700542 float* vtx = mTextMeshPtr;
543 float* tex = vtx + 3;
Romain Guy694b5192010-07-21 21:33:20 -0700544
545 // position is slot 0
546 uint32_t slot = 0;
547 glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx);
548
549 // texture0 is slot 1
550 slot = 1;
551 glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex);
552
553 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
554 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
555}
556
557void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
558 float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
559 float x4, float y4, float z4, float u4, float v4) {
Romain Guy09147fb2010-07-22 13:08:20 -0700560 if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) {
561 return;
562 }
563
Romain Guy694b5192010-07-21 21:33:20 -0700564 const uint32_t vertsPerQuad = 4;
565 const uint32_t floatsPerVert = 5;
Romain Guy51769a62010-07-23 00:28:00 -0700566 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700567
Romain Guy694b5192010-07-21 21:33:20 -0700568 (*currentPos++) = x1;
569 (*currentPos++) = y1;
570 (*currentPos++) = z1;
571 (*currentPos++) = u1;
572 (*currentPos++) = v1;
573
574 (*currentPos++) = x2;
575 (*currentPos++) = y2;
576 (*currentPos++) = z2;
577 (*currentPos++) = u2;
578 (*currentPos++) = v2;
579
580 (*currentPos++) = x3;
581 (*currentPos++) = y3;
582 (*currentPos++) = z3;
583 (*currentPos++) = u3;
584 (*currentPos++) = v3;
585
586 (*currentPos++) = x4;
587 (*currentPos++) = y4;
588 (*currentPos++) = z4;
589 (*currentPos++) = u4;
590 (*currentPos++) = v4;
591
592 mCurrentQuadIndex++;
593
594 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
595 issueDrawCommand();
596 mCurrentQuadIndex = 0;
597 }
598}
599
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700600uint32_t FontRenderer::getRemainingCacheCapacity() {
601 uint32_t remainingCapacity = 0;
602 float totalPixels = 0;
603 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
604 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
605 totalPixels += mCacheLines[i]->mMaxWidth;
606 }
607 remainingCapacity = (remainingCapacity * 100) / totalPixels;
608 return remainingCapacity;
609}
610
611void FontRenderer::precacheLatin(SkPaint* paint) {
612 // Remaining capacity is measured in %
613 uint32_t remainingCapacity = getRemainingCacheCapacity();
614 uint32_t precacheIdx = 0;
615 while(remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
616 mCurrentFont->getCachedUTFChar(paint, (int32_t)mLatinPrecache[precacheIdx]);
617 remainingCapacity = getRemainingCacheCapacity();
618 precacheIdx ++;
619 }
620}
621
622void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
623 uint32_t currentNumFonts = mActiveFonts.size();
Romain Guy694b5192010-07-21 21:33:20 -0700624 mCurrentFont = Font::create(this, fontId, fontSize);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700625
626 const float maxPrecacheFontSize = 40.0f;
627 bool isNewFont = currentNumFonts != mActiveFonts.size();
628
Romain Guy2bffd262010-09-12 17:40:02 -0700629 if (isNewFont && fontSize <= maxPrecacheFontSize) {
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700630 precacheLatin(paint);
631 }
Romain Guy694b5192010-07-21 21:33:20 -0700632}
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700633FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Romain Guy1e45aae2010-08-13 19:39:53 -0700634 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
635 checkInit();
636
637 if (!mCurrentFont) {
638 DropShadow image;
639 image.width = 0;
640 image.height = 0;
641 image.image = NULL;
642 image.penX = 0;
643 image.penY = 0;
644 return image;
645 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700646
647 Rect bounds;
648 mCurrentFont->measureUTF(paint, text, startIndex, len, numGlyphs, &bounds);
Romain Guy1e45aae2010-08-13 19:39:53 -0700649 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
650 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700651 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guy1e45aae2010-08-13 19:39:53 -0700652 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700653 dataBuffer[i] = 0;
654 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700655
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700656 int penX = radius - bounds.left;
657 int penY = radius - bounds.bottom;
658
659 mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, penX, penY,
Romain Guy1e45aae2010-08-13 19:39:53 -0700660 dataBuffer, paddedWidth, paddedHeight);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700661 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
662
663 DropShadow image;
664 image.width = paddedWidth;
665 image.height = paddedHeight;
666 image.image = dataBuffer;
667 image.penX = penX;
668 image.penY = penY;
669 return image;
670}
Romain Guy694b5192010-07-21 21:33:20 -0700671
Romain Guy51769a62010-07-23 00:28:00 -0700672void FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
673 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y) {
Romain Guy694b5192010-07-21 21:33:20 -0700674 checkInit();
675
Romain Guy09147fb2010-07-22 13:08:20 -0700676 if (!mCurrentFont) {
677 LOGE("No font set");
Romain Guy694b5192010-07-21 21:33:20 -0700678 return;
679 }
680
Romain Guy09147fb2010-07-22 13:08:20 -0700681 mClip = clip;
Romain Guy51769a62010-07-23 00:28:00 -0700682 mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y);
Romain Guy694b5192010-07-21 21:33:20 -0700683
684 if (mCurrentQuadIndex != 0) {
685 issueDrawCommand();
686 mCurrentQuadIndex = 0;
687 }
688}
689
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700690void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
691 // Compute gaussian weights for the blur
692 // e is the euler's number
693 float e = 2.718281828459045f;
694 float pi = 3.1415926535897932f;
695 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
696 // x is of the form [-radius .. 0 .. radius]
697 // and sigma varies with radius.
698 // Based on some experimental radius values and sigma's
699 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700700 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700701 // The larger the radius gets, the more our gaussian blur
702 // will resemble a box blur since with large sigma
703 // the gaussian curve begins to lose its shape
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700704 float sigma = 0.3f * (float)radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700705
706 // Now compute the coefficints
707 // We will store some redundant values to save some math during
708 // the blur calculations
709 // precompute some values
710 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
711 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
712
713 float normalizeFactor = 0.0f;
714 for(int32_t r = -radius; r <= radius; r ++) {
715 float floatR = (float)r;
716 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
717 normalizeFactor += weights[r + radius];
718 }
719
720 //Now we need to normalize the weights because all our coefficients need to add up to one
721 normalizeFactor = 1.0f / normalizeFactor;
722 for(int32_t r = -radius; r <= radius; r ++) {
723 weights[r + radius] *= normalizeFactor;
724 }
725}
726
727void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700728 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700729 float blurredPixel = 0.0f;
730 float currentPixel = 0.0f;
731
732 for(int32_t y = 0; y < height; y ++) {
733
734 const uint8_t* input = source + y * width;
735 uint8_t* output = dest + y * width;
736
737 for(int32_t x = 0; x < width; x ++) {
738 blurredPixel = 0.0f;
739 const float* gPtr = weights;
740 // Optimization for non-border pixels
741 if ((x > radius) && (x < (width - radius))) {
742 const uint8_t *i = input + (x - radius);
743 for(int r = -radius; r <= radius; r ++) {
744 currentPixel = (float)(*i);
745 blurredPixel += currentPixel * gPtr[0];
746 gPtr++;
747 i++;
748 }
749 } else {
750 for(int32_t r = -radius; r <= radius; r ++) {
751 // Stepping left and right away from the pixel
752 int validW = x + r;
753 if(validW < 0) {
754 validW = 0;
755 }
756 if(validW > width - 1) {
757 validW = width - 1;
758 }
759
760 currentPixel = (float)(input[validW]);
761 blurredPixel += currentPixel * gPtr[0];
762 gPtr++;
763 }
764 }
765 *output = (uint8_t)blurredPixel;
766 output ++;
767 }
768 }
769}
770
771void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700772 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700773 float blurredPixel = 0.0f;
774 float currentPixel = 0.0f;
775
776 for(int32_t y = 0; y < height; y ++) {
777
778 uint8_t* output = dest + y * width;
779
780 for(int32_t x = 0; x < width; x ++) {
781 blurredPixel = 0.0f;
782 const float* gPtr = weights;
783 const uint8_t* input = source + x;
784 // Optimization for non-border pixels
785 if ((y > radius) && (y < (height - radius))) {
786 const uint8_t *i = input + ((y - radius) * width);
787 for(int32_t r = -radius; r <= radius; r ++) {
788 currentPixel = (float)(*i);
789 blurredPixel += currentPixel * gPtr[0];
790 gPtr++;
791 i += width;
792 }
793 } else {
794 for(int32_t r = -radius; r <= radius; r ++) {
795 int validH = y + r;
796 // Clamp to zero and width
797 if(validH < 0) {
798 validH = 0;
799 }
800 if(validH > height - 1) {
801 validH = height - 1;
802 }
803
804 const uint8_t *i = input + validH * width;
805 currentPixel = (float)(*i);
806 blurredPixel += currentPixel * gPtr[0];
807 gPtr++;
808 }
809 }
810 *output = (uint8_t)blurredPixel;
811 output ++;
812 }
813 }
814}
815
816
817void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
818 float *gaussian = new float[2 * radius + 1];
819 computeGaussianWeights(gaussian, radius);
820 uint8_t* scratch = new uint8_t[width * height];
821 horizontalBlur(gaussian, radius, image, scratch, width, height);
822 verticalBlur(gaussian, radius, scratch, image, width, height);
823 delete[] gaussian;
824 delete[] scratch;
825}
826
Romain Guy694b5192010-07-21 21:33:20 -0700827}; // namespace uirenderer
828}; // namespace android