blob: 354bd7732c12e124169848321d71db50c70a0a2b [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 Guy51769a62010-07-23 00:28:00 -070036#include "FontRenderer.h"
Romain Guy9f5dab32012-09-04 12:55:44 -070037#include "Rect.h"
Romain Guy51769a62010-07-23 00:28:00 -070038
Romain Guy694b5192010-07-21 21:33:20 -070039namespace android {
40namespace uirenderer {
41
Chris Craikf2d8ccc2013-02-13 16:14:17 -080042// blur inputs smaller than this constant will bypass renderscript
43#define RS_MIN_INPUT_CUTOFF 10000
44
Romain Guy694b5192010-07-21 21:33:20 -070045///////////////////////////////////////////////////////////////////////////////
Romain Guy694b5192010-07-21 21:33:20 -070046// FontRenderer
47///////////////////////////////////////////////////////////////////////////////
48
Romain Guy514fb182011-01-19 14:38:29 -080049static bool sLogFontRendererCreate = true;
50
Romain Guye3a9b242013-01-08 11:15:30 -080051FontRenderer::FontRenderer() :
52 mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity) {
53
Romain Guyc9855a52011-01-21 21:14:15 -080054 if (sLogFontRendererCreate) {
55 INIT_LOGD("Creating FontRenderer");
56 }
Romain Guy51769a62010-07-23 00:28:00 -070057
Romain Guyb45c0c92010-08-26 20:35:23 -070058 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -070059 mInitialized = false;
60 mMaxNumberOfQuads = 1024;
Romain Guy694b5192010-07-21 21:33:20 -070061
Chet Haase7de0cb12011-12-05 16:35:38 -080062 mCurrentCacheTexture = NULL;
Romain Guy9cccc2b92010-08-07 23:46:15 -070063
Chet Haase2a47c142011-12-14 15:22:56 -080064 mLinearFiltering = false;
65
Romain Guy694b5192010-07-21 21:33:20 -070066 mIndexBufferID = 0;
67
Chet Haaseeb32a492012-08-31 13:54:03 -070068 mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH;
69 mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT;
70 mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH;
71 mLargeCacheHeight = DEFAULT_TEXT_LARGE_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -070072
73 char property[PROPERTY_VALUE_MAX];
Chet Haaseeb32a492012-08-31 13:54:03 -070074 if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, NULL) > 0) {
Chet Haase7de0cb12011-12-05 16:35:38 -080075 mSmallCacheWidth = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -070076 }
Romain Guy9f5dab32012-09-04 12:55:44 -070077
Chet Haaseeb32a492012-08-31 13:54:03 -070078 if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, NULL) > 0) {
Chet Haase7de0cb12011-12-05 16:35:38 -080079 mSmallCacheHeight = atoi(property);
Chet Haaseeb32a492012-08-31 13:54:03 -070080 }
Romain Guy9f5dab32012-09-04 12:55:44 -070081
Chet Haaseeb32a492012-08-31 13:54:03 -070082 if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, NULL) > 0) {
83 mLargeCacheWidth = atoi(property);
84 }
Romain Guy9f5dab32012-09-04 12:55:44 -070085
Chet Haaseeb32a492012-08-31 13:54:03 -070086 if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, NULL) > 0) {
87 mLargeCacheHeight = atoi(property);
88 }
Romain Guy9f5dab32012-09-04 12:55:44 -070089
90 uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize;
91 mSmallCacheWidth = mSmallCacheWidth > maxTextureSize ? maxTextureSize : mSmallCacheWidth;
92 mSmallCacheHeight = mSmallCacheHeight > maxTextureSize ? maxTextureSize : mSmallCacheHeight;
93 mLargeCacheWidth = mLargeCacheWidth > maxTextureSize ? maxTextureSize : mLargeCacheWidth;
94 mLargeCacheHeight = mLargeCacheHeight > maxTextureSize ? maxTextureSize : mLargeCacheHeight;
95
Chet Haaseeb32a492012-08-31 13:54:03 -070096 if (sLogFontRendererCreate) {
97 INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
98 mSmallCacheWidth, mSmallCacheHeight,
99 mLargeCacheWidth, mLargeCacheHeight >> 1,
100 mLargeCacheWidth, mLargeCacheHeight >> 1,
101 mLargeCacheWidth, mLargeCacheHeight);
Romain Guy51769a62010-07-23 00:28:00 -0700102 }
Romain Guy514fb182011-01-19 14:38:29 -0800103
104 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -0700105}
106
107FontRenderer::~FontRenderer() {
Chet Haase378e9192012-08-15 15:54:54 -0700108 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
109 delete mCacheTextures[i];
Romain Guy694b5192010-07-21 21:33:20 -0700110 }
Chet Haase378e9192012-08-15 15:54:54 -0700111 mCacheTextures.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700112
Romain Guy9cccc2b92010-08-07 23:46:15 -0700113 if (mInitialized) {
Romain Guya9dd8202012-03-26 14:52:00 -0700114 // Unbinding the buffer shouldn't be necessary but it crashes with some drivers
115 Caches::getInstance().unbindIndicesBuffer();
Romain Guyb0317982012-03-21 11:52:52 -0700116 glDeleteBuffers(1, &mIndexBufferID);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700117 }
Romain Guy694b5192010-07-21 21:33:20 -0700118
Romain Guye3a9b242013-01-08 11:15:30 -0800119 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
120 while (it.next()) {
121 delete it.value();
Romain Guy694b5192010-07-21 21:33:20 -0700122 }
Romain Guye3a9b242013-01-08 11:15:30 -0800123 mActiveFonts.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700124}
125
126void FontRenderer::flushAllAndInvalidate() {
Romain Guy661a87e2013-03-19 15:24:36 -0700127 issueDrawCommand();
Romain Guy9d9758a2012-05-14 15:19:58 -0700128
Romain Guye3a9b242013-01-08 11:15:30 -0800129 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
130 while (it.next()) {
131 it.value()->invalidateTextureCache();
Romain Guy694b5192010-07-21 21:33:20 -0700132 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700133
Chet Haase378e9192012-08-15 15:54:54 -0700134 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
135 mCacheTextures[i]->init();
Romain Guy694b5192010-07-21 21:33:20 -0700136 }
Chet Haasee816bae2012-08-09 13:39:02 -0700137
Romain Guy80872462012-09-04 16:42:01 -0700138#if DEBUG_FONT_RENDERER
Chet Haase378e9192012-08-15 15:54:54 -0700139 uint16_t totalGlyphs = 0;
140 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
Romain Guy80872462012-09-04 16:42:01 -0700141 totalGlyphs += mCacheTextures[i]->getGlyphCount();
Chet Haase378e9192012-08-15 15:54:54 -0700142 // Erase caches, just as a debugging facility
Romain Guy80872462012-09-04 16:42:01 -0700143 if (mCacheTextures[i]->getTexture()) {
144 memset(mCacheTextures[i]->getTexture(), 0,
145 mCacheTextures[i]->getWidth() * mCacheTextures[i]->getHeight());
Chet Haase378e9192012-08-15 15:54:54 -0700146 }
Chet Haasee816bae2012-08-09 13:39:02 -0700147 }
148 ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
149#endif
Romain Guy694b5192010-07-21 21:33:20 -0700150}
151
Chet Haase9a824562011-12-16 15:44:59 -0800152void FontRenderer::flushLargeCaches() {
Chet Haase378e9192012-08-15 15:54:54 -0700153 // Start from 1; don't deallocate smallest/default texture
154 for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
155 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guy80872462012-09-04 16:42:01 -0700156 if (cacheTexture->getTexture()) {
Chet Haase378e9192012-08-15 15:54:54 -0700157 cacheTexture->init();
Romain Guye3a9b242013-01-08 11:15:30 -0800158 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
159 while (it.next()) {
160 it.value()->invalidateTextureCache(cacheTexture);
Chet Haasee816bae2012-08-09 13:39:02 -0700161 }
Romain Guy80872462012-09-04 16:42:01 -0700162 cacheTexture->releaseTexture();
Chet Haase9a824562011-12-16 15:44:59 -0800163 }
164 }
Chet Haase9a824562011-12-16 15:44:59 -0800165}
166
Chet Haase378e9192012-08-15 15:54:54 -0700167CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
168 uint32_t* startX, uint32_t* startY) {
169 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
170 if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
171 return mCacheTextures[i];
172 }
173 }
174 // Could not fit glyph into current cache textures
175 return NULL;
176}
177
Chet Haase7de0cb12011-12-05 16:35:38 -0800178void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
Chet Haasef942cf12012-08-30 09:06:46 -0700179 uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700180 checkInit();
Romain Guya4adcf02013-02-28 12:15:35 -0800181
182 // If the glyph bitmap is empty let's assum the glyph is valid
183 // so we can avoid doing extra work later on
184 if (glyph.fWidth == 0 || glyph.fHeight == 0) {
185 cachedGlyph->mIsValid = true;
186 cachedGlyph->mCacheTexture = NULL;
187 return;
188 }
189
Chet Haase7de0cb12011-12-05 16:35:38 -0800190 cachedGlyph->mIsValid = false;
Romain Guya4adcf02013-02-28 12:15:35 -0800191
Romain Guy694b5192010-07-21 21:33:20 -0700192 // If the glyph is too tall, don't cache it
Chet Haase378e9192012-08-15 15:54:54 -0700193 if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
Romain Guy80872462012-09-04 16:42:01 -0700194 mCacheTextures[mCacheTextures.size() - 1]->getHeight()) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700195 ALOGE("Font size too large to fit in cache. width, height = %i, %i",
196 (int) glyph.fWidth, (int) glyph.fHeight);
Chet Haase7de0cb12011-12-05 16:35:38 -0800197 return;
Romain Guy694b5192010-07-21 21:33:20 -0700198 }
199
200 // Now copy the bitmap into the cache texture
201 uint32_t startX = 0;
202 uint32_t startY = 0;
203
Chet Haase378e9192012-08-15 15:54:54 -0700204 CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700205
Chet Haase378e9192012-08-15 15:54:54 -0700206 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700207 if (!precaching) {
208 // If the new glyph didn't fit and we are not just trying to precache it,
209 // clear out the cache and try again
210 flushAllAndInvalidate();
211 cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
212 }
Romain Guy694b5192010-07-21 21:33:20 -0700213
Chet Haase378e9192012-08-15 15:54:54 -0700214 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700215 // either the glyph didn't fit or we're precaching and will cache it when we draw
Chet Haase7de0cb12011-12-05 16:35:38 -0800216 return;
Romain Guy694b5192010-07-21 21:33:20 -0700217 }
218 }
219
Chet Haase378e9192012-08-15 15:54:54 -0700220 cachedGlyph->mCacheTexture = cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800221
Romain Guy694b5192010-07-21 21:33:20 -0700222 *retOriginX = startX;
223 *retOriginY = startY;
224
225 uint32_t endX = startX + glyph.fWidth;
226 uint32_t endY = startY + glyph.fHeight;
227
Romain Guy80872462012-09-04 16:42:01 -0700228 uint32_t cacheWidth = cacheTexture->getWidth();
Romain Guy694b5192010-07-21 21:33:20 -0700229
Romain Guy80872462012-09-04 16:42:01 -0700230 if (!cacheTexture->getTexture()) {
231 Caches::getInstance().activeTexture(0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800232 // Large-glyph texture memory is allocated only as needed
Romain Guy80872462012-09-04 16:42:01 -0700233 cacheTexture->allocateTexture();
Chet Haase7de0cb12011-12-05 16:35:38 -0800234 }
Romain Guy661a87e2013-03-19 15:24:36 -0700235 if (!cacheTexture->mesh()) {
236 cacheTexture->allocateMesh();
237 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700238
Romain Guyb969a0d2013-02-05 14:38:40 -0800239 // Tells us whether the glyphs is B&W (1 bit per pixel)
240 // or anti-aliased (8 bits per pixel)
241 SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
Romain Guy694b5192010-07-21 21:33:20 -0700242
Romain Guyb969a0d2013-02-05 14:38:40 -0800243 uint8_t* cacheBuffer = cacheTexture->getTexture();
Romain Guy694b5192010-07-21 21:33:20 -0700244 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
Romain Guy33fa1f72012-08-07 19:09:57 -0700245
Romain Guyb969a0d2013-02-05 14:38:40 -0800246 // Copy the glyph image, taking the mask format into account
247 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
248 int stride = glyph.rowBytes();
249
Romain Guy0b58a3d2013-03-05 12:16:27 -0800250 uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
251 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
252
Romain Guyb969a0d2013-02-05 14:38:40 -0800253 switch (format) {
254 case SkMask::kA8_Format: {
255 if (mGammaTable) {
256 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
Romain Guy0b58a3d2013-03-05 12:16:27 -0800257 row = cacheY * cacheWidth;
258 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800259 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
260 uint8_t tempCol = bitmapBuffer[bY + bX];
Romain Guy0b58a3d2013-03-05 12:16:27 -0800261 cacheBuffer[row + cacheX] = mGammaTable[tempCol];
Romain Guyb969a0d2013-02-05 14:38:40 -0800262 }
Romain Guy0b58a3d2013-03-05 12:16:27 -0800263 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800264 }
265 } else {
266 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
Romain Guy0b58a3d2013-03-05 12:16:27 -0800267 row = cacheY * cacheWidth;
268 memcpy(&cacheBuffer[row + startX], &bitmapBuffer[bY], glyph.fWidth);
269 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
270 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800271 }
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700272 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800273 break;
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700274 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800275 case SkMask::kBW_Format: {
276 static const uint8_t COLORS[2] = { 0, 255 };
277
278 for (cacheY = startY; cacheY < endY; cacheY++) {
279 cacheX = startX;
280 int rowBytes = stride;
281 uint8_t* buffer = bitmapBuffer;
282
Romain Guy0b58a3d2013-03-05 12:16:27 -0800283 row = cacheY * cacheWidth;
284 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800285 while (--rowBytes >= 0) {
286 uint8_t b = *buffer++;
287 for (int8_t mask = 7; mask >= 0 && cacheX < endX; mask--) {
288 cacheBuffer[cacheY * cacheWidth + cacheX++] = COLORS[(b >> mask) & 0x1];
289 }
290 }
Romain Guy0b58a3d2013-03-05 12:16:27 -0800291 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800292
293 bitmapBuffer += stride;
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700294 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800295 break;
Romain Guy694b5192010-07-21 21:33:20 -0700296 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800297 default:
298 ALOGW("Unkown glyph format: 0x%x", format);
299 break;
Romain Guy694b5192010-07-21 21:33:20 -0700300 }
Romain Guy97771732012-02-28 18:17:02 -0800301
Romain Guy0b58a3d2013-03-05 12:16:27 -0800302 row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
303 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
304
Chet Haase7de0cb12011-12-05 16:35:38 -0800305 cachedGlyph->mIsValid = true;
Romain Guy694b5192010-07-21 21:33:20 -0700306}
307
Chet Haase7de0cb12011-12-05 16:35:38 -0800308CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
Romain Guy661a87e2013-03-19 15:24:36 -0700309 CacheTexture* cacheTexture = new CacheTexture(width, height, mMaxNumberOfQuads);
Romain Guy9d9758a2012-05-14 15:19:58 -0700310
Chet Haase2a47c142011-12-14 15:22:56 -0800311 if (allocate) {
Romain Guy80872462012-09-04 16:42:01 -0700312 Caches::getInstance().activeTexture(0);
313 cacheTexture->allocateTexture();
Romain Guy661a87e2013-03-19 15:24:36 -0700314 cacheTexture->allocateMesh();
Chet Haase2a47c142011-12-14 15:22:56 -0800315 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700316
Chet Haase2a47c142011-12-14 15:22:56 -0800317 return cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800318}
319
320void FontRenderer::initTextTexture() {
Chet Haase378e9192012-08-15 15:54:54 -0700321 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
322 delete mCacheTextures[i];
Romain Guy9d9758a2012-05-14 15:19:58 -0700323 }
Chet Haase378e9192012-08-15 15:54:54 -0700324 mCacheTextures.clear();
Romain Guy9d9758a2012-05-14 15:19:58 -0700325
Chet Haase7de0cb12011-12-05 16:35:38 -0800326 mUploadTexture = false;
Chet Haase378e9192012-08-15 15:54:54 -0700327 mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
Chet Haaseeb32a492012-08-31 13:54:03 -0700328 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
329 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
330 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false));
Chet Haase378e9192012-08-15 15:54:54 -0700331 mCurrentCacheTexture = mCacheTextures[0];
Romain Guy694b5192010-07-21 21:33:20 -0700332}
333
334// Avoid having to reallocate memory and render quad by quad
335void FontRenderer::initVertexArrayBuffers() {
Romain Guyd71dd362011-12-12 19:03:35 -0800336 uint32_t numIndices = mMaxNumberOfQuads * 6;
337 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700338 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700339
340 // Four verts, two triangles , six indices per quad
341 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
342 int i6 = i * 6;
343 int i4 = i * 4;
344
345 indexBufferData[i6 + 0] = i4 + 0;
346 indexBufferData[i6 + 1] = i4 + 1;
347 indexBufferData[i6 + 2] = i4 + 2;
348
349 indexBufferData[i6 + 3] = i4 + 0;
350 indexBufferData[i6 + 4] = i4 + 2;
351 indexBufferData[i6 + 5] = i4 + 3;
352 }
353
354 glGenBuffers(1, &mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800355 Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700356 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
Romain Guy694b5192010-07-21 21:33:20 -0700357
358 free(indexBufferData);
Romain Guy694b5192010-07-21 21:33:20 -0700359}
360
361// We don't want to allocate anything unless we actually draw text
362void FontRenderer::checkInit() {
363 if (mInitialized) {
364 return;
365 }
366
367 initTextTexture();
368 initVertexArrayBuffers();
369
370 mInitialized = true;
371}
372
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700373void FontRenderer::checkTextureUpdate() {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900374 if (!mUploadTexture) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700375 return;
Romain Guy694b5192010-07-21 21:33:20 -0700376 }
377
Romain Guy2d4fd362011-12-13 22:00:19 -0800378 Caches& caches = Caches::getInstance();
379 GLuint lastTextureId = 0;
Chet Haase378e9192012-08-15 15:54:54 -0700380 // Iterate over all the cache textures and see which ones need to be updated
381 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
382 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guy80872462012-09-04 16:42:01 -0700383 if (cacheTexture->isDirty() && cacheTexture->getTexture()) {
Chet Haaseb92d8f72012-09-21 08:40:46 -0700384 // Can't copy inner rect; glTexSubimage expects pointer to deal with entire buffer
385 // of data. So expand the dirty rect to the encompassing horizontal stripe.
386 const Rect* dirtyRect = cacheTexture->getDirtyRect();
387 uint32_t x = 0;
388 uint32_t y = dirtyRect->top;
Romain Guy80872462012-09-04 16:42:01 -0700389 uint32_t width = cacheTexture->getWidth();
Chet Haaseb92d8f72012-09-21 08:40:46 -0700390 uint32_t height = dirtyRect->getHeight();
391 void* textureData = cacheTexture->getTexture() + y * width;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700392
Romain Guy80872462012-09-04 16:42:01 -0700393 if (cacheTexture->getTextureId() != lastTextureId) {
394 lastTextureId = cacheTexture->getTextureId();
Romain Guy2d4fd362011-12-13 22:00:19 -0800395 caches.activeTexture(0);
Romain Guy80872462012-09-04 16:42:01 -0700396 glBindTexture(GL_TEXTURE_2D, lastTextureId);
Romain Guy2d4fd362011-12-13 22:00:19 -0800397 }
Chet Haasee816bae2012-08-09 13:39:02 -0700398#if DEBUG_FONT_RENDERER
Chet Haaseb92d8f72012-09-21 08:40:46 -0700399 ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d",
400 i, x, y, width, height);
Chet Haasee816bae2012-08-09 13:39:02 -0700401#endif
Chet Haaseb92d8f72012-09-21 08:40:46 -0700402 glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700403 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Romain Guy80872462012-09-04 16:42:01 -0700404 cacheTexture->setDirty(false);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700405 }
406 }
407
408 mUploadTexture = false;
409}
410
411void FontRenderer::issueDrawCommand() {
Romain Guy661a87e2013-03-19 15:24:36 -0700412 bool first = true;
413 bool force = false;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700414
Romain Guy115096f2013-03-19 11:32:41 -0700415 GLuint lastId = 0;
Romain Guy661a87e2013-03-19 15:24:36 -0700416 Caches& caches = Caches::getInstance();
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900417
Romain Guy661a87e2013-03-19 15:24:36 -0700418 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
419 CacheTexture* texture = mCacheTextures[i];
420 if (texture->canDraw()) {
421 if (first) {
Romain Guy257ae352013-03-20 16:31:12 -0700422 if (mFunctor) (*mFunctor)(0, NULL);
423
Romain Guy661a87e2013-03-19 15:24:36 -0700424 checkTextureUpdate();
425 caches.bindIndicesBuffer(mIndexBufferID);
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900426
Romain Guy661a87e2013-03-19 15:24:36 -0700427 if (!mDrawn) {
428 // If returns true, a VBO was bound and we must
429 // rebind our vertex attrib pointers even if
430 // they have the same values as the current pointers
431 force = caches.unbindMeshBuffer();
432 }
433
434 caches.activeTexture(0);
435 first = false;
436 }
437
438 glBindTexture(GL_TEXTURE_2D, texture->getTextureId());
439 texture->setLinearFiltering(mLinearFiltering, false);
440
441 TextureVertex* mesh = texture->mesh();
442 caches.bindPositionVertexPointer(force, &mesh[0].position[0]);
443 caches.bindTexCoordsVertexPointer(force, &mesh[0].texture[0]);
444 force = false;
445
446 glDrawElements(GL_TRIANGLES, texture->meshElementCount(),
447 GL_UNSIGNED_SHORT, texture->indices());
448
449 texture->resetMesh();
Romain Guy115096f2013-03-19 11:32:41 -0700450 }
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900451 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700452
453 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700454}
455
Romain Guy97771732012-02-28 18:17:02 -0800456void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
457 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800458 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800459 if (texture != mCurrentCacheTexture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800460 // Now use the new texture id
461 mCurrentCacheTexture = texture;
462 }
Romain Guy09147fb2010-07-22 13:08:20 -0700463
Romain Guy661a87e2013-03-19 15:24:36 -0700464 mCurrentCacheTexture->addQuad(x1, y1, u1, v1, x2, y2, u2, v2,
465 x3, y3, u3, v3, x4, y4, u4, v4);
Romain Guy97771732012-02-28 18:17:02 -0800466}
467
468void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
469 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
470 float x4, float y4, float u4, float v4, CacheTexture* texture) {
471
472 if (mClip &&
473 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
474 return;
475 }
476
477 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 -0700478
Romain Guy5b3b3522010-10-27 18:57:51 -0700479 if (mBounds) {
480 mBounds->left = fmin(mBounds->left, x1);
481 mBounds->top = fmin(mBounds->top, y3);
482 mBounds->right = fmax(mBounds->right, x3);
483 mBounds->bottom = fmax(mBounds->bottom, y1);
484 }
485
Romain Guy661a87e2013-03-19 15:24:36 -0700486 if (mCurrentCacheTexture->endOfMesh()) {
Romain Guy694b5192010-07-21 21:33:20 -0700487 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700488 }
489}
490
Romain Guy97771732012-02-28 18:17:02 -0800491void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
492 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
493 float x4, float y4, float u4, float v4, CacheTexture* texture) {
494
495 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
496
497 if (mBounds) {
498 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
499 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
500 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
501 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
502 }
503
Romain Guy661a87e2013-03-19 15:24:36 -0700504 if (mCurrentCacheTexture->endOfMesh()) {
Romain Guy97771732012-02-28 18:17:02 -0800505 issueDrawCommand();
Romain Guy97771732012-02-28 18:17:02 -0800506 }
507}
508
Romain Guye3a9b242013-01-08 11:15:30 -0800509void FontRenderer::setFont(SkPaint* paint, const mat4& matrix) {
510 mCurrentFont = Font::create(this, paint, matrix);
Romain Guy694b5192010-07-21 21:33:20 -0700511}
Romain Guy7975fb62010-10-01 16:36:14 -0700512
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700513FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Raph Levien416a8472012-07-19 22:48:17 -0700514 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700515 checkInit();
516
517 if (!mCurrentFont) {
518 DropShadow image;
519 image.width = 0;
520 image.height = 0;
521 image.image = NULL;
522 image.penX = 0;
523 image.penY = 0;
524 return image;
525 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700526
Romain Guy2d4fd362011-12-13 22:00:19 -0800527 mDrawn = false;
Romain Guyff98fa52011-11-28 09:35:09 -0800528 mClip = NULL;
529 mBounds = NULL;
530
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700531 Rect bounds;
Raph Levien416a8472012-07-19 22:48:17 -0700532 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
Romain Guyff98fa52011-11-28 09:35:09 -0800533
Romain Guy1e45aae2010-08-13 19:39:53 -0700534 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
535 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Romain Guyff98fa52011-11-28 09:35:09 -0800536
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
561 blurImage(&dataBuffer, paddedWidth, paddedHeight, radius);
562 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700563
564 DropShadow image;
565 image.width = paddedWidth;
566 image.height = paddedHeight;
567 image.image = dataBuffer;
568 image.penX = penX;
569 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -0800570
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700571 return image;
572}
Romain Guy694b5192010-07-21 21:33:20 -0700573
Romain Guy257ae352013-03-20 16:31:12 -0700574void FontRenderer::initRender(const Rect* clip, Rect* bounds, Functor* functor) {
Romain Guy694b5192010-07-21 21:33:20 -0700575 checkInit();
576
Romain Guy5b3b3522010-10-27 18:57:51 -0700577 mDrawn = false;
578 mBounds = bounds;
Romain Guy257ae352013-03-20 16:31:12 -0700579 mFunctor = functor;
Romain Guy09147fb2010-07-22 13:08:20 -0700580 mClip = clip;
Romain Guy671d6cf2012-01-18 12:39:17 -0800581}
Romain Guyff98fa52011-11-28 09:35:09 -0800582
Romain Guy671d6cf2012-01-18 12:39:17 -0800583void FontRenderer::finishRender() {
Romain Guy5b3b3522010-10-27 18:57:51 -0700584 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -0800585 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700586
Romain Guy661a87e2013-03-19 15:24:36 -0700587 issueDrawCommand();
Romain Guy671d6cf2012-01-18 12:39:17 -0800588}
589
Romain Guye3a9b242013-01-08 11:15:30 -0800590void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix) {
591 Font* font = Font::create(this, paint, matrix);
Chet Haasee816bae2012-08-09 13:39:02 -0700592 font->precache(paint, text, numGlyphs);
593}
594
Romain Guy671d6cf2012-01-18 12:39:17 -0800595bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
596 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
Romain Guy257ae352013-03-20 16:31:12 -0700597 const float* positions, Rect* bounds, Functor* functor) {
Romain Guy671d6cf2012-01-18 12:39:17 -0800598 if (!mCurrentFont) {
599 ALOGE("No font set");
600 return false;
601 }
602
Romain Guy257ae352013-03-20 16:31:12 -0700603 initRender(clip, bounds, functor);
Romain Guy671d6cf2012-01-18 12:39:17 -0800604 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
605 finishRender();
Romain Guy5b3b3522010-10-27 18:57:51 -0700606
607 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700608}
609
Romain Guy97771732012-02-28 18:17:02 -0800610bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
611 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
612 float hOffset, float vOffset, Rect* bounds) {
613 if (!mCurrentFont) {
614 ALOGE("No font set");
615 return false;
616 }
617
Romain Guy257ae352013-03-20 16:31:12 -0700618 initRender(clip, bounds, NULL);
Romain Guy97771732012-02-28 18:17:02 -0800619 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
620 finishRender();
621
622 return mDrawn;
623}
624
Romain Guy9b1204b2012-09-04 15:22:57 -0700625void FontRenderer::removeFont(const Font* font) {
Romain Guye3a9b242013-01-08 11:15:30 -0800626 mActiveFonts.remove(font->getDescription());
Romain Guy9b1204b2012-09-04 15:22:57 -0700627
628 if (mCurrentFont == font) {
629 mCurrentFont = NULL;
630 }
631}
632
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800633void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) {
Dan Morrille4d9a012013-03-28 18:10:43 -0700634#ifdef ANDROID_ENABLE_RENDERSCRIPT
635 if (width * height * radius >= RS_MIN_INPUT_CUTOFF) {
636 uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700637
Dan Morrille4d9a012013-03-28 18:10:43 -0700638 if (mRs.get() == 0) {
639 mRs = new RSC::RS();
640 if (!mRs->init(true, true)) {
641 ALOGE("blur RS failed to init");
642 }
Romain Guyd71dd362011-12-12 19:03:35 -0800643
Dan Morrille4d9a012013-03-28 18:10:43 -0700644 mRsElement = RSC::Element::A_8(mRs);
645 mRsScript = new RSC::ScriptIntrinsicBlur(mRs, mRsElement);
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800646 }
647
Dan Morrille4d9a012013-03-28 18:10:43 -0700648 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
658 free(*image);
659 *image = outImage;
660
661 return;
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800662 }
Dan Morrille4d9a012013-03-28 18:10:43 -0700663#endif
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800664
Dan Morrille4d9a012013-03-28 18:10:43 -0700665 float *gaussian = new float[2 * radius + 1];
666 Blur::generateGaussianWeights(gaussian, radius);
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800667
Dan Morrille4d9a012013-03-28 18:10:43 -0700668 uint8_t* scratch = new uint8_t[width * height];
669 Blur::horizontal(gaussian, radius, *image, scratch, width, height);
670 Blur::vertical(gaussian, radius, scratch, *image, width, height);
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800671
Dan Morrille4d9a012013-03-28 18:10:43 -0700672 delete[] gaussian;
673 delete[] scratch;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700674}
675
Romain Guy694b5192010-07-21 21:33:20 -0700676}; // namespace uirenderer
677}; // namespace android