blob: dbea1147b87d081612b3c4f3fb490be8ebad4a2d [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
72 if(bounds->bottom > nPenY) {
73 bounds->bottom = nPenY;
74 }
75 if(bounds->left > nPenX) {
76 bounds->left = nPenX;
77 }
78 if(bounds->right < nPenX + width) {
79 bounds->right = nPenX + width;
80 }
81 if(bounds->top < nPenY + height) {
82 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
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700104void Font::drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
105 uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
106 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++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700119 if(bX < 0 || bY < 0 || bX >= (int32_t)bitmapW || bY >= (int32_t)bitmapH) {
120 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) {
131 CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueFor(utfChar);
132 if (cachedGlyph == NULL) {
133 cachedGlyph = cacheGlyph(paint, utfChar);
134 }
135
136 // Is the glyph still in texture cache?
137 if (!cachedGlyph->mIsValid) {
138 const SkGlyph& skiaGlyph = paint->getUnicharMetrics(utfChar);
139 updateGlyphCache(paint, skiaGlyph, cachedGlyph);
140 }
141
142 return cachedGlyph;
143}
144
Romain Guy51769a62010-07-23 00:28:00 -0700145void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700146 int numGlyphs, int x, int y,
147 uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
148 if(bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
149 renderUTF(paint, text, start, len, numGlyphs, x, y, BITMAP,
150 bitmap, bitmapW, bitmapH, NULL);
151 }
152 else {
153 renderUTF(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER,
154 NULL, 0, 0, NULL);
155 }
156
157}
158
159void Font::measureUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
160 int numGlyphs, Rect *bounds) {
161 if(bounds == NULL) {
162 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,
170 int numGlyphs, int x, int y, RenderMode mode,
171 uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
172 Rect *bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700173 if (numGlyphs == 0 || text == NULL || len == 0) {
174 return;
175 }
176
177 int penX = x, penY = y;
178 int glyphsLeft = 1;
179 if (numGlyphs > 0) {
180 glyphsLeft = numGlyphs;
181 }
182
Romain Guy694b5192010-07-21 21:33:20 -0700183 text += start;
184
185 while (glyphsLeft > 0) {
Romain Guy694b5192010-07-21 21:33:20 -0700186 int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text);
187
188 // Reached the end of the string or encountered
189 if (utfChar < 0) {
190 break;
191 }
192
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700193 CachedGlyphInfo* cachedGlyph = getCachedUTFChar(paint, utfChar);
Romain Guy694b5192010-07-21 21:33:20 -0700194
195 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
196 if (cachedGlyph->mIsValid) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700197 switch(mode) {
198 case FRAMEBUFFER:
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700199 drawCachedGlyph(cachedGlyph, penX, penY);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700200 break;
201 case BITMAP:
202 drawCachedGlyph(cachedGlyph, penX, penY, bitmap, bitmapW, bitmapH);
203 break;
204 case MEASURE:
205 measureCachedGlyph(cachedGlyph, penX, penY, bounds);
206 break;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700207 }
Romain Guy694b5192010-07-21 21:33:20 -0700208 }
209
Romain Guy09147fb2010-07-22 13:08:20 -0700210 penX += SkFixedFloor(cachedGlyph->mAdvanceX);
Romain Guy694b5192010-07-21 21:33:20 -0700211
212 // If we were given a specific number of glyphs, decrement
213 if (numGlyphs > 0) {
214 glyphsLeft--;
215 }
216 }
217}
218
Romain Guy51769a62010-07-23 00:28:00 -0700219void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
Romain Guy694b5192010-07-21 21:33:20 -0700220 glyph->mAdvanceX = skiaGlyph.fAdvanceX;
221 glyph->mAdvanceY = skiaGlyph.fAdvanceY;
222 glyph->mBitmapLeft = skiaGlyph.fLeft;
223 glyph->mBitmapTop = skiaGlyph.fTop;
224
225 uint32_t startX = 0;
226 uint32_t startY = 0;
227
Romain Guy694b5192010-07-21 21:33:20 -0700228 // Get the bitmap for the glyph
229 paint->findImage(skiaGlyph);
Romain Guy51769a62010-07-23 00:28:00 -0700230 glyph->mIsValid = mState->cacheBitmap(skiaGlyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700231
232 if (!glyph->mIsValid) {
233 return;
234 }
235
236 uint32_t endX = startX + skiaGlyph.fWidth;
237 uint32_t endY = startY + skiaGlyph.fHeight;
238
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700239 glyph->mStartX = startX;
240 glyph->mStartY = startY;
Romain Guy694b5192010-07-21 21:33:20 -0700241 glyph->mBitmapWidth = skiaGlyph.fWidth;
242 glyph->mBitmapHeight = skiaGlyph.fHeight;
243
Romain Guy51769a62010-07-23 00:28:00 -0700244 uint32_t cacheWidth = mState->getCacheWidth();
245 uint32_t cacheHeight = mState->getCacheHeight();
Romain Guy694b5192010-07-21 21:33:20 -0700246
247 glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
248 glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
249 glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
250 glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
251
Romain Guy51769a62010-07-23 00:28:00 -0700252 mState->mUploadTexture = true;
Romain Guy694b5192010-07-21 21:33:20 -0700253}
254
Romain Guy51769a62010-07-23 00:28:00 -0700255Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, int32_t glyph) {
256 CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
Romain Guy694b5192010-07-21 21:33:20 -0700257 mCachedGlyphs.add(glyph, newGlyph);
258
259 const SkGlyph& skiaGlyph = paint->getUnicharMetrics(glyph);
260 newGlyph->mGlyphIndex = skiaGlyph.fID;
261 newGlyph->mIsValid = false;
262
263 updateGlyphCache(paint, skiaGlyph, newGlyph);
264
265 return newGlyph;
266}
267
268Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize) {
269 Vector<Font*> &activeFonts = state->mActiveFonts;
270
271 for (uint32_t i = 0; i < activeFonts.size(); i++) {
Romain Guy51769a62010-07-23 00:28:00 -0700272 Font* font = activeFonts[i];
273 if (font->mFontId == fontId && font->mFontSize == fontSize) {
274 return font;
Romain Guy694b5192010-07-21 21:33:20 -0700275 }
276 }
277
278 Font* newFont = new Font(state, fontId, fontSize);
279 activeFonts.push(newFont);
280 return newFont;
281}
282
283///////////////////////////////////////////////////////////////////////////////
284// FontRenderer
285///////////////////////////////////////////////////////////////////////////////
286
287FontRenderer::FontRenderer() {
Romain Guy51769a62010-07-23 00:28:00 -0700288 LOGD("Creating FontRenderer");
289
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
295 mIndexBufferID = 0;
296
Romain Guy51769a62010-07-23 00:28:00 -0700297 mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700298 mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -0700299
300 char property[PROPERTY_VALUE_MAX];
301 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
302 LOGD(" Setting text cache width to %s pixels", property);
303 mCacheWidth = atoi(property);
304 } else {
305 LOGD(" Using default text cache width of %i pixels", mCacheWidth);
306 }
307
308 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
309 LOGD(" Setting text cache width to %s pixels", property);
310 mCacheHeight = atoi(property);
311 } else {
312 LOGD(" Using default text cache height of %i pixels", mCacheHeight);
313 }
Romain Guy694b5192010-07-21 21:33:20 -0700314}
315
316FontRenderer::~FontRenderer() {
317 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
318 delete mCacheLines[i];
319 }
320 mCacheLines.clear();
321
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700322 delete[] mTextMeshPtr;
323 delete[] mTextTexture;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700324
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700325 if(mTextureId) {
326 glDeleteTextures(1, &mTextureId);
327 }
Romain Guy694b5192010-07-21 21:33:20 -0700328
329 Vector<Font*> fontsToDereference = mActiveFonts;
330 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
331 delete fontsToDereference[i];
332 }
333}
334
335void FontRenderer::flushAllAndInvalidate() {
336 if (mCurrentQuadIndex != 0) {
337 issueDrawCommand();
338 mCurrentQuadIndex = 0;
339 }
340 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
341 mActiveFonts[i]->invalidateTextureCache();
342 }
343 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
344 mCacheLines[i]->mCurrentCol = 0;
345 }
346}
347
Romain Guy51769a62010-07-23 00:28:00 -0700348bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
Romain Guy694b5192010-07-21 21:33:20 -0700349 // If the glyph is too tall, don't cache it
350 if (glyph.fWidth > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
351 LOGE("Font size to large to fit in cache. width, height = %i, %i",
352 (int) glyph.fWidth, (int) glyph.fHeight);
353 return false;
354 }
355
356 // Now copy the bitmap into the cache texture
357 uint32_t startX = 0;
358 uint32_t startY = 0;
359
360 bool bitmapFit = false;
361 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
362 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
363 if (bitmapFit) {
364 break;
365 }
366 }
367
368 // If the new glyph didn't fit, flush the state so far and invalidate everything
369 if (!bitmapFit) {
370 flushAllAndInvalidate();
371
372 // Try to fit it again
373 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
374 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
375 if (bitmapFit) {
376 break;
377 }
378 }
379
380 // if we still don't fit, something is wrong and we shouldn't draw
381 if (!bitmapFit) {
382 LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
383 (int) glyph.fWidth, (int) glyph.fHeight);
384 return false;
385 }
386 }
387
388 *retOriginX = startX;
389 *retOriginY = startY;
390
391 uint32_t endX = startX + glyph.fWidth;
392 uint32_t endY = startY + glyph.fHeight;
393
394 uint32_t cacheWidth = mCacheWidth;
395
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700396 uint8_t* cacheBuffer = mTextTexture;
397 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700398 unsigned int stride = glyph.rowBytes();
399
400 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
401 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
402 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700403 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
Romain Guy694b5192010-07-21 21:33:20 -0700404 cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
405 }
406 }
407
408 return true;
409}
410
411void FontRenderer::initTextTexture() {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700412 mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
Romain Guy694b5192010-07-21 21:33:20 -0700413 mUploadTexture = false;
414
415 glGenTextures(1, &mTextureId);
416 glBindTexture(GL_TEXTURE_2D, mTextureId);
417 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700418 // Initialize texture dimentions
419 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
420 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Romain Guy694b5192010-07-21 21:33:20 -0700421
422 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
423 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
424
425 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
426 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
427
428 // Split up our cache texture into lines of certain widths
429 int nextLine = 0;
Romain Guy51769a62010-07-23 00:28:00 -0700430 mCacheLines.push(new CacheTextureLine(mCacheWidth, 16, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700431 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700432 mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700433 nextLine += mCacheLines.top()->mMaxHeight;
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700434 mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0));
435 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700436 mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700437 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700438 mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700439 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700440 mCacheLines.push(new CacheTextureLine(mCacheWidth, 40, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700441 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700442 mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700443}
444
445// Avoid having to reallocate memory and render quad by quad
446void FontRenderer::initVertexArrayBuffers() {
447 uint32_t numIndicies = mMaxNumberOfQuads * 6;
448 uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700449 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700450
451 // Four verts, two triangles , six indices per quad
452 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
453 int i6 = i * 6;
454 int i4 = i * 4;
455
456 indexBufferData[i6 + 0] = i4 + 0;
457 indexBufferData[i6 + 1] = i4 + 1;
458 indexBufferData[i6 + 2] = i4 + 2;
459
460 indexBufferData[i6 + 3] = i4 + 0;
461 indexBufferData[i6 + 4] = i4 + 2;
462 indexBufferData[i6 + 5] = i4 + 3;
463 }
464
465 glGenBuffers(1, &mIndexBufferID);
466 glBindBuffer(GL_ARRAY_BUFFER, mIndexBufferID);
467 glBufferData(GL_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_DYNAMIC_DRAW);
468 glBindBuffer(GL_ARRAY_BUFFER, 0);
469
470 free(indexBufferData);
471
472 uint32_t coordSize = 3;
473 uint32_t uvSize = 2;
474 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700475 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
476 mTextMeshPtr = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700477}
478
479// We don't want to allocate anything unless we actually draw text
480void FontRenderer::checkInit() {
481 if (mInitialized) {
482 return;
483 }
484
485 initTextTexture();
486 initVertexArrayBuffers();
487
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700488 // We store a string with letters in a rough frequency of occurrence
489 mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
490 mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
491 mLatinPrecache += String16(",.?!()-+@;:`'");
492 mLatinPrecache += String16("0123456789");
493
Romain Guy694b5192010-07-21 21:33:20 -0700494 mInitialized = true;
495}
496
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700497void FontRenderer::checkTextureUpdate() {
498 if (!mUploadTexture) {
499 return;
Romain Guy694b5192010-07-21 21:33:20 -0700500 }
501
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700502 glBindTexture(GL_TEXTURE_2D, mTextureId);
503
504 // Iterate over all the cache lines and see which ones need to be updated
505 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
506 CacheTextureLine* cl = mCacheLines[i];
507 if(cl->mDirty) {
508 uint32_t xOffset = 0;
509 uint32_t yOffset = cl->mCurrentRow;
510 uint32_t width = mCacheWidth;
511 uint32_t height = cl->mMaxHeight;
512 void* textureData = mTextTexture + yOffset*width;
513
514 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
515 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
516
517 cl->mDirty = false;
518 }
519 }
520
521 mUploadTexture = false;
522}
523
524void FontRenderer::issueDrawCommand() {
525
526 checkTextureUpdate();
527
Romain Guy51769a62010-07-23 00:28:00 -0700528 float* vtx = mTextMeshPtr;
529 float* tex = vtx + 3;
Romain Guy694b5192010-07-21 21:33:20 -0700530
531 // position is slot 0
532 uint32_t slot = 0;
533 glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx);
534
535 // texture0 is slot 1
536 slot = 1;
537 glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex);
538
539 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
540 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
541}
542
543void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
544 float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
545 float x4, float y4, float z4, float u4, float v4) {
Romain Guy09147fb2010-07-22 13:08:20 -0700546 if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) {
547 return;
548 }
549
Romain Guy694b5192010-07-21 21:33:20 -0700550 const uint32_t vertsPerQuad = 4;
551 const uint32_t floatsPerVert = 5;
Romain Guy51769a62010-07-23 00:28:00 -0700552 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700553
Romain Guy694b5192010-07-21 21:33:20 -0700554 (*currentPos++) = x1;
555 (*currentPos++) = y1;
556 (*currentPos++) = z1;
557 (*currentPos++) = u1;
558 (*currentPos++) = v1;
559
560 (*currentPos++) = x2;
561 (*currentPos++) = y2;
562 (*currentPos++) = z2;
563 (*currentPos++) = u2;
564 (*currentPos++) = v2;
565
566 (*currentPos++) = x3;
567 (*currentPos++) = y3;
568 (*currentPos++) = z3;
569 (*currentPos++) = u3;
570 (*currentPos++) = v3;
571
572 (*currentPos++) = x4;
573 (*currentPos++) = y4;
574 (*currentPos++) = z4;
575 (*currentPos++) = u4;
576 (*currentPos++) = v4;
577
578 mCurrentQuadIndex++;
579
580 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
581 issueDrawCommand();
582 mCurrentQuadIndex = 0;
583 }
584}
585
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700586uint32_t FontRenderer::getRemainingCacheCapacity() {
587 uint32_t remainingCapacity = 0;
588 float totalPixels = 0;
589 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
590 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
591 totalPixels += mCacheLines[i]->mMaxWidth;
592 }
593 remainingCapacity = (remainingCapacity * 100) / totalPixels;
594 return remainingCapacity;
595}
596
597void FontRenderer::precacheLatin(SkPaint* paint) {
598 // Remaining capacity is measured in %
599 uint32_t remainingCapacity = getRemainingCacheCapacity();
600 uint32_t precacheIdx = 0;
601 while(remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
602 mCurrentFont->getCachedUTFChar(paint, (int32_t)mLatinPrecache[precacheIdx]);
603 remainingCapacity = getRemainingCacheCapacity();
604 precacheIdx ++;
605 }
606}
607
608void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
609 uint32_t currentNumFonts = mActiveFonts.size();
Romain Guy694b5192010-07-21 21:33:20 -0700610 mCurrentFont = Font::create(this, fontId, fontSize);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700611
612 const float maxPrecacheFontSize = 40.0f;
613 bool isNewFont = currentNumFonts != mActiveFonts.size();
614
615 if(isNewFont && fontSize <= maxPrecacheFontSize ){
616 precacheLatin(paint);
617 }
Romain Guy694b5192010-07-21 21:33:20 -0700618}
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700619FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
620 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
621
622 Rect bounds;
623 mCurrentFont->measureUTF(paint, text, startIndex, len, numGlyphs, &bounds);
624 uint32_t paddedWidth = (uint32_t)(bounds.right - bounds.left) + 2*radius;
625 uint32_t paddedHeight = (uint32_t)(bounds.top - bounds.bottom) + 2*radius;
626 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
627 for(uint32_t i = 0; i < paddedWidth * paddedHeight; i ++) {
628 dataBuffer[i] = 0;
629 }
630 int penX = radius - bounds.left;
631 int penY = radius - bounds.bottom;
632
633 mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, penX, penY,
634 dataBuffer, paddedWidth, paddedHeight);
635 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
636
637 DropShadow image;
638 image.width = paddedWidth;
639 image.height = paddedHeight;
640 image.image = dataBuffer;
641 image.penX = penX;
642 image.penY = penY;
643 return image;
644}
Romain Guy694b5192010-07-21 21:33:20 -0700645
Romain Guy51769a62010-07-23 00:28:00 -0700646void FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
647 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y) {
Romain Guy694b5192010-07-21 21:33:20 -0700648 checkInit();
649
Romain Guy09147fb2010-07-22 13:08:20 -0700650 if (!mCurrentFont) {
651 LOGE("No font set");
Romain Guy694b5192010-07-21 21:33:20 -0700652 return;
653 }
654
Romain Guy09147fb2010-07-22 13:08:20 -0700655 mClip = clip;
Romain Guy51769a62010-07-23 00:28:00 -0700656 mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y);
Romain Guy694b5192010-07-21 21:33:20 -0700657
658 if (mCurrentQuadIndex != 0) {
659 issueDrawCommand();
660 mCurrentQuadIndex = 0;
661 }
662}
663
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700664void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
665 // Compute gaussian weights for the blur
666 // e is the euler's number
667 float e = 2.718281828459045f;
668 float pi = 3.1415926535897932f;
669 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
670 // x is of the form [-radius .. 0 .. radius]
671 // and sigma varies with radius.
672 // Based on some experimental radius values and sigma's
673 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700674 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700675 // The larger the radius gets, the more our gaussian blur
676 // will resemble a box blur since with large sigma
677 // the gaussian curve begins to lose its shape
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700678 float sigma = 0.3f * (float)radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700679
680 // Now compute the coefficints
681 // We will store some redundant values to save some math during
682 // the blur calculations
683 // precompute some values
684 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
685 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
686
687 float normalizeFactor = 0.0f;
688 for(int32_t r = -radius; r <= radius; r ++) {
689 float floatR = (float)r;
690 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
691 normalizeFactor += weights[r + radius];
692 }
693
694 //Now we need to normalize the weights because all our coefficients need to add up to one
695 normalizeFactor = 1.0f / normalizeFactor;
696 for(int32_t r = -radius; r <= radius; r ++) {
697 weights[r + radius] *= normalizeFactor;
698 }
699}
700
701void FontRenderer::horizontalBlur(float* weights, int32_t radius,
702 const uint8_t* source, uint8_t* dest,
703 int32_t width, int32_t height) {
704 float blurredPixel = 0.0f;
705 float currentPixel = 0.0f;
706
707 for(int32_t y = 0; y < height; y ++) {
708
709 const uint8_t* input = source + y * width;
710 uint8_t* output = dest + y * width;
711
712 for(int32_t x = 0; x < width; x ++) {
713 blurredPixel = 0.0f;
714 const float* gPtr = weights;
715 // Optimization for non-border pixels
716 if ((x > radius) && (x < (width - radius))) {
717 const uint8_t *i = input + (x - radius);
718 for(int r = -radius; r <= radius; r ++) {
719 currentPixel = (float)(*i);
720 blurredPixel += currentPixel * gPtr[0];
721 gPtr++;
722 i++;
723 }
724 } else {
725 for(int32_t r = -radius; r <= radius; r ++) {
726 // Stepping left and right away from the pixel
727 int validW = x + r;
728 if(validW < 0) {
729 validW = 0;
730 }
731 if(validW > width - 1) {
732 validW = width - 1;
733 }
734
735 currentPixel = (float)(input[validW]);
736 blurredPixel += currentPixel * gPtr[0];
737 gPtr++;
738 }
739 }
740 *output = (uint8_t)blurredPixel;
741 output ++;
742 }
743 }
744}
745
746void FontRenderer::verticalBlur(float* weights, int32_t radius,
747 const uint8_t* source, uint8_t* dest,
748 int32_t width, int32_t height) {
749 float blurredPixel = 0.0f;
750 float currentPixel = 0.0f;
751
752 for(int32_t y = 0; y < height; y ++) {
753
754 uint8_t* output = dest + y * width;
755
756 for(int32_t x = 0; x < width; x ++) {
757 blurredPixel = 0.0f;
758 const float* gPtr = weights;
759 const uint8_t* input = source + x;
760 // Optimization for non-border pixels
761 if ((y > radius) && (y < (height - radius))) {
762 const uint8_t *i = input + ((y - radius) * width);
763 for(int32_t r = -radius; r <= radius; r ++) {
764 currentPixel = (float)(*i);
765 blurredPixel += currentPixel * gPtr[0];
766 gPtr++;
767 i += width;
768 }
769 } else {
770 for(int32_t r = -radius; r <= radius; r ++) {
771 int validH = y + r;
772 // Clamp to zero and width
773 if(validH < 0) {
774 validH = 0;
775 }
776 if(validH > height - 1) {
777 validH = height - 1;
778 }
779
780 const uint8_t *i = input + validH * width;
781 currentPixel = (float)(*i);
782 blurredPixel += currentPixel * gPtr[0];
783 gPtr++;
784 }
785 }
786 *output = (uint8_t)blurredPixel;
787 output ++;
788 }
789 }
790}
791
792
793void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
794 float *gaussian = new float[2 * radius + 1];
795 computeGaussianWeights(gaussian, radius);
796 uint8_t* scratch = new uint8_t[width * height];
797 horizontalBlur(gaussian, radius, image, scratch, width, height);
798 verticalBlur(gaussian, radius, scratch, image, width, height);
799 delete[] gaussian;
800 delete[] scratch;
801}
802
Romain Guy694b5192010-07-21 21:33:20 -0700803}; // namespace uirenderer
804}; // namespace android