blob: 79a7a934e78d799727967ec8192a5bf32fcbfc6e [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 Guy9cccc2b2010-08-07 23:46:15 -070064
Chet Haase2a47c142011-12-14 15:22:56 -080065 mLinearFiltering = false;
66
Chet Haaseeb32a492012-08-31 13:54:03 -070067 mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH;
68 mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT;
69 mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH;
70 mLargeCacheHeight = DEFAULT_TEXT_LARGE_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -070071
72 char property[PROPERTY_VALUE_MAX];
Chet Haaseeb32a492012-08-31 13:54:03 -070073 if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, NULL) > 0) {
Chet Haase7de0cb12011-12-05 16:35:38 -080074 mSmallCacheWidth = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -070075 }
Romain Guy9f5dab32012-09-04 12:55:44 -070076
Chet Haaseeb32a492012-08-31 13:54:03 -070077 if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, NULL) > 0) {
Chet Haase7de0cb12011-12-05 16:35:38 -080078 mSmallCacheHeight = atoi(property);
Chet Haaseeb32a492012-08-31 13:54:03 -070079 }
Romain Guy9f5dab32012-09-04 12:55:44 -070080
Chet Haaseeb32a492012-08-31 13:54:03 -070081 if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, NULL) > 0) {
82 mLargeCacheWidth = atoi(property);
83 }
Romain Guy9f5dab32012-09-04 12:55:44 -070084
Chet Haaseeb32a492012-08-31 13:54:03 -070085 if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, NULL) > 0) {
86 mLargeCacheHeight = atoi(property);
87 }
Romain Guy9f5dab32012-09-04 12:55:44 -070088
89 uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize;
90 mSmallCacheWidth = mSmallCacheWidth > maxTextureSize ? maxTextureSize : mSmallCacheWidth;
91 mSmallCacheHeight = mSmallCacheHeight > maxTextureSize ? maxTextureSize : mSmallCacheHeight;
92 mLargeCacheWidth = mLargeCacheWidth > maxTextureSize ? maxTextureSize : mLargeCacheWidth;
93 mLargeCacheHeight = mLargeCacheHeight > maxTextureSize ? maxTextureSize : mLargeCacheHeight;
94
Chet Haaseeb32a492012-08-31 13:54:03 -070095 if (sLogFontRendererCreate) {
96 INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
97 mSmallCacheWidth, mSmallCacheHeight,
98 mLargeCacheWidth, mLargeCacheHeight >> 1,
99 mLargeCacheWidth, mLargeCacheHeight >> 1,
100 mLargeCacheWidth, mLargeCacheHeight);
Romain Guy51769a62010-07-23 00:28:00 -0700101 }
Romain Guy514fb182011-01-19 14:38:29 -0800102
103 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -0700104}
105
106FontRenderer::~FontRenderer() {
Chet Haase378e9192012-08-15 15:54:54 -0700107 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
108 delete mCacheTextures[i];
Romain Guy694b5192010-07-21 21:33:20 -0700109 }
Chet Haase378e9192012-08-15 15:54:54 -0700110 mCacheTextures.clear();
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() {
Romain Guy661a87e2013-03-19 15:24:36 -0700120 issueDrawCommand();
Romain Guy9d9758a2012-05-14 15:19:58 -0700121
Romain Guye3a9b242013-01-08 11:15:30 -0800122 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
123 while (it.next()) {
124 it.value()->invalidateTextureCache();
Romain Guy694b5192010-07-21 21:33:20 -0700125 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700126
Chet Haase378e9192012-08-15 15:54:54 -0700127 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
128 mCacheTextures[i]->init();
Romain Guy694b5192010-07-21 21:33:20 -0700129 }
130}
131
Chet Haase9a824562011-12-16 15:44:59 -0800132void FontRenderer::flushLargeCaches() {
Chet Haase378e9192012-08-15 15:54:54 -0700133 // Start from 1; don't deallocate smallest/default texture
134 for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
135 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guycf51a412013-04-08 19:40:31 -0700136 if (cacheTexture->getPixelBuffer()) {
Chet Haase378e9192012-08-15 15:54:54 -0700137 cacheTexture->init();
Romain Guye3a9b242013-01-08 11:15:30 -0800138 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
139 while (it.next()) {
140 it.value()->invalidateTextureCache(cacheTexture);
Chet Haasee816bae2012-08-09 13:39:02 -0700141 }
Romain Guy80872462012-09-04 16:42:01 -0700142 cacheTexture->releaseTexture();
Chet Haase9a824562011-12-16 15:44:59 -0800143 }
144 }
Chet Haase9a824562011-12-16 15:44:59 -0800145}
146
Chet Haase378e9192012-08-15 15:54:54 -0700147CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
148 uint32_t* startX, uint32_t* startY) {
149 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
150 if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
151 return mCacheTextures[i];
152 }
153 }
154 // Could not fit glyph into current cache textures
155 return NULL;
156}
157
Chet Haase7de0cb12011-12-05 16:35:38 -0800158void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
Chet Haasef942cf12012-08-30 09:06:46 -0700159 uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700160 checkInit();
Romain Guya4adcf02013-02-28 12:15:35 -0800161
162 // If the glyph bitmap is empty let's assum the glyph is valid
163 // so we can avoid doing extra work later on
164 if (glyph.fWidth == 0 || glyph.fHeight == 0) {
165 cachedGlyph->mIsValid = true;
166 cachedGlyph->mCacheTexture = NULL;
167 return;
168 }
169
Chet Haase7de0cb12011-12-05 16:35:38 -0800170 cachedGlyph->mIsValid = false;
Romain Guya4adcf02013-02-28 12:15:35 -0800171
Romain Guy694b5192010-07-21 21:33:20 -0700172 // If the glyph is too tall, don't cache it
Chet Haase378e9192012-08-15 15:54:54 -0700173 if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
Romain Guy80872462012-09-04 16:42:01 -0700174 mCacheTextures[mCacheTextures.size() - 1]->getHeight()) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700175 ALOGE("Font size too large to fit in cache. width, height = %i, %i",
176 (int) glyph.fWidth, (int) glyph.fHeight);
Chet Haase7de0cb12011-12-05 16:35:38 -0800177 return;
Romain Guy694b5192010-07-21 21:33:20 -0700178 }
179
180 // Now copy the bitmap into the cache texture
181 uint32_t startX = 0;
182 uint32_t startY = 0;
183
Chet Haase378e9192012-08-15 15:54:54 -0700184 CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700185
Chet Haase378e9192012-08-15 15:54:54 -0700186 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700187 if (!precaching) {
188 // If the new glyph didn't fit and we are not just trying to precache it,
189 // clear out the cache and try again
190 flushAllAndInvalidate();
191 cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
192 }
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 // either the glyph didn't fit or we're precaching and will cache it when we draw
Chet Haase7de0cb12011-12-05 16:35:38 -0800196 return;
Romain Guy694b5192010-07-21 21:33:20 -0700197 }
198 }
199
Chet Haase378e9192012-08-15 15:54:54 -0700200 cachedGlyph->mCacheTexture = cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800201
Romain Guy694b5192010-07-21 21:33:20 -0700202 *retOriginX = startX;
203 *retOriginY = startY;
204
205 uint32_t endX = startX + glyph.fWidth;
206 uint32_t endY = startY + glyph.fHeight;
207
Romain Guy80872462012-09-04 16:42:01 -0700208 uint32_t cacheWidth = cacheTexture->getWidth();
Romain Guy694b5192010-07-21 21:33:20 -0700209
Romain Guycf51a412013-04-08 19:40:31 -0700210 if (!cacheTexture->getPixelBuffer()) {
Romain Guy80872462012-09-04 16:42:01 -0700211 Caches::getInstance().activeTexture(0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800212 // Large-glyph texture memory is allocated only as needed
Romain Guy80872462012-09-04 16:42:01 -0700213 cacheTexture->allocateTexture();
Chet Haase7de0cb12011-12-05 16:35:38 -0800214 }
Romain Guy661a87e2013-03-19 15:24:36 -0700215 if (!cacheTexture->mesh()) {
216 cacheTexture->allocateMesh();
217 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700218
Romain Guyb969a0d2013-02-05 14:38:40 -0800219 // Tells us whether the glyphs is B&W (1 bit per pixel)
220 // or anti-aliased (8 bits per pixel)
221 SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
Romain Guy694b5192010-07-21 21:33:20 -0700222
Romain Guycf51a412013-04-08 19:40:31 -0700223 uint8_t* cacheBuffer = cacheTexture->getPixelBuffer()->map();
Romain Guy694b5192010-07-21 21:33:20 -0700224 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
Romain Guy33fa1f72012-08-07 19:09:57 -0700225
Romain Guyb969a0d2013-02-05 14:38:40 -0800226 // Copy the glyph image, taking the mask format into account
227 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
228 int stride = glyph.rowBytes();
229
Romain Guy0b58a3d2013-03-05 12:16:27 -0800230 uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
231 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
232
Romain Guyb969a0d2013-02-05 14:38:40 -0800233 switch (format) {
234 case SkMask::kA8_Format: {
235 if (mGammaTable) {
236 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
Romain Guy0b58a3d2013-03-05 12:16:27 -0800237 row = cacheY * cacheWidth;
238 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800239 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
240 uint8_t tempCol = bitmapBuffer[bY + bX];
Romain Guy0b58a3d2013-03-05 12:16:27 -0800241 cacheBuffer[row + cacheX] = mGammaTable[tempCol];
Romain Guyb969a0d2013-02-05 14:38:40 -0800242 }
Romain Guy0b58a3d2013-03-05 12:16:27 -0800243 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800244 }
245 } else {
246 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
Romain Guy0b58a3d2013-03-05 12:16:27 -0800247 row = cacheY * cacheWidth;
248 memcpy(&cacheBuffer[row + startX], &bitmapBuffer[bY], glyph.fWidth);
249 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
250 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800251 }
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700252 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800253 break;
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700254 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800255 case SkMask::kBW_Format: {
256 static const uint8_t COLORS[2] = { 0, 255 };
257
258 for (cacheY = startY; cacheY < endY; cacheY++) {
259 cacheX = startX;
260 int rowBytes = stride;
261 uint8_t* buffer = bitmapBuffer;
262
Romain Guy0b58a3d2013-03-05 12:16:27 -0800263 row = cacheY * cacheWidth;
264 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800265 while (--rowBytes >= 0) {
266 uint8_t b = *buffer++;
267 for (int8_t mask = 7; mask >= 0 && cacheX < endX; mask--) {
268 cacheBuffer[cacheY * cacheWidth + cacheX++] = COLORS[(b >> mask) & 0x1];
269 }
270 }
Romain Guy0b58a3d2013-03-05 12:16:27 -0800271 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800272
273 bitmapBuffer += stride;
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700274 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800275 break;
Romain Guy694b5192010-07-21 21:33:20 -0700276 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800277 default:
278 ALOGW("Unkown glyph format: 0x%x", format);
279 break;
Romain Guy694b5192010-07-21 21:33:20 -0700280 }
Romain Guy97771732012-02-28 18:17:02 -0800281
Romain Guy0b58a3d2013-03-05 12:16:27 -0800282 row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
283 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
284
Chet Haase7de0cb12011-12-05 16:35:38 -0800285 cachedGlyph->mIsValid = true;
Romain Guy694b5192010-07-21 21:33:20 -0700286}
287
Chet Haase7de0cb12011-12-05 16:35:38 -0800288CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
Chris Craik527a3aa2013-03-04 10:19:31 -0800289 CacheTexture* cacheTexture = new CacheTexture(width, height, gMaxNumberOfQuads);
Romain Guy9d9758a2012-05-14 15:19:58 -0700290
Chet Haase2a47c142011-12-14 15:22:56 -0800291 if (allocate) {
Romain Guy80872462012-09-04 16:42:01 -0700292 Caches::getInstance().activeTexture(0);
293 cacheTexture->allocateTexture();
Romain Guy661a87e2013-03-19 15:24:36 -0700294 cacheTexture->allocateMesh();
Chet Haase2a47c142011-12-14 15:22:56 -0800295 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700296
Chet Haase2a47c142011-12-14 15:22:56 -0800297 return cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800298}
299
300void FontRenderer::initTextTexture() {
Chet Haase378e9192012-08-15 15:54:54 -0700301 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
302 delete mCacheTextures[i];
Romain Guy9d9758a2012-05-14 15:19:58 -0700303 }
Chet Haase378e9192012-08-15 15:54:54 -0700304 mCacheTextures.clear();
Romain Guy9d9758a2012-05-14 15:19:58 -0700305
Chet Haase7de0cb12011-12-05 16:35:38 -0800306 mUploadTexture = false;
Chet Haase378e9192012-08-15 15:54:54 -0700307 mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
Chet Haaseeb32a492012-08-31 13:54:03 -0700308 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
309 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
310 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false));
Chet Haase378e9192012-08-15 15:54:54 -0700311 mCurrentCacheTexture = mCacheTextures[0];
Romain Guy694b5192010-07-21 21:33:20 -0700312}
313
Romain Guy694b5192010-07-21 21:33:20 -0700314// We don't want to allocate anything unless we actually draw text
315void FontRenderer::checkInit() {
316 if (mInitialized) {
317 return;
318 }
319
320 initTextTexture();
Romain Guy694b5192010-07-21 21:33:20 -0700321
322 mInitialized = true;
323}
324
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700325void FontRenderer::checkTextureUpdate() {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900326 if (!mUploadTexture) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700327 return;
Romain Guy694b5192010-07-21 21:33:20 -0700328 }
329
Romain Guy2d4fd362011-12-13 22:00:19 -0800330 Caches& caches = Caches::getInstance();
331 GLuint lastTextureId = 0;
Romain Guy09087642013-04-04 12:27:54 -0700332
Romain Guycf51a412013-04-08 19:40:31 -0700333 bool resetPixelStore = false;
Romain Guy09087642013-04-04 12:27:54 -0700334 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
335
Chet Haase378e9192012-08-15 15:54:54 -0700336 // Iterate over all the cache textures and see which ones need to be updated
337 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
338 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guycf51a412013-04-08 19:40:31 -0700339 if (cacheTexture->isDirty() && cacheTexture->getPixelBuffer()) {
Romain Guy80872462012-09-04 16:42:01 -0700340 if (cacheTexture->getTextureId() != lastTextureId) {
341 lastTextureId = cacheTexture->getTextureId();
Romain Guy2d4fd362011-12-13 22:00:19 -0800342 caches.activeTexture(0);
Romain Guy8aa195d2013-06-04 18:00:09 -0700343 caches.bindTexture(lastTextureId);
Romain Guy2d4fd362011-12-13 22:00:19 -0800344 }
Romain Guy09087642013-04-04 12:27:54 -0700345
Romain Guycf51a412013-04-08 19:40:31 -0700346 if (cacheTexture->upload()) {
347 resetPixelStore = true;
Romain Guy09087642013-04-04 12:27:54 -0700348 }
349
Chet Haasee816bae2012-08-09 13:39:02 -0700350#if DEBUG_FONT_RENDERER
Chet Haaseb92d8f72012-09-21 08:40:46 -0700351 ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d",
352 i, x, y, width, height);
Chet Haasee816bae2012-08-09 13:39:02 -0700353#endif
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700354 }
355 }
356
Romain Guycf51a412013-04-08 19:40:31 -0700357 // Unbind any PBO we might have used to update textures
358 caches.unbindPixelBuffer();
359
Romain Guy09087642013-04-04 12:27:54 -0700360 // Reset to default unpack row length to avoid affecting texture
361 // uploads in other parts of the renderer
Romain Guycf51a412013-04-08 19:40:31 -0700362 if (resetPixelStore) {
Romain Guy09087642013-04-04 12:27:54 -0700363 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
364 }
365
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700366 mUploadTexture = false;
367}
368
369void FontRenderer::issueDrawCommand() {
Romain Guy661a87e2013-03-19 15:24:36 -0700370 bool first = true;
371 bool force = false;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700372
Romain Guy115096f2013-03-19 11:32:41 -0700373 GLuint lastId = 0;
Romain Guy661a87e2013-03-19 15:24:36 -0700374 Caches& caches = Caches::getInstance();
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900375
Romain Guy661a87e2013-03-19 15:24:36 -0700376 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
377 CacheTexture* texture = mCacheTextures[i];
378 if (texture->canDraw()) {
379 if (first) {
Romain Guy257ae352013-03-20 16:31:12 -0700380 if (mFunctor) (*mFunctor)(0, NULL);
381
Romain Guy661a87e2013-03-19 15:24:36 -0700382 checkTextureUpdate();
Romain Guy31e08e92013-06-18 15:53:53 -0700383 caches.bindIndicesBuffer();
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900384
Romain Guy661a87e2013-03-19 15:24:36 -0700385 if (!mDrawn) {
386 // If returns true, a VBO was bound and we must
387 // rebind our vertex attrib pointers even if
388 // they have the same values as the current pointers
389 force = caches.unbindMeshBuffer();
390 }
391
392 caches.activeTexture(0);
393 first = false;
394 }
395
Romain Guy8aa195d2013-06-04 18:00:09 -0700396 caches.bindTexture(texture->getTextureId());
Romain Guy661a87e2013-03-19 15:24:36 -0700397 texture->setLinearFiltering(mLinearFiltering, false);
398
399 TextureVertex* mesh = texture->mesh();
400 caches.bindPositionVertexPointer(force, &mesh[0].position[0]);
401 caches.bindTexCoordsVertexPointer(force, &mesh[0].texture[0]);
402 force = false;
403
404 glDrawElements(GL_TRIANGLES, texture->meshElementCount(),
405 GL_UNSIGNED_SHORT, texture->indices());
406
407 texture->resetMesh();
Romain Guy115096f2013-03-19 11:32:41 -0700408 }
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900409 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700410
411 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700412}
413
Romain Guy97771732012-02-28 18:17:02 -0800414void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
415 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800416 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800417 if (texture != mCurrentCacheTexture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800418 // Now use the new texture id
419 mCurrentCacheTexture = texture;
420 }
Romain Guy09147fb2010-07-22 13:08:20 -0700421
Romain Guy661a87e2013-03-19 15:24:36 -0700422 mCurrentCacheTexture->addQuad(x1, y1, u1, v1, x2, y2, u2, v2,
423 x3, y3, u3, v3, x4, y4, u4, v4);
Romain Guy97771732012-02-28 18:17:02 -0800424}
425
426void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
427 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
428 float x4, float y4, float u4, float v4, CacheTexture* texture) {
429
430 if (mClip &&
431 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
432 return;
433 }
434
435 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 -0700436
Romain Guy5b3b3522010-10-27 18:57:51 -0700437 if (mBounds) {
438 mBounds->left = fmin(mBounds->left, x1);
439 mBounds->top = fmin(mBounds->top, y3);
440 mBounds->right = fmax(mBounds->right, x3);
441 mBounds->bottom = fmax(mBounds->bottom, y1);
442 }
443
Romain Guy661a87e2013-03-19 15:24:36 -0700444 if (mCurrentCacheTexture->endOfMesh()) {
Romain Guy694b5192010-07-21 21:33:20 -0700445 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700446 }
447}
448
Romain Guy97771732012-02-28 18:17:02 -0800449void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
450 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
451 float x4, float y4, float u4, float v4, CacheTexture* texture) {
452
453 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
454
455 if (mBounds) {
456 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
457 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
458 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
459 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
460 }
461
Romain Guy661a87e2013-03-19 15:24:36 -0700462 if (mCurrentCacheTexture->endOfMesh()) {
Romain Guy97771732012-02-28 18:17:02 -0800463 issueDrawCommand();
Romain Guy97771732012-02-28 18:17:02 -0800464 }
465}
466
Romain Guye3a9b242013-01-08 11:15:30 -0800467void FontRenderer::setFont(SkPaint* paint, const mat4& matrix) {
468 mCurrentFont = Font::create(this, paint, matrix);
Romain Guy694b5192010-07-21 21:33:20 -0700469}
Romain Guy7975fb62010-10-01 16:36:14 -0700470
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700471FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Raph Levien416a8472012-07-19 22:48:17 -0700472 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700473 checkInit();
474
Romain Guycf51a412013-04-08 19:40:31 -0700475 DropShadow image;
476 image.width = 0;
477 image.height = 0;
478 image.image = NULL;
479 image.penX = 0;
480 image.penY = 0;
481
Romain Guy1e45aae2010-08-13 19:39:53 -0700482 if (!mCurrentFont) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700483 return image;
484 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700485
Romain Guy2d4fd362011-12-13 22:00:19 -0800486 mDrawn = false;
Romain Guyff98fa52011-11-28 09:35:09 -0800487 mClip = NULL;
488 mBounds = NULL;
489
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700490 Rect bounds;
Raph Levien416a8472012-07-19 22:48:17 -0700491 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
Romain Guyff98fa52011-11-28 09:35:09 -0800492
Romain Guy1e45aae2010-08-13 19:39:53 -0700493 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
494 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Romain Guyff98fa52011-11-28 09:35:09 -0800495
Romain Guycf51a412013-04-08 19:40:31 -0700496 uint32_t maxSize = Caches::getInstance().maxTextureSize;
497 if (paddedWidth > maxSize || paddedHeight > maxSize) {
498 return image;
499 }
500
Dan Morrille4d9a012013-03-28 18:10:43 -0700501#ifdef ANDROID_ENABLE_RENDERSCRIPT
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800502 // Align buffers for renderscript usage
503 if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) {
504 paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700505 }
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800506 int size = paddedWidth * paddedHeight;
Romain Guy6e200402013-03-08 11:28:22 -0800507 uint8_t* dataBuffer = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, size);
Dan Morrille4d9a012013-03-28 18:10:43 -0700508#else
509 int size = paddedWidth * paddedHeight;
510 uint8_t* dataBuffer = (uint8_t*) malloc(size);
511#endif
512
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800513 memset(dataBuffer, 0, size);
514
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700515 int penX = radius - bounds.left;
516 int penY = radius - bounds.bottom;
517
Chris Craikdd8697c2013-02-22 10:41:36 -0800518 if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) {
519 // text has non-whitespace, so draw and blur to create the shadow
520 // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted
521 // TODO: don't draw pure whitespace in the first place, and avoid needing this check
522 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
523 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
524
Romain Guycf51a412013-04-08 19:40:31 -0700525 // Unbind any PBO we might have used
526 Caches::getInstance().unbindPixelBuffer();
527
Chris Craikdd8697c2013-02-22 10:41:36 -0800528 blurImage(&dataBuffer, paddedWidth, paddedHeight, radius);
529 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700530
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700531 image.width = paddedWidth;
532 image.height = paddedHeight;
533 image.image = dataBuffer;
534 image.penX = penX;
535 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -0800536
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700537 return image;
538}
Romain Guy694b5192010-07-21 21:33:20 -0700539
Romain Guy257ae352013-03-20 16:31:12 -0700540void FontRenderer::initRender(const Rect* clip, Rect* bounds, Functor* functor) {
Romain Guy694b5192010-07-21 21:33:20 -0700541 checkInit();
542
Romain Guy5b3b3522010-10-27 18:57:51 -0700543 mDrawn = false;
544 mBounds = bounds;
Romain Guy257ae352013-03-20 16:31:12 -0700545 mFunctor = functor;
Romain Guy09147fb2010-07-22 13:08:20 -0700546 mClip = clip;
Romain Guy671d6cf2012-01-18 12:39:17 -0800547}
Romain Guyff98fa52011-11-28 09:35:09 -0800548
Romain Guy671d6cf2012-01-18 12:39:17 -0800549void FontRenderer::finishRender() {
Romain Guy5b3b3522010-10-27 18:57:51 -0700550 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -0800551 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700552
Romain Guy661a87e2013-03-19 15:24:36 -0700553 issueDrawCommand();
Romain Guy671d6cf2012-01-18 12:39:17 -0800554}
555
Romain Guye3a9b242013-01-08 11:15:30 -0800556void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix) {
557 Font* font = Font::create(this, paint, matrix);
Chet Haasee816bae2012-08-09 13:39:02 -0700558 font->precache(paint, text, numGlyphs);
559}
560
Romain Guycf51a412013-04-08 19:40:31 -0700561void FontRenderer::endPrecaching() {
562 checkTextureUpdate();
563}
564
Romain Guy671d6cf2012-01-18 12:39:17 -0800565bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
566 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
Chris Craik527a3aa2013-03-04 10:19:31 -0800567 const float* positions, Rect* bounds, Functor* functor, bool forceFinish) {
Romain Guy671d6cf2012-01-18 12:39:17 -0800568 if (!mCurrentFont) {
569 ALOGE("No font set");
570 return false;
571 }
572
Romain Guy257ae352013-03-20 16:31:12 -0700573 initRender(clip, bounds, functor);
Romain Guy671d6cf2012-01-18 12:39:17 -0800574 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
Chris Craik527a3aa2013-03-04 10:19:31 -0800575
576 if (forceFinish) {
577 finishRender();
578 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700579
580 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700581}
582
Romain Guy97771732012-02-28 18:17:02 -0800583bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
584 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
585 float hOffset, float vOffset, Rect* bounds) {
586 if (!mCurrentFont) {
587 ALOGE("No font set");
588 return false;
589 }
590
Romain Guy257ae352013-03-20 16:31:12 -0700591 initRender(clip, bounds, NULL);
Romain Guy97771732012-02-28 18:17:02 -0800592 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
593 finishRender();
594
595 return mDrawn;
596}
597
Romain Guy9b1204b2012-09-04 15:22:57 -0700598void FontRenderer::removeFont(const Font* font) {
Romain Guye3a9b242013-01-08 11:15:30 -0800599 mActiveFonts.remove(font->getDescription());
Romain Guy9b1204b2012-09-04 15:22:57 -0700600
601 if (mCurrentFont == font) {
602 mCurrentFont = NULL;
603 }
604}
605
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800606void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) {
Dan Morrille4d9a012013-03-28 18:10:43 -0700607#ifdef ANDROID_ENABLE_RENDERSCRIPT
608 if (width * height * radius >= RS_MIN_INPUT_CUTOFF) {
609 uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700610
Dan Morrille4d9a012013-03-28 18:10:43 -0700611 if (mRs.get() == 0) {
612 mRs = new RSC::RS();
613 if (!mRs->init(true, true)) {
614 ALOGE("blur RS failed to init");
615 }
Romain Guyd71dd362011-12-12 19:03:35 -0800616
Dan Morrille4d9a012013-03-28 18:10:43 -0700617 mRsElement = RSC::Element::A_8(mRs);
618 mRsScript = new RSC::ScriptIntrinsicBlur(mRs, mRsElement);
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800619 }
620
Dan Morrille4d9a012013-03-28 18:10:43 -0700621 sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0);
622 sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
623 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, *image);
624 sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
625 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, outImage);
626
627 mRsScript->setRadius(radius);
628 mRsScript->blur(ain, aout);
629
630 // replace the original image's pointer, avoiding a copy back to the original buffer
631 free(*image);
632 *image = outImage;
633
634 return;
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800635 }
Dan Morrille4d9a012013-03-28 18:10:43 -0700636#endif
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800637
Dan Morrille4d9a012013-03-28 18:10:43 -0700638 float *gaussian = new float[2 * radius + 1];
639 Blur::generateGaussianWeights(gaussian, radius);
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800640
Dan Morrille4d9a012013-03-28 18:10:43 -0700641 uint8_t* scratch = new uint8_t[width * height];
642 Blur::horizontal(gaussian, radius, *image, scratch, width, height);
643 Blur::vertical(gaussian, radius, scratch, *image, width, height);
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800644
Dan Morrille4d9a012013-03-28 18:10:43 -0700645 delete[] gaussian;
646 delete[] scratch;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700647}
648
Romain Guycf51a412013-04-08 19:40:31 -0700649uint32_t FontRenderer::getCacheSize() const {
650 uint32_t size = 0;
651 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
652 CacheTexture* cacheTexture = mCacheTextures[i];
653 if (cacheTexture && cacheTexture->getPixelBuffer()) {
654 size += cacheTexture->getPixelBuffer()->getSize();
655 }
656 }
657 return size;
658}
659
Romain Guy694b5192010-07-21 21:33:20 -0700660}; // namespace uirenderer
661}; // namespace android