blob: feb45ec034f276f329ab7a1fcabdbf0bbac55db0 [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
65void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
Romain Guy694b5192010-07-21 21:33:20 -070066 int nPenX = x + glyph->mBitmapLeft;
67 int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
68
Romain Guy51769a62010-07-23 00:28:00 -070069 float u1 = glyph->mBitmapMinU;
70 float u2 = glyph->mBitmapMaxU;
71 float v1 = glyph->mBitmapMinV;
72 float v2 = glyph->mBitmapMaxV;
73
74 int width = (int) glyph->mBitmapWidth;
75 int height = (int) glyph->mBitmapHeight;
76
77 mState->appendMeshQuad(nPenX, nPenY, 0, u1, v2,
78 nPenX + width, nPenY, 0, u2, v2,
79 nPenX + width, nPenY - height, 0, u2, v1,
80 nPenX, nPenY - height, 0, u1, v1);
Romain Guy694b5192010-07-21 21:33:20 -070081}
82
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -070083void Font::drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
84 uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
85 int nPenX = x + glyph->mBitmapLeft;
86 int nPenY = y + glyph->mBitmapTop;
87
88 uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
89 uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
90
91 if(nPenX < 0 || nPenY < 0) {
92 LOGE("Cannot render into a bitmap, some of the glyph is below zero");
93 return;
94 }
95
96 if(nPenX + glyph->mBitmapWidth >= bitmapW || nPenY + glyph->mBitmapHeight >= bitmapH) {
97 LOGE("Cannot render into a bitmap, dimentions too small");
98 return;
99 }
100
101 uint32_t cacheWidth = mState->getCacheWidth();
102 const uint8_t* cacheBuffer = mState->getTextTextureData();
103
104 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
105 for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
106 for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
107 uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
108 bitmap[bY * bitmapW + bX] = tempCol;
109 }
110 }
111
112}
113
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700114Font::CachedGlyphInfo* Font::getCachedUTFChar(SkPaint* paint, int32_t utfChar) {
115 CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueFor(utfChar);
116 if (cachedGlyph == NULL) {
117 cachedGlyph = cacheGlyph(paint, utfChar);
118 }
119
120 // Is the glyph still in texture cache?
121 if (!cachedGlyph->mIsValid) {
122 const SkGlyph& skiaGlyph = paint->getUnicharMetrics(utfChar);
123 updateGlyphCache(paint, skiaGlyph, cachedGlyph);
124 }
125
126 return cachedGlyph;
127}
128
Romain Guy51769a62010-07-23 00:28:00 -0700129void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700130 int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
Romain Guy694b5192010-07-21 21:33:20 -0700131 if (numGlyphs == 0 || text == NULL || len == 0) {
132 return;
133 }
134
135 int penX = x, penY = y;
136 int glyphsLeft = 1;
137 if (numGlyphs > 0) {
138 glyphsLeft = numGlyphs;
139 }
140
Romain Guy694b5192010-07-21 21:33:20 -0700141 text += start;
142
143 while (glyphsLeft > 0) {
Romain Guy694b5192010-07-21 21:33:20 -0700144 int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text);
145
146 // Reached the end of the string or encountered
147 if (utfChar < 0) {
148 break;
149 }
150
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700151 CachedGlyphInfo* cachedGlyph = getCachedUTFChar(paint, utfChar);
Romain Guy694b5192010-07-21 21:33:20 -0700152
153 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
154 if (cachedGlyph->mIsValid) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700155 if(bitmap != NULL) {
156 drawCachedGlyph(cachedGlyph, penX, penY, bitmap, bitmapW, bitmapH);
157 }
158 else {
159 drawCachedGlyph(cachedGlyph, penX, penY);
160 }
Romain Guy694b5192010-07-21 21:33:20 -0700161 }
162
Romain Guy09147fb2010-07-22 13:08:20 -0700163 penX += SkFixedFloor(cachedGlyph->mAdvanceX);
Romain Guy694b5192010-07-21 21:33:20 -0700164
165 // If we were given a specific number of glyphs, decrement
166 if (numGlyphs > 0) {
167 glyphsLeft--;
168 }
169 }
170}
171
Romain Guy51769a62010-07-23 00:28:00 -0700172void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
Romain Guy694b5192010-07-21 21:33:20 -0700173 glyph->mAdvanceX = skiaGlyph.fAdvanceX;
174 glyph->mAdvanceY = skiaGlyph.fAdvanceY;
175 glyph->mBitmapLeft = skiaGlyph.fLeft;
176 glyph->mBitmapTop = skiaGlyph.fTop;
177
178 uint32_t startX = 0;
179 uint32_t startY = 0;
180
Romain Guy694b5192010-07-21 21:33:20 -0700181 // Get the bitmap for the glyph
182 paint->findImage(skiaGlyph);
Romain Guy51769a62010-07-23 00:28:00 -0700183 glyph->mIsValid = mState->cacheBitmap(skiaGlyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700184
185 if (!glyph->mIsValid) {
186 return;
187 }
188
189 uint32_t endX = startX + skiaGlyph.fWidth;
190 uint32_t endY = startY + skiaGlyph.fHeight;
191
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700192 glyph->mStartX = startX;
193 glyph->mStartY = startY;
Romain Guy694b5192010-07-21 21:33:20 -0700194 glyph->mBitmapWidth = skiaGlyph.fWidth;
195 glyph->mBitmapHeight = skiaGlyph.fHeight;
196
Romain Guy51769a62010-07-23 00:28:00 -0700197 uint32_t cacheWidth = mState->getCacheWidth();
198 uint32_t cacheHeight = mState->getCacheHeight();
Romain Guy694b5192010-07-21 21:33:20 -0700199
200 glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
201 glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
202 glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
203 glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
204
Romain Guy51769a62010-07-23 00:28:00 -0700205 mState->mUploadTexture = true;
Romain Guy694b5192010-07-21 21:33:20 -0700206}
207
Romain Guy51769a62010-07-23 00:28:00 -0700208Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, int32_t glyph) {
209 CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
Romain Guy694b5192010-07-21 21:33:20 -0700210 mCachedGlyphs.add(glyph, newGlyph);
211
212 const SkGlyph& skiaGlyph = paint->getUnicharMetrics(glyph);
213 newGlyph->mGlyphIndex = skiaGlyph.fID;
214 newGlyph->mIsValid = false;
215
216 updateGlyphCache(paint, skiaGlyph, newGlyph);
217
218 return newGlyph;
219}
220
221Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize) {
222 Vector<Font*> &activeFonts = state->mActiveFonts;
223
224 for (uint32_t i = 0; i < activeFonts.size(); i++) {
Romain Guy51769a62010-07-23 00:28:00 -0700225 Font* font = activeFonts[i];
226 if (font->mFontId == fontId && font->mFontSize == fontSize) {
227 return font;
Romain Guy694b5192010-07-21 21:33:20 -0700228 }
229 }
230
231 Font* newFont = new Font(state, fontId, fontSize);
232 activeFonts.push(newFont);
233 return newFont;
234}
235
236///////////////////////////////////////////////////////////////////////////////
237// FontRenderer
238///////////////////////////////////////////////////////////////////////////////
239
240FontRenderer::FontRenderer() {
Romain Guy51769a62010-07-23 00:28:00 -0700241 LOGD("Creating FontRenderer");
242
Romain Guy694b5192010-07-21 21:33:20 -0700243 mInitialized = false;
244 mMaxNumberOfQuads = 1024;
245 mCurrentQuadIndex = 0;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700246 mTextureId = 0;
Romain Guy694b5192010-07-21 21:33:20 -0700247
248 mIndexBufferID = 0;
249
Romain Guy51769a62010-07-23 00:28:00 -0700250 mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700251 mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -0700252
253 char property[PROPERTY_VALUE_MAX];
254 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
255 LOGD(" Setting text cache width to %s pixels", property);
256 mCacheWidth = atoi(property);
257 } else {
258 LOGD(" Using default text cache width of %i pixels", mCacheWidth);
259 }
260
261 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
262 LOGD(" Setting text cache width to %s pixels", property);
263 mCacheHeight = atoi(property);
264 } else {
265 LOGD(" Using default text cache height of %i pixels", mCacheHeight);
266 }
Romain Guy694b5192010-07-21 21:33:20 -0700267}
268
269FontRenderer::~FontRenderer() {
270 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
271 delete mCacheLines[i];
272 }
273 mCacheLines.clear();
274
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700275 delete[] mTextMeshPtr;
276 delete[] mTextTexture;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700277
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700278 if(mTextureId) {
279 glDeleteTextures(1, &mTextureId);
280 }
Romain Guy694b5192010-07-21 21:33:20 -0700281
282 Vector<Font*> fontsToDereference = mActiveFonts;
283 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
284 delete fontsToDereference[i];
285 }
286}
287
288void FontRenderer::flushAllAndInvalidate() {
289 if (mCurrentQuadIndex != 0) {
290 issueDrawCommand();
291 mCurrentQuadIndex = 0;
292 }
293 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
294 mActiveFonts[i]->invalidateTextureCache();
295 }
296 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
297 mCacheLines[i]->mCurrentCol = 0;
298 }
299}
300
Romain Guy51769a62010-07-23 00:28:00 -0700301bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
Romain Guy694b5192010-07-21 21:33:20 -0700302 // If the glyph is too tall, don't cache it
303 if (glyph.fWidth > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
304 LOGE("Font size to large to fit in cache. width, height = %i, %i",
305 (int) glyph.fWidth, (int) glyph.fHeight);
306 return false;
307 }
308
309 // Now copy the bitmap into the cache texture
310 uint32_t startX = 0;
311 uint32_t startY = 0;
312
313 bool bitmapFit = false;
314 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
315 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
316 if (bitmapFit) {
317 break;
318 }
319 }
320
321 // If the new glyph didn't fit, flush the state so far and invalidate everything
322 if (!bitmapFit) {
323 flushAllAndInvalidate();
324
325 // Try to fit it again
326 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
327 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
328 if (bitmapFit) {
329 break;
330 }
331 }
332
333 // if we still don't fit, something is wrong and we shouldn't draw
334 if (!bitmapFit) {
335 LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
336 (int) glyph.fWidth, (int) glyph.fHeight);
337 return false;
338 }
339 }
340
341 *retOriginX = startX;
342 *retOriginY = startY;
343
344 uint32_t endX = startX + glyph.fWidth;
345 uint32_t endY = startY + glyph.fHeight;
346
347 uint32_t cacheWidth = mCacheWidth;
348
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700349 uint8_t* cacheBuffer = mTextTexture;
350 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700351 unsigned int stride = glyph.rowBytes();
352
353 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
354 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
355 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700356 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
Romain Guy694b5192010-07-21 21:33:20 -0700357 cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
358 }
359 }
360
361 return true;
362}
363
364void FontRenderer::initTextTexture() {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700365 mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
Romain Guy694b5192010-07-21 21:33:20 -0700366 mUploadTexture = false;
367
368 glGenTextures(1, &mTextureId);
369 glBindTexture(GL_TEXTURE_2D, mTextureId);
370 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700371 // Initialize texture dimentions
372 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
373 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Romain Guy694b5192010-07-21 21:33:20 -0700374
375 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
376 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
377
378 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
379 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
380
381 // Split up our cache texture into lines of certain widths
382 int nextLine = 0;
Romain Guy51769a62010-07-23 00:28:00 -0700383 mCacheLines.push(new CacheTextureLine(mCacheWidth, 16, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700384 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700385 mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700386 nextLine += mCacheLines.top()->mMaxHeight;
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700387 mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0));
388 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700389 mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700390 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700391 mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700392 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700393 mCacheLines.push(new CacheTextureLine(mCacheWidth, 40, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700394 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700395 mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700396}
397
398// Avoid having to reallocate memory and render quad by quad
399void FontRenderer::initVertexArrayBuffers() {
400 uint32_t numIndicies = mMaxNumberOfQuads * 6;
401 uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700402 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700403
404 // Four verts, two triangles , six indices per quad
405 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
406 int i6 = i * 6;
407 int i4 = i * 4;
408
409 indexBufferData[i6 + 0] = i4 + 0;
410 indexBufferData[i6 + 1] = i4 + 1;
411 indexBufferData[i6 + 2] = i4 + 2;
412
413 indexBufferData[i6 + 3] = i4 + 0;
414 indexBufferData[i6 + 4] = i4 + 2;
415 indexBufferData[i6 + 5] = i4 + 3;
416 }
417
418 glGenBuffers(1, &mIndexBufferID);
419 glBindBuffer(GL_ARRAY_BUFFER, mIndexBufferID);
420 glBufferData(GL_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_DYNAMIC_DRAW);
421 glBindBuffer(GL_ARRAY_BUFFER, 0);
422
423 free(indexBufferData);
424
425 uint32_t coordSize = 3;
426 uint32_t uvSize = 2;
427 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700428 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
429 mTextMeshPtr = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700430}
431
432// We don't want to allocate anything unless we actually draw text
433void FontRenderer::checkInit() {
434 if (mInitialized) {
435 return;
436 }
437
438 initTextTexture();
439 initVertexArrayBuffers();
440
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700441 // We store a string with letters in a rough frequency of occurrence
442 mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
443 mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
444 mLatinPrecache += String16(",.?!()-+@;:`'");
445 mLatinPrecache += String16("0123456789");
446
Romain Guy694b5192010-07-21 21:33:20 -0700447 mInitialized = true;
448}
449
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700450void FontRenderer::checkTextureUpdate() {
451 if (!mUploadTexture) {
452 return;
Romain Guy694b5192010-07-21 21:33:20 -0700453 }
454
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700455 glBindTexture(GL_TEXTURE_2D, mTextureId);
456
457 // Iterate over all the cache lines and see which ones need to be updated
458 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
459 CacheTextureLine* cl = mCacheLines[i];
460 if(cl->mDirty) {
461 uint32_t xOffset = 0;
462 uint32_t yOffset = cl->mCurrentRow;
463 uint32_t width = mCacheWidth;
464 uint32_t height = cl->mMaxHeight;
465 void* textureData = mTextTexture + yOffset*width;
466
467 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
468 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
469
470 cl->mDirty = false;
471 }
472 }
473
474 mUploadTexture = false;
475}
476
477void FontRenderer::issueDrawCommand() {
478
479 checkTextureUpdate();
480
Romain Guy51769a62010-07-23 00:28:00 -0700481 float* vtx = mTextMeshPtr;
482 float* tex = vtx + 3;
Romain Guy694b5192010-07-21 21:33:20 -0700483
484 // position is slot 0
485 uint32_t slot = 0;
486 glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx);
487
488 // texture0 is slot 1
489 slot = 1;
490 glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex);
491
492 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
493 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
494}
495
496void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
497 float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
498 float x4, float y4, float z4, float u4, float v4) {
Romain Guy09147fb2010-07-22 13:08:20 -0700499 if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) {
500 return;
501 }
502
Romain Guy694b5192010-07-21 21:33:20 -0700503 const uint32_t vertsPerQuad = 4;
504 const uint32_t floatsPerVert = 5;
Romain Guy51769a62010-07-23 00:28:00 -0700505 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700506
Romain Guy694b5192010-07-21 21:33:20 -0700507 (*currentPos++) = x1;
508 (*currentPos++) = y1;
509 (*currentPos++) = z1;
510 (*currentPos++) = u1;
511 (*currentPos++) = v1;
512
513 (*currentPos++) = x2;
514 (*currentPos++) = y2;
515 (*currentPos++) = z2;
516 (*currentPos++) = u2;
517 (*currentPos++) = v2;
518
519 (*currentPos++) = x3;
520 (*currentPos++) = y3;
521 (*currentPos++) = z3;
522 (*currentPos++) = u3;
523 (*currentPos++) = v3;
524
525 (*currentPos++) = x4;
526 (*currentPos++) = y4;
527 (*currentPos++) = z4;
528 (*currentPos++) = u4;
529 (*currentPos++) = v4;
530
531 mCurrentQuadIndex++;
532
533 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
534 issueDrawCommand();
535 mCurrentQuadIndex = 0;
536 }
537}
538
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700539uint32_t FontRenderer::getRemainingCacheCapacity() {
540 uint32_t remainingCapacity = 0;
541 float totalPixels = 0;
542 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
543 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
544 totalPixels += mCacheLines[i]->mMaxWidth;
545 }
546 remainingCapacity = (remainingCapacity * 100) / totalPixels;
547 return remainingCapacity;
548}
549
550void FontRenderer::precacheLatin(SkPaint* paint) {
551 // Remaining capacity is measured in %
552 uint32_t remainingCapacity = getRemainingCacheCapacity();
553 uint32_t precacheIdx = 0;
554 while(remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
555 mCurrentFont->getCachedUTFChar(paint, (int32_t)mLatinPrecache[precacheIdx]);
556 remainingCapacity = getRemainingCacheCapacity();
557 precacheIdx ++;
558 }
559}
560
561void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
562 uint32_t currentNumFonts = mActiveFonts.size();
Romain Guy694b5192010-07-21 21:33:20 -0700563 mCurrentFont = Font::create(this, fontId, fontSize);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700564
565 const float maxPrecacheFontSize = 40.0f;
566 bool isNewFont = currentNumFonts != mActiveFonts.size();
567
568 if(isNewFont && fontSize <= maxPrecacheFontSize ){
569 precacheLatin(paint);
570 }
Romain Guy694b5192010-07-21 21:33:20 -0700571}
572
Romain Guy51769a62010-07-23 00:28:00 -0700573void FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
574 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y) {
Romain Guy694b5192010-07-21 21:33:20 -0700575 checkInit();
576
Romain Guy09147fb2010-07-22 13:08:20 -0700577 if (!mCurrentFont) {
578 LOGE("No font set");
Romain Guy694b5192010-07-21 21:33:20 -0700579 return;
580 }
581
Romain Guy09147fb2010-07-22 13:08:20 -0700582 mClip = clip;
Romain Guy51769a62010-07-23 00:28:00 -0700583 mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y);
Romain Guy694b5192010-07-21 21:33:20 -0700584
585 if (mCurrentQuadIndex != 0) {
586 issueDrawCommand();
587 mCurrentQuadIndex = 0;
588 }
589}
590
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700591void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
592 // Compute gaussian weights for the blur
593 // e is the euler's number
594 float e = 2.718281828459045f;
595 float pi = 3.1415926535897932f;
596 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
597 // x is of the form [-radius .. 0 .. radius]
598 // and sigma varies with radius.
599 // Based on some experimental radius values and sigma's
600 // we approximately fit sigma = f(radius) as
601 // sigma = radius * 0.4 + 0.6
602 // The larger the radius gets, the more our gaussian blur
603 // will resemble a box blur since with large sigma
604 // the gaussian curve begins to lose its shape
605 float sigma = 0.4f * (float)radius + 0.6f;
606
607 // Now compute the coefficints
608 // We will store some redundant values to save some math during
609 // the blur calculations
610 // precompute some values
611 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
612 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
613
614 float normalizeFactor = 0.0f;
615 for(int32_t r = -radius; r <= radius; r ++) {
616 float floatR = (float)r;
617 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
618 normalizeFactor += weights[r + radius];
619 }
620
621 //Now we need to normalize the weights because all our coefficients need to add up to one
622 normalizeFactor = 1.0f / normalizeFactor;
623 for(int32_t r = -radius; r <= radius; r ++) {
624 weights[r + radius] *= normalizeFactor;
625 }
626}
627
628void FontRenderer::horizontalBlur(float* weights, int32_t radius,
629 const uint8_t* source, uint8_t* dest,
630 int32_t width, int32_t height) {
631 float blurredPixel = 0.0f;
632 float currentPixel = 0.0f;
633
634 for(int32_t y = 0; y < height; y ++) {
635
636 const uint8_t* input = source + y * width;
637 uint8_t* output = dest + y * width;
638
639 for(int32_t x = 0; x < width; x ++) {
640 blurredPixel = 0.0f;
641 const float* gPtr = weights;
642 // Optimization for non-border pixels
643 if ((x > radius) && (x < (width - radius))) {
644 const uint8_t *i = input + (x - radius);
645 for(int r = -radius; r <= radius; r ++) {
646 currentPixel = (float)(*i);
647 blurredPixel += currentPixel * gPtr[0];
648 gPtr++;
649 i++;
650 }
651 } else {
652 for(int32_t r = -radius; r <= radius; r ++) {
653 // Stepping left and right away from the pixel
654 int validW = x + r;
655 if(validW < 0) {
656 validW = 0;
657 }
658 if(validW > width - 1) {
659 validW = width - 1;
660 }
661
662 currentPixel = (float)(input[validW]);
663 blurredPixel += currentPixel * gPtr[0];
664 gPtr++;
665 }
666 }
667 *output = (uint8_t)blurredPixel;
668 output ++;
669 }
670 }
671}
672
673void FontRenderer::verticalBlur(float* weights, int32_t radius,
674 const uint8_t* source, uint8_t* dest,
675 int32_t width, int32_t height) {
676 float blurredPixel = 0.0f;
677 float currentPixel = 0.0f;
678
679 for(int32_t y = 0; y < height; y ++) {
680
681 uint8_t* output = dest + y * width;
682
683 for(int32_t x = 0; x < width; x ++) {
684 blurredPixel = 0.0f;
685 const float* gPtr = weights;
686 const uint8_t* input = source + x;
687 // Optimization for non-border pixels
688 if ((y > radius) && (y < (height - radius))) {
689 const uint8_t *i = input + ((y - radius) * width);
690 for(int32_t r = -radius; r <= radius; r ++) {
691 currentPixel = (float)(*i);
692 blurredPixel += currentPixel * gPtr[0];
693 gPtr++;
694 i += width;
695 }
696 } else {
697 for(int32_t r = -radius; r <= radius; r ++) {
698 int validH = y + r;
699 // Clamp to zero and width
700 if(validH < 0) {
701 validH = 0;
702 }
703 if(validH > height - 1) {
704 validH = height - 1;
705 }
706
707 const uint8_t *i = input + validH * width;
708 currentPixel = (float)(*i);
709 blurredPixel += currentPixel * gPtr[0];
710 gPtr++;
711 }
712 }
713 *output = (uint8_t)blurredPixel;
714 output ++;
715 }
716 }
717}
718
719
720void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
721 float *gaussian = new float[2 * radius + 1];
722 computeGaussianWeights(gaussian, radius);
723 uint8_t* scratch = new uint8_t[width * height];
724 horizontalBlur(gaussian, radius, image, scratch, width, height);
725 verticalBlur(gaussian, radius, scratch, image, width, height);
726 delete[] gaussian;
727 delete[] scratch;
728}
729
Romain Guy694b5192010-07-21 21:33:20 -0700730}; // namespace uirenderer
731}; // namespace android