blob: e807aba12a9ce8ebf3299fcd7dc542cbe7a1b6a3 [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) {
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,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700146 int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
147 if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
148 renderUTF(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
149 bitmapW, bitmapH, NULL);
150 } else {
151 renderUTF(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, 0, 0, NULL);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700152 }
153
154}
155
156void Font::measureUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700157 int numGlyphs, Rect *bounds) {
158 if (bounds == NULL) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700159 LOGE("No return rectangle provided to measure text");
160 return;
161 }
162 bounds->set(1e6, -1e6, -1e6, 1e6);
163 renderUTF(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds);
164}
165
166void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700167 int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
168 uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700169 if (numGlyphs == 0 || text == NULL || len == 0) {
170 return;
171 }
172
173 int penX = x, penY = y;
174 int glyphsLeft = 1;
175 if (numGlyphs > 0) {
176 glyphsLeft = numGlyphs;
177 }
178
Romain Guy694b5192010-07-21 21:33:20 -0700179 text += start;
180
181 while (glyphsLeft > 0) {
Romain Guy694b5192010-07-21 21:33:20 -0700182 int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text);
183
Romain Guy61c8c9c2010-08-09 20:48:09 -0700184 // Reached the end of the string
Romain Guy694b5192010-07-21 21:33:20 -0700185 if (utfChar < 0) {
186 break;
187 }
188
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700189 CachedGlyphInfo* cachedGlyph = getCachedUTFChar(paint, utfChar);
Romain Guy694b5192010-07-21 21:33:20 -0700190
191 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
192 if (cachedGlyph->mIsValid) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700193 switch(mode) {
194 case FRAMEBUFFER:
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700195 drawCachedGlyph(cachedGlyph, penX, penY);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700196 break;
197 case BITMAP:
198 drawCachedGlyph(cachedGlyph, penX, penY, bitmap, bitmapW, bitmapH);
199 break;
200 case MEASURE:
201 measureCachedGlyph(cachedGlyph, penX, penY, bounds);
202 break;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700203 }
Romain Guy694b5192010-07-21 21:33:20 -0700204 }
205
Romain Guy09147fb2010-07-22 13:08:20 -0700206 penX += SkFixedFloor(cachedGlyph->mAdvanceX);
Romain Guy694b5192010-07-21 21:33:20 -0700207
208 // If we were given a specific number of glyphs, decrement
209 if (numGlyphs > 0) {
210 glyphsLeft--;
211 }
212 }
213}
214
Romain Guy51769a62010-07-23 00:28:00 -0700215void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
Romain Guy694b5192010-07-21 21:33:20 -0700216 glyph->mAdvanceX = skiaGlyph.fAdvanceX;
217 glyph->mAdvanceY = skiaGlyph.fAdvanceY;
218 glyph->mBitmapLeft = skiaGlyph.fLeft;
219 glyph->mBitmapTop = skiaGlyph.fTop;
220
221 uint32_t startX = 0;
222 uint32_t startY = 0;
223
Romain Guy694b5192010-07-21 21:33:20 -0700224 // Get the bitmap for the glyph
225 paint->findImage(skiaGlyph);
Romain Guy51769a62010-07-23 00:28:00 -0700226 glyph->mIsValid = mState->cacheBitmap(skiaGlyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700227
228 if (!glyph->mIsValid) {
229 return;
230 }
231
232 uint32_t endX = startX + skiaGlyph.fWidth;
233 uint32_t endY = startY + skiaGlyph.fHeight;
234
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700235 glyph->mStartX = startX;
236 glyph->mStartY = startY;
Romain Guy694b5192010-07-21 21:33:20 -0700237 glyph->mBitmapWidth = skiaGlyph.fWidth;
238 glyph->mBitmapHeight = skiaGlyph.fHeight;
239
Romain Guy51769a62010-07-23 00:28:00 -0700240 uint32_t cacheWidth = mState->getCacheWidth();
241 uint32_t cacheHeight = mState->getCacheHeight();
Romain Guy694b5192010-07-21 21:33:20 -0700242
243 glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
244 glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
245 glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
246 glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
247
Romain Guy51769a62010-07-23 00:28:00 -0700248 mState->mUploadTexture = true;
Romain Guy694b5192010-07-21 21:33:20 -0700249}
250
Romain Guy51769a62010-07-23 00:28:00 -0700251Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, int32_t glyph) {
252 CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
Romain Guy694b5192010-07-21 21:33:20 -0700253 mCachedGlyphs.add(glyph, newGlyph);
254
255 const SkGlyph& skiaGlyph = paint->getUnicharMetrics(glyph);
256 newGlyph->mGlyphIndex = skiaGlyph.fID;
257 newGlyph->mIsValid = false;
258
259 updateGlyphCache(paint, skiaGlyph, newGlyph);
260
261 return newGlyph;
262}
263
264Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize) {
265 Vector<Font*> &activeFonts = state->mActiveFonts;
266
267 for (uint32_t i = 0; i < activeFonts.size(); i++) {
Romain Guy51769a62010-07-23 00:28:00 -0700268 Font* font = activeFonts[i];
269 if (font->mFontId == fontId && font->mFontSize == fontSize) {
270 return font;
Romain Guy694b5192010-07-21 21:33:20 -0700271 }
272 }
273
274 Font* newFont = new Font(state, fontId, fontSize);
275 activeFonts.push(newFont);
276 return newFont;
277}
278
279///////////////////////////////////////////////////////////////////////////////
280// FontRenderer
281///////////////////////////////////////////////////////////////////////////////
282
283FontRenderer::FontRenderer() {
Romain Guy51769a62010-07-23 00:28:00 -0700284 LOGD("Creating FontRenderer");
285
Romain Guy694b5192010-07-21 21:33:20 -0700286 mInitialized = false;
287 mMaxNumberOfQuads = 1024;
288 mCurrentQuadIndex = 0;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700289 mTextureId = 0;
Romain Guy694b5192010-07-21 21:33:20 -0700290
Romain Guy9cccc2b92010-08-07 23:46:15 -0700291 mTextMeshPtr = NULL;
292 mTextTexture = NULL;
293
Romain Guy694b5192010-07-21 21:33:20 -0700294 mIndexBufferID = 0;
295
Romain Guy51769a62010-07-23 00:28:00 -0700296 mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700297 mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -0700298
299 char property[PROPERTY_VALUE_MAX];
300 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
301 LOGD(" Setting text cache width to %s pixels", property);
302 mCacheWidth = atoi(property);
303 } else {
304 LOGD(" Using default text cache width of %i pixels", mCacheWidth);
305 }
306
307 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
308 LOGD(" Setting text cache width to %s pixels", property);
309 mCacheHeight = atoi(property);
310 } else {
311 LOGD(" Using default text cache height of %i pixels", mCacheHeight);
312 }
Romain Guy694b5192010-07-21 21:33:20 -0700313}
314
315FontRenderer::~FontRenderer() {
316 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
317 delete mCacheLines[i];
318 }
319 mCacheLines.clear();
320
Romain Guy9cccc2b92010-08-07 23:46:15 -0700321 if (mInitialized) {
322 delete[] mTextMeshPtr;
323 delete[] mTextTexture;
324 }
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700325
Romain Guy9cccc2b92010-08-07 23:46:15 -0700326 if (mTextureId) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700327 glDeleteTextures(1, &mTextureId);
328 }
Romain Guy694b5192010-07-21 21:33:20 -0700329
330 Vector<Font*> fontsToDereference = mActiveFonts;
331 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
332 delete fontsToDereference[i];
333 }
334}
335
336void FontRenderer::flushAllAndInvalidate() {
337 if (mCurrentQuadIndex != 0) {
338 issueDrawCommand();
339 mCurrentQuadIndex = 0;
340 }
341 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
342 mActiveFonts[i]->invalidateTextureCache();
343 }
344 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
345 mCacheLines[i]->mCurrentCol = 0;
346 }
347}
348
Romain Guy51769a62010-07-23 00:28:00 -0700349bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
Romain Guy694b5192010-07-21 21:33:20 -0700350 // If the glyph is too tall, don't cache it
351 if (glyph.fWidth > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
352 LOGE("Font size to large to fit in cache. width, height = %i, %i",
353 (int) glyph.fWidth, (int) glyph.fHeight);
354 return false;
355 }
356
357 // Now copy the bitmap into the cache texture
358 uint32_t startX = 0;
359 uint32_t startY = 0;
360
361 bool bitmapFit = false;
362 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
363 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
364 if (bitmapFit) {
365 break;
366 }
367 }
368
369 // If the new glyph didn't fit, flush the state so far and invalidate everything
370 if (!bitmapFit) {
371 flushAllAndInvalidate();
372
373 // Try to fit it again
374 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
375 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
376 if (bitmapFit) {
377 break;
378 }
379 }
380
381 // if we still don't fit, something is wrong and we shouldn't draw
382 if (!bitmapFit) {
383 LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
384 (int) glyph.fWidth, (int) glyph.fHeight);
385 return false;
386 }
387 }
388
389 *retOriginX = startX;
390 *retOriginY = startY;
391
392 uint32_t endX = startX + glyph.fWidth;
393 uint32_t endY = startY + glyph.fHeight;
394
395 uint32_t cacheWidth = mCacheWidth;
396
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700397 uint8_t* cacheBuffer = mTextTexture;
398 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700399 unsigned int stride = glyph.rowBytes();
400
401 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
402 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
403 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700404 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
Romain Guy694b5192010-07-21 21:33:20 -0700405 cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
406 }
407 }
408
409 return true;
410}
411
412void FontRenderer::initTextTexture() {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700413 mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
Romain Guy694b5192010-07-21 21:33:20 -0700414 mUploadTexture = false;
415
416 glGenTextures(1, &mTextureId);
417 glBindTexture(GL_TEXTURE_2D, mTextureId);
418 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700419 // Initialize texture dimentions
420 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
Romain Guy61c8c9c2010-08-09 20:48:09 -0700421 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Romain Guy694b5192010-07-21 21:33:20 -0700422
423 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
424 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
425
426 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
427 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
428
429 // Split up our cache texture into lines of certain widths
430 int nextLine = 0;
Romain Guy51769a62010-07-23 00:28:00 -0700431 mCacheLines.push(new CacheTextureLine(mCacheWidth, 16, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700432 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700433 mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700434 nextLine += mCacheLines.top()->mMaxHeight;
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700435 mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0));
436 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700437 mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700438 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700439 mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700440 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700441 mCacheLines.push(new CacheTextureLine(mCacheWidth, 40, 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, mCacheHeight - nextLine, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700444}
445
446// Avoid having to reallocate memory and render quad by quad
447void FontRenderer::initVertexArrayBuffers() {
448 uint32_t numIndicies = mMaxNumberOfQuads * 6;
449 uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700450 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700451
452 // Four verts, two triangles , six indices per quad
453 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
454 int i6 = i * 6;
455 int i4 = i * 4;
456
457 indexBufferData[i6 + 0] = i4 + 0;
458 indexBufferData[i6 + 1] = i4 + 1;
459 indexBufferData[i6 + 2] = i4 + 2;
460
461 indexBufferData[i6 + 3] = i4 + 0;
462 indexBufferData[i6 + 4] = i4 + 2;
463 indexBufferData[i6 + 5] = i4 + 3;
464 }
465
466 glGenBuffers(1, &mIndexBufferID);
467 glBindBuffer(GL_ARRAY_BUFFER, mIndexBufferID);
468 glBufferData(GL_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_DYNAMIC_DRAW);
469 glBindBuffer(GL_ARRAY_BUFFER, 0);
470
471 free(indexBufferData);
472
473 uint32_t coordSize = 3;
474 uint32_t uvSize = 2;
475 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700476 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
477 mTextMeshPtr = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700478}
479
480// We don't want to allocate anything unless we actually draw text
481void FontRenderer::checkInit() {
482 if (mInitialized) {
483 return;
484 }
485
486 initTextTexture();
487 initVertexArrayBuffers();
488
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700489 // We store a string with letters in a rough frequency of occurrence
490 mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
491 mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
492 mLatinPrecache += String16(",.?!()-+@;:`'");
493 mLatinPrecache += String16("0123456789");
494
Romain Guy694b5192010-07-21 21:33:20 -0700495 mInitialized = true;
496}
497
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700498void FontRenderer::checkTextureUpdate() {
499 if (!mUploadTexture) {
500 return;
Romain Guy694b5192010-07-21 21:33:20 -0700501 }
502
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700503 glBindTexture(GL_TEXTURE_2D, mTextureId);
504
505 // Iterate over all the cache lines and see which ones need to be updated
506 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
507 CacheTextureLine* cl = mCacheLines[i];
508 if(cl->mDirty) {
509 uint32_t xOffset = 0;
510 uint32_t yOffset = cl->mCurrentRow;
511 uint32_t width = mCacheWidth;
512 uint32_t height = cl->mMaxHeight;
513 void* textureData = mTextTexture + yOffset*width;
514
515 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
516 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
517
518 cl->mDirty = false;
519 }
520 }
521
522 mUploadTexture = false;
523}
524
525void FontRenderer::issueDrawCommand() {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700526 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