blob: 009dcb97aa0ef33af6c23647ebb930eb60c06bad [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 Guy51769a62010-07-23 00:28:00 -070024#include <utils/Log.h>
25
Romain Guy6e200402013-03-08 11:28:22 -080026#include <RenderScript.h>
Chris Craikf2d8ccc2013-02-13 16:14:17 -080027
Romain Guy6e200402013-03-08 11:28:22 -080028#include "utils/Blur.h"
Chris Craikf2d8ccc2013-02-13 16:14:17 -080029#include "utils/Timing.h"
Romain Guy6e200402013-03-08 11:28:22 -080030
Romain Guy15bc6432011-12-13 13:11:32 -080031#include "Caches.h"
Romain Guyc9855a52011-01-21 21:14:15 -080032#include "Debug.h"
Romain Guy51769a62010-07-23 00:28:00 -070033#include "FontRenderer.h"
Romain Guy9f5dab32012-09-04 12:55:44 -070034#include "Rect.h"
Romain Guy51769a62010-07-23 00:28:00 -070035
Romain Guy694b5192010-07-21 21:33:20 -070036namespace android {
37namespace uirenderer {
38
Chris Craikf2d8ccc2013-02-13 16:14:17 -080039// blur inputs smaller than this constant will bypass renderscript
40#define RS_MIN_INPUT_CUTOFF 10000
41
Romain Guy694b5192010-07-21 21:33:20 -070042///////////////////////////////////////////////////////////////////////////////
Romain Guy694b5192010-07-21 21:33:20 -070043// FontRenderer
44///////////////////////////////////////////////////////////////////////////////
45
Romain Guy514fb182011-01-19 14:38:29 -080046static bool sLogFontRendererCreate = true;
47
Romain Guye3a9b242013-01-08 11:15:30 -080048FontRenderer::FontRenderer() :
49 mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity) {
50
Romain Guyc9855a52011-01-21 21:14:15 -080051 if (sLogFontRendererCreate) {
52 INIT_LOGD("Creating FontRenderer");
53 }
Romain Guy51769a62010-07-23 00:28:00 -070054
Romain Guyb45c0c92010-08-26 20:35:23 -070055 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -070056 mInitialized = false;
57 mMaxNumberOfQuads = 1024;
Romain Guy694b5192010-07-21 21:33:20 -070058
Chet Haase7de0cb12011-12-05 16:35:38 -080059 mCurrentCacheTexture = NULL;
Romain Guy9cccc2b92010-08-07 23:46:15 -070060
Chet Haase2a47c142011-12-14 15:22:56 -080061 mLinearFiltering = false;
62
Romain Guy694b5192010-07-21 21:33:20 -070063 mIndexBufferID = 0;
64
Chet Haaseeb32a492012-08-31 13:54:03 -070065 mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH;
66 mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT;
67 mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH;
68 mLargeCacheHeight = DEFAULT_TEXT_LARGE_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -070069
70 char property[PROPERTY_VALUE_MAX];
Chet Haaseeb32a492012-08-31 13:54:03 -070071 if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, NULL) > 0) {
Chet Haase7de0cb12011-12-05 16:35:38 -080072 mSmallCacheWidth = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -070073 }
Romain Guy9f5dab32012-09-04 12:55:44 -070074
Chet Haaseeb32a492012-08-31 13:54:03 -070075 if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, NULL) > 0) {
Chet Haase7de0cb12011-12-05 16:35:38 -080076 mSmallCacheHeight = atoi(property);
Chet Haaseeb32a492012-08-31 13:54:03 -070077 }
Romain Guy9f5dab32012-09-04 12:55:44 -070078
Chet Haaseeb32a492012-08-31 13:54:03 -070079 if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, NULL) > 0) {
80 mLargeCacheWidth = atoi(property);
81 }
Romain Guy9f5dab32012-09-04 12:55:44 -070082
Chet Haaseeb32a492012-08-31 13:54:03 -070083 if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, NULL) > 0) {
84 mLargeCacheHeight = atoi(property);
85 }
Romain Guy9f5dab32012-09-04 12:55:44 -070086
87 uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize;
88 mSmallCacheWidth = mSmallCacheWidth > maxTextureSize ? maxTextureSize : mSmallCacheWidth;
89 mSmallCacheHeight = mSmallCacheHeight > maxTextureSize ? maxTextureSize : mSmallCacheHeight;
90 mLargeCacheWidth = mLargeCacheWidth > maxTextureSize ? maxTextureSize : mLargeCacheWidth;
91 mLargeCacheHeight = mLargeCacheHeight > maxTextureSize ? maxTextureSize : mLargeCacheHeight;
92
Chet Haaseeb32a492012-08-31 13:54:03 -070093 if (sLogFontRendererCreate) {
94 INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
95 mSmallCacheWidth, mSmallCacheHeight,
96 mLargeCacheWidth, mLargeCacheHeight >> 1,
97 mLargeCacheWidth, mLargeCacheHeight >> 1,
98 mLargeCacheWidth, mLargeCacheHeight);
Romain Guy51769a62010-07-23 00:28:00 -070099 }
Romain Guy514fb182011-01-19 14:38:29 -0800100
101 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -0700102}
103
104FontRenderer::~FontRenderer() {
Chet Haase378e9192012-08-15 15:54:54 -0700105 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
106 delete mCacheTextures[i];
Romain Guy694b5192010-07-21 21:33:20 -0700107 }
Chet Haase378e9192012-08-15 15:54:54 -0700108 mCacheTextures.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700109
Romain Guy9cccc2b92010-08-07 23:46:15 -0700110 if (mInitialized) {
Romain Guya9dd8202012-03-26 14:52:00 -0700111 // Unbinding the buffer shouldn't be necessary but it crashes with some drivers
112 Caches::getInstance().unbindIndicesBuffer();
Romain Guyb0317982012-03-21 11:52:52 -0700113 glDeleteBuffers(1, &mIndexBufferID);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700114 }
Romain Guy694b5192010-07-21 21:33:20 -0700115
Romain Guye3a9b242013-01-08 11:15:30 -0800116 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
117 while (it.next()) {
118 delete it.value();
Romain Guy694b5192010-07-21 21:33:20 -0700119 }
Romain Guye3a9b242013-01-08 11:15:30 -0800120 mActiveFonts.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700121}
122
123void FontRenderer::flushAllAndInvalidate() {
Romain Guy661a87e2013-03-19 15:24:36 -0700124 issueDrawCommand();
Romain Guy9d9758a2012-05-14 15:19:58 -0700125
Romain Guye3a9b242013-01-08 11:15:30 -0800126 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
127 while (it.next()) {
128 it.value()->invalidateTextureCache();
Romain Guy694b5192010-07-21 21:33:20 -0700129 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700130
Chet Haase378e9192012-08-15 15:54:54 -0700131 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
132 mCacheTextures[i]->init();
Romain Guy694b5192010-07-21 21:33:20 -0700133 }
Chet Haasee816bae2012-08-09 13:39:02 -0700134
Romain Guy80872462012-09-04 16:42:01 -0700135#if DEBUG_FONT_RENDERER
Chet Haase378e9192012-08-15 15:54:54 -0700136 uint16_t totalGlyphs = 0;
137 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
Romain Guy80872462012-09-04 16:42:01 -0700138 totalGlyphs += mCacheTextures[i]->getGlyphCount();
Chet Haase378e9192012-08-15 15:54:54 -0700139 // Erase caches, just as a debugging facility
Romain Guy80872462012-09-04 16:42:01 -0700140 if (mCacheTextures[i]->getTexture()) {
141 memset(mCacheTextures[i]->getTexture(), 0,
142 mCacheTextures[i]->getWidth() * mCacheTextures[i]->getHeight());
Chet Haase378e9192012-08-15 15:54:54 -0700143 }
Chet Haasee816bae2012-08-09 13:39:02 -0700144 }
145 ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
146#endif
Romain Guy694b5192010-07-21 21:33:20 -0700147}
148
Chet Haase9a824562011-12-16 15:44:59 -0800149void FontRenderer::flushLargeCaches() {
Chet Haase378e9192012-08-15 15:54:54 -0700150 // Start from 1; don't deallocate smallest/default texture
151 for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
152 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guy80872462012-09-04 16:42:01 -0700153 if (cacheTexture->getTexture()) {
Chet Haase378e9192012-08-15 15:54:54 -0700154 cacheTexture->init();
Romain Guye3a9b242013-01-08 11:15:30 -0800155 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
156 while (it.next()) {
157 it.value()->invalidateTextureCache(cacheTexture);
Chet Haasee816bae2012-08-09 13:39:02 -0700158 }
Romain Guy80872462012-09-04 16:42:01 -0700159 cacheTexture->releaseTexture();
Chet Haase9a824562011-12-16 15:44:59 -0800160 }
161 }
Chet Haase9a824562011-12-16 15:44:59 -0800162}
163
Chet Haase378e9192012-08-15 15:54:54 -0700164CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
165 uint32_t* startX, uint32_t* startY) {
166 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
167 if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
168 return mCacheTextures[i];
169 }
170 }
171 // Could not fit glyph into current cache textures
172 return NULL;
173}
174
Chet Haase7de0cb12011-12-05 16:35:38 -0800175void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
Chet Haasef942cf12012-08-30 09:06:46 -0700176 uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700177 checkInit();
Romain Guya4adcf02013-02-28 12:15:35 -0800178
179 // If the glyph bitmap is empty let's assum the glyph is valid
180 // so we can avoid doing extra work later on
181 if (glyph.fWidth == 0 || glyph.fHeight == 0) {
182 cachedGlyph->mIsValid = true;
183 cachedGlyph->mCacheTexture = NULL;
184 return;
185 }
186
Chet Haase7de0cb12011-12-05 16:35:38 -0800187 cachedGlyph->mIsValid = false;
Romain Guya4adcf02013-02-28 12:15:35 -0800188
Romain Guy694b5192010-07-21 21:33:20 -0700189 // If the glyph is too tall, don't cache it
Chet Haase378e9192012-08-15 15:54:54 -0700190 if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
Romain Guy80872462012-09-04 16:42:01 -0700191 mCacheTextures[mCacheTextures.size() - 1]->getHeight()) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700192 ALOGE("Font size too large to fit in cache. width, height = %i, %i",
193 (int) glyph.fWidth, (int) glyph.fHeight);
Chet Haase7de0cb12011-12-05 16:35:38 -0800194 return;
Romain Guy694b5192010-07-21 21:33:20 -0700195 }
196
197 // Now copy the bitmap into the cache texture
198 uint32_t startX = 0;
199 uint32_t startY = 0;
200
Chet Haase378e9192012-08-15 15:54:54 -0700201 CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700202
Chet Haase378e9192012-08-15 15:54:54 -0700203 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700204 if (!precaching) {
205 // If the new glyph didn't fit and we are not just trying to precache it,
206 // clear out the cache and try again
207 flushAllAndInvalidate();
208 cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
209 }
Romain Guy694b5192010-07-21 21:33:20 -0700210
Chet Haase378e9192012-08-15 15:54:54 -0700211 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700212 // either the glyph didn't fit or we're precaching and will cache it when we draw
Chet Haase7de0cb12011-12-05 16:35:38 -0800213 return;
Romain Guy694b5192010-07-21 21:33:20 -0700214 }
215 }
216
Chet Haase378e9192012-08-15 15:54:54 -0700217 cachedGlyph->mCacheTexture = cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800218
Romain Guy694b5192010-07-21 21:33:20 -0700219 *retOriginX = startX;
220 *retOriginY = startY;
221
222 uint32_t endX = startX + glyph.fWidth;
223 uint32_t endY = startY + glyph.fHeight;
224
Romain Guy80872462012-09-04 16:42:01 -0700225 uint32_t cacheWidth = cacheTexture->getWidth();
Romain Guy694b5192010-07-21 21:33:20 -0700226
Romain Guy80872462012-09-04 16:42:01 -0700227 if (!cacheTexture->getTexture()) {
228 Caches::getInstance().activeTexture(0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800229 // Large-glyph texture memory is allocated only as needed
Romain Guy80872462012-09-04 16:42:01 -0700230 cacheTexture->allocateTexture();
Chet Haase7de0cb12011-12-05 16:35:38 -0800231 }
Romain Guy661a87e2013-03-19 15:24:36 -0700232 if (!cacheTexture->mesh()) {
233 cacheTexture->allocateMesh();
234 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700235
Romain Guyb969a0d2013-02-05 14:38:40 -0800236 // Tells us whether the glyphs is B&W (1 bit per pixel)
237 // or anti-aliased (8 bits per pixel)
238 SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
Romain Guy694b5192010-07-21 21:33:20 -0700239
Romain Guyb969a0d2013-02-05 14:38:40 -0800240 uint8_t* cacheBuffer = cacheTexture->getTexture();
Romain Guy694b5192010-07-21 21:33:20 -0700241 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
Romain Guy33fa1f72012-08-07 19:09:57 -0700242
Romain Guyb969a0d2013-02-05 14:38:40 -0800243 // Copy the glyph image, taking the mask format into account
244 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
245 int stride = glyph.rowBytes();
246
Romain Guy0b58a3d2013-03-05 12:16:27 -0800247 uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
248 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
249
Romain Guyb969a0d2013-02-05 14:38:40 -0800250 switch (format) {
251 case SkMask::kA8_Format: {
252 if (mGammaTable) {
253 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
Romain Guy0b58a3d2013-03-05 12:16:27 -0800254 row = cacheY * cacheWidth;
255 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800256 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
257 uint8_t tempCol = bitmapBuffer[bY + bX];
Romain Guy0b58a3d2013-03-05 12:16:27 -0800258 cacheBuffer[row + cacheX] = mGammaTable[tempCol];
Romain Guyb969a0d2013-02-05 14:38:40 -0800259 }
Romain Guy0b58a3d2013-03-05 12:16:27 -0800260 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800261 }
262 } else {
263 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
Romain Guy0b58a3d2013-03-05 12:16:27 -0800264 row = cacheY * cacheWidth;
265 memcpy(&cacheBuffer[row + startX], &bitmapBuffer[bY], glyph.fWidth);
266 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
267 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800268 }
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700269 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800270 break;
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700271 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800272 case SkMask::kBW_Format: {
273 static const uint8_t COLORS[2] = { 0, 255 };
274
275 for (cacheY = startY; cacheY < endY; cacheY++) {
276 cacheX = startX;
277 int rowBytes = stride;
278 uint8_t* buffer = bitmapBuffer;
279
Romain Guy0b58a3d2013-03-05 12:16:27 -0800280 row = cacheY * cacheWidth;
281 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800282 while (--rowBytes >= 0) {
283 uint8_t b = *buffer++;
284 for (int8_t mask = 7; mask >= 0 && cacheX < endX; mask--) {
285 cacheBuffer[cacheY * cacheWidth + cacheX++] = COLORS[(b >> mask) & 0x1];
286 }
287 }
Romain Guy0b58a3d2013-03-05 12:16:27 -0800288 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800289
290 bitmapBuffer += stride;
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700291 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800292 break;
Romain Guy694b5192010-07-21 21:33:20 -0700293 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800294 default:
295 ALOGW("Unkown glyph format: 0x%x", format);
296 break;
Romain Guy694b5192010-07-21 21:33:20 -0700297 }
Romain Guy97771732012-02-28 18:17:02 -0800298
Romain Guy0b58a3d2013-03-05 12:16:27 -0800299 row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
300 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
301
Chet Haase7de0cb12011-12-05 16:35:38 -0800302 cachedGlyph->mIsValid = true;
Romain Guy694b5192010-07-21 21:33:20 -0700303}
304
Chet Haase7de0cb12011-12-05 16:35:38 -0800305CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
Romain Guy661a87e2013-03-19 15:24:36 -0700306 CacheTexture* cacheTexture = new CacheTexture(width, height, mMaxNumberOfQuads);
Romain Guy9d9758a2012-05-14 15:19:58 -0700307
Chet Haase2a47c142011-12-14 15:22:56 -0800308 if (allocate) {
Romain Guy80872462012-09-04 16:42:01 -0700309 Caches::getInstance().activeTexture(0);
310 cacheTexture->allocateTexture();
Romain Guy661a87e2013-03-19 15:24:36 -0700311 cacheTexture->allocateMesh();
Chet Haase2a47c142011-12-14 15:22:56 -0800312 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700313
Chet Haase2a47c142011-12-14 15:22:56 -0800314 return cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800315}
316
317void FontRenderer::initTextTexture() {
Chet Haase378e9192012-08-15 15:54:54 -0700318 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
319 delete mCacheTextures[i];
Romain Guy9d9758a2012-05-14 15:19:58 -0700320 }
Chet Haase378e9192012-08-15 15:54:54 -0700321 mCacheTextures.clear();
Romain Guy9d9758a2012-05-14 15:19:58 -0700322
Chet Haase7de0cb12011-12-05 16:35:38 -0800323 mUploadTexture = false;
Chet Haase378e9192012-08-15 15:54:54 -0700324 mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
Chet Haaseeb32a492012-08-31 13:54:03 -0700325 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
326 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
327 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false));
Chet Haase378e9192012-08-15 15:54:54 -0700328 mCurrentCacheTexture = mCacheTextures[0];
Romain Guy694b5192010-07-21 21:33:20 -0700329}
330
331// Avoid having to reallocate memory and render quad by quad
332void FontRenderer::initVertexArrayBuffers() {
Romain Guyd71dd362011-12-12 19:03:35 -0800333 uint32_t numIndices = mMaxNumberOfQuads * 6;
334 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700335 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700336
337 // Four verts, two triangles , six indices per quad
338 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
339 int i6 = i * 6;
340 int i4 = i * 4;
341
342 indexBufferData[i6 + 0] = i4 + 0;
343 indexBufferData[i6 + 1] = i4 + 1;
344 indexBufferData[i6 + 2] = i4 + 2;
345
346 indexBufferData[i6 + 3] = i4 + 0;
347 indexBufferData[i6 + 4] = i4 + 2;
348 indexBufferData[i6 + 5] = i4 + 3;
349 }
350
351 glGenBuffers(1, &mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800352 Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700353 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
Romain Guy694b5192010-07-21 21:33:20 -0700354
355 free(indexBufferData);
Romain Guy694b5192010-07-21 21:33:20 -0700356}
357
358// We don't want to allocate anything unless we actually draw text
359void FontRenderer::checkInit() {
360 if (mInitialized) {
361 return;
362 }
363
364 initTextTexture();
365 initVertexArrayBuffers();
366
367 mInitialized = true;
368}
369
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700370void FontRenderer::checkTextureUpdate() {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900371 if (!mUploadTexture) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700372 return;
Romain Guy694b5192010-07-21 21:33:20 -0700373 }
374
Romain Guy2d4fd362011-12-13 22:00:19 -0800375 Caches& caches = Caches::getInstance();
376 GLuint lastTextureId = 0;
Chet Haase378e9192012-08-15 15:54:54 -0700377 // Iterate over all the cache textures and see which ones need to be updated
378 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
379 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guy80872462012-09-04 16:42:01 -0700380 if (cacheTexture->isDirty() && cacheTexture->getTexture()) {
Chet Haaseb92d8f72012-09-21 08:40:46 -0700381 // Can't copy inner rect; glTexSubimage expects pointer to deal with entire buffer
382 // of data. So expand the dirty rect to the encompassing horizontal stripe.
383 const Rect* dirtyRect = cacheTexture->getDirtyRect();
384 uint32_t x = 0;
385 uint32_t y = dirtyRect->top;
Romain Guy80872462012-09-04 16:42:01 -0700386 uint32_t width = cacheTexture->getWidth();
Chet Haaseb92d8f72012-09-21 08:40:46 -0700387 uint32_t height = dirtyRect->getHeight();
388 void* textureData = cacheTexture->getTexture() + y * width;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700389
Romain Guy80872462012-09-04 16:42:01 -0700390 if (cacheTexture->getTextureId() != lastTextureId) {
391 lastTextureId = cacheTexture->getTextureId();
Romain Guy2d4fd362011-12-13 22:00:19 -0800392 caches.activeTexture(0);
Romain Guy80872462012-09-04 16:42:01 -0700393 glBindTexture(GL_TEXTURE_2D, lastTextureId);
Romain Guy2d4fd362011-12-13 22:00:19 -0800394 }
Chet Haasee816bae2012-08-09 13:39:02 -0700395#if DEBUG_FONT_RENDERER
Chet Haaseb92d8f72012-09-21 08:40:46 -0700396 ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d",
397 i, x, y, width, height);
Chet Haasee816bae2012-08-09 13:39:02 -0700398#endif
Chet Haaseb92d8f72012-09-21 08:40:46 -0700399 glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700400 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Romain Guy80872462012-09-04 16:42:01 -0700401 cacheTexture->setDirty(false);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700402 }
403 }
404
405 mUploadTexture = false;
406}
407
408void FontRenderer::issueDrawCommand() {
Romain Guy661a87e2013-03-19 15:24:36 -0700409 bool first = true;
410 bool force = false;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700411
Romain Guy115096f2013-03-19 11:32:41 -0700412 GLuint lastId = 0;
Romain Guy661a87e2013-03-19 15:24:36 -0700413 Caches& caches = Caches::getInstance();
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900414
Romain Guy661a87e2013-03-19 15:24:36 -0700415 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
416 CacheTexture* texture = mCacheTextures[i];
417 if (texture->canDraw()) {
418 if (first) {
419 checkTextureUpdate();
420 caches.bindIndicesBuffer(mIndexBufferID);
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900421
Romain Guy661a87e2013-03-19 15:24:36 -0700422 if (!mDrawn) {
423 // If returns true, a VBO was bound and we must
424 // rebind our vertex attrib pointers even if
425 // they have the same values as the current pointers
426 force = caches.unbindMeshBuffer();
427 }
428
429 caches.activeTexture(0);
430 first = false;
431 }
432
433 glBindTexture(GL_TEXTURE_2D, texture->getTextureId());
434 texture->setLinearFiltering(mLinearFiltering, false);
435
436 TextureVertex* mesh = texture->mesh();
437 caches.bindPositionVertexPointer(force, &mesh[0].position[0]);
438 caches.bindTexCoordsVertexPointer(force, &mesh[0].texture[0]);
439 force = false;
440
441 glDrawElements(GL_TRIANGLES, texture->meshElementCount(),
442 GL_UNSIGNED_SHORT, texture->indices());
443
444 texture->resetMesh();
Romain Guy115096f2013-03-19 11:32:41 -0700445 }
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900446 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700447
448 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700449}
450
Romain Guy97771732012-02-28 18:17:02 -0800451void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
452 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800453 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800454 if (texture != mCurrentCacheTexture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800455 // Now use the new texture id
456 mCurrentCacheTexture = texture;
457 }
Romain Guy09147fb2010-07-22 13:08:20 -0700458
Romain Guy661a87e2013-03-19 15:24:36 -0700459 mCurrentCacheTexture->addQuad(x1, y1, u1, v1, x2, y2, u2, v2,
460 x3, y3, u3, v3, x4, y4, u4, v4);
Romain Guy97771732012-02-28 18:17:02 -0800461}
462
463void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
464 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
465 float x4, float y4, float u4, float v4, CacheTexture* texture) {
466
467 if (mClip &&
468 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
469 return;
470 }
471
472 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 -0700473
Romain Guy5b3b3522010-10-27 18:57:51 -0700474 if (mBounds) {
475 mBounds->left = fmin(mBounds->left, x1);
476 mBounds->top = fmin(mBounds->top, y3);
477 mBounds->right = fmax(mBounds->right, x3);
478 mBounds->bottom = fmax(mBounds->bottom, y1);
479 }
480
Romain Guy661a87e2013-03-19 15:24:36 -0700481 if (mCurrentCacheTexture->endOfMesh()) {
Romain Guy694b5192010-07-21 21:33:20 -0700482 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700483 }
484}
485
Romain Guy97771732012-02-28 18:17:02 -0800486void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
487 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
488 float x4, float y4, float u4, float v4, CacheTexture* texture) {
489
490 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
491
492 if (mBounds) {
493 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
494 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
495 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
496 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
497 }
498
Romain Guy661a87e2013-03-19 15:24:36 -0700499 if (mCurrentCacheTexture->endOfMesh()) {
Romain Guy97771732012-02-28 18:17:02 -0800500 issueDrawCommand();
Romain Guy97771732012-02-28 18:17:02 -0800501 }
502}
503
Romain Guye3a9b242013-01-08 11:15:30 -0800504void FontRenderer::setFont(SkPaint* paint, const mat4& matrix) {
505 mCurrentFont = Font::create(this, paint, matrix);
Romain Guy694b5192010-07-21 21:33:20 -0700506}
Romain Guy7975fb62010-10-01 16:36:14 -0700507
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700508FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Raph Levien416a8472012-07-19 22:48:17 -0700509 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700510 checkInit();
511
512 if (!mCurrentFont) {
513 DropShadow image;
514 image.width = 0;
515 image.height = 0;
516 image.image = NULL;
517 image.penX = 0;
518 image.penY = 0;
519 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
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800532 // Align buffers for renderscript usage
533 if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) {
534 paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700535 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700536
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800537 int size = paddedWidth * paddedHeight;
Romain Guy6e200402013-03-08 11:28:22 -0800538 uint8_t* dataBuffer = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, size);
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800539 memset(dataBuffer, 0, size);
540
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700541 int penX = radius - bounds.left;
542 int penY = radius - bounds.bottom;
543
Chris Craikdd8697c2013-02-22 10:41:36 -0800544 if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) {
545 // text has non-whitespace, so draw and blur to create the shadow
546 // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted
547 // TODO: don't draw pure whitespace in the first place, and avoid needing this check
548 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
549 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
550
551 blurImage(&dataBuffer, paddedWidth, paddedHeight, radius);
552 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700553
554 DropShadow image;
555 image.width = paddedWidth;
556 image.height = paddedHeight;
557 image.image = dataBuffer;
558 image.penX = penX;
559 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -0800560
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700561 return image;
562}
Romain Guy694b5192010-07-21 21:33:20 -0700563
Romain Guy671d6cf2012-01-18 12:39:17 -0800564void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700565 checkInit();
566
Romain Guy5b3b3522010-10-27 18:57:51 -0700567 mDrawn = false;
568 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -0700569 mClip = clip;
Romain Guy671d6cf2012-01-18 12:39:17 -0800570}
Romain Guyff98fa52011-11-28 09:35:09 -0800571
Romain Guy671d6cf2012-01-18 12:39:17 -0800572void FontRenderer::finishRender() {
Romain Guy5b3b3522010-10-27 18:57:51 -0700573 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -0800574 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700575
Romain Guy661a87e2013-03-19 15:24:36 -0700576 issueDrawCommand();
Romain Guy671d6cf2012-01-18 12:39:17 -0800577}
578
Romain Guye3a9b242013-01-08 11:15:30 -0800579void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix) {
580 Font* font = Font::create(this, paint, matrix);
Chet Haasee816bae2012-08-09 13:39:02 -0700581 font->precache(paint, text, numGlyphs);
582}
583
Romain Guy671d6cf2012-01-18 12:39:17 -0800584bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
585 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
586 const float* positions, Rect* bounds) {
587 if (!mCurrentFont) {
588 ALOGE("No font set");
589 return false;
590 }
591
592 initRender(clip, bounds);
593 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
594 finishRender();
Romain Guy5b3b3522010-10-27 18:57:51 -0700595
596 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700597}
598
Romain Guy97771732012-02-28 18:17:02 -0800599bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
600 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
601 float hOffset, float vOffset, Rect* bounds) {
602 if (!mCurrentFont) {
603 ALOGE("No font set");
604 return false;
605 }
606
607 initRender(clip, bounds);
608 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
609 finishRender();
610
611 return mDrawn;
612}
613
Romain Guy9b1204b2012-09-04 15:22:57 -0700614void FontRenderer::removeFont(const Font* font) {
Romain Guye3a9b242013-01-08 11:15:30 -0800615 mActiveFonts.remove(font->getDescription());
Romain Guy9b1204b2012-09-04 15:22:57 -0700616
617 if (mCurrentFont == font) {
618 mCurrentFont = NULL;
619 }
620}
621
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800622void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) {
623 if (width * height * radius < RS_MIN_INPUT_CUTOFF) {
624 float *gaussian = new float[2 * radius + 1];
Romain Guy6e200402013-03-08 11:28:22 -0800625 Blur::generateGaussianWeights(gaussian, radius);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700626
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800627 uint8_t* scratch = new uint8_t[width * height];
Romain Guy6e200402013-03-08 11:28:22 -0800628 Blur::horizontal(gaussian, radius, *image, scratch, width, height);
629 Blur::vertical(gaussian, radius, scratch, *image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -0800630
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800631 delete[] gaussian;
632 delete[] scratch;
Chris Craikdd8697c2013-02-22 10:41:36 -0800633 return;
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800634 }
Romain Guyd71dd362011-12-12 19:03:35 -0800635
Romain Guy6e200402013-03-08 11:28:22 -0800636 uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800637
638 if (mRs.get() == 0) {
639 mRs = new RSC::RS();
640 if (!mRs->init(true, true)) {
641 ALOGE("blur RS failed to init");
642 }
643
644 mRsElement = RSC::Element::A_8(mRs);
645 mRsScript = new RSC::ScriptIntrinsicBlur(mRs, mRsElement);
646 }
647
648 sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0);
649 sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
650 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, *image);
651 sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
652 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, outImage);
653
654 mRsScript->setRadius(radius);
655 mRsScript->blur(ain, aout);
656
657 // replace the original image's pointer, avoiding a copy back to the original buffer
Ben Cheng15641a612013-02-20 13:20:03 -0800658 free(*image);
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800659 *image = outImage;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700660}
661
Romain Guy694b5192010-07-21 21:33:20 -0700662}; // namespace uirenderer
663}; // namespace android