blob: 5d7f8bfc0635abe77a231bd8edfe26ceb80becfc [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
169void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700170 int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
171 uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700172 if (numGlyphs == 0 || text == NULL || len == 0) {
173 return;
174 }
175
176 int penX = x, penY = y;
177 int glyphsLeft = 1;
178 if (numGlyphs > 0) {
179 glyphsLeft = numGlyphs;
180 }
181
Romain Guy694b5192010-07-21 21:33:20 -0700182 text += start;
183
184 while (glyphsLeft > 0) {
Romain Guy694b5192010-07-21 21:33:20 -0700185 int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text);
186
Romain Guy61c8c9c2010-08-09 20:48:09 -0700187 // Reached the end of the string
Romain Guy694b5192010-07-21 21:33:20 -0700188 if (utfChar < 0) {
189 break;
190 }
191
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700192 CachedGlyphInfo* cachedGlyph = getCachedUTFChar(paint, utfChar);
Romain Guy694b5192010-07-21 21:33:20 -0700193
194 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
195 if (cachedGlyph->mIsValid) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700196 switch(mode) {
197 case FRAMEBUFFER:
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700198 drawCachedGlyph(cachedGlyph, penX, penY);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700199 break;
200 case BITMAP:
201 drawCachedGlyph(cachedGlyph, penX, penY, bitmap, bitmapW, bitmapH);
202 break;
203 case MEASURE:
204 measureCachedGlyph(cachedGlyph, penX, penY, bounds);
205 break;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700206 }
Romain Guy694b5192010-07-21 21:33:20 -0700207 }
208
Romain Guy09147fb2010-07-22 13:08:20 -0700209 penX += SkFixedFloor(cachedGlyph->mAdvanceX);
Romain Guy694b5192010-07-21 21:33:20 -0700210
211 // If we were given a specific number of glyphs, decrement
212 if (numGlyphs > 0) {
213 glyphsLeft--;
214 }
215 }
216}
217
Romain Guy51769a62010-07-23 00:28:00 -0700218void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
Romain Guy694b5192010-07-21 21:33:20 -0700219 glyph->mAdvanceX = skiaGlyph.fAdvanceX;
220 glyph->mAdvanceY = skiaGlyph.fAdvanceY;
221 glyph->mBitmapLeft = skiaGlyph.fLeft;
222 glyph->mBitmapTop = skiaGlyph.fTop;
223
224 uint32_t startX = 0;
225 uint32_t startY = 0;
226
Romain Guy694b5192010-07-21 21:33:20 -0700227 // Get the bitmap for the glyph
228 paint->findImage(skiaGlyph);
Romain Guy51769a62010-07-23 00:28:00 -0700229 glyph->mIsValid = mState->cacheBitmap(skiaGlyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700230
231 if (!glyph->mIsValid) {
232 return;
233 }
234
235 uint32_t endX = startX + skiaGlyph.fWidth;
236 uint32_t endY = startY + skiaGlyph.fHeight;
237
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700238 glyph->mStartX = startX;
239 glyph->mStartY = startY;
Romain Guy694b5192010-07-21 21:33:20 -0700240 glyph->mBitmapWidth = skiaGlyph.fWidth;
241 glyph->mBitmapHeight = skiaGlyph.fHeight;
242
Romain Guy51769a62010-07-23 00:28:00 -0700243 uint32_t cacheWidth = mState->getCacheWidth();
244 uint32_t cacheHeight = mState->getCacheHeight();
Romain Guy694b5192010-07-21 21:33:20 -0700245
246 glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
247 glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
248 glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
249 glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
250
Romain Guy51769a62010-07-23 00:28:00 -0700251 mState->mUploadTexture = true;
Romain Guy694b5192010-07-21 21:33:20 -0700252}
253
Romain Guy51769a62010-07-23 00:28:00 -0700254Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, int32_t glyph) {
255 CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
Romain Guy694b5192010-07-21 21:33:20 -0700256 mCachedGlyphs.add(glyph, newGlyph);
257
258 const SkGlyph& skiaGlyph = paint->getUnicharMetrics(glyph);
259 newGlyph->mGlyphIndex = skiaGlyph.fID;
260 newGlyph->mIsValid = false;
261
262 updateGlyphCache(paint, skiaGlyph, newGlyph);
263
264 return newGlyph;
265}
266
267Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize) {
268 Vector<Font*> &activeFonts = state->mActiveFonts;
269
270 for (uint32_t i = 0; i < activeFonts.size(); i++) {
Romain Guy51769a62010-07-23 00:28:00 -0700271 Font* font = activeFonts[i];
272 if (font->mFontId == fontId && font->mFontSize == fontSize) {
273 return font;
Romain Guy694b5192010-07-21 21:33:20 -0700274 }
275 }
276
277 Font* newFont = new Font(state, fontId, fontSize);
278 activeFonts.push(newFont);
279 return newFont;
280}
281
282///////////////////////////////////////////////////////////////////////////////
283// FontRenderer
284///////////////////////////////////////////////////////////////////////////////
285
286FontRenderer::FontRenderer() {
Romain Guy51769a62010-07-23 00:28:00 -0700287 LOGD("Creating FontRenderer");
288
Romain Guyb45c0c92010-08-26 20:35:23 -0700289 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700290 mInitialized = false;
291 mMaxNumberOfQuads = 1024;
292 mCurrentQuadIndex = 0;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700293 mTextureId = 0;
Romain Guy694b5192010-07-21 21:33:20 -0700294
Romain Guy9cccc2b92010-08-07 23:46:15 -0700295 mTextMeshPtr = NULL;
296 mTextTexture = NULL;
297
Romain Guy694b5192010-07-21 21:33:20 -0700298 mIndexBufferID = 0;
299
Romain Guy51769a62010-07-23 00:28:00 -0700300 mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700301 mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -0700302
303 char property[PROPERTY_VALUE_MAX];
304 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
305 LOGD(" Setting text cache width to %s pixels", property);
306 mCacheWidth = atoi(property);
307 } else {
308 LOGD(" Using default text cache width of %i pixels", mCacheWidth);
309 }
310
311 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
312 LOGD(" Setting text cache width to %s pixels", property);
313 mCacheHeight = atoi(property);
314 } else {
315 LOGD(" Using default text cache height of %i pixels", mCacheHeight);
316 }
Romain Guy694b5192010-07-21 21:33:20 -0700317}
318
319FontRenderer::~FontRenderer() {
320 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
321 delete mCacheLines[i];
322 }
323 mCacheLines.clear();
324
Romain Guy9cccc2b92010-08-07 23:46:15 -0700325 if (mInitialized) {
326 delete[] mTextMeshPtr;
327 delete[] mTextTexture;
328 }
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700329
Romain Guy9cccc2b92010-08-07 23:46:15 -0700330 if (mTextureId) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700331 glDeleteTextures(1, &mTextureId);
332 }
Romain Guy694b5192010-07-21 21:33:20 -0700333
334 Vector<Font*> fontsToDereference = mActiveFonts;
335 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
336 delete fontsToDereference[i];
337 }
338}
339
340void FontRenderer::flushAllAndInvalidate() {
341 if (mCurrentQuadIndex != 0) {
342 issueDrawCommand();
343 mCurrentQuadIndex = 0;
344 }
345 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
346 mActiveFonts[i]->invalidateTextureCache();
347 }
348 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
349 mCacheLines[i]->mCurrentCol = 0;
350 }
351}
352
Romain Guy51769a62010-07-23 00:28:00 -0700353bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
Romain Guy694b5192010-07-21 21:33:20 -0700354 // If the glyph is too tall, don't cache it
355 if (glyph.fWidth > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
356 LOGE("Font size to large to fit in cache. width, height = %i, %i",
357 (int) glyph.fWidth, (int) glyph.fHeight);
358 return false;
359 }
360
361 // Now copy the bitmap into the cache texture
362 uint32_t startX = 0;
363 uint32_t startY = 0;
364
365 bool bitmapFit = false;
366 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
367 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
368 if (bitmapFit) {
369 break;
370 }
371 }
372
373 // If the new glyph didn't fit, flush the state so far and invalidate everything
374 if (!bitmapFit) {
375 flushAllAndInvalidate();
376
377 // Try to fit it again
378 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
379 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
380 if (bitmapFit) {
381 break;
382 }
383 }
384
385 // if we still don't fit, something is wrong and we shouldn't draw
386 if (!bitmapFit) {
387 LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
388 (int) glyph.fWidth, (int) glyph.fHeight);
389 return false;
390 }
391 }
392
393 *retOriginX = startX;
394 *retOriginY = startY;
395
396 uint32_t endX = startX + glyph.fWidth;
397 uint32_t endY = startY + glyph.fHeight;
398
399 uint32_t cacheWidth = mCacheWidth;
400
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700401 uint8_t* cacheBuffer = mTextTexture;
402 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700403 unsigned int stride = glyph.rowBytes();
404
405 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
406 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
407 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700408 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
Romain Guyb45c0c92010-08-26 20:35:23 -0700409 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
Romain Guy694b5192010-07-21 21:33:20 -0700410 }
411 }
412
413 return true;
414}
415
416void FontRenderer::initTextTexture() {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700417 mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
Romain Guy694b5192010-07-21 21:33:20 -0700418 mUploadTexture = false;
419
420 glGenTextures(1, &mTextureId);
421 glBindTexture(GL_TEXTURE_2D, mTextureId);
422 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700423 // Initialize texture dimentions
424 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700425 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Romain Guy694b5192010-07-21 21:33:20 -0700426
427 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
428 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
429
430 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
431 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
432
433 // Split up our cache texture into lines of certain widths
434 int nextLine = 0;
Romain Guy51769a62010-07-23 00:28:00 -0700435 mCacheLines.push(new CacheTextureLine(mCacheWidth, 16, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700436 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700437 mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700438 nextLine += mCacheLines.top()->mMaxHeight;
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700439 mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0));
440 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700441 mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700442 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700443 mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700444 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700445 mCacheLines.push(new CacheTextureLine(mCacheWidth, 40, 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, mCacheHeight - nextLine, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700448}
449
450// Avoid having to reallocate memory and render quad by quad
451void FontRenderer::initVertexArrayBuffers() {
452 uint32_t numIndicies = mMaxNumberOfQuads * 6;
453 uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700454 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700455
456 // Four verts, two triangles , six indices per quad
457 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
458 int i6 = i * 6;
459 int i4 = i * 4;
460
461 indexBufferData[i6 + 0] = i4 + 0;
462 indexBufferData[i6 + 1] = i4 + 1;
463 indexBufferData[i6 + 2] = i4 + 2;
464
465 indexBufferData[i6 + 3] = i4 + 0;
466 indexBufferData[i6 + 4] = i4 + 2;
467 indexBufferData[i6 + 5] = i4 + 3;
468 }
469
470 glGenBuffers(1, &mIndexBufferID);
471 glBindBuffer(GL_ARRAY_BUFFER, mIndexBufferID);
472 glBufferData(GL_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_DYNAMIC_DRAW);
473 glBindBuffer(GL_ARRAY_BUFFER, 0);
474
475 free(indexBufferData);
476
477 uint32_t coordSize = 3;
478 uint32_t uvSize = 2;
479 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700480 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
481 mTextMeshPtr = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700482}
483
484// We don't want to allocate anything unless we actually draw text
485void FontRenderer::checkInit() {
486 if (mInitialized) {
487 return;
488 }
489
490 initTextTexture();
491 initVertexArrayBuffers();
492
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700493 // We store a string with letters in a rough frequency of occurrence
494 mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
495 mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
496 mLatinPrecache += String16(",.?!()-+@;:`'");
497 mLatinPrecache += String16("0123456789");
498
Romain Guy694b5192010-07-21 21:33:20 -0700499 mInitialized = true;
500}
501
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700502void FontRenderer::checkTextureUpdate() {
503 if (!mUploadTexture) {
504 return;
Romain Guy694b5192010-07-21 21:33:20 -0700505 }
506
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700507 glBindTexture(GL_TEXTURE_2D, mTextureId);
508
509 // Iterate over all the cache lines and see which ones need to be updated
510 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
511 CacheTextureLine* cl = mCacheLines[i];
512 if(cl->mDirty) {
513 uint32_t xOffset = 0;
514 uint32_t yOffset = cl->mCurrentRow;
515 uint32_t width = mCacheWidth;
516 uint32_t height = cl->mMaxHeight;
Romain Guy1e45aae2010-08-13 19:39:53 -0700517 void* textureData = mTextTexture + yOffset*width;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700518
519 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700520 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700521
522 cl->mDirty = false;
523 }
524 }
525
526 mUploadTexture = false;
527}
528
529void FontRenderer::issueDrawCommand() {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700530 checkTextureUpdate();
531
Romain Guy51769a62010-07-23 00:28:00 -0700532 float* vtx = mTextMeshPtr;
533 float* tex = vtx + 3;
Romain Guy694b5192010-07-21 21:33:20 -0700534
535 // position is slot 0
536 uint32_t slot = 0;
537 glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx);
538
539 // texture0 is slot 1
540 slot = 1;
541 glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex);
542
543 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
544 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
545}
546
547void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
548 float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
549 float x4, float y4, float z4, float u4, float v4) {
Romain Guy09147fb2010-07-22 13:08:20 -0700550 if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) {
551 return;
552 }
553
Romain Guy694b5192010-07-21 21:33:20 -0700554 const uint32_t vertsPerQuad = 4;
555 const uint32_t floatsPerVert = 5;
Romain Guy51769a62010-07-23 00:28:00 -0700556 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700557
Romain Guy694b5192010-07-21 21:33:20 -0700558 (*currentPos++) = x1;
559 (*currentPos++) = y1;
560 (*currentPos++) = z1;
561 (*currentPos++) = u1;
562 (*currentPos++) = v1;
563
564 (*currentPos++) = x2;
565 (*currentPos++) = y2;
566 (*currentPos++) = z2;
567 (*currentPos++) = u2;
568 (*currentPos++) = v2;
569
570 (*currentPos++) = x3;
571 (*currentPos++) = y3;
572 (*currentPos++) = z3;
573 (*currentPos++) = u3;
574 (*currentPos++) = v3;
575
576 (*currentPos++) = x4;
577 (*currentPos++) = y4;
578 (*currentPos++) = z4;
579 (*currentPos++) = u4;
580 (*currentPos++) = v4;
581
582 mCurrentQuadIndex++;
583
584 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
585 issueDrawCommand();
586 mCurrentQuadIndex = 0;
587 }
588}
589
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700590uint32_t FontRenderer::getRemainingCacheCapacity() {
591 uint32_t remainingCapacity = 0;
592 float totalPixels = 0;
593 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
594 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
595 totalPixels += mCacheLines[i]->mMaxWidth;
596 }
597 remainingCapacity = (remainingCapacity * 100) / totalPixels;
598 return remainingCapacity;
599}
600
601void FontRenderer::precacheLatin(SkPaint* paint) {
602 // Remaining capacity is measured in %
603 uint32_t remainingCapacity = getRemainingCacheCapacity();
604 uint32_t precacheIdx = 0;
605 while(remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
606 mCurrentFont->getCachedUTFChar(paint, (int32_t)mLatinPrecache[precacheIdx]);
607 remainingCapacity = getRemainingCacheCapacity();
608 precacheIdx ++;
609 }
610}
611
612void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
613 uint32_t currentNumFonts = mActiveFonts.size();
Romain Guy694b5192010-07-21 21:33:20 -0700614 mCurrentFont = Font::create(this, fontId, fontSize);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700615
616 const float maxPrecacheFontSize = 40.0f;
617 bool isNewFont = currentNumFonts != mActiveFonts.size();
618
619 if(isNewFont && fontSize <= maxPrecacheFontSize ){
620 precacheLatin(paint);
621 }
Romain Guy694b5192010-07-21 21:33:20 -0700622}
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700623FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Romain Guy1e45aae2010-08-13 19:39:53 -0700624 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
625 checkInit();
626
627 if (!mCurrentFont) {
628 DropShadow image;
629 image.width = 0;
630 image.height = 0;
631 image.image = NULL;
632 image.penX = 0;
633 image.penY = 0;
634 return image;
635 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700636
637 Rect bounds;
638 mCurrentFont->measureUTF(paint, text, startIndex, len, numGlyphs, &bounds);
Romain Guy1e45aae2010-08-13 19:39:53 -0700639 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
640 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700641 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guy1e45aae2010-08-13 19:39:53 -0700642 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700643 dataBuffer[i] = 0;
644 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700645
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700646 int penX = radius - bounds.left;
647 int penY = radius - bounds.bottom;
648
649 mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, penX, penY,
Romain Guy1e45aae2010-08-13 19:39:53 -0700650 dataBuffer, paddedWidth, paddedHeight);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700651 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
652
653 DropShadow image;
654 image.width = paddedWidth;
655 image.height = paddedHeight;
656 image.image = dataBuffer;
657 image.penX = penX;
658 image.penY = penY;
659 return image;
660}
Romain Guy694b5192010-07-21 21:33:20 -0700661
Romain Guy51769a62010-07-23 00:28:00 -0700662void FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
663 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y) {
Romain Guy694b5192010-07-21 21:33:20 -0700664 checkInit();
665
Romain Guy09147fb2010-07-22 13:08:20 -0700666 if (!mCurrentFont) {
667 LOGE("No font set");
Romain Guy694b5192010-07-21 21:33:20 -0700668 return;
669 }
670
Romain Guy09147fb2010-07-22 13:08:20 -0700671 mClip = clip;
Romain Guy51769a62010-07-23 00:28:00 -0700672 mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y);
Romain Guy694b5192010-07-21 21:33:20 -0700673
674 if (mCurrentQuadIndex != 0) {
675 issueDrawCommand();
676 mCurrentQuadIndex = 0;
677 }
678}
679
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700680void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
681 // Compute gaussian weights for the blur
682 // e is the euler's number
683 float e = 2.718281828459045f;
684 float pi = 3.1415926535897932f;
685 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
686 // x is of the form [-radius .. 0 .. radius]
687 // and sigma varies with radius.
688 // Based on some experimental radius values and sigma's
689 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700690 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700691 // The larger the radius gets, the more our gaussian blur
692 // will resemble a box blur since with large sigma
693 // the gaussian curve begins to lose its shape
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700694 float sigma = 0.3f * (float)radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700695
696 // Now compute the coefficints
697 // We will store some redundant values to save some math during
698 // the blur calculations
699 // precompute some values
700 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
701 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
702
703 float normalizeFactor = 0.0f;
704 for(int32_t r = -radius; r <= radius; r ++) {
705 float floatR = (float)r;
706 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
707 normalizeFactor += weights[r + radius];
708 }
709
710 //Now we need to normalize the weights because all our coefficients need to add up to one
711 normalizeFactor = 1.0f / normalizeFactor;
712 for(int32_t r = -radius; r <= radius; r ++) {
713 weights[r + radius] *= normalizeFactor;
714 }
715}
716
717void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700718 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700719 float blurredPixel = 0.0f;
720 float currentPixel = 0.0f;
721
722 for(int32_t y = 0; y < height; y ++) {
723
724 const uint8_t* input = source + y * width;
725 uint8_t* output = dest + y * width;
726
727 for(int32_t x = 0; x < width; x ++) {
728 blurredPixel = 0.0f;
729 const float* gPtr = weights;
730 // Optimization for non-border pixels
731 if ((x > radius) && (x < (width - radius))) {
732 const uint8_t *i = input + (x - radius);
733 for(int r = -radius; r <= radius; r ++) {
734 currentPixel = (float)(*i);
735 blurredPixel += currentPixel * gPtr[0];
736 gPtr++;
737 i++;
738 }
739 } else {
740 for(int32_t r = -radius; r <= radius; r ++) {
741 // Stepping left and right away from the pixel
742 int validW = x + r;
743 if(validW < 0) {
744 validW = 0;
745 }
746 if(validW > width - 1) {
747 validW = width - 1;
748 }
749
750 currentPixel = (float)(input[validW]);
751 blurredPixel += currentPixel * gPtr[0];
752 gPtr++;
753 }
754 }
755 *output = (uint8_t)blurredPixel;
756 output ++;
757 }
758 }
759}
760
761void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700762 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700763 float blurredPixel = 0.0f;
764 float currentPixel = 0.0f;
765
766 for(int32_t y = 0; y < height; y ++) {
767
768 uint8_t* output = dest + y * width;
769
770 for(int32_t x = 0; x < width; x ++) {
771 blurredPixel = 0.0f;
772 const float* gPtr = weights;
773 const uint8_t* input = source + x;
774 // Optimization for non-border pixels
775 if ((y > radius) && (y < (height - radius))) {
776 const uint8_t *i = input + ((y - radius) * width);
777 for(int32_t r = -radius; r <= radius; r ++) {
778 currentPixel = (float)(*i);
779 blurredPixel += currentPixel * gPtr[0];
780 gPtr++;
781 i += width;
782 }
783 } else {
784 for(int32_t r = -radius; r <= radius; r ++) {
785 int validH = y + r;
786 // Clamp to zero and width
787 if(validH < 0) {
788 validH = 0;
789 }
790 if(validH > height - 1) {
791 validH = height - 1;
792 }
793
794 const uint8_t *i = input + validH * width;
795 currentPixel = (float)(*i);
796 blurredPixel += currentPixel * gPtr[0];
797 gPtr++;
798 }
799 }
800 *output = (uint8_t)blurredPixel;
801 output ++;
802 }
803 }
804}
805
806
807void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
808 float *gaussian = new float[2 * radius + 1];
809 computeGaussianWeights(gaussian, radius);
810 uint8_t* scratch = new uint8_t[width * height];
811 horizontalBlur(gaussian, radius, image, scratch, width, height);
812 verticalBlur(gaussian, radius, scratch, image, width, height);
813 delete[] gaussian;
814 delete[] scratch;
815}
816
Romain Guy694b5192010-07-21 21:33:20 -0700817}; // namespace uirenderer
818}; // namespace android