blob: 8d00e85ffa2ccc18068ef63c037263b2bfdae214 [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
Romain Guy9cccc2b2010-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 Guy9cccc2b2010-08-07 23:46:15 -0700325 if (mInitialized) {
326 delete[] mTextMeshPtr;
327 delete[] mTextTexture;
328 }
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700329
Romain Guy9cccc2b2010-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 Guy694b5192010-07-21 21:33:20 -0700409 cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
410 }
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,
425 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;
517 void* textureData = mTextTexture + yOffset*width;
518
519 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
520 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
521
522 cl->mDirty = false;
523 }
524 }
525
526 mUploadTexture = false;
527}
528
529void FontRenderer::issueDrawCommand() {
530
531 checkTextureUpdate();
532
Romain Guy51769a62010-07-23 00:28:00 -0700533 float* vtx = mTextMeshPtr;
534 float* tex = vtx + 3;
Romain Guy694b5192010-07-21 21:33:20 -0700535
536 // position is slot 0
537 uint32_t slot = 0;
538 glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx);
539
540 // texture0 is slot 1
541 slot = 1;
542 glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex);
543
544 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
545 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
546}
547
548void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
549 float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
550 float x4, float y4, float z4, float u4, float v4) {
Romain Guy09147fb2010-07-22 13:08:20 -0700551 if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) {
552 return;
553 }
554
Romain Guy694b5192010-07-21 21:33:20 -0700555 const uint32_t vertsPerQuad = 4;
556 const uint32_t floatsPerVert = 5;
Romain Guy51769a62010-07-23 00:28:00 -0700557 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700558
Romain Guy694b5192010-07-21 21:33:20 -0700559 (*currentPos++) = x1;
560 (*currentPos++) = y1;
561 (*currentPos++) = z1;
562 (*currentPos++) = u1;
563 (*currentPos++) = v1;
564
565 (*currentPos++) = x2;
566 (*currentPos++) = y2;
567 (*currentPos++) = z2;
568 (*currentPos++) = u2;
569 (*currentPos++) = v2;
570
571 (*currentPos++) = x3;
572 (*currentPos++) = y3;
573 (*currentPos++) = z3;
574 (*currentPos++) = u3;
575 (*currentPos++) = v3;
576
577 (*currentPos++) = x4;
578 (*currentPos++) = y4;
579 (*currentPos++) = z4;
580 (*currentPos++) = u4;
581 (*currentPos++) = v4;
582
583 mCurrentQuadIndex++;
584
585 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
586 issueDrawCommand();
587 mCurrentQuadIndex = 0;
588 }
589}
590
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700591uint32_t FontRenderer::getRemainingCacheCapacity() {
592 uint32_t remainingCapacity = 0;
593 float totalPixels = 0;
594 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
595 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
596 totalPixels += mCacheLines[i]->mMaxWidth;
597 }
598 remainingCapacity = (remainingCapacity * 100) / totalPixels;
599 return remainingCapacity;
600}
601
602void FontRenderer::precacheLatin(SkPaint* paint) {
603 // Remaining capacity is measured in %
604 uint32_t remainingCapacity = getRemainingCacheCapacity();
605 uint32_t precacheIdx = 0;
606 while(remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
607 mCurrentFont->getCachedUTFChar(paint, (int32_t)mLatinPrecache[precacheIdx]);
608 remainingCapacity = getRemainingCacheCapacity();
609 precacheIdx ++;
610 }
611}
612
613void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
614 uint32_t currentNumFonts = mActiveFonts.size();
Romain Guy694b5192010-07-21 21:33:20 -0700615 mCurrentFont = Font::create(this, fontId, fontSize);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700616
617 const float maxPrecacheFontSize = 40.0f;
618 bool isNewFont = currentNumFonts != mActiveFonts.size();
619
620 if(isNewFont && fontSize <= maxPrecacheFontSize ){
621 precacheLatin(paint);
622 }
Romain Guy694b5192010-07-21 21:33:20 -0700623}
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700624FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
625 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
626
627 Rect bounds;
628 mCurrentFont->measureUTF(paint, text, startIndex, len, numGlyphs, &bounds);
629 uint32_t paddedWidth = (uint32_t)(bounds.right - bounds.left) + 2*radius;
630 uint32_t paddedHeight = (uint32_t)(bounds.top - bounds.bottom) + 2*radius;
631 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
632 for(uint32_t i = 0; i < paddedWidth * paddedHeight; i ++) {
633 dataBuffer[i] = 0;
634 }
635 int penX = radius - bounds.left;
636 int penY = radius - bounds.bottom;
637
638 mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, penX, penY,
639 dataBuffer, paddedWidth, paddedHeight);
640 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
641
642 DropShadow image;
643 image.width = paddedWidth;
644 image.height = paddedHeight;
645 image.image = dataBuffer;
646 image.penX = penX;
647 image.penY = penY;
648 return image;
649}
Romain Guy694b5192010-07-21 21:33:20 -0700650
Romain Guy51769a62010-07-23 00:28:00 -0700651void FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
652 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y) {
Romain Guy694b5192010-07-21 21:33:20 -0700653 checkInit();
654
Romain Guy09147fb2010-07-22 13:08:20 -0700655 if (!mCurrentFont) {
656 LOGE("No font set");
Romain Guy694b5192010-07-21 21:33:20 -0700657 return;
658 }
659
Romain Guy09147fb2010-07-22 13:08:20 -0700660 mClip = clip;
Romain Guy51769a62010-07-23 00:28:00 -0700661 mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y);
Romain Guy694b5192010-07-21 21:33:20 -0700662
663 if (mCurrentQuadIndex != 0) {
664 issueDrawCommand();
665 mCurrentQuadIndex = 0;
666 }
667}
668
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700669void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
670 // Compute gaussian weights for the blur
671 // e is the euler's number
672 float e = 2.718281828459045f;
673 float pi = 3.1415926535897932f;
674 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
675 // x is of the form [-radius .. 0 .. radius]
676 // and sigma varies with radius.
677 // Based on some experimental radius values and sigma's
678 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700679 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700680 // The larger the radius gets, the more our gaussian blur
681 // will resemble a box blur since with large sigma
682 // the gaussian curve begins to lose its shape
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700683 float sigma = 0.3f * (float)radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700684
685 // Now compute the coefficints
686 // We will store some redundant values to save some math during
687 // the blur calculations
688 // precompute some values
689 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
690 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
691
692 float normalizeFactor = 0.0f;
693 for(int32_t r = -radius; r <= radius; r ++) {
694 float floatR = (float)r;
695 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
696 normalizeFactor += weights[r + radius];
697 }
698
699 //Now we need to normalize the weights because all our coefficients need to add up to one
700 normalizeFactor = 1.0f / normalizeFactor;
701 for(int32_t r = -radius; r <= radius; r ++) {
702 weights[r + radius] *= normalizeFactor;
703 }
704}
705
706void FontRenderer::horizontalBlur(float* weights, int32_t radius,
707 const uint8_t* source, uint8_t* dest,
708 int32_t width, int32_t height) {
709 float blurredPixel = 0.0f;
710 float currentPixel = 0.0f;
711
712 for(int32_t y = 0; y < height; y ++) {
713
714 const uint8_t* input = source + y * width;
715 uint8_t* output = dest + y * width;
716
717 for(int32_t x = 0; x < width; x ++) {
718 blurredPixel = 0.0f;
719 const float* gPtr = weights;
720 // Optimization for non-border pixels
721 if ((x > radius) && (x < (width - radius))) {
722 const uint8_t *i = input + (x - radius);
723 for(int r = -radius; r <= radius; r ++) {
724 currentPixel = (float)(*i);
725 blurredPixel += currentPixel * gPtr[0];
726 gPtr++;
727 i++;
728 }
729 } else {
730 for(int32_t r = -radius; r <= radius; r ++) {
731 // Stepping left and right away from the pixel
732 int validW = x + r;
733 if(validW < 0) {
734 validW = 0;
735 }
736 if(validW > width - 1) {
737 validW = width - 1;
738 }
739
740 currentPixel = (float)(input[validW]);
741 blurredPixel += currentPixel * gPtr[0];
742 gPtr++;
743 }
744 }
745 *output = (uint8_t)blurredPixel;
746 output ++;
747 }
748 }
749}
750
751void FontRenderer::verticalBlur(float* weights, int32_t radius,
752 const uint8_t* source, uint8_t* dest,
753 int32_t width, int32_t height) {
754 float blurredPixel = 0.0f;
755 float currentPixel = 0.0f;
756
757 for(int32_t y = 0; y < height; y ++) {
758
759 uint8_t* output = dest + y * width;
760
761 for(int32_t x = 0; x < width; x ++) {
762 blurredPixel = 0.0f;
763 const float* gPtr = weights;
764 const uint8_t* input = source + x;
765 // Optimization for non-border pixels
766 if ((y > radius) && (y < (height - radius))) {
767 const uint8_t *i = input + ((y - radius) * width);
768 for(int32_t r = -radius; r <= radius; r ++) {
769 currentPixel = (float)(*i);
770 blurredPixel += currentPixel * gPtr[0];
771 gPtr++;
772 i += width;
773 }
774 } else {
775 for(int32_t r = -radius; r <= radius; r ++) {
776 int validH = y + r;
777 // Clamp to zero and width
778 if(validH < 0) {
779 validH = 0;
780 }
781 if(validH > height - 1) {
782 validH = height - 1;
783 }
784
785 const uint8_t *i = input + validH * width;
786 currentPixel = (float)(*i);
787 blurredPixel += currentPixel * gPtr[0];
788 gPtr++;
789 }
790 }
791 *output = (uint8_t)blurredPixel;
792 output ++;
793 }
794 }
795}
796
797
798void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
799 float *gaussian = new float[2 * radius + 1];
800 computeGaussianWeights(gaussian, radius);
801 uint8_t* scratch = new uint8_t[width * height];
802 horizontalBlur(gaussian, radius, image, scratch, width, height);
803 verticalBlur(gaussian, radius, scratch, image, width, height);
804 delete[] gaussian;
805 delete[] scratch;
806}
807
Romain Guy694b5192010-07-21 21:33:20 -0700808}; // namespace uirenderer
809}; // namespace android