blob: ccc92ebdfe18466de8aee89513cecb146629429a [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
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700104void Font::drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700105 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 Guy61c8c9c2010-08-09 20:48:09 -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 Guy694b5192010-07-21 21:33:20 -0700289 mInitialized = false;
290 mMaxNumberOfQuads = 1024;
291 mCurrentQuadIndex = 0;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700292 mTextureId = 0;
Romain Guy694b5192010-07-21 21:33:20 -0700293
Romain Guy9cccc2b92010-08-07 23:46:15 -0700294 mTextMeshPtr = NULL;
295 mTextTexture = NULL;
296
Romain Guy694b5192010-07-21 21:33:20 -0700297 mIndexBufferID = 0;
298
Romain Guy51769a62010-07-23 00:28:00 -0700299 mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700300 mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -0700301
302 char property[PROPERTY_VALUE_MAX];
303 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
304 LOGD(" Setting text cache width to %s pixels", property);
305 mCacheWidth = atoi(property);
306 } else {
307 LOGD(" Using default text cache width of %i pixels", mCacheWidth);
308 }
309
310 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
311 LOGD(" Setting text cache width to %s pixels", property);
312 mCacheHeight = atoi(property);
313 } else {
314 LOGD(" Using default text cache height of %i pixels", mCacheHeight);
315 }
Romain Guy694b5192010-07-21 21:33:20 -0700316}
317
318FontRenderer::~FontRenderer() {
319 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
320 delete mCacheLines[i];
321 }
322 mCacheLines.clear();
323
Romain Guy9cccc2b92010-08-07 23:46:15 -0700324 if (mInitialized) {
325 delete[] mTextMeshPtr;
326 delete[] mTextTexture;
327 }
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700328
Romain Guy9cccc2b92010-08-07 23:46:15 -0700329 if (mTextureId) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700330 glDeleteTextures(1, &mTextureId);
331 }
Romain Guy694b5192010-07-21 21:33:20 -0700332
333 Vector<Font*> fontsToDereference = mActiveFonts;
334 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
335 delete fontsToDereference[i];
336 }
337}
338
339void FontRenderer::flushAllAndInvalidate() {
340 if (mCurrentQuadIndex != 0) {
341 issueDrawCommand();
342 mCurrentQuadIndex = 0;
343 }
344 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
345 mActiveFonts[i]->invalidateTextureCache();
346 }
347 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
348 mCacheLines[i]->mCurrentCol = 0;
349 }
350}
351
Romain Guy51769a62010-07-23 00:28:00 -0700352bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
Romain Guy694b5192010-07-21 21:33:20 -0700353 // If the glyph is too tall, don't cache it
354 if (glyph.fWidth > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
355 LOGE("Font size to large to fit in cache. width, height = %i, %i",
356 (int) glyph.fWidth, (int) glyph.fHeight);
357 return false;
358 }
359
360 // Now copy the bitmap into the cache texture
361 uint32_t startX = 0;
362 uint32_t startY = 0;
363
364 bool bitmapFit = false;
365 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
366 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
367 if (bitmapFit) {
368 break;
369 }
370 }
371
372 // If the new glyph didn't fit, flush the state so far and invalidate everything
373 if (!bitmapFit) {
374 flushAllAndInvalidate();
375
376 // Try to fit it again
377 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
378 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
379 if (bitmapFit) {
380 break;
381 }
382 }
383
384 // if we still don't fit, something is wrong and we shouldn't draw
385 if (!bitmapFit) {
386 LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
387 (int) glyph.fWidth, (int) glyph.fHeight);
388 return false;
389 }
390 }
391
392 *retOriginX = startX;
393 *retOriginY = startY;
394
395 uint32_t endX = startX + glyph.fWidth;
396 uint32_t endY = startY + glyph.fHeight;
397
398 uint32_t cacheWidth = mCacheWidth;
399
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700400 uint8_t* cacheBuffer = mTextTexture;
401 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700402 unsigned int stride = glyph.rowBytes();
403
404 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
405 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
406 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700407 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
Romain Guy694b5192010-07-21 21:33:20 -0700408 cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
409 }
410 }
411
412 return true;
413}
414
415void FontRenderer::initTextTexture() {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700416 mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
Romain Guy694b5192010-07-21 21:33:20 -0700417 mUploadTexture = false;
418
419 glGenTextures(1, &mTextureId);
420 glBindTexture(GL_TEXTURE_2D, mTextureId);
421 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700422 // Initialize texture dimentions
423 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700424 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Romain Guy694b5192010-07-21 21:33:20 -0700425
426 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
427 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
428
429 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
430 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
431
432 // Split up our cache texture into lines of certain widths
433 int nextLine = 0;
Romain Guy51769a62010-07-23 00:28:00 -0700434 mCacheLines.push(new CacheTextureLine(mCacheWidth, 16, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700435 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700436 mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700437 nextLine += mCacheLines.top()->mMaxHeight;
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700438 mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0));
439 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700440 mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, 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, 32, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700443 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700444 mCacheLines.push(new CacheTextureLine(mCacheWidth, 40, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700445 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700446 mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700447}
448
449// Avoid having to reallocate memory and render quad by quad
450void FontRenderer::initVertexArrayBuffers() {
451 uint32_t numIndicies = mMaxNumberOfQuads * 6;
452 uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700453 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700454
455 // Four verts, two triangles , six indices per quad
456 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
457 int i6 = i * 6;
458 int i4 = i * 4;
459
460 indexBufferData[i6 + 0] = i4 + 0;
461 indexBufferData[i6 + 1] = i4 + 1;
462 indexBufferData[i6 + 2] = i4 + 2;
463
464 indexBufferData[i6 + 3] = i4 + 0;
465 indexBufferData[i6 + 4] = i4 + 2;
466 indexBufferData[i6 + 5] = i4 + 3;
467 }
468
469 glGenBuffers(1, &mIndexBufferID);
470 glBindBuffer(GL_ARRAY_BUFFER, mIndexBufferID);
471 glBufferData(GL_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_DYNAMIC_DRAW);
472 glBindBuffer(GL_ARRAY_BUFFER, 0);
473
474 free(indexBufferData);
475
476 uint32_t coordSize = 3;
477 uint32_t uvSize = 2;
478 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700479 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
480 mTextMeshPtr = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700481}
482
483// We don't want to allocate anything unless we actually draw text
484void FontRenderer::checkInit() {
485 if (mInitialized) {
486 return;
487 }
488
489 initTextTexture();
490 initVertexArrayBuffers();
491
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700492 // We store a string with letters in a rough frequency of occurrence
493 mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
494 mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
495 mLatinPrecache += String16(",.?!()-+@;:`'");
496 mLatinPrecache += String16("0123456789");
497
Romain Guy694b5192010-07-21 21:33:20 -0700498 mInitialized = true;
499}
500
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700501void FontRenderer::checkTextureUpdate() {
502 if (!mUploadTexture) {
503 return;
Romain Guy694b5192010-07-21 21:33:20 -0700504 }
505
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700506 glBindTexture(GL_TEXTURE_2D, mTextureId);
507
508 // Iterate over all the cache lines and see which ones need to be updated
509 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
510 CacheTextureLine* cl = mCacheLines[i];
511 if(cl->mDirty) {
512 uint32_t xOffset = 0;
513 uint32_t yOffset = cl->mCurrentRow;
514 uint32_t width = mCacheWidth;
515 uint32_t height = cl->mMaxHeight;
Romain Guy1e45aae2010-08-13 19:39:53 -0700516 void* textureData = mTextTexture + yOffset*width;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700517
518 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700519 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700520
521 cl->mDirty = false;
522 }
523 }
524
525 mUploadTexture = false;
526}
527
528void FontRenderer::issueDrawCommand() {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700529 checkTextureUpdate();
530
Romain Guy51769a62010-07-23 00:28:00 -0700531 float* vtx = mTextMeshPtr;
532 float* tex = vtx + 3;
Romain Guy694b5192010-07-21 21:33:20 -0700533
534 // position is slot 0
535 uint32_t slot = 0;
536 glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx);
537
538 // texture0 is slot 1
539 slot = 1;
540 glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex);
541
542 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
543 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
544}
545
546void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
547 float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
548 float x4, float y4, float z4, float u4, float v4) {
Romain Guy09147fb2010-07-22 13:08:20 -0700549 if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) {
550 return;
551 }
552
Romain Guy694b5192010-07-21 21:33:20 -0700553 const uint32_t vertsPerQuad = 4;
554 const uint32_t floatsPerVert = 5;
Romain Guy51769a62010-07-23 00:28:00 -0700555 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700556
Romain Guy694b5192010-07-21 21:33:20 -0700557 (*currentPos++) = x1;
558 (*currentPos++) = y1;
559 (*currentPos++) = z1;
560 (*currentPos++) = u1;
561 (*currentPos++) = v1;
562
563 (*currentPos++) = x2;
564 (*currentPos++) = y2;
565 (*currentPos++) = z2;
566 (*currentPos++) = u2;
567 (*currentPos++) = v2;
568
569 (*currentPos++) = x3;
570 (*currentPos++) = y3;
571 (*currentPos++) = z3;
572 (*currentPos++) = u3;
573 (*currentPos++) = v3;
574
575 (*currentPos++) = x4;
576 (*currentPos++) = y4;
577 (*currentPos++) = z4;
578 (*currentPos++) = u4;
579 (*currentPos++) = v4;
580
581 mCurrentQuadIndex++;
582
583 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
584 issueDrawCommand();
585 mCurrentQuadIndex = 0;
586 }
587}
588
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700589uint32_t FontRenderer::getRemainingCacheCapacity() {
590 uint32_t remainingCapacity = 0;
591 float totalPixels = 0;
592 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
593 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
594 totalPixels += mCacheLines[i]->mMaxWidth;
595 }
596 remainingCapacity = (remainingCapacity * 100) / totalPixels;
597 return remainingCapacity;
598}
599
600void FontRenderer::precacheLatin(SkPaint* paint) {
601 // Remaining capacity is measured in %
602 uint32_t remainingCapacity = getRemainingCacheCapacity();
603 uint32_t precacheIdx = 0;
604 while(remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
605 mCurrentFont->getCachedUTFChar(paint, (int32_t)mLatinPrecache[precacheIdx]);
606 remainingCapacity = getRemainingCacheCapacity();
607 precacheIdx ++;
608 }
609}
610
611void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
612 uint32_t currentNumFonts = mActiveFonts.size();
Romain Guy694b5192010-07-21 21:33:20 -0700613 mCurrentFont = Font::create(this, fontId, fontSize);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700614
615 const float maxPrecacheFontSize = 40.0f;
616 bool isNewFont = currentNumFonts != mActiveFonts.size();
617
618 if(isNewFont && fontSize <= maxPrecacheFontSize ){
619 precacheLatin(paint);
620 }
Romain Guy694b5192010-07-21 21:33:20 -0700621}
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700622FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Romain Guy1e45aae2010-08-13 19:39:53 -0700623 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
624 checkInit();
625
626 if (!mCurrentFont) {
627 DropShadow image;
628 image.width = 0;
629 image.height = 0;
630 image.image = NULL;
631 image.penX = 0;
632 image.penY = 0;
633 return image;
634 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700635
636 Rect bounds;
637 mCurrentFont->measureUTF(paint, text, startIndex, len, numGlyphs, &bounds);
Romain Guy1e45aae2010-08-13 19:39:53 -0700638 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
639 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700640 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guy1e45aae2010-08-13 19:39:53 -0700641 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700642 dataBuffer[i] = 0;
643 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700644
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700645 int penX = radius - bounds.left;
646 int penY = radius - bounds.bottom;
647
648 mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, penX, penY,
Romain Guy1e45aae2010-08-13 19:39:53 -0700649 dataBuffer, paddedWidth, paddedHeight);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700650 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
651
652 DropShadow image;
653 image.width = paddedWidth;
654 image.height = paddedHeight;
655 image.image = dataBuffer;
656 image.penX = penX;
657 image.penY = penY;
658 return image;
659}
Romain Guy694b5192010-07-21 21:33:20 -0700660
Romain Guy51769a62010-07-23 00:28:00 -0700661void FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
662 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y) {
Romain Guy694b5192010-07-21 21:33:20 -0700663 checkInit();
664
Romain Guy09147fb2010-07-22 13:08:20 -0700665 if (!mCurrentFont) {
666 LOGE("No font set");
Romain Guy694b5192010-07-21 21:33:20 -0700667 return;
668 }
669
Romain Guy09147fb2010-07-22 13:08:20 -0700670 mClip = clip;
Romain Guy51769a62010-07-23 00:28:00 -0700671 mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y);
Romain Guy694b5192010-07-21 21:33:20 -0700672
673 if (mCurrentQuadIndex != 0) {
674 issueDrawCommand();
675 mCurrentQuadIndex = 0;
676 }
677}
678
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700679void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
680 // Compute gaussian weights for the blur
681 // e is the euler's number
682 float e = 2.718281828459045f;
683 float pi = 3.1415926535897932f;
684 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
685 // x is of the form [-radius .. 0 .. radius]
686 // and sigma varies with radius.
687 // Based on some experimental radius values and sigma's
688 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700689 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700690 // The larger the radius gets, the more our gaussian blur
691 // will resemble a box blur since with large sigma
692 // the gaussian curve begins to lose its shape
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700693 float sigma = 0.3f * (float)radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700694
695 // Now compute the coefficints
696 // We will store some redundant values to save some math during
697 // the blur calculations
698 // precompute some values
699 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
700 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
701
702 float normalizeFactor = 0.0f;
703 for(int32_t r = -radius; r <= radius; r ++) {
704 float floatR = (float)r;
705 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
706 normalizeFactor += weights[r + radius];
707 }
708
709 //Now we need to normalize the weights because all our coefficients need to add up to one
710 normalizeFactor = 1.0f / normalizeFactor;
711 for(int32_t r = -radius; r <= radius; r ++) {
712 weights[r + radius] *= normalizeFactor;
713 }
714}
715
716void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700717 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700718 float blurredPixel = 0.0f;
719 float currentPixel = 0.0f;
720
721 for(int32_t y = 0; y < height; y ++) {
722
723 const uint8_t* input = source + y * width;
724 uint8_t* output = dest + y * width;
725
726 for(int32_t x = 0; x < width; x ++) {
727 blurredPixel = 0.0f;
728 const float* gPtr = weights;
729 // Optimization for non-border pixels
730 if ((x > radius) && (x < (width - radius))) {
731 const uint8_t *i = input + (x - radius);
732 for(int r = -radius; r <= radius; r ++) {
733 currentPixel = (float)(*i);
734 blurredPixel += currentPixel * gPtr[0];
735 gPtr++;
736 i++;
737 }
738 } else {
739 for(int32_t r = -radius; r <= radius; r ++) {
740 // Stepping left and right away from the pixel
741 int validW = x + r;
742 if(validW < 0) {
743 validW = 0;
744 }
745 if(validW > width - 1) {
746 validW = width - 1;
747 }
748
749 currentPixel = (float)(input[validW]);
750 blurredPixel += currentPixel * gPtr[0];
751 gPtr++;
752 }
753 }
754 *output = (uint8_t)blurredPixel;
755 output ++;
756 }
757 }
758}
759
760void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700761 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700762 float blurredPixel = 0.0f;
763 float currentPixel = 0.0f;
764
765 for(int32_t y = 0; y < height; y ++) {
766
767 uint8_t* output = dest + y * width;
768
769 for(int32_t x = 0; x < width; x ++) {
770 blurredPixel = 0.0f;
771 const float* gPtr = weights;
772 const uint8_t* input = source + x;
773 // Optimization for non-border pixels
774 if ((y > radius) && (y < (height - radius))) {
775 const uint8_t *i = input + ((y - radius) * width);
776 for(int32_t r = -radius; r <= radius; r ++) {
777 currentPixel = (float)(*i);
778 blurredPixel += currentPixel * gPtr[0];
779 gPtr++;
780 i += width;
781 }
782 } else {
783 for(int32_t r = -radius; r <= radius; r ++) {
784 int validH = y + r;
785 // Clamp to zero and width
786 if(validH < 0) {
787 validH = 0;
788 }
789 if(validH > height - 1) {
790 validH = height - 1;
791 }
792
793 const uint8_t *i = input + validH * width;
794 currentPixel = (float)(*i);
795 blurredPixel += currentPixel * gPtr[0];
796 gPtr++;
797 }
798 }
799 *output = (uint8_t)blurredPixel;
800 output ++;
801 }
802 }
803}
804
805
806void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
807 float *gaussian = new float[2 * radius + 1];
808 computeGaussianWeights(gaussian, radius);
809 uint8_t* scratch = new uint8_t[width * height];
810 horizontalBlur(gaussian, radius, image, scratch, width, height);
811 verticalBlur(gaussian, radius, scratch, image, width, height);
812 delete[] gaussian;
813 delete[] scratch;
814}
815
Romain Guy694b5192010-07-21 21:33:20 -0700816}; // namespace uirenderer
817}; // namespace android