blob: a9bf13e8c7f598132743ca63b300266ac4f26db3 [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
Derek Sollenbergerca79cf62012-08-14 16:44:52 -040019#include <SkGlyph.h>
Romain Guy694b5192010-07-21 21:33:20 -070020#include <SkUtils.h>
21
Romain Guy51769a62010-07-23 00:28:00 -070022#include <cutils/properties.h>
Romain Guye2d345e2010-09-24 18:39:22 -070023
Romain Guy257ae352013-03-20 16:31:12 -070024#include <utils/Functor.h>
Romain Guy51769a62010-07-23 00:28:00 -070025#include <utils/Log.h>
26
Dan Morrille4d9a012013-03-28 18:10:43 -070027#ifdef ANDROID_ENABLE_RENDERSCRIPT
Romain Guy6e200402013-03-08 11:28:22 -080028#include <RenderScript.h>
Dan Morrille4d9a012013-03-28 18:10:43 -070029#endif
Chris Craikf2d8ccc2013-02-13 16:14:17 -080030
Romain Guy6e200402013-03-08 11:28:22 -080031#include "utils/Blur.h"
Chris Craikf2d8ccc2013-02-13 16:14:17 -080032#include "utils/Timing.h"
Romain Guy6e200402013-03-08 11:28:22 -080033
Romain Guy15bc6432011-12-13 13:11:32 -080034#include "Caches.h"
Romain Guyc9855a52011-01-21 21:14:15 -080035#include "Debug.h"
Romain Guy09087642013-04-04 12:27:54 -070036#include "Extensions.h"
Romain Guy51769a62010-07-23 00:28:00 -070037#include "FontRenderer.h"
Romain Guycf51a412013-04-08 19:40:31 -070038#include "PixelBuffer.h"
Romain Guy9f5dab32012-09-04 12:55:44 -070039#include "Rect.h"
Romain Guy51769a62010-07-23 00:28:00 -070040
Romain Guy694b5192010-07-21 21:33:20 -070041namespace android {
42namespace uirenderer {
43
Chris Craikf2d8ccc2013-02-13 16:14:17 -080044// blur inputs smaller than this constant will bypass renderscript
45#define RS_MIN_INPUT_CUTOFF 10000
46
Romain Guy694b5192010-07-21 21:33:20 -070047///////////////////////////////////////////////////////////////////////////////
Romain Guy694b5192010-07-21 21:33:20 -070048// FontRenderer
49///////////////////////////////////////////////////////////////////////////////
50
Romain Guy514fb182011-01-19 14:38:29 -080051static bool sLogFontRendererCreate = true;
52
Romain Guye3a9b242013-01-08 11:15:30 -080053FontRenderer::FontRenderer() :
54 mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity) {
55
Romain Guyc9855a52011-01-21 21:14:15 -080056 if (sLogFontRendererCreate) {
57 INIT_LOGD("Creating FontRenderer");
58 }
Romain Guy51769a62010-07-23 00:28:00 -070059
Romain Guyb45c0c92010-08-26 20:35:23 -070060 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -070061 mInitialized = false;
Romain Guy694b5192010-07-21 21:33:20 -070062
Chet Haase7de0cb12011-12-05 16:35:38 -080063 mCurrentCacheTexture = NULL;
Romain Guy9cccc2b92010-08-07 23:46:15 -070064
Chet Haase2a47c142011-12-14 15:22:56 -080065 mLinearFiltering = false;
66
Romain Guy694b5192010-07-21 21:33:20 -070067 mIndexBufferID = 0;
68
Chet Haaseeb32a492012-08-31 13:54:03 -070069 mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH;
70 mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT;
71 mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH;
72 mLargeCacheHeight = DEFAULT_TEXT_LARGE_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -070073
74 char property[PROPERTY_VALUE_MAX];
Chet Haaseeb32a492012-08-31 13:54:03 -070075 if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, NULL) > 0) {
Chet Haase7de0cb12011-12-05 16:35:38 -080076 mSmallCacheWidth = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -070077 }
Romain Guy9f5dab32012-09-04 12:55:44 -070078
Chet Haaseeb32a492012-08-31 13:54:03 -070079 if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, NULL) > 0) {
Chet Haase7de0cb12011-12-05 16:35:38 -080080 mSmallCacheHeight = atoi(property);
Chet Haaseeb32a492012-08-31 13:54:03 -070081 }
Romain Guy9f5dab32012-09-04 12:55:44 -070082
Chet Haaseeb32a492012-08-31 13:54:03 -070083 if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, NULL) > 0) {
84 mLargeCacheWidth = atoi(property);
85 }
Romain Guy9f5dab32012-09-04 12:55:44 -070086
Chet Haaseeb32a492012-08-31 13:54:03 -070087 if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, NULL) > 0) {
88 mLargeCacheHeight = atoi(property);
89 }
Romain Guy9f5dab32012-09-04 12:55:44 -070090
91 uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize;
92 mSmallCacheWidth = mSmallCacheWidth > maxTextureSize ? maxTextureSize : mSmallCacheWidth;
93 mSmallCacheHeight = mSmallCacheHeight > maxTextureSize ? maxTextureSize : mSmallCacheHeight;
94 mLargeCacheWidth = mLargeCacheWidth > maxTextureSize ? maxTextureSize : mLargeCacheWidth;
95 mLargeCacheHeight = mLargeCacheHeight > maxTextureSize ? maxTextureSize : mLargeCacheHeight;
96
Chet Haaseeb32a492012-08-31 13:54:03 -070097 if (sLogFontRendererCreate) {
98 INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
99 mSmallCacheWidth, mSmallCacheHeight,
100 mLargeCacheWidth, mLargeCacheHeight >> 1,
101 mLargeCacheWidth, mLargeCacheHeight >> 1,
102 mLargeCacheWidth, mLargeCacheHeight);
Romain Guy51769a62010-07-23 00:28:00 -0700103 }
Romain Guy514fb182011-01-19 14:38:29 -0800104
105 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -0700106}
107
108FontRenderer::~FontRenderer() {
Chet Haase378e9192012-08-15 15:54:54 -0700109 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
110 delete mCacheTextures[i];
Romain Guy694b5192010-07-21 21:33:20 -0700111 }
Chet Haase378e9192012-08-15 15:54:54 -0700112 mCacheTextures.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700113
Romain Guy9cccc2b92010-08-07 23:46:15 -0700114 if (mInitialized) {
Romain Guya9dd8202012-03-26 14:52:00 -0700115 // Unbinding the buffer shouldn't be necessary but it crashes with some drivers
116 Caches::getInstance().unbindIndicesBuffer();
Romain Guyb0317982012-03-21 11:52:52 -0700117 glDeleteBuffers(1, &mIndexBufferID);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700118 }
Romain Guy694b5192010-07-21 21:33:20 -0700119
Romain Guye3a9b242013-01-08 11:15:30 -0800120 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
121 while (it.next()) {
122 delete it.value();
Romain Guy694b5192010-07-21 21:33:20 -0700123 }
Romain Guye3a9b242013-01-08 11:15:30 -0800124 mActiveFonts.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700125}
126
127void FontRenderer::flushAllAndInvalidate() {
Romain Guy661a87e2013-03-19 15:24:36 -0700128 issueDrawCommand();
Romain Guy9d9758a2012-05-14 15:19:58 -0700129
Romain Guye3a9b242013-01-08 11:15:30 -0800130 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
131 while (it.next()) {
132 it.value()->invalidateTextureCache();
Romain Guy694b5192010-07-21 21:33:20 -0700133 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700134
Chet Haase378e9192012-08-15 15:54:54 -0700135 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
136 mCacheTextures[i]->init();
Romain Guy694b5192010-07-21 21:33:20 -0700137 }
138}
139
Chet Haase9a824562011-12-16 15:44:59 -0800140void FontRenderer::flushLargeCaches() {
Chet Haase378e9192012-08-15 15:54:54 -0700141 // Start from 1; don't deallocate smallest/default texture
142 for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
143 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guycf51a412013-04-08 19:40:31 -0700144 if (cacheTexture->getPixelBuffer()) {
Chet Haase378e9192012-08-15 15:54:54 -0700145 cacheTexture->init();
Romain Guye3a9b242013-01-08 11:15:30 -0800146 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
147 while (it.next()) {
148 it.value()->invalidateTextureCache(cacheTexture);
Chet Haasee816bae2012-08-09 13:39:02 -0700149 }
Romain Guy80872462012-09-04 16:42:01 -0700150 cacheTexture->releaseTexture();
Chet Haase9a824562011-12-16 15:44:59 -0800151 }
152 }
Chet Haase9a824562011-12-16 15:44:59 -0800153}
154
Chet Haase378e9192012-08-15 15:54:54 -0700155CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
156 uint32_t* startX, uint32_t* startY) {
157 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
158 if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
159 return mCacheTextures[i];
160 }
161 }
162 // Could not fit glyph into current cache textures
163 return NULL;
164}
165
Chet Haase7de0cb12011-12-05 16:35:38 -0800166void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
Chet Haasef942cf12012-08-30 09:06:46 -0700167 uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700168 checkInit();
Romain Guya4adcf02013-02-28 12:15:35 -0800169
170 // If the glyph bitmap is empty let's assum the glyph is valid
171 // so we can avoid doing extra work later on
172 if (glyph.fWidth == 0 || glyph.fHeight == 0) {
173 cachedGlyph->mIsValid = true;
174 cachedGlyph->mCacheTexture = NULL;
175 return;
176 }
177
Chet Haase7de0cb12011-12-05 16:35:38 -0800178 cachedGlyph->mIsValid = false;
Romain Guya4adcf02013-02-28 12:15:35 -0800179
Romain Guy694b5192010-07-21 21:33:20 -0700180 // If the glyph is too tall, don't cache it
Chet Haase378e9192012-08-15 15:54:54 -0700181 if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
Romain Guy80872462012-09-04 16:42:01 -0700182 mCacheTextures[mCacheTextures.size() - 1]->getHeight()) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700183 ALOGE("Font size too large to fit in cache. width, height = %i, %i",
184 (int) glyph.fWidth, (int) glyph.fHeight);
Chet Haase7de0cb12011-12-05 16:35:38 -0800185 return;
Romain Guy694b5192010-07-21 21:33:20 -0700186 }
187
188 // Now copy the bitmap into the cache texture
189 uint32_t startX = 0;
190 uint32_t startY = 0;
191
Chet Haase378e9192012-08-15 15:54:54 -0700192 CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700193
Chet Haase378e9192012-08-15 15:54:54 -0700194 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700195 if (!precaching) {
196 // If the new glyph didn't fit and we are not just trying to precache it,
197 // clear out the cache and try again
198 flushAllAndInvalidate();
199 cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
200 }
Romain Guy694b5192010-07-21 21:33:20 -0700201
Chet Haase378e9192012-08-15 15:54:54 -0700202 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700203 // either the glyph didn't fit or we're precaching and will cache it when we draw
Chet Haase7de0cb12011-12-05 16:35:38 -0800204 return;
Romain Guy694b5192010-07-21 21:33:20 -0700205 }
206 }
207
Chet Haase378e9192012-08-15 15:54:54 -0700208 cachedGlyph->mCacheTexture = cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800209
Romain Guy694b5192010-07-21 21:33:20 -0700210 *retOriginX = startX;
211 *retOriginY = startY;
212
213 uint32_t endX = startX + glyph.fWidth;
214 uint32_t endY = startY + glyph.fHeight;
215
Romain Guy80872462012-09-04 16:42:01 -0700216 uint32_t cacheWidth = cacheTexture->getWidth();
Romain Guy694b5192010-07-21 21:33:20 -0700217
Romain Guycf51a412013-04-08 19:40:31 -0700218 if (!cacheTexture->getPixelBuffer()) {
Romain Guy80872462012-09-04 16:42:01 -0700219 Caches::getInstance().activeTexture(0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800220 // Large-glyph texture memory is allocated only as needed
Romain Guy80872462012-09-04 16:42:01 -0700221 cacheTexture->allocateTexture();
Chet Haase7de0cb12011-12-05 16:35:38 -0800222 }
Romain Guy661a87e2013-03-19 15:24:36 -0700223 if (!cacheTexture->mesh()) {
224 cacheTexture->allocateMesh();
225 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700226
Romain Guyb969a0d2013-02-05 14:38:40 -0800227 // Tells us whether the glyphs is B&W (1 bit per pixel)
228 // or anti-aliased (8 bits per pixel)
229 SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
Romain Guy694b5192010-07-21 21:33:20 -0700230
Romain Guycf51a412013-04-08 19:40:31 -0700231 uint8_t* cacheBuffer = cacheTexture->getPixelBuffer()->map();
Romain Guy694b5192010-07-21 21:33:20 -0700232 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
Romain Guy33fa1f72012-08-07 19:09:57 -0700233
Romain Guyb969a0d2013-02-05 14:38:40 -0800234 // Copy the glyph image, taking the mask format into account
235 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
236 int stride = glyph.rowBytes();
237
Romain Guy0b58a3d2013-03-05 12:16:27 -0800238 uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
239 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
240
Romain Guyb969a0d2013-02-05 14:38:40 -0800241 switch (format) {
242 case SkMask::kA8_Format: {
243 if (mGammaTable) {
244 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
Romain Guy0b58a3d2013-03-05 12:16:27 -0800245 row = cacheY * cacheWidth;
246 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800247 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
248 uint8_t tempCol = bitmapBuffer[bY + bX];
Romain Guy0b58a3d2013-03-05 12:16:27 -0800249 cacheBuffer[row + cacheX] = mGammaTable[tempCol];
Romain Guyb969a0d2013-02-05 14:38:40 -0800250 }
Romain Guy0b58a3d2013-03-05 12:16:27 -0800251 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800252 }
253 } else {
254 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
Romain Guy0b58a3d2013-03-05 12:16:27 -0800255 row = cacheY * cacheWidth;
256 memcpy(&cacheBuffer[row + startX], &bitmapBuffer[bY], glyph.fWidth);
257 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
258 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800259 }
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700260 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800261 break;
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700262 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800263 case SkMask::kBW_Format: {
264 static const uint8_t COLORS[2] = { 0, 255 };
265
266 for (cacheY = startY; cacheY < endY; cacheY++) {
267 cacheX = startX;
268 int rowBytes = stride;
269 uint8_t* buffer = bitmapBuffer;
270
Romain Guy0b58a3d2013-03-05 12:16:27 -0800271 row = cacheY * cacheWidth;
272 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800273 while (--rowBytes >= 0) {
274 uint8_t b = *buffer++;
275 for (int8_t mask = 7; mask >= 0 && cacheX < endX; mask--) {
276 cacheBuffer[cacheY * cacheWidth + cacheX++] = COLORS[(b >> mask) & 0x1];
277 }
278 }
Romain Guy0b58a3d2013-03-05 12:16:27 -0800279 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800280
281 bitmapBuffer += stride;
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700282 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800283 break;
Romain Guy694b5192010-07-21 21:33:20 -0700284 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800285 default:
286 ALOGW("Unkown glyph format: 0x%x", format);
287 break;
Romain Guy694b5192010-07-21 21:33:20 -0700288 }
Romain Guy97771732012-02-28 18:17:02 -0800289
Romain Guy0b58a3d2013-03-05 12:16:27 -0800290 row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
291 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
292
Chet Haase7de0cb12011-12-05 16:35:38 -0800293 cachedGlyph->mIsValid = true;
Romain Guy694b5192010-07-21 21:33:20 -0700294}
295
Chet Haase7de0cb12011-12-05 16:35:38 -0800296CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
Chris Craik527a3aa2013-03-04 10:19:31 -0800297 CacheTexture* cacheTexture = new CacheTexture(width, height, gMaxNumberOfQuads);
Romain Guy9d9758a2012-05-14 15:19:58 -0700298
Chet Haase2a47c142011-12-14 15:22:56 -0800299 if (allocate) {
Romain Guy80872462012-09-04 16:42:01 -0700300 Caches::getInstance().activeTexture(0);
301 cacheTexture->allocateTexture();
Romain Guy661a87e2013-03-19 15:24:36 -0700302 cacheTexture->allocateMesh();
Chet Haase2a47c142011-12-14 15:22:56 -0800303 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700304
Chet Haase2a47c142011-12-14 15:22:56 -0800305 return cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800306}
307
308void FontRenderer::initTextTexture() {
Chet Haase378e9192012-08-15 15:54:54 -0700309 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
310 delete mCacheTextures[i];
Romain Guy9d9758a2012-05-14 15:19:58 -0700311 }
Chet Haase378e9192012-08-15 15:54:54 -0700312 mCacheTextures.clear();
Romain Guy9d9758a2012-05-14 15:19:58 -0700313
Chet Haase7de0cb12011-12-05 16:35:38 -0800314 mUploadTexture = false;
Chet Haase378e9192012-08-15 15:54:54 -0700315 mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
Chet Haaseeb32a492012-08-31 13:54:03 -0700316 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
317 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
318 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false));
Chet Haase378e9192012-08-15 15:54:54 -0700319 mCurrentCacheTexture = mCacheTextures[0];
Romain Guy694b5192010-07-21 21:33:20 -0700320}
321
322// Avoid having to reallocate memory and render quad by quad
323void FontRenderer::initVertexArrayBuffers() {
Chris Craik527a3aa2013-03-04 10:19:31 -0800324 uint32_t numIndices = gMaxNumberOfQuads * 6;
Romain Guyd71dd362011-12-12 19:03:35 -0800325 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700326 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700327
328 // Four verts, two triangles , six indices per quad
Chris Craik527a3aa2013-03-04 10:19:31 -0800329 for (uint32_t i = 0; i < gMaxNumberOfQuads; i++) {
Romain Guy694b5192010-07-21 21:33:20 -0700330 int i6 = i * 6;
331 int i4 = i * 4;
332
333 indexBufferData[i6 + 0] = i4 + 0;
334 indexBufferData[i6 + 1] = i4 + 1;
335 indexBufferData[i6 + 2] = i4 + 2;
336
337 indexBufferData[i6 + 3] = i4 + 0;
338 indexBufferData[i6 + 4] = i4 + 2;
339 indexBufferData[i6 + 5] = i4 + 3;
340 }
341
342 glGenBuffers(1, &mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800343 Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700344 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
Romain Guy694b5192010-07-21 21:33:20 -0700345
346 free(indexBufferData);
Romain Guy694b5192010-07-21 21:33:20 -0700347}
348
349// We don't want to allocate anything unless we actually draw text
350void FontRenderer::checkInit() {
351 if (mInitialized) {
352 return;
353 }
354
355 initTextTexture();
356 initVertexArrayBuffers();
357
358 mInitialized = true;
359}
360
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700361void FontRenderer::checkTextureUpdate() {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900362 if (!mUploadTexture) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700363 return;
Romain Guy694b5192010-07-21 21:33:20 -0700364 }
365
Romain Guy2d4fd362011-12-13 22:00:19 -0800366 Caches& caches = Caches::getInstance();
367 GLuint lastTextureId = 0;
Romain Guy09087642013-04-04 12:27:54 -0700368
Romain Guycf51a412013-04-08 19:40:31 -0700369 bool resetPixelStore = false;
Romain Guy09087642013-04-04 12:27:54 -0700370 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
371
Chet Haase378e9192012-08-15 15:54:54 -0700372 // Iterate over all the cache textures and see which ones need to be updated
373 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
374 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guycf51a412013-04-08 19:40:31 -0700375 if (cacheTexture->isDirty() && cacheTexture->getPixelBuffer()) {
Romain Guy80872462012-09-04 16:42:01 -0700376 if (cacheTexture->getTextureId() != lastTextureId) {
377 lastTextureId = cacheTexture->getTextureId();
Romain Guy2d4fd362011-12-13 22:00:19 -0800378 caches.activeTexture(0);
Romain Guy80872462012-09-04 16:42:01 -0700379 glBindTexture(GL_TEXTURE_2D, lastTextureId);
Romain Guy2d4fd362011-12-13 22:00:19 -0800380 }
Romain Guy09087642013-04-04 12:27:54 -0700381
Romain Guycf51a412013-04-08 19:40:31 -0700382 if (cacheTexture->upload()) {
383 resetPixelStore = true;
Romain Guy09087642013-04-04 12:27:54 -0700384 }
385
Chet Haasee816bae2012-08-09 13:39:02 -0700386#if DEBUG_FONT_RENDERER
Chet Haaseb92d8f72012-09-21 08:40:46 -0700387 ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d",
388 i, x, y, width, height);
Chet Haasee816bae2012-08-09 13:39:02 -0700389#endif
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700390 }
391 }
392
Romain Guycf51a412013-04-08 19:40:31 -0700393 // Unbind any PBO we might have used to update textures
394 caches.unbindPixelBuffer();
395
Romain Guy09087642013-04-04 12:27:54 -0700396 // Reset to default unpack row length to avoid affecting texture
397 // uploads in other parts of the renderer
Romain Guycf51a412013-04-08 19:40:31 -0700398 if (resetPixelStore) {
Romain Guy09087642013-04-04 12:27:54 -0700399 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
400 }
401
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700402 mUploadTexture = false;
403}
404
405void FontRenderer::issueDrawCommand() {
Romain Guy661a87e2013-03-19 15:24:36 -0700406 bool first = true;
407 bool force = false;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700408
Romain Guy115096f2013-03-19 11:32:41 -0700409 GLuint lastId = 0;
Romain Guy661a87e2013-03-19 15:24:36 -0700410 Caches& caches = Caches::getInstance();
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900411
Romain Guy661a87e2013-03-19 15:24:36 -0700412 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
413 CacheTexture* texture = mCacheTextures[i];
414 if (texture->canDraw()) {
415 if (first) {
Romain Guy257ae352013-03-20 16:31:12 -0700416 if (mFunctor) (*mFunctor)(0, NULL);
417
Romain Guy661a87e2013-03-19 15:24:36 -0700418 checkTextureUpdate();
419 caches.bindIndicesBuffer(mIndexBufferID);
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900420
Romain Guy661a87e2013-03-19 15:24:36 -0700421 if (!mDrawn) {
422 // If returns true, a VBO was bound and we must
423 // rebind our vertex attrib pointers even if
424 // they have the same values as the current pointers
425 force = caches.unbindMeshBuffer();
426 }
427
428 caches.activeTexture(0);
429 first = false;
430 }
431
432 glBindTexture(GL_TEXTURE_2D, texture->getTextureId());
433 texture->setLinearFiltering(mLinearFiltering, false);
434
435 TextureVertex* mesh = texture->mesh();
436 caches.bindPositionVertexPointer(force, &mesh[0].position[0]);
437 caches.bindTexCoordsVertexPointer(force, &mesh[0].texture[0]);
438 force = false;
439
440 glDrawElements(GL_TRIANGLES, texture->meshElementCount(),
441 GL_UNSIGNED_SHORT, texture->indices());
442
443 texture->resetMesh();
Romain Guy115096f2013-03-19 11:32:41 -0700444 }
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900445 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700446
447 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700448}
449
Romain Guy97771732012-02-28 18:17:02 -0800450void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
451 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800452 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800453 if (texture != mCurrentCacheTexture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800454 // Now use the new texture id
455 mCurrentCacheTexture = texture;
456 }
Romain Guy09147fb2010-07-22 13:08:20 -0700457
Romain Guy661a87e2013-03-19 15:24:36 -0700458 mCurrentCacheTexture->addQuad(x1, y1, u1, v1, x2, y2, u2, v2,
459 x3, y3, u3, v3, x4, y4, u4, v4);
Romain Guy97771732012-02-28 18:17:02 -0800460}
461
462void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
463 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
464 float x4, float y4, float u4, float v4, CacheTexture* texture) {
465
466 if (mClip &&
467 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
468 return;
469 }
470
471 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 -0700472
Romain Guy5b3b3522010-10-27 18:57:51 -0700473 if (mBounds) {
474 mBounds->left = fmin(mBounds->left, x1);
475 mBounds->top = fmin(mBounds->top, y3);
476 mBounds->right = fmax(mBounds->right, x3);
477 mBounds->bottom = fmax(mBounds->bottom, y1);
478 }
479
Romain Guy661a87e2013-03-19 15:24:36 -0700480 if (mCurrentCacheTexture->endOfMesh()) {
Romain Guy694b5192010-07-21 21:33:20 -0700481 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700482 }
483}
484
Romain Guy97771732012-02-28 18:17:02 -0800485void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
486 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
487 float x4, float y4, float u4, float v4, CacheTexture* texture) {
488
489 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
490
491 if (mBounds) {
492 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
493 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
494 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
495 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
496 }
497
Romain Guy661a87e2013-03-19 15:24:36 -0700498 if (mCurrentCacheTexture->endOfMesh()) {
Romain Guy97771732012-02-28 18:17:02 -0800499 issueDrawCommand();
Romain Guy97771732012-02-28 18:17:02 -0800500 }
501}
502
Romain Guye3a9b242013-01-08 11:15:30 -0800503void FontRenderer::setFont(SkPaint* paint, const mat4& matrix) {
504 mCurrentFont = Font::create(this, paint, matrix);
Romain Guy694b5192010-07-21 21:33:20 -0700505}
Romain Guy7975fb62010-10-01 16:36:14 -0700506
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700507FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Raph Levien416a8472012-07-19 22:48:17 -0700508 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700509 checkInit();
510
Romain Guycf51a412013-04-08 19:40:31 -0700511 DropShadow image;
512 image.width = 0;
513 image.height = 0;
514 image.image = NULL;
515 image.penX = 0;
516 image.penY = 0;
517
Romain Guy1e45aae2010-08-13 19:39:53 -0700518 if (!mCurrentFont) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700519 return image;
520 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700521
Romain Guy2d4fd362011-12-13 22:00:19 -0800522 mDrawn = false;
Romain Guyff98fa52011-11-28 09:35:09 -0800523 mClip = NULL;
524 mBounds = NULL;
525
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700526 Rect bounds;
Raph Levien416a8472012-07-19 22:48:17 -0700527 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
Romain Guyff98fa52011-11-28 09:35:09 -0800528
Romain Guy1e45aae2010-08-13 19:39:53 -0700529 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
530 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Romain Guyff98fa52011-11-28 09:35:09 -0800531
Romain Guycf51a412013-04-08 19:40:31 -0700532 uint32_t maxSize = Caches::getInstance().maxTextureSize;
533 if (paddedWidth > maxSize || paddedHeight > maxSize) {
534 return image;
535 }
536
Dan Morrille4d9a012013-03-28 18:10:43 -0700537#ifdef ANDROID_ENABLE_RENDERSCRIPT
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800538 // Align buffers for renderscript usage
539 if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) {
540 paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700541 }
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800542 int size = paddedWidth * paddedHeight;
Romain Guy6e200402013-03-08 11:28:22 -0800543 uint8_t* dataBuffer = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, size);
Dan Morrille4d9a012013-03-28 18:10:43 -0700544#else
545 int size = paddedWidth * paddedHeight;
546 uint8_t* dataBuffer = (uint8_t*) malloc(size);
547#endif
548
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800549 memset(dataBuffer, 0, size);
550
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700551 int penX = radius - bounds.left;
552 int penY = radius - bounds.bottom;
553
Chris Craikdd8697c2013-02-22 10:41:36 -0800554 if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) {
555 // text has non-whitespace, so draw and blur to create the shadow
556 // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted
557 // TODO: don't draw pure whitespace in the first place, and avoid needing this check
558 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
559 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
560
Romain Guycf51a412013-04-08 19:40:31 -0700561 // Unbind any PBO we might have used
562 Caches::getInstance().unbindPixelBuffer();
563
Chris Craikdd8697c2013-02-22 10:41:36 -0800564 blurImage(&dataBuffer, paddedWidth, paddedHeight, radius);
565 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700566
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700567 image.width = paddedWidth;
568 image.height = paddedHeight;
569 image.image = dataBuffer;
570 image.penX = penX;
571 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -0800572
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700573 return image;
574}
Romain Guy694b5192010-07-21 21:33:20 -0700575
Romain Guy257ae352013-03-20 16:31:12 -0700576void FontRenderer::initRender(const Rect* clip, Rect* bounds, Functor* functor) {
Romain Guy694b5192010-07-21 21:33:20 -0700577 checkInit();
578
Romain Guy5b3b3522010-10-27 18:57:51 -0700579 mDrawn = false;
580 mBounds = bounds;
Romain Guy257ae352013-03-20 16:31:12 -0700581 mFunctor = functor;
Romain Guy09147fb2010-07-22 13:08:20 -0700582 mClip = clip;
Romain Guy671d6cf2012-01-18 12:39:17 -0800583}
Romain Guyff98fa52011-11-28 09:35:09 -0800584
Romain Guy671d6cf2012-01-18 12:39:17 -0800585void FontRenderer::finishRender() {
Romain Guy5b3b3522010-10-27 18:57:51 -0700586 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -0800587 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700588
Romain Guy661a87e2013-03-19 15:24:36 -0700589 issueDrawCommand();
Romain Guy671d6cf2012-01-18 12:39:17 -0800590}
591
Romain Guye3a9b242013-01-08 11:15:30 -0800592void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix) {
593 Font* font = Font::create(this, paint, matrix);
Chet Haasee816bae2012-08-09 13:39:02 -0700594 font->precache(paint, text, numGlyphs);
595}
596
Romain Guycf51a412013-04-08 19:40:31 -0700597void FontRenderer::endPrecaching() {
598 checkTextureUpdate();
599}
600
Romain Guy671d6cf2012-01-18 12:39:17 -0800601bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
602 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
Chris Craik527a3aa2013-03-04 10:19:31 -0800603 const float* positions, Rect* bounds, Functor* functor, bool forceFinish) {
Romain Guy671d6cf2012-01-18 12:39:17 -0800604 if (!mCurrentFont) {
605 ALOGE("No font set");
606 return false;
607 }
608
Romain Guy257ae352013-03-20 16:31:12 -0700609 initRender(clip, bounds, functor);
Romain Guy671d6cf2012-01-18 12:39:17 -0800610 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
Chris Craik527a3aa2013-03-04 10:19:31 -0800611
612 if (forceFinish) {
613 finishRender();
614 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700615
616 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700617}
618
Romain Guy97771732012-02-28 18:17:02 -0800619bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
620 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
621 float hOffset, float vOffset, Rect* bounds) {
622 if (!mCurrentFont) {
623 ALOGE("No font set");
624 return false;
625 }
626
Romain Guy257ae352013-03-20 16:31:12 -0700627 initRender(clip, bounds, NULL);
Romain Guy97771732012-02-28 18:17:02 -0800628 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
629 finishRender();
630
631 return mDrawn;
632}
633
Romain Guy9b1204b2012-09-04 15:22:57 -0700634void FontRenderer::removeFont(const Font* font) {
Romain Guye3a9b242013-01-08 11:15:30 -0800635 mActiveFonts.remove(font->getDescription());
Romain Guy9b1204b2012-09-04 15:22:57 -0700636
637 if (mCurrentFont == font) {
638 mCurrentFont = NULL;
639 }
640}
641
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800642void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) {
Dan Morrille4d9a012013-03-28 18:10:43 -0700643#ifdef ANDROID_ENABLE_RENDERSCRIPT
644 if (width * height * radius >= RS_MIN_INPUT_CUTOFF) {
645 uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700646
Dan Morrille4d9a012013-03-28 18:10:43 -0700647 if (mRs.get() == 0) {
648 mRs = new RSC::RS();
649 if (!mRs->init(true, true)) {
650 ALOGE("blur RS failed to init");
651 }
Romain Guyd71dd362011-12-12 19:03:35 -0800652
Dan Morrille4d9a012013-03-28 18:10:43 -0700653 mRsElement = RSC::Element::A_8(mRs);
654 mRsScript = new RSC::ScriptIntrinsicBlur(mRs, mRsElement);
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800655 }
656
Dan Morrille4d9a012013-03-28 18:10:43 -0700657 sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0);
658 sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
659 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, *image);
660 sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
661 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, outImage);
662
663 mRsScript->setRadius(radius);
664 mRsScript->blur(ain, aout);
665
666 // replace the original image's pointer, avoiding a copy back to the original buffer
667 free(*image);
668 *image = outImage;
669
670 return;
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800671 }
Dan Morrille4d9a012013-03-28 18:10:43 -0700672#endif
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800673
Dan Morrille4d9a012013-03-28 18:10:43 -0700674 float *gaussian = new float[2 * radius + 1];
675 Blur::generateGaussianWeights(gaussian, radius);
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800676
Dan Morrille4d9a012013-03-28 18:10:43 -0700677 uint8_t* scratch = new uint8_t[width * height];
678 Blur::horizontal(gaussian, radius, *image, scratch, width, height);
679 Blur::vertical(gaussian, radius, scratch, *image, width, height);
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800680
Dan Morrille4d9a012013-03-28 18:10:43 -0700681 delete[] gaussian;
682 delete[] scratch;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700683}
684
Romain Guycf51a412013-04-08 19:40:31 -0700685uint32_t FontRenderer::getCacheSize() const {
686 uint32_t size = 0;
687 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
688 CacheTexture* cacheTexture = mCacheTextures[i];
689 if (cacheTexture && cacheTexture->getPixelBuffer()) {
690 size += cacheTexture->getPixelBuffer()->getSize();
691 }
692 }
693 return size;
694}
695
Romain Guy694b5192010-07-21 21:33:20 -0700696}; // namespace uirenderer
697}; // namespace android