blob: 6894ef916570221a271d3329c8e820e403522ccf [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
Romain Guy6e200402013-03-08 11:28:22 -080027#include <RenderScript.h>
Chris Craikf2d8ccc2013-02-13 16:14:17 -080028
Romain Guy6e200402013-03-08 11:28:22 -080029#include "utils/Blur.h"
Chris Craikf2d8ccc2013-02-13 16:14:17 -080030#include "utils/Timing.h"
Romain Guy6e200402013-03-08 11:28:22 -080031
Romain Guy15bc6432011-12-13 13:11:32 -080032#include "Caches.h"
Romain Guyc9855a52011-01-21 21:14:15 -080033#include "Debug.h"
Romain Guy09087642013-04-04 12:27:54 -070034#include "Extensions.h"
Romain Guy51769a62010-07-23 00:28:00 -070035#include "FontRenderer.h"
Romain Guycf51a412013-04-08 19:40:31 -070036#include "PixelBuffer.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 Guy9cccc2b2010-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 Guy9cccc2b2010-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 }
137}
138
Chet Haase9a824562011-12-16 15:44:59 -0800139void FontRenderer::flushLargeCaches() {
Chet Haase378e9192012-08-15 15:54:54 -0700140 // Start from 1; don't deallocate smallest/default texture
141 for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
142 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guycf51a412013-04-08 19:40:31 -0700143 if (cacheTexture->getPixelBuffer()) {
Chet Haase378e9192012-08-15 15:54:54 -0700144 cacheTexture->init();
Romain Guye3a9b242013-01-08 11:15:30 -0800145 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
146 while (it.next()) {
147 it.value()->invalidateTextureCache(cacheTexture);
Chet Haasee816bae2012-08-09 13:39:02 -0700148 }
Romain Guy80872462012-09-04 16:42:01 -0700149 cacheTexture->releaseTexture();
Chet Haase9a824562011-12-16 15:44:59 -0800150 }
151 }
Chet Haase9a824562011-12-16 15:44:59 -0800152}
153
Chet Haase378e9192012-08-15 15:54:54 -0700154CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
155 uint32_t* startX, uint32_t* startY) {
156 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
157 if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
158 return mCacheTextures[i];
159 }
160 }
161 // Could not fit glyph into current cache textures
162 return NULL;
163}
164
Chet Haase7de0cb12011-12-05 16:35:38 -0800165void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
Chet Haasef942cf12012-08-30 09:06:46 -0700166 uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700167 checkInit();
Romain Guya4adcf02013-02-28 12:15:35 -0800168
169 // If the glyph bitmap is empty let's assum the glyph is valid
170 // so we can avoid doing extra work later on
171 if (glyph.fWidth == 0 || glyph.fHeight == 0) {
172 cachedGlyph->mIsValid = true;
173 cachedGlyph->mCacheTexture = NULL;
174 return;
175 }
176
Chet Haase7de0cb12011-12-05 16:35:38 -0800177 cachedGlyph->mIsValid = false;
Romain Guya4adcf02013-02-28 12:15:35 -0800178
Romain Guy694b5192010-07-21 21:33:20 -0700179 // If the glyph is too tall, don't cache it
Chet Haase378e9192012-08-15 15:54:54 -0700180 if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
Romain Guy80872462012-09-04 16:42:01 -0700181 mCacheTextures[mCacheTextures.size() - 1]->getHeight()) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700182 ALOGE("Font size too large to fit in cache. width, height = %i, %i",
183 (int) glyph.fWidth, (int) glyph.fHeight);
Chet Haase7de0cb12011-12-05 16:35:38 -0800184 return;
Romain Guy694b5192010-07-21 21:33:20 -0700185 }
186
187 // Now copy the bitmap into the cache texture
188 uint32_t startX = 0;
189 uint32_t startY = 0;
190
Chet Haase378e9192012-08-15 15:54:54 -0700191 CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700192
Chet Haase378e9192012-08-15 15:54:54 -0700193 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700194 if (!precaching) {
195 // If the new glyph didn't fit and we are not just trying to precache it,
196 // clear out the cache and try again
197 flushAllAndInvalidate();
198 cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
199 }
Romain Guy694b5192010-07-21 21:33:20 -0700200
Chet Haase378e9192012-08-15 15:54:54 -0700201 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700202 // either the glyph didn't fit or we're precaching and will cache it when we draw
Chet Haase7de0cb12011-12-05 16:35:38 -0800203 return;
Romain Guy694b5192010-07-21 21:33:20 -0700204 }
205 }
206
Chet Haase378e9192012-08-15 15:54:54 -0700207 cachedGlyph->mCacheTexture = cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800208
Romain Guy694b5192010-07-21 21:33:20 -0700209 *retOriginX = startX;
210 *retOriginY = startY;
211
212 uint32_t endX = startX + glyph.fWidth;
213 uint32_t endY = startY + glyph.fHeight;
214
Romain Guy80872462012-09-04 16:42:01 -0700215 uint32_t cacheWidth = cacheTexture->getWidth();
Romain Guy694b5192010-07-21 21:33:20 -0700216
Romain Guycf51a412013-04-08 19:40:31 -0700217 if (!cacheTexture->getPixelBuffer()) {
Romain Guy80872462012-09-04 16:42:01 -0700218 Caches::getInstance().activeTexture(0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800219 // Large-glyph texture memory is allocated only as needed
Romain Guy80872462012-09-04 16:42:01 -0700220 cacheTexture->allocateTexture();
Chet Haase7de0cb12011-12-05 16:35:38 -0800221 }
Romain Guy661a87e2013-03-19 15:24:36 -0700222 if (!cacheTexture->mesh()) {
223 cacheTexture->allocateMesh();
224 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700225
Romain Guyb969a0d2013-02-05 14:38:40 -0800226 // Tells us whether the glyphs is B&W (1 bit per pixel)
227 // or anti-aliased (8 bits per pixel)
228 SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
Romain Guy694b5192010-07-21 21:33:20 -0700229
Romain Guycf51a412013-04-08 19:40:31 -0700230 uint8_t* cacheBuffer = cacheTexture->getPixelBuffer()->map();
Romain Guy694b5192010-07-21 21:33:20 -0700231 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
Romain Guy33fa1f72012-08-07 19:09:57 -0700232
Romain Guyb969a0d2013-02-05 14:38:40 -0800233 // Copy the glyph image, taking the mask format into account
234 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
235 int stride = glyph.rowBytes();
236
Romain Guy0b58a3d2013-03-05 12:16:27 -0800237 uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
238 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
239
Romain Guyb969a0d2013-02-05 14:38:40 -0800240 switch (format) {
241 case SkMask::kA8_Format: {
242 if (mGammaTable) {
243 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
Romain Guy0b58a3d2013-03-05 12:16:27 -0800244 row = cacheY * cacheWidth;
245 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800246 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
247 uint8_t tempCol = bitmapBuffer[bY + bX];
Romain Guy0b58a3d2013-03-05 12:16:27 -0800248 cacheBuffer[row + cacheX] = mGammaTable[tempCol];
Romain Guyb969a0d2013-02-05 14:38:40 -0800249 }
Romain Guy0b58a3d2013-03-05 12:16:27 -0800250 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800251 }
252 } else {
253 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
Romain Guy0b58a3d2013-03-05 12:16:27 -0800254 row = cacheY * cacheWidth;
255 memcpy(&cacheBuffer[row + startX], &bitmapBuffer[bY], glyph.fWidth);
256 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
257 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800258 }
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700259 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800260 break;
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700261 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800262 case SkMask::kBW_Format: {
263 static const uint8_t COLORS[2] = { 0, 255 };
264
265 for (cacheY = startY; cacheY < endY; cacheY++) {
266 cacheX = startX;
267 int rowBytes = stride;
268 uint8_t* buffer = bitmapBuffer;
269
Romain Guy0b58a3d2013-03-05 12:16:27 -0800270 row = cacheY * cacheWidth;
271 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800272 while (--rowBytes >= 0) {
273 uint8_t b = *buffer++;
274 for (int8_t mask = 7; mask >= 0 && cacheX < endX; mask--) {
275 cacheBuffer[cacheY * cacheWidth + cacheX++] = COLORS[(b >> mask) & 0x1];
276 }
277 }
Romain Guy0b58a3d2013-03-05 12:16:27 -0800278 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800279
280 bitmapBuffer += stride;
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700281 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800282 break;
Romain Guy694b5192010-07-21 21:33:20 -0700283 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800284 default:
285 ALOGW("Unkown glyph format: 0x%x", format);
286 break;
Romain Guy694b5192010-07-21 21:33:20 -0700287 }
Romain Guy97771732012-02-28 18:17:02 -0800288
Romain Guy0b58a3d2013-03-05 12:16:27 -0800289 row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
290 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
291
Chet Haase7de0cb12011-12-05 16:35:38 -0800292 cachedGlyph->mIsValid = true;
Romain Guy694b5192010-07-21 21:33:20 -0700293}
294
Chet Haase7de0cb12011-12-05 16:35:38 -0800295CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
Romain Guy661a87e2013-03-19 15:24:36 -0700296 CacheTexture* cacheTexture = new CacheTexture(width, height, mMaxNumberOfQuads);
Romain Guy9d9758a2012-05-14 15:19:58 -0700297
Chet Haase2a47c142011-12-14 15:22:56 -0800298 if (allocate) {
Romain Guy80872462012-09-04 16:42:01 -0700299 Caches::getInstance().activeTexture(0);
300 cacheTexture->allocateTexture();
Romain Guy661a87e2013-03-19 15:24:36 -0700301 cacheTexture->allocateMesh();
Chet Haase2a47c142011-12-14 15:22:56 -0800302 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700303
Chet Haase2a47c142011-12-14 15:22:56 -0800304 return cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800305}
306
307void FontRenderer::initTextTexture() {
Chet Haase378e9192012-08-15 15:54:54 -0700308 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
309 delete mCacheTextures[i];
Romain Guy9d9758a2012-05-14 15:19:58 -0700310 }
Chet Haase378e9192012-08-15 15:54:54 -0700311 mCacheTextures.clear();
Romain Guy9d9758a2012-05-14 15:19:58 -0700312
Chet Haase7de0cb12011-12-05 16:35:38 -0800313 mUploadTexture = false;
Chet Haase378e9192012-08-15 15:54:54 -0700314 mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
Chet Haaseeb32a492012-08-31 13:54:03 -0700315 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
316 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
317 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false));
Chet Haase378e9192012-08-15 15:54:54 -0700318 mCurrentCacheTexture = mCacheTextures[0];
Romain Guy694b5192010-07-21 21:33:20 -0700319}
320
321// Avoid having to reallocate memory and render quad by quad
322void FontRenderer::initVertexArrayBuffers() {
Romain Guyd71dd362011-12-12 19:03:35 -0800323 uint32_t numIndices = mMaxNumberOfQuads * 6;
324 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700325 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700326
327 // Four verts, two triangles , six indices per quad
328 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
329 int i6 = i * 6;
330 int i4 = i * 4;
331
332 indexBufferData[i6 + 0] = i4 + 0;
333 indexBufferData[i6 + 1] = i4 + 1;
334 indexBufferData[i6 + 2] = i4 + 2;
335
336 indexBufferData[i6 + 3] = i4 + 0;
337 indexBufferData[i6 + 4] = i4 + 2;
338 indexBufferData[i6 + 5] = i4 + 3;
339 }
340
341 glGenBuffers(1, &mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800342 Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700343 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
Romain Guy694b5192010-07-21 21:33:20 -0700344
345 free(indexBufferData);
Romain Guy694b5192010-07-21 21:33:20 -0700346}
347
348// We don't want to allocate anything unless we actually draw text
349void FontRenderer::checkInit() {
350 if (mInitialized) {
351 return;
352 }
353
354 initTextTexture();
355 initVertexArrayBuffers();
356
357 mInitialized = true;
358}
359
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700360void FontRenderer::checkTextureUpdate() {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900361 if (!mUploadTexture) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700362 return;
Romain Guy694b5192010-07-21 21:33:20 -0700363 }
364
Romain Guy2d4fd362011-12-13 22:00:19 -0800365 Caches& caches = Caches::getInstance();
366 GLuint lastTextureId = 0;
Romain Guy09087642013-04-04 12:27:54 -0700367
Romain Guycf51a412013-04-08 19:40:31 -0700368 bool resetPixelStore = false;
Romain Guy09087642013-04-04 12:27:54 -0700369 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
370
Chet Haase378e9192012-08-15 15:54:54 -0700371 // Iterate over all the cache textures and see which ones need to be updated
372 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
373 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guycf51a412013-04-08 19:40:31 -0700374 if (cacheTexture->isDirty() && cacheTexture->getPixelBuffer()) {
Romain Guy80872462012-09-04 16:42:01 -0700375 if (cacheTexture->getTextureId() != lastTextureId) {
376 lastTextureId = cacheTexture->getTextureId();
Romain Guy2d4fd362011-12-13 22:00:19 -0800377 caches.activeTexture(0);
Romain Guy80872462012-09-04 16:42:01 -0700378 glBindTexture(GL_TEXTURE_2D, lastTextureId);
Romain Guy2d4fd362011-12-13 22:00:19 -0800379 }
Romain Guy09087642013-04-04 12:27:54 -0700380
Romain Guycf51a412013-04-08 19:40:31 -0700381 if (cacheTexture->upload()) {
382 resetPixelStore = true;
Romain Guy09087642013-04-04 12:27:54 -0700383 }
384
Chet Haasee816bae2012-08-09 13:39:02 -0700385#if DEBUG_FONT_RENDERER
Chet Haaseb92d8f72012-09-21 08:40:46 -0700386 ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d",
387 i, x, y, width, height);
Chet Haasee816bae2012-08-09 13:39:02 -0700388#endif
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700389 }
390 }
391
Romain Guycf51a412013-04-08 19:40:31 -0700392 // Unbind any PBO we might have used to update textures
393 caches.unbindPixelBuffer();
394
Romain Guy09087642013-04-04 12:27:54 -0700395 // Reset to default unpack row length to avoid affecting texture
396 // uploads in other parts of the renderer
Romain Guycf51a412013-04-08 19:40:31 -0700397 if (resetPixelStore) {
Romain Guy09087642013-04-04 12:27:54 -0700398 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
399 }
400
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700401 mUploadTexture = false;
402}
403
404void FontRenderer::issueDrawCommand() {
Romain Guy661a87e2013-03-19 15:24:36 -0700405 bool first = true;
406 bool force = false;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700407
Romain Guy115096f2013-03-19 11:32:41 -0700408 GLuint lastId = 0;
Romain Guy661a87e2013-03-19 15:24:36 -0700409 Caches& caches = Caches::getInstance();
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900410
Romain Guy661a87e2013-03-19 15:24:36 -0700411 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
412 CacheTexture* texture = mCacheTextures[i];
413 if (texture->canDraw()) {
414 if (first) {
Romain Guy257ae352013-03-20 16:31:12 -0700415 if (mFunctor) (*mFunctor)(0, NULL);
416
Romain Guy661a87e2013-03-19 15:24:36 -0700417 checkTextureUpdate();
418 caches.bindIndicesBuffer(mIndexBufferID);
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900419
Romain Guy661a87e2013-03-19 15:24:36 -0700420 if (!mDrawn) {
421 // If returns true, a VBO was bound and we must
422 // rebind our vertex attrib pointers even if
423 // they have the same values as the current pointers
424 force = caches.unbindMeshBuffer();
425 }
426
427 caches.activeTexture(0);
428 first = false;
429 }
430
431 glBindTexture(GL_TEXTURE_2D, texture->getTextureId());
432 texture->setLinearFiltering(mLinearFiltering, false);
433
434 TextureVertex* mesh = texture->mesh();
435 caches.bindPositionVertexPointer(force, &mesh[0].position[0]);
436 caches.bindTexCoordsVertexPointer(force, &mesh[0].texture[0]);
437 force = false;
438
439 glDrawElements(GL_TRIANGLES, texture->meshElementCount(),
440 GL_UNSIGNED_SHORT, texture->indices());
441
442 texture->resetMesh();
Romain Guy115096f2013-03-19 11:32:41 -0700443 }
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900444 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700445
446 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700447}
448
Romain Guy97771732012-02-28 18:17:02 -0800449void FontRenderer::appendMeshQuadNoClip(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,
Chet Haase7de0cb12011-12-05 16:35:38 -0800451 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800452 if (texture != mCurrentCacheTexture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800453 // Now use the new texture id
454 mCurrentCacheTexture = texture;
455 }
Romain Guy09147fb2010-07-22 13:08:20 -0700456
Romain Guy661a87e2013-03-19 15:24:36 -0700457 mCurrentCacheTexture->addQuad(x1, y1, u1, v1, x2, y2, u2, v2,
458 x3, y3, u3, v3, x4, y4, u4, v4);
Romain Guy97771732012-02-28 18:17:02 -0800459}
460
461void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
462 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
463 float x4, float y4, float u4, float v4, CacheTexture* texture) {
464
465 if (mClip &&
466 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
467 return;
468 }
469
470 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 -0700471
Romain Guy5b3b3522010-10-27 18:57:51 -0700472 if (mBounds) {
473 mBounds->left = fmin(mBounds->left, x1);
474 mBounds->top = fmin(mBounds->top, y3);
475 mBounds->right = fmax(mBounds->right, x3);
476 mBounds->bottom = fmax(mBounds->bottom, y1);
477 }
478
Romain Guy661a87e2013-03-19 15:24:36 -0700479 if (mCurrentCacheTexture->endOfMesh()) {
Romain Guy694b5192010-07-21 21:33:20 -0700480 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700481 }
482}
483
Romain Guy97771732012-02-28 18:17:02 -0800484void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
485 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
486 float x4, float y4, float u4, float v4, CacheTexture* texture) {
487
488 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
489
490 if (mBounds) {
491 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
492 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
493 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
494 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
495 }
496
Romain Guy661a87e2013-03-19 15:24:36 -0700497 if (mCurrentCacheTexture->endOfMesh()) {
Romain Guy97771732012-02-28 18:17:02 -0800498 issueDrawCommand();
Romain Guy97771732012-02-28 18:17:02 -0800499 }
500}
501
Romain Guye3a9b242013-01-08 11:15:30 -0800502void FontRenderer::setFont(SkPaint* paint, const mat4& matrix) {
503 mCurrentFont = Font::create(this, paint, matrix);
Romain Guy694b5192010-07-21 21:33:20 -0700504}
Romain Guy7975fb62010-10-01 16:36:14 -0700505
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700506FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Raph Levien416a8472012-07-19 22:48:17 -0700507 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700508 checkInit();
509
Romain Guycf51a412013-04-08 19:40:31 -0700510 DropShadow image;
511 image.width = 0;
512 image.height = 0;
513 image.image = NULL;
514 image.penX = 0;
515 image.penY = 0;
516
Romain Guy1e45aae2010-08-13 19:39:53 -0700517 if (!mCurrentFont) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700518 return image;
519 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700520
Romain Guy2d4fd362011-12-13 22:00:19 -0800521 mDrawn = false;
Romain Guyff98fa52011-11-28 09:35:09 -0800522 mClip = NULL;
523 mBounds = NULL;
524
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700525 Rect bounds;
Raph Levien416a8472012-07-19 22:48:17 -0700526 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
Romain Guyff98fa52011-11-28 09:35:09 -0800527
Romain Guy1e45aae2010-08-13 19:39:53 -0700528 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
529 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Romain Guyff98fa52011-11-28 09:35:09 -0800530
Romain Guycf51a412013-04-08 19:40:31 -0700531 uint32_t maxSize = Caches::getInstance().maxTextureSize;
532 if (paddedWidth > maxSize || paddedHeight > maxSize) {
533 return image;
534 }
535
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800536 // Align buffers for renderscript usage
537 if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) {
538 paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700539 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700540
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800541 int size = paddedWidth * paddedHeight;
Romain Guy6e200402013-03-08 11:28:22 -0800542 uint8_t* dataBuffer = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, size);
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800543 memset(dataBuffer, 0, size);
544
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700545 int penX = radius - bounds.left;
546 int penY = radius - bounds.bottom;
547
Chris Craikdd8697c2013-02-22 10:41:36 -0800548 if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) {
549 // text has non-whitespace, so draw and blur to create the shadow
550 // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted
551 // TODO: don't draw pure whitespace in the first place, and avoid needing this check
552 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
553 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
554
Romain Guycf51a412013-04-08 19:40:31 -0700555 // Unbind any PBO we might have used
556 Caches::getInstance().unbindPixelBuffer();
557
Chris Craikdd8697c2013-02-22 10:41:36 -0800558 blurImage(&dataBuffer, paddedWidth, paddedHeight, radius);
559 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700560
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700561 image.width = paddedWidth;
562 image.height = paddedHeight;
563 image.image = dataBuffer;
564 image.penX = penX;
565 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -0800566
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700567 return image;
568}
Romain Guy694b5192010-07-21 21:33:20 -0700569
Romain Guy257ae352013-03-20 16:31:12 -0700570void FontRenderer::initRender(const Rect* clip, Rect* bounds, Functor* functor) {
Romain Guy694b5192010-07-21 21:33:20 -0700571 checkInit();
572
Romain Guy5b3b3522010-10-27 18:57:51 -0700573 mDrawn = false;
574 mBounds = bounds;
Romain Guy257ae352013-03-20 16:31:12 -0700575 mFunctor = functor;
Romain Guy09147fb2010-07-22 13:08:20 -0700576 mClip = clip;
Romain Guy671d6cf2012-01-18 12:39:17 -0800577}
Romain Guyff98fa52011-11-28 09:35:09 -0800578
Romain Guy671d6cf2012-01-18 12:39:17 -0800579void FontRenderer::finishRender() {
Romain Guy5b3b3522010-10-27 18:57:51 -0700580 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -0800581 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700582
Romain Guy661a87e2013-03-19 15:24:36 -0700583 issueDrawCommand();
Romain Guy671d6cf2012-01-18 12:39:17 -0800584}
585
Romain Guye3a9b242013-01-08 11:15:30 -0800586void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix) {
587 Font* font = Font::create(this, paint, matrix);
Chet Haasee816bae2012-08-09 13:39:02 -0700588 font->precache(paint, text, numGlyphs);
589}
590
Romain Guycf51a412013-04-08 19:40:31 -0700591void FontRenderer::endPrecaching() {
592 checkTextureUpdate();
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) {
634 if (width * height * radius < RS_MIN_INPUT_CUTOFF) {
635 float *gaussian = new float[2 * radius + 1];
Romain Guy6e200402013-03-08 11:28:22 -0800636 Blur::generateGaussianWeights(gaussian, radius);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700637
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800638 uint8_t* scratch = new uint8_t[width * height];
Romain Guy6e200402013-03-08 11:28:22 -0800639 Blur::horizontal(gaussian, radius, *image, scratch, width, height);
640 Blur::vertical(gaussian, radius, scratch, *image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -0800641
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800642 delete[] gaussian;
643 delete[] scratch;
Chris Craikdd8697c2013-02-22 10:41:36 -0800644 return;
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800645 }
Romain Guyd71dd362011-12-12 19:03:35 -0800646
Romain Guy6e200402013-03-08 11:28:22 -0800647 uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800648
649 if (mRs.get() == 0) {
650 mRs = new RSC::RS();
651 if (!mRs->init(true, true)) {
652 ALOGE("blur RS failed to init");
653 }
654
655 mRsElement = RSC::Element::A_8(mRs);
656 mRsScript = new RSC::ScriptIntrinsicBlur(mRs, mRsElement);
657 }
658
659 sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0);
660 sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
661 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, *image);
662 sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
663 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, outImage);
664
665 mRsScript->setRadius(radius);
666 mRsScript->blur(ain, aout);
667
668 // replace the original image's pointer, avoiding a copy back to the original buffer
Ben Cheng15641a62013-02-20 13:20:03 -0800669 free(*image);
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800670 *image = outImage;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700671}
672
Romain Guycf51a412013-04-08 19:40:31 -0700673uint32_t FontRenderer::getCacheSize() const {
674 uint32_t size = 0;
675 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
676 CacheTexture* cacheTexture = mCacheTextures[i];
677 if (cacheTexture && cacheTexture->getPixelBuffer()) {
678 size += cacheTexture->getPixelBuffer()->getSize();
679 }
680 }
681 return size;
682}
683
Romain Guy694b5192010-07-21 21:33:20 -0700684}; // namespace uirenderer
685}; // namespace android