blob: 5c1eb38786a2dced1ca1418b7a0ee7e9de5acbd9 [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>
Romain Guye2d345e2010-09-24 18:39:22 -070022
Romain Guy51769a62010-07-23 00:28:00 -070023#include <utils/Log.h>
24
Romain Guy15bc6432011-12-13 13:11:32 -080025#include "Caches.h"
Romain Guyc9855a52011-01-21 21:14:15 -080026#include "Debug.h"
Romain Guy51769a62010-07-23 00:28:00 -070027#include "FontRenderer.h"
Romain Guy9f5dab32012-09-04 12:55:44 -070028#include "Rect.h"
Romain Guy51769a62010-07-23 00:28:00 -070029
Romain Guy694b5192010-07-21 21:33:20 -070030namespace android {
31namespace uirenderer {
32
33///////////////////////////////////////////////////////////////////////////////
Romain Guy694b5192010-07-21 21:33:20 -070034// FontRenderer
35///////////////////////////////////////////////////////////////////////////////
36
Romain Guy514fb182011-01-19 14:38:29 -080037static bool sLogFontRendererCreate = true;
38
Romain Guye3a9b242013-01-08 11:15:30 -080039FontRenderer::FontRenderer() :
40 mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity) {
41
Romain Guyc9855a52011-01-21 21:14:15 -080042 if (sLogFontRendererCreate) {
43 INIT_LOGD("Creating FontRenderer");
44 }
Romain Guy51769a62010-07-23 00:28:00 -070045
Romain Guyb45c0c92010-08-26 20:35:23 -070046 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -070047 mInitialized = false;
48 mMaxNumberOfQuads = 1024;
49 mCurrentQuadIndex = 0;
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +090050 mLastQuadIndex = 0;
Romain Guy694b5192010-07-21 21:33:20 -070051
Romain Guy9b1204b2012-09-04 15:22:57 -070052 mTextMesh = NULL;
Chet Haase7de0cb12011-12-05 16:35:38 -080053 mCurrentCacheTexture = NULL;
Romain Guy9cccc2b92010-08-07 23:46:15 -070054
Chet Haase2a47c142011-12-14 15:22:56 -080055 mLinearFiltering = false;
56
Romain Guy694b5192010-07-21 21:33:20 -070057 mIndexBufferID = 0;
58
Chet Haaseeb32a492012-08-31 13:54:03 -070059 mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH;
60 mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT;
61 mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH;
62 mLargeCacheHeight = DEFAULT_TEXT_LARGE_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -070063
64 char property[PROPERTY_VALUE_MAX];
Chet Haaseeb32a492012-08-31 13:54:03 -070065 if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, NULL) > 0) {
Chet Haase7de0cb12011-12-05 16:35:38 -080066 mSmallCacheWidth = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -070067 }
Romain Guy9f5dab32012-09-04 12:55:44 -070068
Chet Haaseeb32a492012-08-31 13:54:03 -070069 if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, NULL) > 0) {
Chet Haase7de0cb12011-12-05 16:35:38 -080070 mSmallCacheHeight = atoi(property);
Chet Haaseeb32a492012-08-31 13:54:03 -070071 }
Romain Guy9f5dab32012-09-04 12:55:44 -070072
Chet Haaseeb32a492012-08-31 13:54:03 -070073 if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, NULL) > 0) {
74 mLargeCacheWidth = atoi(property);
75 }
Romain Guy9f5dab32012-09-04 12:55:44 -070076
Chet Haaseeb32a492012-08-31 13:54:03 -070077 if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, NULL) > 0) {
78 mLargeCacheHeight = atoi(property);
79 }
Romain Guy9f5dab32012-09-04 12:55:44 -070080
81 uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize;
82 mSmallCacheWidth = mSmallCacheWidth > maxTextureSize ? maxTextureSize : mSmallCacheWidth;
83 mSmallCacheHeight = mSmallCacheHeight > maxTextureSize ? maxTextureSize : mSmallCacheHeight;
84 mLargeCacheWidth = mLargeCacheWidth > maxTextureSize ? maxTextureSize : mLargeCacheWidth;
85 mLargeCacheHeight = mLargeCacheHeight > maxTextureSize ? maxTextureSize : mLargeCacheHeight;
86
Chet Haaseeb32a492012-08-31 13:54:03 -070087 if (sLogFontRendererCreate) {
88 INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
89 mSmallCacheWidth, mSmallCacheHeight,
90 mLargeCacheWidth, mLargeCacheHeight >> 1,
91 mLargeCacheWidth, mLargeCacheHeight >> 1,
92 mLargeCacheWidth, mLargeCacheHeight);
Romain Guy51769a62010-07-23 00:28:00 -070093 }
Romain Guy514fb182011-01-19 14:38:29 -080094
95 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -070096}
97
98FontRenderer::~FontRenderer() {
Chet Haase378e9192012-08-15 15:54:54 -070099 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
100 delete mCacheTextures[i];
Romain Guy694b5192010-07-21 21:33:20 -0700101 }
Chet Haase378e9192012-08-15 15:54:54 -0700102 mCacheTextures.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700103
Romain Guy9cccc2b92010-08-07 23:46:15 -0700104 if (mInitialized) {
Romain Guya9dd8202012-03-26 14:52:00 -0700105 // Unbinding the buffer shouldn't be necessary but it crashes with some drivers
106 Caches::getInstance().unbindIndicesBuffer();
Romain Guyb0317982012-03-21 11:52:52 -0700107 glDeleteBuffers(1, &mIndexBufferID);
108
Romain Guy9b1204b2012-09-04 15:22:57 -0700109 delete[] mTextMesh;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700110 }
Romain Guy694b5192010-07-21 21:33:20 -0700111
Romain Guye3a9b242013-01-08 11:15:30 -0800112 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
113 while (it.next()) {
114 delete it.value();
Romain Guy694b5192010-07-21 21:33:20 -0700115 }
Romain Guye3a9b242013-01-08 11:15:30 -0800116 mActiveFonts.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700117}
118
119void FontRenderer::flushAllAndInvalidate() {
120 if (mCurrentQuadIndex != 0) {
121 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700122 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700123
Romain Guye3a9b242013-01-08 11:15:30 -0800124 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
125 while (it.next()) {
126 it.value()->invalidateTextureCache();
Romain Guy694b5192010-07-21 21:33:20 -0700127 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700128
Chet Haase378e9192012-08-15 15:54:54 -0700129 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
130 mCacheTextures[i]->init();
Romain Guy694b5192010-07-21 21:33:20 -0700131 }
Chet Haasee816bae2012-08-09 13:39:02 -0700132
Romain Guy80872462012-09-04 16:42:01 -0700133#if DEBUG_FONT_RENDERER
Chet Haase378e9192012-08-15 15:54:54 -0700134 uint16_t totalGlyphs = 0;
135 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
Romain Guy80872462012-09-04 16:42:01 -0700136 totalGlyphs += mCacheTextures[i]->getGlyphCount();
Chet Haase378e9192012-08-15 15:54:54 -0700137 // Erase caches, just as a debugging facility
Romain Guy80872462012-09-04 16:42:01 -0700138 if (mCacheTextures[i]->getTexture()) {
139 memset(mCacheTextures[i]->getTexture(), 0,
140 mCacheTextures[i]->getWidth() * mCacheTextures[i]->getHeight());
Chet Haase378e9192012-08-15 15:54:54 -0700141 }
Chet Haasee816bae2012-08-09 13:39:02 -0700142 }
143 ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
144#endif
Romain Guy694b5192010-07-21 21:33:20 -0700145}
146
Chet Haase9a824562011-12-16 15:44:59 -0800147void FontRenderer::flushLargeCaches() {
Chet Haase378e9192012-08-15 15:54:54 -0700148 // Start from 1; don't deallocate smallest/default texture
149 for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
150 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guy80872462012-09-04 16:42:01 -0700151 if (cacheTexture->getTexture()) {
Chet Haase378e9192012-08-15 15:54:54 -0700152 cacheTexture->init();
Romain Guye3a9b242013-01-08 11:15:30 -0800153 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
154 while (it.next()) {
155 it.value()->invalidateTextureCache(cacheTexture);
Chet Haasee816bae2012-08-09 13:39:02 -0700156 }
Romain Guy80872462012-09-04 16:42:01 -0700157 cacheTexture->releaseTexture();
Chet Haase9a824562011-12-16 15:44:59 -0800158 }
159 }
Chet Haase9a824562011-12-16 15:44:59 -0800160}
161
Chet Haase378e9192012-08-15 15:54:54 -0700162CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
163 uint32_t* startX, uint32_t* startY) {
164 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
165 if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
166 return mCacheTextures[i];
167 }
168 }
169 // Could not fit glyph into current cache textures
170 return NULL;
171}
172
Chet Haase7de0cb12011-12-05 16:35:38 -0800173void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
Chet Haasef942cf12012-08-30 09:06:46 -0700174 uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700175 checkInit();
Chet Haase7de0cb12011-12-05 16:35:38 -0800176 cachedGlyph->mIsValid = false;
Romain Guy694b5192010-07-21 21:33:20 -0700177 // If the glyph is too tall, don't cache it
Chet Haase378e9192012-08-15 15:54:54 -0700178 if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
Romain Guy80872462012-09-04 16:42:01 -0700179 mCacheTextures[mCacheTextures.size() - 1]->getHeight()) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700180 ALOGE("Font size too large to fit in cache. width, height = %i, %i",
181 (int) glyph.fWidth, (int) glyph.fHeight);
Chet Haase7de0cb12011-12-05 16:35:38 -0800182 return;
Romain Guy694b5192010-07-21 21:33:20 -0700183 }
184
185 // Now copy the bitmap into the cache texture
186 uint32_t startX = 0;
187 uint32_t startY = 0;
188
Chet Haase378e9192012-08-15 15:54:54 -0700189 CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700190
Chet Haase378e9192012-08-15 15:54:54 -0700191 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700192 if (!precaching) {
193 // If the new glyph didn't fit and we are not just trying to precache it,
194 // clear out the cache and try again
195 flushAllAndInvalidate();
196 cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
197 }
Romain Guy694b5192010-07-21 21:33:20 -0700198
Chet Haase378e9192012-08-15 15:54:54 -0700199 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700200 // either the glyph didn't fit or we're precaching and will cache it when we draw
Chet Haase7de0cb12011-12-05 16:35:38 -0800201 return;
Romain Guy694b5192010-07-21 21:33:20 -0700202 }
203 }
204
Chet Haase378e9192012-08-15 15:54:54 -0700205 cachedGlyph->mCacheTexture = cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800206
Romain Guy694b5192010-07-21 21:33:20 -0700207 *retOriginX = startX;
208 *retOriginY = startY;
209
210 uint32_t endX = startX + glyph.fWidth;
211 uint32_t endY = startY + glyph.fHeight;
212
Romain Guy80872462012-09-04 16:42:01 -0700213 uint32_t cacheWidth = cacheTexture->getWidth();
Romain Guy694b5192010-07-21 21:33:20 -0700214
Romain Guy80872462012-09-04 16:42:01 -0700215 if (!cacheTexture->getTexture()) {
216 Caches::getInstance().activeTexture(0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800217 // Large-glyph texture memory is allocated only as needed
Romain Guy80872462012-09-04 16:42:01 -0700218 cacheTexture->allocateTexture();
Chet Haase7de0cb12011-12-05 16:35:38 -0800219 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700220
Romain Guy80872462012-09-04 16:42:01 -0700221 uint8_t* cacheBuffer = cacheTexture->getTexture();
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700222 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700223 unsigned int stride = glyph.rowBytes();
224
225 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
Romain Guy33fa1f72012-08-07 19:09:57 -0700226
227 for (cacheX = startX - TEXTURE_BORDER_SIZE; cacheX < endX + TEXTURE_BORDER_SIZE; cacheX++) {
228 cacheBuffer[(startY - TEXTURE_BORDER_SIZE) * cacheWidth + cacheX] = 0;
229 cacheBuffer[(endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + cacheX] = 0;
230 }
231
232 for (cacheY = startY - TEXTURE_BORDER_SIZE + 1;
233 cacheY < endY + TEXTURE_BORDER_SIZE - 1; cacheY++) {
234 cacheBuffer[cacheY * cacheWidth + startX - TEXTURE_BORDER_SIZE] = 0;
235 cacheBuffer[cacheY * cacheWidth + endX + TEXTURE_BORDER_SIZE - 1] = 0;
236 }
237
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700238 if (mGammaTable) {
239 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
240 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
241 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
242 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
243 }
244 }
245 } else {
246 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
247 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
248 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
249 cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
250 }
Romain Guy694b5192010-07-21 21:33:20 -0700251 }
252 }
Romain Guy97771732012-02-28 18:17:02 -0800253
Chet Haase7de0cb12011-12-05 16:35:38 -0800254 cachedGlyph->mIsValid = true;
Romain Guy694b5192010-07-21 21:33:20 -0700255}
256
Chet Haase7de0cb12011-12-05 16:35:38 -0800257CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
Romain Guy0aa87bb2012-07-20 11:14:32 -0700258 CacheTexture* cacheTexture = new CacheTexture(width, height);
Romain Guy9d9758a2012-05-14 15:19:58 -0700259
Chet Haase2a47c142011-12-14 15:22:56 -0800260 if (allocate) {
Romain Guy80872462012-09-04 16:42:01 -0700261 Caches::getInstance().activeTexture(0);
262 cacheTexture->allocateTexture();
Chet Haase2a47c142011-12-14 15:22:56 -0800263 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700264
Chet Haase2a47c142011-12-14 15:22:56 -0800265 return cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800266}
267
268void FontRenderer::initTextTexture() {
Chet Haase378e9192012-08-15 15:54:54 -0700269 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
270 delete mCacheTextures[i];
Romain Guy9d9758a2012-05-14 15:19:58 -0700271 }
Chet Haase378e9192012-08-15 15:54:54 -0700272 mCacheTextures.clear();
Romain Guy9d9758a2012-05-14 15:19:58 -0700273
Chet Haase7de0cb12011-12-05 16:35:38 -0800274 mUploadTexture = false;
Chet Haase378e9192012-08-15 15:54:54 -0700275 mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
Chet Haaseeb32a492012-08-31 13:54:03 -0700276 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
277 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
278 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false));
Chet Haase378e9192012-08-15 15:54:54 -0700279 mCurrentCacheTexture = mCacheTextures[0];
Romain Guy694b5192010-07-21 21:33:20 -0700280}
281
282// Avoid having to reallocate memory and render quad by quad
283void FontRenderer::initVertexArrayBuffers() {
Romain Guyd71dd362011-12-12 19:03:35 -0800284 uint32_t numIndices = mMaxNumberOfQuads * 6;
285 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700286 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700287
288 // Four verts, two triangles , six indices per quad
289 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
290 int i6 = i * 6;
291 int i4 = i * 4;
292
293 indexBufferData[i6 + 0] = i4 + 0;
294 indexBufferData[i6 + 1] = i4 + 1;
295 indexBufferData[i6 + 2] = i4 + 2;
296
297 indexBufferData[i6 + 3] = i4 + 0;
298 indexBufferData[i6 + 4] = i4 + 2;
299 indexBufferData[i6 + 5] = i4 + 3;
300 }
301
302 glGenBuffers(1, &mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800303 Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700304 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
Romain Guy694b5192010-07-21 21:33:20 -0700305
306 free(indexBufferData);
307
Romain Guyd71dd362011-12-12 19:03:35 -0800308 uint32_t coordSize = 2;
Romain Guy694b5192010-07-21 21:33:20 -0700309 uint32_t uvSize = 2;
310 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700311 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
Romain Guy9b1204b2012-09-04 15:22:57 -0700312 mTextMesh = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700313}
314
315// We don't want to allocate anything unless we actually draw text
316void FontRenderer::checkInit() {
317 if (mInitialized) {
318 return;
319 }
320
321 initTextTexture();
322 initVertexArrayBuffers();
323
324 mInitialized = true;
325}
326
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900327void FontRenderer::updateDrawParams() {
328 if (mCurrentQuadIndex != mLastQuadIndex) {
329 mDrawOffsets.add((uint16_t*)(mLastQuadIndex * sizeof(uint16_t) * 6));
330 mDrawCounts.add(mCurrentQuadIndex - mLastQuadIndex);
331 mDrawCacheTextures.add(mCurrentCacheTexture);
332 mLastQuadIndex = mCurrentQuadIndex;
333 }
334}
335
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700336void FontRenderer::checkTextureUpdate() {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900337 if (!mUploadTexture) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700338 return;
Romain Guy694b5192010-07-21 21:33:20 -0700339 }
340
Romain Guy2d4fd362011-12-13 22:00:19 -0800341 Caches& caches = Caches::getInstance();
342 GLuint lastTextureId = 0;
Chet Haase378e9192012-08-15 15:54:54 -0700343 // Iterate over all the cache textures and see which ones need to be updated
344 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
345 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guy80872462012-09-04 16:42:01 -0700346 if (cacheTexture->isDirty() && cacheTexture->getTexture()) {
Chet Haaseb92d8f72012-09-21 08:40:46 -0700347 // Can't copy inner rect; glTexSubimage expects pointer to deal with entire buffer
348 // of data. So expand the dirty rect to the encompassing horizontal stripe.
349 const Rect* dirtyRect = cacheTexture->getDirtyRect();
350 uint32_t x = 0;
351 uint32_t y = dirtyRect->top;
Romain Guy80872462012-09-04 16:42:01 -0700352 uint32_t width = cacheTexture->getWidth();
Chet Haaseb92d8f72012-09-21 08:40:46 -0700353 uint32_t height = dirtyRect->getHeight();
354 void* textureData = cacheTexture->getTexture() + y * width;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700355
Romain Guy80872462012-09-04 16:42:01 -0700356 if (cacheTexture->getTextureId() != lastTextureId) {
357 lastTextureId = cacheTexture->getTextureId();
Romain Guy2d4fd362011-12-13 22:00:19 -0800358 caches.activeTexture(0);
Romain Guy80872462012-09-04 16:42:01 -0700359 glBindTexture(GL_TEXTURE_2D, lastTextureId);
Romain Guy2d4fd362011-12-13 22:00:19 -0800360 }
Chet Haasee816bae2012-08-09 13:39:02 -0700361#if DEBUG_FONT_RENDERER
Chet Haaseb92d8f72012-09-21 08:40:46 -0700362 ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d",
363 i, x, y, width, height);
Chet Haasee816bae2012-08-09 13:39:02 -0700364#endif
Chet Haaseb92d8f72012-09-21 08:40:46 -0700365 glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700366 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Romain Guy80872462012-09-04 16:42:01 -0700367 cacheTexture->setDirty(false);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700368 }
369 }
370
371 mUploadTexture = false;
372}
373
374void FontRenderer::issueDrawCommand() {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900375 updateDrawParams();
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700376 checkTextureUpdate();
377
Romain Guy15bc6432011-12-13 13:11:32 -0800378 Caches& caches = Caches::getInstance();
Romain Guy2d4fd362011-12-13 22:00:19 -0800379 caches.bindIndicesBuffer(mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800380 if (!mDrawn) {
Romain Guy9b1204b2012-09-04 15:22:57 -0700381 float* buffer = mTextMesh;
Romain Guy15bc6432011-12-13 13:11:32 -0800382 int offset = 2;
383
384 bool force = caches.unbindMeshBuffer();
Chris Craikcb4d6002012-09-25 12:00:29 -0700385 caches.bindPositionVertexPointer(force, buffer);
386 caches.bindTexCoordsVertexPointer(force, buffer + offset);
Romain Guy15bc6432011-12-13 13:11:32 -0800387 }
388
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900389 for (uint32_t i = 0; i < mDrawOffsets.size(); i++) {
390 uint16_t* offset = mDrawOffsets[i];
391 uint32_t count = mDrawCounts[i];
392 CacheTexture* texture = mDrawCacheTextures[i];
393
394 caches.activeTexture(0);
395 glBindTexture(GL_TEXTURE_2D, texture->getTextureId());
396
397 texture->setLinearFiltering(mLinearFiltering, false);
398
399 glDrawElements(GL_TRIANGLES, count * 6, GL_UNSIGNED_SHORT, offset);
400 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700401
402 mDrawn = true;
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900403
404 mCurrentQuadIndex = 0;
405 mLastQuadIndex = 0;
406 mDrawOffsets.clear();
407 mDrawCounts.clear();
408 mDrawCacheTextures.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700409}
410
Romain Guy97771732012-02-28 18:17:02 -0800411void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
412 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800413 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800414 if (texture != mCurrentCacheTexture) {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900415 updateDrawParams();
Chet Haase7de0cb12011-12-05 16:35:38 -0800416 // Now use the new texture id
417 mCurrentCacheTexture = texture;
418 }
Romain Guy09147fb2010-07-22 13:08:20 -0700419
Romain Guy694b5192010-07-21 21:33:20 -0700420 const uint32_t vertsPerQuad = 4;
Romain Guyd71dd362011-12-12 19:03:35 -0800421 const uint32_t floatsPerVert = 4;
Romain Guy9b1204b2012-09-04 15:22:57 -0700422 float* currentPos = mTextMesh + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700423
Romain Guy694b5192010-07-21 21:33:20 -0700424 (*currentPos++) = x1;
425 (*currentPos++) = y1;
Romain Guy694b5192010-07-21 21:33:20 -0700426 (*currentPos++) = u1;
427 (*currentPos++) = v1;
428
429 (*currentPos++) = x2;
430 (*currentPos++) = y2;
Romain Guy694b5192010-07-21 21:33:20 -0700431 (*currentPos++) = u2;
432 (*currentPos++) = v2;
433
434 (*currentPos++) = x3;
435 (*currentPos++) = y3;
Romain Guy694b5192010-07-21 21:33:20 -0700436 (*currentPos++) = u3;
437 (*currentPos++) = v3;
438
439 (*currentPos++) = x4;
440 (*currentPos++) = y4;
Romain Guy694b5192010-07-21 21:33:20 -0700441 (*currentPos++) = u4;
442 (*currentPos++) = v4;
443
444 mCurrentQuadIndex++;
Romain Guy97771732012-02-28 18:17:02 -0800445}
446
447void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
448 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
449 float x4, float y4, float u4, float v4, CacheTexture* texture) {
450
451 if (mClip &&
452 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
453 return;
454 }
455
456 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
Romain Guy694b5192010-07-21 21:33:20 -0700457
Romain Guy5b3b3522010-10-27 18:57:51 -0700458 if (mBounds) {
459 mBounds->left = fmin(mBounds->left, x1);
460 mBounds->top = fmin(mBounds->top, y3);
461 mBounds->right = fmax(mBounds->right, x3);
462 mBounds->bottom = fmax(mBounds->bottom, y1);
463 }
464
Romain Guy694b5192010-07-21 21:33:20 -0700465 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
466 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700467 }
468}
469
Romain Guy97771732012-02-28 18:17:02 -0800470void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
471 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
472 float x4, float y4, float u4, float v4, CacheTexture* texture) {
473
474 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
475
476 if (mBounds) {
477 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
478 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
479 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
480 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
481 }
482
483 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
484 issueDrawCommand();
Romain Guy97771732012-02-28 18:17:02 -0800485 }
486}
487
Romain Guye3a9b242013-01-08 11:15:30 -0800488void FontRenderer::setFont(SkPaint* paint, const mat4& matrix) {
489 mCurrentFont = Font::create(this, paint, matrix);
Romain Guy694b5192010-07-21 21:33:20 -0700490}
Romain Guy7975fb62010-10-01 16:36:14 -0700491
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700492FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Raph Levien416a8472012-07-19 22:48:17 -0700493 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700494 checkInit();
495
496 if (!mCurrentFont) {
497 DropShadow image;
498 image.width = 0;
499 image.height = 0;
500 image.image = NULL;
501 image.penX = 0;
502 image.penY = 0;
503 return image;
504 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700505
Romain Guy2d4fd362011-12-13 22:00:19 -0800506 mDrawn = false;
Romain Guyff98fa52011-11-28 09:35:09 -0800507 mClip = NULL;
508 mBounds = NULL;
509
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700510 Rect bounds;
Raph Levien416a8472012-07-19 22:48:17 -0700511 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
Romain Guyff98fa52011-11-28 09:35:09 -0800512
Romain Guy1e45aae2010-08-13 19:39:53 -0700513 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
514 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700515 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guyff98fa52011-11-28 09:35:09 -0800516
Romain Guy1e45aae2010-08-13 19:39:53 -0700517 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700518 dataBuffer[i] = 0;
519 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700520
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700521 int penX = radius - bounds.left;
522 int penY = radius - bounds.bottom;
523
Romain Guy726aeba2011-06-01 14:52:00 -0700524 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
Raph Levien416a8472012-07-19 22:48:17 -0700525 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700526 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
527
528 DropShadow image;
529 image.width = paddedWidth;
530 image.height = paddedHeight;
531 image.image = dataBuffer;
532 image.penX = penX;
533 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -0800534
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700535 return image;
536}
Romain Guy694b5192010-07-21 21:33:20 -0700537
Romain Guy671d6cf2012-01-18 12:39:17 -0800538void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700539 checkInit();
540
Romain Guy5b3b3522010-10-27 18:57:51 -0700541 mDrawn = false;
542 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -0700543 mClip = clip;
Romain Guy671d6cf2012-01-18 12:39:17 -0800544}
Romain Guyff98fa52011-11-28 09:35:09 -0800545
Romain Guy671d6cf2012-01-18 12:39:17 -0800546void FontRenderer::finishRender() {
Romain Guy5b3b3522010-10-27 18:57:51 -0700547 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -0800548 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700549
550 if (mCurrentQuadIndex != 0) {
551 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700552 }
Romain Guy671d6cf2012-01-18 12:39:17 -0800553}
554
Romain Guye3a9b242013-01-08 11:15:30 -0800555void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix) {
556 Font* font = Font::create(this, paint, matrix);
Chet Haasee816bae2012-08-09 13:39:02 -0700557 font->precache(paint, text, numGlyphs);
558}
559
Romain Guy671d6cf2012-01-18 12:39:17 -0800560bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
561 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
562 const float* positions, Rect* bounds) {
563 if (!mCurrentFont) {
564 ALOGE("No font set");
565 return false;
566 }
567
568 initRender(clip, bounds);
569 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
570 finishRender();
Romain Guy5b3b3522010-10-27 18:57:51 -0700571
572 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700573}
574
Romain Guy97771732012-02-28 18:17:02 -0800575bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
576 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
577 float hOffset, float vOffset, Rect* bounds) {
578 if (!mCurrentFont) {
579 ALOGE("No font set");
580 return false;
581 }
582
583 initRender(clip, bounds);
584 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
585 finishRender();
586
587 return mDrawn;
588}
589
Romain Guy9b1204b2012-09-04 15:22:57 -0700590void FontRenderer::removeFont(const Font* font) {
Romain Guye3a9b242013-01-08 11:15:30 -0800591 mActiveFonts.remove(font->getDescription());
Romain Guy9b1204b2012-09-04 15:22:57 -0700592
593 if (mCurrentFont == font) {
594 mCurrentFont = NULL;
595 }
596}
597
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700598void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
599 // Compute gaussian weights for the blur
600 // e is the euler's number
601 float e = 2.718281828459045f;
602 float pi = 3.1415926535897932f;
603 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
604 // x is of the form [-radius .. 0 .. radius]
605 // and sigma varies with radius.
606 // Based on some experimental radius values and sigma's
607 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700608 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700609 // The larger the radius gets, the more our gaussian blur
610 // will resemble a box blur since with large sigma
611 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -0800612 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700613
614 // Now compute the coefficints
615 // We will store some redundant values to save some math during
616 // the blur calculations
617 // precompute some values
618 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
619 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
620
621 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -0800622 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700623 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700624 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
625 normalizeFactor += weights[r + radius];
626 }
627
628 //Now we need to normalize the weights because all our coefficients need to add up to one
629 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -0800630 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700631 weights[r + radius] *= normalizeFactor;
632 }
633}
634
635void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700636 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700637 float blurredPixel = 0.0f;
638 float currentPixel = 0.0f;
639
Romain Guy325a0f92011-01-05 15:26:55 -0800640 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700641
642 const uint8_t* input = source + y * width;
643 uint8_t* output = dest + y * width;
644
Romain Guy325a0f92011-01-05 15:26:55 -0800645 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700646 blurredPixel = 0.0f;
647 const float* gPtr = weights;
648 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800649 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700650 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -0800651 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700652 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700653 blurredPixel += currentPixel * gPtr[0];
654 gPtr++;
655 i++;
656 }
657 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800658 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700659 // Stepping left and right away from the pixel
660 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -0800661 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700662 validW = 0;
663 }
Romain Guy325a0f92011-01-05 15:26:55 -0800664 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700665 validW = width - 1;
666 }
667
Romain Guy325a0f92011-01-05 15:26:55 -0800668 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700669 blurredPixel += currentPixel * gPtr[0];
670 gPtr++;
671 }
672 }
673 *output = (uint8_t)blurredPixel;
674 output ++;
675 }
676 }
677}
678
679void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700680 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700681 float blurredPixel = 0.0f;
682 float currentPixel = 0.0f;
683
Romain Guy325a0f92011-01-05 15:26:55 -0800684 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700685 uint8_t* output = dest + y * width;
686
Romain Guy325a0f92011-01-05 15:26:55 -0800687 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700688 blurredPixel = 0.0f;
689 const float* gPtr = weights;
690 const uint8_t* input = source + x;
691 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800692 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700693 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -0800694 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700695 currentPixel = (float)(*i);
696 blurredPixel += currentPixel * gPtr[0];
697 gPtr++;
698 i += width;
699 }
700 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800701 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700702 int validH = y + r;
703 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -0800704 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700705 validH = 0;
706 }
Romain Guy325a0f92011-01-05 15:26:55 -0800707 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700708 validH = height - 1;
709 }
710
711 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -0800712 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700713 blurredPixel += currentPixel * gPtr[0];
714 gPtr++;
715 }
716 }
Romain Guy325a0f92011-01-05 15:26:55 -0800717 *output = (uint8_t) blurredPixel;
Romain Guy9b1204b2012-09-04 15:22:57 -0700718 output++;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700719 }
720 }
721}
722
723
724void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
725 float *gaussian = new float[2 * radius + 1];
726 computeGaussianWeights(gaussian, radius);
Romain Guyd71dd362011-12-12 19:03:35 -0800727
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700728 uint8_t* scratch = new uint8_t[width * height];
Romain Guyd71dd362011-12-12 19:03:35 -0800729
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700730 horizontalBlur(gaussian, radius, image, scratch, width, height);
731 verticalBlur(gaussian, radius, scratch, image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -0800732
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700733 delete[] gaussian;
734 delete[] scratch;
735}
736
Romain Guy694b5192010-07-21 21:33:20 -0700737}; // namespace uirenderer
738}; // namespace android