blob: 86667eeea7ecb68501ca138921a1801bf45ce6a4 [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 Guy694b5192010-07-21 21:33:20 -070039FontRenderer::FontRenderer() {
Romain Guyc9855a52011-01-21 21:14:15 -080040 if (sLogFontRendererCreate) {
41 INIT_LOGD("Creating FontRenderer");
42 }
Romain Guy51769a62010-07-23 00:28:00 -070043
Romain Guyb45c0c92010-08-26 20:35:23 -070044 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -070045 mInitialized = false;
46 mMaxNumberOfQuads = 1024;
47 mCurrentQuadIndex = 0;
48
Romain Guy9b1204b2012-09-04 15:22:57 -070049 mTextMesh = NULL;
Chet Haase7de0cb12011-12-05 16:35:38 -080050 mCurrentCacheTexture = NULL;
51 mLastCacheTexture = NULL;
Romain Guy9cccc2b92010-08-07 23:46:15 -070052
Chet Haase2a47c142011-12-14 15:22:56 -080053 mLinearFiltering = false;
54
Romain Guy694b5192010-07-21 21:33:20 -070055 mIndexBufferID = 0;
56
Chet Haaseeb32a492012-08-31 13:54:03 -070057 mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH;
58 mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT;
59 mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH;
60 mLargeCacheHeight = DEFAULT_TEXT_LARGE_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -070061
62 char property[PROPERTY_VALUE_MAX];
Chet Haaseeb32a492012-08-31 13:54:03 -070063 if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, NULL) > 0) {
Chet Haase7de0cb12011-12-05 16:35:38 -080064 mSmallCacheWidth = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -070065 }
Romain Guy9f5dab32012-09-04 12:55:44 -070066
Chet Haaseeb32a492012-08-31 13:54:03 -070067 if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, NULL) > 0) {
Chet Haase7de0cb12011-12-05 16:35:38 -080068 mSmallCacheHeight = atoi(property);
Chet Haaseeb32a492012-08-31 13:54:03 -070069 }
Romain Guy9f5dab32012-09-04 12:55:44 -070070
Chet Haaseeb32a492012-08-31 13:54:03 -070071 if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, NULL) > 0) {
72 mLargeCacheWidth = atoi(property);
73 }
Romain Guy9f5dab32012-09-04 12:55:44 -070074
Chet Haaseeb32a492012-08-31 13:54:03 -070075 if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, NULL) > 0) {
76 mLargeCacheHeight = atoi(property);
77 }
Romain Guy9f5dab32012-09-04 12:55:44 -070078
79 uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize;
80 mSmallCacheWidth = mSmallCacheWidth > maxTextureSize ? maxTextureSize : mSmallCacheWidth;
81 mSmallCacheHeight = mSmallCacheHeight > maxTextureSize ? maxTextureSize : mSmallCacheHeight;
82 mLargeCacheWidth = mLargeCacheWidth > maxTextureSize ? maxTextureSize : mLargeCacheWidth;
83 mLargeCacheHeight = mLargeCacheHeight > maxTextureSize ? maxTextureSize : mLargeCacheHeight;
84
Chet Haaseeb32a492012-08-31 13:54:03 -070085 if (sLogFontRendererCreate) {
86 INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
87 mSmallCacheWidth, mSmallCacheHeight,
88 mLargeCacheWidth, mLargeCacheHeight >> 1,
89 mLargeCacheWidth, mLargeCacheHeight >> 1,
90 mLargeCacheWidth, mLargeCacheHeight);
Romain Guy51769a62010-07-23 00:28:00 -070091 }
Romain Guy514fb182011-01-19 14:38:29 -080092
93 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -070094}
95
96FontRenderer::~FontRenderer() {
Chet Haase378e9192012-08-15 15:54:54 -070097 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
98 delete mCacheTextures[i];
Romain Guy694b5192010-07-21 21:33:20 -070099 }
Chet Haase378e9192012-08-15 15:54:54 -0700100 mCacheTextures.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700101
Romain Guy9cccc2b92010-08-07 23:46:15 -0700102 if (mInitialized) {
Romain Guya9dd8202012-03-26 14:52:00 -0700103 // Unbinding the buffer shouldn't be necessary but it crashes with some drivers
104 Caches::getInstance().unbindIndicesBuffer();
Romain Guyb0317982012-03-21 11:52:52 -0700105 glDeleteBuffers(1, &mIndexBufferID);
106
Romain Guy9b1204b2012-09-04 15:22:57 -0700107 delete[] mTextMesh;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700108 }
Romain Guy694b5192010-07-21 21:33:20 -0700109
110 Vector<Font*> fontsToDereference = mActiveFonts;
111 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
112 delete fontsToDereference[i];
113 }
114}
115
116void FontRenderer::flushAllAndInvalidate() {
117 if (mCurrentQuadIndex != 0) {
118 issueDrawCommand();
119 mCurrentQuadIndex = 0;
120 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700121
Romain Guy694b5192010-07-21 21:33:20 -0700122 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
123 mActiveFonts[i]->invalidateTextureCache();
124 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700125
Chet Haase378e9192012-08-15 15:54:54 -0700126 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
127 mCacheTextures[i]->init();
Romain Guy694b5192010-07-21 21:33:20 -0700128 }
Chet Haasee816bae2012-08-09 13:39:02 -0700129
Romain Guy80872462012-09-04 16:42:01 -0700130#if DEBUG_FONT_RENDERER
Chet Haase378e9192012-08-15 15:54:54 -0700131 uint16_t totalGlyphs = 0;
132 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
Romain Guy80872462012-09-04 16:42:01 -0700133 totalGlyphs += mCacheTextures[i]->getGlyphCount();
Chet Haase378e9192012-08-15 15:54:54 -0700134 // Erase caches, just as a debugging facility
Romain Guy80872462012-09-04 16:42:01 -0700135 if (mCacheTextures[i]->getTexture()) {
136 memset(mCacheTextures[i]->getTexture(), 0,
137 mCacheTextures[i]->getWidth() * mCacheTextures[i]->getHeight());
Chet Haase378e9192012-08-15 15:54:54 -0700138 }
Chet Haasee816bae2012-08-09 13:39:02 -0700139 }
140 ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
141#endif
Romain Guy694b5192010-07-21 21:33:20 -0700142}
143
Chet Haase9a824562011-12-16 15:44:59 -0800144void FontRenderer::flushLargeCaches() {
Chet Haase378e9192012-08-15 15:54:54 -0700145 // Start from 1; don't deallocate smallest/default texture
146 for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
147 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guy80872462012-09-04 16:42:01 -0700148 if (cacheTexture->getTexture()) {
Chet Haase378e9192012-08-15 15:54:54 -0700149 cacheTexture->init();
150 for (uint32_t j = 0; j < mActiveFonts.size(); j++) {
151 mActiveFonts[j]->invalidateTextureCache(cacheTexture);
Chet Haasee816bae2012-08-09 13:39:02 -0700152 }
Romain Guy80872462012-09-04 16:42:01 -0700153 cacheTexture->releaseTexture();
Chet Haase9a824562011-12-16 15:44:59 -0800154 }
155 }
Chet Haase9a824562011-12-16 15:44:59 -0800156}
157
Chet Haase378e9192012-08-15 15:54:54 -0700158CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
159 uint32_t* startX, uint32_t* startY) {
160 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
161 if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
162 return mCacheTextures[i];
163 }
164 }
165 // Could not fit glyph into current cache textures
166 return NULL;
167}
168
Chet Haase7de0cb12011-12-05 16:35:38 -0800169void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
Chet Haasef942cf12012-08-30 09:06:46 -0700170 uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700171 checkInit();
Chet Haase7de0cb12011-12-05 16:35:38 -0800172 cachedGlyph->mIsValid = false;
Romain Guy694b5192010-07-21 21:33:20 -0700173 // If the glyph is too tall, don't cache it
Chet Haase378e9192012-08-15 15:54:54 -0700174 if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
Romain Guy80872462012-09-04 16:42:01 -0700175 mCacheTextures[mCacheTextures.size() - 1]->getHeight()) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700176 ALOGE("Font size too large to fit in cache. width, height = %i, %i",
177 (int) glyph.fWidth, (int) glyph.fHeight);
Chet Haase7de0cb12011-12-05 16:35:38 -0800178 return;
Romain Guy694b5192010-07-21 21:33:20 -0700179 }
180
181 // Now copy the bitmap into the cache texture
182 uint32_t startX = 0;
183 uint32_t startY = 0;
184
Chet Haase378e9192012-08-15 15:54:54 -0700185 CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700186
Chet Haase378e9192012-08-15 15:54:54 -0700187 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700188 if (!precaching) {
189 // If the new glyph didn't fit and we are not just trying to precache it,
190 // clear out the cache and try again
191 flushAllAndInvalidate();
192 cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
193 }
Romain Guy694b5192010-07-21 21:33:20 -0700194
Chet Haase378e9192012-08-15 15:54:54 -0700195 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700196 // either the glyph didn't fit or we're precaching and will cache it when we draw
Chet Haase7de0cb12011-12-05 16:35:38 -0800197 return;
Romain Guy694b5192010-07-21 21:33:20 -0700198 }
199 }
200
Chet Haase378e9192012-08-15 15:54:54 -0700201 cachedGlyph->mCacheTexture = cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800202
Romain Guy694b5192010-07-21 21:33:20 -0700203 *retOriginX = startX;
204 *retOriginY = startY;
205
206 uint32_t endX = startX + glyph.fWidth;
207 uint32_t endY = startY + glyph.fHeight;
208
Romain Guy80872462012-09-04 16:42:01 -0700209 uint32_t cacheWidth = cacheTexture->getWidth();
Romain Guy694b5192010-07-21 21:33:20 -0700210
Romain Guy80872462012-09-04 16:42:01 -0700211 if (!cacheTexture->getTexture()) {
212 Caches::getInstance().activeTexture(0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800213 // Large-glyph texture memory is allocated only as needed
Romain Guy80872462012-09-04 16:42:01 -0700214 cacheTexture->allocateTexture();
Chet Haase7de0cb12011-12-05 16:35:38 -0800215 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700216
Romain Guy80872462012-09-04 16:42:01 -0700217 uint8_t* cacheBuffer = cacheTexture->getTexture();
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700218 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700219 unsigned int stride = glyph.rowBytes();
220
221 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
Romain Guy33fa1f72012-08-07 19:09:57 -0700222
223 for (cacheX = startX - TEXTURE_BORDER_SIZE; cacheX < endX + TEXTURE_BORDER_SIZE; cacheX++) {
224 cacheBuffer[(startY - TEXTURE_BORDER_SIZE) * cacheWidth + cacheX] = 0;
225 cacheBuffer[(endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + cacheX] = 0;
226 }
227
228 for (cacheY = startY - TEXTURE_BORDER_SIZE + 1;
229 cacheY < endY + TEXTURE_BORDER_SIZE - 1; cacheY++) {
230 cacheBuffer[cacheY * cacheWidth + startX - TEXTURE_BORDER_SIZE] = 0;
231 cacheBuffer[cacheY * cacheWidth + endX + TEXTURE_BORDER_SIZE - 1] = 0;
232 }
233
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700234 if (mGammaTable) {
235 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
236 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
237 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
238 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
239 }
240 }
241 } else {
242 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
243 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
244 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
245 cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
246 }
Romain Guy694b5192010-07-21 21:33:20 -0700247 }
248 }
Romain Guy97771732012-02-28 18:17:02 -0800249
Chet Haase7de0cb12011-12-05 16:35:38 -0800250 cachedGlyph->mIsValid = true;
Romain Guy694b5192010-07-21 21:33:20 -0700251}
252
Chet Haase7de0cb12011-12-05 16:35:38 -0800253CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
Romain Guy0aa87bb2012-07-20 11:14:32 -0700254 CacheTexture* cacheTexture = new CacheTexture(width, height);
Romain Guy9d9758a2012-05-14 15:19:58 -0700255
Chet Haase2a47c142011-12-14 15:22:56 -0800256 if (allocate) {
Romain Guy80872462012-09-04 16:42:01 -0700257 Caches::getInstance().activeTexture(0);
258 cacheTexture->allocateTexture();
Chet Haase2a47c142011-12-14 15:22:56 -0800259 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700260
Chet Haase2a47c142011-12-14 15:22:56 -0800261 return cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800262}
263
264void FontRenderer::initTextTexture() {
Chet Haase378e9192012-08-15 15:54:54 -0700265 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
266 delete mCacheTextures[i];
Romain Guy9d9758a2012-05-14 15:19:58 -0700267 }
Chet Haase378e9192012-08-15 15:54:54 -0700268 mCacheTextures.clear();
Romain Guy9d9758a2012-05-14 15:19:58 -0700269
Chet Haase7de0cb12011-12-05 16:35:38 -0800270 mUploadTexture = false;
Chet Haase378e9192012-08-15 15:54:54 -0700271 mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
Chet Haaseeb32a492012-08-31 13:54:03 -0700272 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
273 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
274 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false));
Chet Haase378e9192012-08-15 15:54:54 -0700275 mCurrentCacheTexture = mCacheTextures[0];
Romain Guy694b5192010-07-21 21:33:20 -0700276}
277
278// Avoid having to reallocate memory and render quad by quad
279void FontRenderer::initVertexArrayBuffers() {
Romain Guyd71dd362011-12-12 19:03:35 -0800280 uint32_t numIndices = mMaxNumberOfQuads * 6;
281 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700282 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700283
284 // Four verts, two triangles , six indices per quad
285 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
286 int i6 = i * 6;
287 int i4 = i * 4;
288
289 indexBufferData[i6 + 0] = i4 + 0;
290 indexBufferData[i6 + 1] = i4 + 1;
291 indexBufferData[i6 + 2] = i4 + 2;
292
293 indexBufferData[i6 + 3] = i4 + 0;
294 indexBufferData[i6 + 4] = i4 + 2;
295 indexBufferData[i6 + 5] = i4 + 3;
296 }
297
298 glGenBuffers(1, &mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800299 Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700300 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
Romain Guy694b5192010-07-21 21:33:20 -0700301
302 free(indexBufferData);
303
Romain Guyd71dd362011-12-12 19:03:35 -0800304 uint32_t coordSize = 2;
Romain Guy694b5192010-07-21 21:33:20 -0700305 uint32_t uvSize = 2;
306 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700307 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
Romain Guy9b1204b2012-09-04 15:22:57 -0700308 mTextMesh = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700309}
310
311// We don't want to allocate anything unless we actually draw text
312void FontRenderer::checkInit() {
313 if (mInitialized) {
314 return;
315 }
316
317 initTextTexture();
318 initVertexArrayBuffers();
319
320 mInitialized = true;
321}
322
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700323void FontRenderer::checkTextureUpdate() {
Chet Haase7de0cb12011-12-05 16:35:38 -0800324 if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700325 return;
Romain Guy694b5192010-07-21 21:33:20 -0700326 }
327
Romain Guy2d4fd362011-12-13 22:00:19 -0800328 Caches& caches = Caches::getInstance();
329 GLuint lastTextureId = 0;
Chet Haase378e9192012-08-15 15:54:54 -0700330 // Iterate over all the cache textures and see which ones need to be updated
331 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
332 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guy80872462012-09-04 16:42:01 -0700333 if (cacheTexture->isDirty() && cacheTexture->getTexture()) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700334 uint32_t xOffset = 0;
Romain Guy80872462012-09-04 16:42:01 -0700335 uint32_t width = cacheTexture->getWidth();
336 uint32_t height = cacheTexture->getHeight();
337 void* textureData = cacheTexture->getTexture();
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700338
Romain Guy80872462012-09-04 16:42:01 -0700339 if (cacheTexture->getTextureId() != lastTextureId) {
340 lastTextureId = cacheTexture->getTextureId();
Romain Guy2d4fd362011-12-13 22:00:19 -0800341 caches.activeTexture(0);
Romain Guy80872462012-09-04 16:42:01 -0700342 glBindTexture(GL_TEXTURE_2D, lastTextureId);
Romain Guy2d4fd362011-12-13 22:00:19 -0800343 }
Chet Haasee816bae2012-08-09 13:39:02 -0700344#if DEBUG_FONT_RENDERER
Chet Haase378e9192012-08-15 15:54:54 -0700345 ALOGD("glTextSubimage for cacheTexture %d: xOff, width height = %d, %d, %d",
346 i, xOffset, width, height);
Chet Haasee816bae2012-08-09 13:39:02 -0700347#endif
Chet Haase378e9192012-08-15 15:54:54 -0700348 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, 0, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700349 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700350
Romain Guy80872462012-09-04 16:42:01 -0700351 cacheTexture->setDirty(false);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700352 }
353 }
354
Romain Guy16c88082012-06-11 16:03:47 -0700355 caches.activeTexture(0);
Romain Guy80872462012-09-04 16:42:01 -0700356 glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->getTextureId());
357
358 mCurrentCacheTexture->setLinearFiltering(mLinearFiltering, false);
Chet Haase7de0cb12011-12-05 16:35:38 -0800359 mLastCacheTexture = mCurrentCacheTexture;
360
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700361 mUploadTexture = false;
362}
363
364void FontRenderer::issueDrawCommand() {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700365 checkTextureUpdate();
366
Romain Guy15bc6432011-12-13 13:11:32 -0800367 Caches& caches = Caches::getInstance();
Romain Guy2d4fd362011-12-13 22:00:19 -0800368 caches.bindIndicesBuffer(mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800369 if (!mDrawn) {
Romain Guy9b1204b2012-09-04 15:22:57 -0700370 float* buffer = mTextMesh;
Romain Guy15bc6432011-12-13 13:11:32 -0800371 int offset = 2;
372
373 bool force = caches.unbindMeshBuffer();
374 caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
375 caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
376 buffer + offset);
377 }
378
Romain Guy694b5192010-07-21 21:33:20 -0700379 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
Romain Guy5b3b3522010-10-27 18:57:51 -0700380
381 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700382}
383
Romain Guy97771732012-02-28 18:17:02 -0800384void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
385 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800386 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800387 if (texture != mCurrentCacheTexture) {
388 if (mCurrentQuadIndex != 0) {
389 // First, draw everything stored already which uses the previous texture
390 issueDrawCommand();
391 mCurrentQuadIndex = 0;
392 }
393 // Now use the new texture id
394 mCurrentCacheTexture = texture;
395 }
Romain Guy09147fb2010-07-22 13:08:20 -0700396
Romain Guy694b5192010-07-21 21:33:20 -0700397 const uint32_t vertsPerQuad = 4;
Romain Guyd71dd362011-12-12 19:03:35 -0800398 const uint32_t floatsPerVert = 4;
Romain Guy9b1204b2012-09-04 15:22:57 -0700399 float* currentPos = mTextMesh + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700400
Romain Guy694b5192010-07-21 21:33:20 -0700401 (*currentPos++) = x1;
402 (*currentPos++) = y1;
Romain Guy694b5192010-07-21 21:33:20 -0700403 (*currentPos++) = u1;
404 (*currentPos++) = v1;
405
406 (*currentPos++) = x2;
407 (*currentPos++) = y2;
Romain Guy694b5192010-07-21 21:33:20 -0700408 (*currentPos++) = u2;
409 (*currentPos++) = v2;
410
411 (*currentPos++) = x3;
412 (*currentPos++) = y3;
Romain Guy694b5192010-07-21 21:33:20 -0700413 (*currentPos++) = u3;
414 (*currentPos++) = v3;
415
416 (*currentPos++) = x4;
417 (*currentPos++) = y4;
Romain Guy694b5192010-07-21 21:33:20 -0700418 (*currentPos++) = u4;
419 (*currentPos++) = v4;
420
421 mCurrentQuadIndex++;
Romain Guy97771732012-02-28 18:17:02 -0800422}
423
424void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
425 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
426 float x4, float y4, float u4, float v4, CacheTexture* texture) {
427
428 if (mClip &&
429 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
430 return;
431 }
432
433 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 -0700434
Romain Guy5b3b3522010-10-27 18:57:51 -0700435 if (mBounds) {
436 mBounds->left = fmin(mBounds->left, x1);
437 mBounds->top = fmin(mBounds->top, y3);
438 mBounds->right = fmax(mBounds->right, x3);
439 mBounds->bottom = fmax(mBounds->bottom, y1);
440 }
441
Romain Guy694b5192010-07-21 21:33:20 -0700442 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
443 issueDrawCommand();
444 mCurrentQuadIndex = 0;
445 }
446}
447
Romain Guy97771732012-02-28 18:17:02 -0800448void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
449 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
450 float x4, float y4, float u4, float v4, CacheTexture* texture) {
451
452 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
453
454 if (mBounds) {
455 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
456 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
457 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
458 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
459 }
460
461 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
462 issueDrawCommand();
463 mCurrentQuadIndex = 0;
464 }
465}
466
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700467void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
Romain Guy325a0f92011-01-05 15:26:55 -0800468 int flags = 0;
469 if (paint->isFakeBoldText()) {
470 flags |= Font::kFakeBold;
471 }
Romain Guy2577db12011-01-18 13:02:38 -0800472
473 const float skewX = paint->getTextSkewX();
474 uint32_t italicStyle = *(uint32_t*) &skewX;
Chet Haase8668f8a2011-03-02 13:51:36 -0800475 const float scaleXFloat = paint->getTextScaleX();
476 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
Romain Guybd496bc2011-08-02 17:32:41 -0700477 SkPaint::Style style = paint->getStyle();
478 const float strokeWidthFloat = paint->getStrokeWidth();
479 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
480 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
481 scaleX, style, strokeWidth);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700482
Romain Guy694b5192010-07-21 21:33:20 -0700483}
Romain Guy7975fb62010-10-01 16:36:14 -0700484
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700485FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Raph Levien416a8472012-07-19 22:48:17 -0700486 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700487 checkInit();
488
489 if (!mCurrentFont) {
490 DropShadow image;
491 image.width = 0;
492 image.height = 0;
493 image.image = NULL;
494 image.penX = 0;
495 image.penY = 0;
496 return image;
497 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700498
Romain Guy2d4fd362011-12-13 22:00:19 -0800499 mDrawn = false;
Romain Guyff98fa52011-11-28 09:35:09 -0800500 mClip = NULL;
501 mBounds = NULL;
502
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700503 Rect bounds;
Raph Levien416a8472012-07-19 22:48:17 -0700504 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
Romain Guyff98fa52011-11-28 09:35:09 -0800505
Romain Guy1e45aae2010-08-13 19:39:53 -0700506 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
507 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700508 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guyff98fa52011-11-28 09:35:09 -0800509
Romain Guy1e45aae2010-08-13 19:39:53 -0700510 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700511 dataBuffer[i] = 0;
512 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700513
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700514 int penX = radius - bounds.left;
515 int penY = radius - bounds.bottom;
516
Romain Guy726aeba2011-06-01 14:52:00 -0700517 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
Raph Levien416a8472012-07-19 22:48:17 -0700518 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700519 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
520
521 DropShadow image;
522 image.width = paddedWidth;
523 image.height = paddedHeight;
524 image.image = dataBuffer;
525 image.penX = penX;
526 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -0800527
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700528 return image;
529}
Romain Guy694b5192010-07-21 21:33:20 -0700530
Romain Guy671d6cf2012-01-18 12:39:17 -0800531void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700532 checkInit();
533
Romain Guy5b3b3522010-10-27 18:57:51 -0700534 mDrawn = false;
535 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -0700536 mClip = clip;
Romain Guy671d6cf2012-01-18 12:39:17 -0800537}
Romain Guyff98fa52011-11-28 09:35:09 -0800538
Romain Guy671d6cf2012-01-18 12:39:17 -0800539void FontRenderer::finishRender() {
Romain Guy5b3b3522010-10-27 18:57:51 -0700540 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -0800541 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700542
543 if (mCurrentQuadIndex != 0) {
544 issueDrawCommand();
545 mCurrentQuadIndex = 0;
546 }
Romain Guy671d6cf2012-01-18 12:39:17 -0800547}
548
Chet Haasee816bae2012-08-09 13:39:02 -0700549void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs) {
550 int flags = 0;
551 if (paint->isFakeBoldText()) {
552 flags |= Font::kFakeBold;
553 }
554 const float skewX = paint->getTextSkewX();
555 uint32_t italicStyle = *(uint32_t*) &skewX;
556 const float scaleXFloat = paint->getTextScaleX();
557 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
558 SkPaint::Style style = paint->getStyle();
559 const float strokeWidthFloat = paint->getStrokeWidth();
560 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
561 float fontSize = paint->getTextSize();
562 Font* font = Font::create(this, SkTypeface::UniqueID(paint->getTypeface()),
563 fontSize, flags, italicStyle, scaleX, style, strokeWidth);
564
565 font->precache(paint, text, numGlyphs);
566}
567
Romain Guy671d6cf2012-01-18 12:39:17 -0800568bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
569 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
570 if (!mCurrentFont) {
571 ALOGE("No font set");
572 return false;
573 }
574
575 initRender(clip, bounds);
576 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
577 finishRender();
578
579 return mDrawn;
580}
581
582bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
583 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
584 const float* positions, Rect* bounds) {
585 if (!mCurrentFont) {
586 ALOGE("No font set");
587 return false;
588 }
589
590 initRender(clip, bounds);
591 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
592 finishRender();
Romain Guy5b3b3522010-10-27 18:57:51 -0700593
594 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700595}
596
Romain Guy97771732012-02-28 18:17:02 -0800597bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
598 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
599 float hOffset, float vOffset, Rect* bounds) {
600 if (!mCurrentFont) {
601 ALOGE("No font set");
602 return false;
603 }
604
605 initRender(clip, bounds);
606 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
607 finishRender();
608
609 return mDrawn;
610}
611
Romain Guy9b1204b2012-09-04 15:22:57 -0700612void FontRenderer::removeFont(const Font* font) {
613 for (uint32_t ct = 0; ct < mActiveFonts.size(); ct++) {
614 if (mActiveFonts[ct] == font) {
615 mActiveFonts.removeAt(ct);
616 break;
617 }
618 }
619
620 if (mCurrentFont == font) {
621 mCurrentFont = NULL;
622 }
623}
624
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700625void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
626 // Compute gaussian weights for the blur
627 // e is the euler's number
628 float e = 2.718281828459045f;
629 float pi = 3.1415926535897932f;
630 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
631 // x is of the form [-radius .. 0 .. radius]
632 // and sigma varies with radius.
633 // Based on some experimental radius values and sigma's
634 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700635 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700636 // The larger the radius gets, the more our gaussian blur
637 // will resemble a box blur since with large sigma
638 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -0800639 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700640
641 // Now compute the coefficints
642 // We will store some redundant values to save some math during
643 // the blur calculations
644 // precompute some values
645 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
646 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
647
648 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -0800649 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700650 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700651 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
652 normalizeFactor += weights[r + radius];
653 }
654
655 //Now we need to normalize the weights because all our coefficients need to add up to one
656 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -0800657 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700658 weights[r + radius] *= normalizeFactor;
659 }
660}
661
662void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700663 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700664 float blurredPixel = 0.0f;
665 float currentPixel = 0.0f;
666
Romain Guy325a0f92011-01-05 15:26:55 -0800667 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700668
669 const uint8_t* input = source + y * width;
670 uint8_t* output = dest + y * width;
671
Romain Guy325a0f92011-01-05 15:26:55 -0800672 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700673 blurredPixel = 0.0f;
674 const float* gPtr = weights;
675 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800676 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700677 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -0800678 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700679 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700680 blurredPixel += currentPixel * gPtr[0];
681 gPtr++;
682 i++;
683 }
684 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800685 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700686 // Stepping left and right away from the pixel
687 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -0800688 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700689 validW = 0;
690 }
Romain Guy325a0f92011-01-05 15:26:55 -0800691 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700692 validW = width - 1;
693 }
694
Romain Guy325a0f92011-01-05 15:26:55 -0800695 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700696 blurredPixel += currentPixel * gPtr[0];
697 gPtr++;
698 }
699 }
700 *output = (uint8_t)blurredPixel;
701 output ++;
702 }
703 }
704}
705
706void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700707 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700708 float blurredPixel = 0.0f;
709 float currentPixel = 0.0f;
710
Romain Guy325a0f92011-01-05 15:26:55 -0800711 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700712 uint8_t* output = dest + y * width;
713
Romain Guy325a0f92011-01-05 15:26:55 -0800714 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700715 blurredPixel = 0.0f;
716 const float* gPtr = weights;
717 const uint8_t* input = source + x;
718 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800719 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700720 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -0800721 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700722 currentPixel = (float)(*i);
723 blurredPixel += currentPixel * gPtr[0];
724 gPtr++;
725 i += width;
726 }
727 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800728 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700729 int validH = y + r;
730 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -0800731 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700732 validH = 0;
733 }
Romain Guy325a0f92011-01-05 15:26:55 -0800734 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700735 validH = height - 1;
736 }
737
738 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -0800739 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700740 blurredPixel += currentPixel * gPtr[0];
741 gPtr++;
742 }
743 }
Romain Guy325a0f92011-01-05 15:26:55 -0800744 *output = (uint8_t) blurredPixel;
Romain Guy9b1204b2012-09-04 15:22:57 -0700745 output++;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700746 }
747 }
748}
749
750
751void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
752 float *gaussian = new float[2 * radius + 1];
753 computeGaussianWeights(gaussian, radius);
Romain Guyd71dd362011-12-12 19:03:35 -0800754
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700755 uint8_t* scratch = new uint8_t[width * height];
Romain Guyd71dd362011-12-12 19:03:35 -0800756
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700757 horizontalBlur(gaussian, radius, image, scratch, width, height);
758 verticalBlur(gaussian, radius, scratch, image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -0800759
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700760 delete[] gaussian;
761 delete[] scratch;
762}
763
Romain Guy694b5192010-07-21 21:33:20 -0700764}; // namespace uirenderer
765}; // namespace android