blob: 26c7e5d1ad6fe504a7d7ae455d8089a6ec0dbf5e [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 Guy51769a62010-07-23 00:28:00 -070034#include "FontRenderer.h"
Romain Guy9f5dab32012-09-04 12:55:44 -070035#include "Rect.h"
Romain Guy51769a62010-07-23 00:28:00 -070036
Romain Guy694b5192010-07-21 21:33:20 -070037namespace android {
38namespace uirenderer {
39
Chris Craikf2d8ccc2013-02-13 16:14:17 -080040// blur inputs smaller than this constant will bypass renderscript
41#define RS_MIN_INPUT_CUTOFF 10000
42
Romain Guy694b5192010-07-21 21:33:20 -070043///////////////////////////////////////////////////////////////////////////////
Romain Guy694b5192010-07-21 21:33:20 -070044// FontRenderer
45///////////////////////////////////////////////////////////////////////////////
46
Romain Guy514fb182011-01-19 14:38:29 -080047static bool sLogFontRendererCreate = true;
48
Romain Guye3a9b242013-01-08 11:15:30 -080049FontRenderer::FontRenderer() :
50 mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity) {
51
Romain Guyc9855a52011-01-21 21:14:15 -080052 if (sLogFontRendererCreate) {
53 INIT_LOGD("Creating FontRenderer");
54 }
Romain Guy51769a62010-07-23 00:28:00 -070055
Romain Guyb45c0c92010-08-26 20:35:23 -070056 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -070057 mInitialized = false;
58 mMaxNumberOfQuads = 1024;
Romain Guy694b5192010-07-21 21:33:20 -070059
Chet Haase7de0cb12011-12-05 16:35:38 -080060 mCurrentCacheTexture = NULL;
Romain Guy9cccc2b92010-08-07 23:46:15 -070061
Chet Haase2a47c142011-12-14 15:22:56 -080062 mLinearFiltering = false;
63
Romain Guy694b5192010-07-21 21:33:20 -070064 mIndexBufferID = 0;
65
Chet Haaseeb32a492012-08-31 13:54:03 -070066 mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH;
67 mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT;
68 mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH;
69 mLargeCacheHeight = DEFAULT_TEXT_LARGE_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -070070
71 char property[PROPERTY_VALUE_MAX];
Chet Haaseeb32a492012-08-31 13:54:03 -070072 if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, NULL) > 0) {
Chet Haase7de0cb12011-12-05 16:35:38 -080073 mSmallCacheWidth = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -070074 }
Romain Guy9f5dab32012-09-04 12:55:44 -070075
Chet Haaseeb32a492012-08-31 13:54:03 -070076 if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, NULL) > 0) {
Chet Haase7de0cb12011-12-05 16:35:38 -080077 mSmallCacheHeight = atoi(property);
Chet Haaseeb32a492012-08-31 13:54:03 -070078 }
Romain Guy9f5dab32012-09-04 12:55:44 -070079
Chet Haaseeb32a492012-08-31 13:54:03 -070080 if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, NULL) > 0) {
81 mLargeCacheWidth = atoi(property);
82 }
Romain Guy9f5dab32012-09-04 12:55:44 -070083
Chet Haaseeb32a492012-08-31 13:54:03 -070084 if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, NULL) > 0) {
85 mLargeCacheHeight = atoi(property);
86 }
Romain Guy9f5dab32012-09-04 12:55:44 -070087
88 uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize;
89 mSmallCacheWidth = mSmallCacheWidth > maxTextureSize ? maxTextureSize : mSmallCacheWidth;
90 mSmallCacheHeight = mSmallCacheHeight > maxTextureSize ? maxTextureSize : mSmallCacheHeight;
91 mLargeCacheWidth = mLargeCacheWidth > maxTextureSize ? maxTextureSize : mLargeCacheWidth;
92 mLargeCacheHeight = mLargeCacheHeight > maxTextureSize ? maxTextureSize : mLargeCacheHeight;
93
Chet Haaseeb32a492012-08-31 13:54:03 -070094 if (sLogFontRendererCreate) {
95 INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
96 mSmallCacheWidth, mSmallCacheHeight,
97 mLargeCacheWidth, mLargeCacheHeight >> 1,
98 mLargeCacheWidth, mLargeCacheHeight >> 1,
99 mLargeCacheWidth, mLargeCacheHeight);
Romain Guy51769a62010-07-23 00:28:00 -0700100 }
Romain Guy514fb182011-01-19 14:38:29 -0800101
102 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -0700103}
104
105FontRenderer::~FontRenderer() {
Chet Haase378e9192012-08-15 15:54:54 -0700106 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
107 delete mCacheTextures[i];
Romain Guy694b5192010-07-21 21:33:20 -0700108 }
Chet Haase378e9192012-08-15 15:54:54 -0700109 mCacheTextures.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700110
Romain Guy9cccc2b92010-08-07 23:46:15 -0700111 if (mInitialized) {
Romain Guya9dd8202012-03-26 14:52:00 -0700112 // Unbinding the buffer shouldn't be necessary but it crashes with some drivers
113 Caches::getInstance().unbindIndicesBuffer();
Romain Guyb0317982012-03-21 11:52:52 -0700114 glDeleteBuffers(1, &mIndexBufferID);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700115 }
Romain Guy694b5192010-07-21 21:33:20 -0700116
Romain Guye3a9b242013-01-08 11:15:30 -0800117 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
118 while (it.next()) {
119 delete it.value();
Romain Guy694b5192010-07-21 21:33:20 -0700120 }
Romain Guye3a9b242013-01-08 11:15:30 -0800121 mActiveFonts.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700122}
123
124void FontRenderer::flushAllAndInvalidate() {
Romain Guy661a87e2013-03-19 15:24:36 -0700125 issueDrawCommand();
Romain Guy9d9758a2012-05-14 15:19:58 -0700126
Romain Guye3a9b242013-01-08 11:15:30 -0800127 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
128 while (it.next()) {
129 it.value()->invalidateTextureCache();
Romain Guy694b5192010-07-21 21:33:20 -0700130 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700131
Chet Haase378e9192012-08-15 15:54:54 -0700132 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
133 mCacheTextures[i]->init();
Romain Guy694b5192010-07-21 21:33:20 -0700134 }
Chet Haasee816bae2012-08-09 13:39:02 -0700135
Romain Guy80872462012-09-04 16:42:01 -0700136#if DEBUG_FONT_RENDERER
Chet Haase378e9192012-08-15 15:54:54 -0700137 uint16_t totalGlyphs = 0;
138 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
Romain Guy80872462012-09-04 16:42:01 -0700139 totalGlyphs += mCacheTextures[i]->getGlyphCount();
Chet Haase378e9192012-08-15 15:54:54 -0700140 // Erase caches, just as a debugging facility
Romain Guy80872462012-09-04 16:42:01 -0700141 if (mCacheTextures[i]->getTexture()) {
142 memset(mCacheTextures[i]->getTexture(), 0,
143 mCacheTextures[i]->getWidth() * mCacheTextures[i]->getHeight());
Chet Haase378e9192012-08-15 15:54:54 -0700144 }
Chet Haasee816bae2012-08-09 13:39:02 -0700145 }
146 ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
147#endif
Romain Guy694b5192010-07-21 21:33:20 -0700148}
149
Chet Haase9a824562011-12-16 15:44:59 -0800150void FontRenderer::flushLargeCaches() {
Chet Haase378e9192012-08-15 15:54:54 -0700151 // Start from 1; don't deallocate smallest/default texture
152 for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
153 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guy80872462012-09-04 16:42:01 -0700154 if (cacheTexture->getTexture()) {
Chet Haase378e9192012-08-15 15:54:54 -0700155 cacheTexture->init();
Romain Guye3a9b242013-01-08 11:15:30 -0800156 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
157 while (it.next()) {
158 it.value()->invalidateTextureCache(cacheTexture);
Chet Haasee816bae2012-08-09 13:39:02 -0700159 }
Romain Guy80872462012-09-04 16:42:01 -0700160 cacheTexture->releaseTexture();
Chet Haase9a824562011-12-16 15:44:59 -0800161 }
162 }
Chet Haase9a824562011-12-16 15:44:59 -0800163}
164
Chet Haase378e9192012-08-15 15:54:54 -0700165CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
166 uint32_t* startX, uint32_t* startY) {
167 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
168 if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
169 return mCacheTextures[i];
170 }
171 }
172 // Could not fit glyph into current cache textures
173 return NULL;
174}
175
Chet Haase7de0cb12011-12-05 16:35:38 -0800176void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
Chet Haasef942cf12012-08-30 09:06:46 -0700177 uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700178 checkInit();
Romain Guya4adcf02013-02-28 12:15:35 -0800179
180 // If the glyph bitmap is empty let's assum the glyph is valid
181 // so we can avoid doing extra work later on
182 if (glyph.fWidth == 0 || glyph.fHeight == 0) {
183 cachedGlyph->mIsValid = true;
184 cachedGlyph->mCacheTexture = NULL;
185 return;
186 }
187
Chet Haase7de0cb12011-12-05 16:35:38 -0800188 cachedGlyph->mIsValid = false;
Romain Guya4adcf02013-02-28 12:15:35 -0800189
Romain Guy694b5192010-07-21 21:33:20 -0700190 // If the glyph is too tall, don't cache it
Chet Haase378e9192012-08-15 15:54:54 -0700191 if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
Romain Guy80872462012-09-04 16:42:01 -0700192 mCacheTextures[mCacheTextures.size() - 1]->getHeight()) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700193 ALOGE("Font size too large to fit in cache. width, height = %i, %i",
194 (int) glyph.fWidth, (int) glyph.fHeight);
Chet Haase7de0cb12011-12-05 16:35:38 -0800195 return;
Romain Guy694b5192010-07-21 21:33:20 -0700196 }
197
198 // Now copy the bitmap into the cache texture
199 uint32_t startX = 0;
200 uint32_t startY = 0;
201
Chet Haase378e9192012-08-15 15:54:54 -0700202 CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700203
Chet Haase378e9192012-08-15 15:54:54 -0700204 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700205 if (!precaching) {
206 // If the new glyph didn't fit and we are not just trying to precache it,
207 // clear out the cache and try again
208 flushAllAndInvalidate();
209 cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
210 }
Romain Guy694b5192010-07-21 21:33:20 -0700211
Chet Haase378e9192012-08-15 15:54:54 -0700212 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700213 // either the glyph didn't fit or we're precaching and will cache it when we draw
Chet Haase7de0cb12011-12-05 16:35:38 -0800214 return;
Romain Guy694b5192010-07-21 21:33:20 -0700215 }
216 }
217
Chet Haase378e9192012-08-15 15:54:54 -0700218 cachedGlyph->mCacheTexture = cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800219
Romain Guy694b5192010-07-21 21:33:20 -0700220 *retOriginX = startX;
221 *retOriginY = startY;
222
223 uint32_t endX = startX + glyph.fWidth;
224 uint32_t endY = startY + glyph.fHeight;
225
Romain Guy80872462012-09-04 16:42:01 -0700226 uint32_t cacheWidth = cacheTexture->getWidth();
Romain Guy694b5192010-07-21 21:33:20 -0700227
Romain Guy80872462012-09-04 16:42:01 -0700228 if (!cacheTexture->getTexture()) {
229 Caches::getInstance().activeTexture(0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800230 // Large-glyph texture memory is allocated only as needed
Romain Guy80872462012-09-04 16:42:01 -0700231 cacheTexture->allocateTexture();
Chet Haase7de0cb12011-12-05 16:35:38 -0800232 }
Romain Guy661a87e2013-03-19 15:24:36 -0700233 if (!cacheTexture->mesh()) {
234 cacheTexture->allocateMesh();
235 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700236
Romain Guyb969a0d2013-02-05 14:38:40 -0800237 // Tells us whether the glyphs is B&W (1 bit per pixel)
238 // or anti-aliased (8 bits per pixel)
239 SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
Romain Guy694b5192010-07-21 21:33:20 -0700240
Romain Guyb969a0d2013-02-05 14:38:40 -0800241 uint8_t* cacheBuffer = cacheTexture->getTexture();
Romain Guy694b5192010-07-21 21:33:20 -0700242 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
Romain Guy33fa1f72012-08-07 19:09:57 -0700243
Romain Guyb969a0d2013-02-05 14:38:40 -0800244 // Copy the glyph image, taking the mask format into account
245 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
246 int stride = glyph.rowBytes();
247
Romain Guy0b58a3d2013-03-05 12:16:27 -0800248 uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
249 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
250
Romain Guyb969a0d2013-02-05 14:38:40 -0800251 switch (format) {
252 case SkMask::kA8_Format: {
253 if (mGammaTable) {
254 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
Romain Guy0b58a3d2013-03-05 12:16:27 -0800255 row = cacheY * cacheWidth;
256 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800257 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
258 uint8_t tempCol = bitmapBuffer[bY + bX];
Romain Guy0b58a3d2013-03-05 12:16:27 -0800259 cacheBuffer[row + cacheX] = mGammaTable[tempCol];
Romain Guyb969a0d2013-02-05 14:38:40 -0800260 }
Romain Guy0b58a3d2013-03-05 12:16:27 -0800261 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800262 }
263 } else {
264 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
Romain Guy0b58a3d2013-03-05 12:16:27 -0800265 row = cacheY * cacheWidth;
266 memcpy(&cacheBuffer[row + startX], &bitmapBuffer[bY], glyph.fWidth);
267 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
268 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800269 }
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700270 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800271 break;
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700272 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800273 case SkMask::kBW_Format: {
274 static const uint8_t COLORS[2] = { 0, 255 };
275
276 for (cacheY = startY; cacheY < endY; cacheY++) {
277 cacheX = startX;
278 int rowBytes = stride;
279 uint8_t* buffer = bitmapBuffer;
280
Romain Guy0b58a3d2013-03-05 12:16:27 -0800281 row = cacheY * cacheWidth;
282 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800283 while (--rowBytes >= 0) {
284 uint8_t b = *buffer++;
285 for (int8_t mask = 7; mask >= 0 && cacheX < endX; mask--) {
286 cacheBuffer[cacheY * cacheWidth + cacheX++] = COLORS[(b >> mask) & 0x1];
287 }
288 }
Romain Guy0b58a3d2013-03-05 12:16:27 -0800289 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800290
291 bitmapBuffer += stride;
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700292 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800293 break;
Romain Guy694b5192010-07-21 21:33:20 -0700294 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800295 default:
296 ALOGW("Unkown glyph format: 0x%x", format);
297 break;
Romain Guy694b5192010-07-21 21:33:20 -0700298 }
Romain Guy97771732012-02-28 18:17:02 -0800299
Romain Guy0b58a3d2013-03-05 12:16:27 -0800300 row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
301 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
302
Chet Haase7de0cb12011-12-05 16:35:38 -0800303 cachedGlyph->mIsValid = true;
Romain Guy694b5192010-07-21 21:33:20 -0700304}
305
Chet Haase7de0cb12011-12-05 16:35:38 -0800306CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
Romain Guy661a87e2013-03-19 15:24:36 -0700307 CacheTexture* cacheTexture = new CacheTexture(width, height, mMaxNumberOfQuads);
Romain Guy9d9758a2012-05-14 15:19:58 -0700308
Chet Haase2a47c142011-12-14 15:22:56 -0800309 if (allocate) {
Romain Guy80872462012-09-04 16:42:01 -0700310 Caches::getInstance().activeTexture(0);
311 cacheTexture->allocateTexture();
Romain Guy661a87e2013-03-19 15:24:36 -0700312 cacheTexture->allocateMesh();
Chet Haase2a47c142011-12-14 15:22:56 -0800313 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700314
Chet Haase2a47c142011-12-14 15:22:56 -0800315 return cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800316}
317
318void FontRenderer::initTextTexture() {
Chet Haase378e9192012-08-15 15:54:54 -0700319 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
320 delete mCacheTextures[i];
Romain Guy9d9758a2012-05-14 15:19:58 -0700321 }
Chet Haase378e9192012-08-15 15:54:54 -0700322 mCacheTextures.clear();
Romain Guy9d9758a2012-05-14 15:19:58 -0700323
Chet Haase7de0cb12011-12-05 16:35:38 -0800324 mUploadTexture = false;
Chet Haase378e9192012-08-15 15:54:54 -0700325 mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
Chet Haaseeb32a492012-08-31 13:54:03 -0700326 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
327 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
328 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false));
Chet Haase378e9192012-08-15 15:54:54 -0700329 mCurrentCacheTexture = mCacheTextures[0];
Romain Guy694b5192010-07-21 21:33:20 -0700330}
331
332// Avoid having to reallocate memory and render quad by quad
333void FontRenderer::initVertexArrayBuffers() {
Romain Guyd71dd362011-12-12 19:03:35 -0800334 uint32_t numIndices = mMaxNumberOfQuads * 6;
335 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700336 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700337
338 // Four verts, two triangles , six indices per quad
339 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
340 int i6 = i * 6;
341 int i4 = i * 4;
342
343 indexBufferData[i6 + 0] = i4 + 0;
344 indexBufferData[i6 + 1] = i4 + 1;
345 indexBufferData[i6 + 2] = i4 + 2;
346
347 indexBufferData[i6 + 3] = i4 + 0;
348 indexBufferData[i6 + 4] = i4 + 2;
349 indexBufferData[i6 + 5] = i4 + 3;
350 }
351
352 glGenBuffers(1, &mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800353 Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700354 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
Romain Guy694b5192010-07-21 21:33:20 -0700355
356 free(indexBufferData);
Romain Guy694b5192010-07-21 21:33:20 -0700357}
358
359// We don't want to allocate anything unless we actually draw text
360void FontRenderer::checkInit() {
361 if (mInitialized) {
362 return;
363 }
364
365 initTextTexture();
366 initVertexArrayBuffers();
367
368 mInitialized = true;
369}
370
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700371void FontRenderer::checkTextureUpdate() {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900372 if (!mUploadTexture) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700373 return;
Romain Guy694b5192010-07-21 21:33:20 -0700374 }
375
Romain Guy2d4fd362011-12-13 22:00:19 -0800376 Caches& caches = Caches::getInstance();
377 GLuint lastTextureId = 0;
Chet Haase378e9192012-08-15 15:54:54 -0700378 // Iterate over all the cache textures and see which ones need to be updated
379 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
380 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guy80872462012-09-04 16:42:01 -0700381 if (cacheTexture->isDirty() && cacheTexture->getTexture()) {
Chet Haaseb92d8f72012-09-21 08:40:46 -0700382 // Can't copy inner rect; glTexSubimage expects pointer to deal with entire buffer
383 // of data. So expand the dirty rect to the encompassing horizontal stripe.
384 const Rect* dirtyRect = cacheTexture->getDirtyRect();
385 uint32_t x = 0;
386 uint32_t y = dirtyRect->top;
Romain Guy80872462012-09-04 16:42:01 -0700387 uint32_t width = cacheTexture->getWidth();
Chet Haaseb92d8f72012-09-21 08:40:46 -0700388 uint32_t height = dirtyRect->getHeight();
389 void* textureData = cacheTexture->getTexture() + y * width;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700390
Romain Guy80872462012-09-04 16:42:01 -0700391 if (cacheTexture->getTextureId() != lastTextureId) {
392 lastTextureId = cacheTexture->getTextureId();
Romain Guy2d4fd362011-12-13 22:00:19 -0800393 caches.activeTexture(0);
Romain Guy80872462012-09-04 16:42:01 -0700394 glBindTexture(GL_TEXTURE_2D, lastTextureId);
Romain Guy2d4fd362011-12-13 22:00:19 -0800395 }
Chet Haasee816bae2012-08-09 13:39:02 -0700396#if DEBUG_FONT_RENDERER
Chet Haaseb92d8f72012-09-21 08:40:46 -0700397 ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d",
398 i, x, y, width, height);
Chet Haasee816bae2012-08-09 13:39:02 -0700399#endif
Chet Haaseb92d8f72012-09-21 08:40:46 -0700400 glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700401 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Romain Guy80872462012-09-04 16:42:01 -0700402 cacheTexture->setDirty(false);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700403 }
404 }
405
406 mUploadTexture = false;
407}
408
409void FontRenderer::issueDrawCommand() {
Romain Guy661a87e2013-03-19 15:24:36 -0700410 bool first = true;
411 bool force = false;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700412
Romain Guy115096f2013-03-19 11:32:41 -0700413 GLuint lastId = 0;
Romain Guy661a87e2013-03-19 15:24:36 -0700414 Caches& caches = Caches::getInstance();
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900415
Romain Guy661a87e2013-03-19 15:24:36 -0700416 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
417 CacheTexture* texture = mCacheTextures[i];
418 if (texture->canDraw()) {
419 if (first) {
Romain Guy257ae352013-03-20 16:31:12 -0700420 if (mFunctor) (*mFunctor)(0, NULL);
421
Romain Guy661a87e2013-03-19 15:24:36 -0700422 checkTextureUpdate();
423 caches.bindIndicesBuffer(mIndexBufferID);
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900424
Romain Guy661a87e2013-03-19 15:24:36 -0700425 if (!mDrawn) {
426 // If returns true, a VBO was bound and we must
427 // rebind our vertex attrib pointers even if
428 // they have the same values as the current pointers
429 force = caches.unbindMeshBuffer();
430 }
431
432 caches.activeTexture(0);
433 first = false;
434 }
435
436 glBindTexture(GL_TEXTURE_2D, texture->getTextureId());
437 texture->setLinearFiltering(mLinearFiltering, false);
438
439 TextureVertex* mesh = texture->mesh();
440 caches.bindPositionVertexPointer(force, &mesh[0].position[0]);
441 caches.bindTexCoordsVertexPointer(force, &mesh[0].texture[0]);
442 force = false;
443
444 glDrawElements(GL_TRIANGLES, texture->meshElementCount(),
445 GL_UNSIGNED_SHORT, texture->indices());
446
447 texture->resetMesh();
Romain Guy115096f2013-03-19 11:32:41 -0700448 }
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900449 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700450
451 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700452}
453
Romain Guy97771732012-02-28 18:17:02 -0800454void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
455 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800456 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800457 if (texture != mCurrentCacheTexture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800458 // Now use the new texture id
459 mCurrentCacheTexture = texture;
460 }
Romain Guy09147fb2010-07-22 13:08:20 -0700461
Romain Guy661a87e2013-03-19 15:24:36 -0700462 mCurrentCacheTexture->addQuad(x1, y1, u1, v1, x2, y2, u2, v2,
463 x3, y3, u3, v3, x4, y4, u4, v4);
Romain Guy97771732012-02-28 18:17:02 -0800464}
465
466void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
467 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
468 float x4, float y4, float u4, float v4, CacheTexture* texture) {
469
470 if (mClip &&
471 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
472 return;
473 }
474
475 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 -0700476
Romain Guy5b3b3522010-10-27 18:57:51 -0700477 if (mBounds) {
478 mBounds->left = fmin(mBounds->left, x1);
479 mBounds->top = fmin(mBounds->top, y3);
480 mBounds->right = fmax(mBounds->right, x3);
481 mBounds->bottom = fmax(mBounds->bottom, y1);
482 }
483
Romain Guy661a87e2013-03-19 15:24:36 -0700484 if (mCurrentCacheTexture->endOfMesh()) {
Romain Guy694b5192010-07-21 21:33:20 -0700485 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700486 }
487}
488
Romain Guy97771732012-02-28 18:17:02 -0800489void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
490 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
491 float x4, float y4, float u4, float v4, CacheTexture* texture) {
492
493 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
494
495 if (mBounds) {
496 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
497 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
498 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
499 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
500 }
501
Romain Guy661a87e2013-03-19 15:24:36 -0700502 if (mCurrentCacheTexture->endOfMesh()) {
Romain Guy97771732012-02-28 18:17:02 -0800503 issueDrawCommand();
Romain Guy97771732012-02-28 18:17:02 -0800504 }
505}
506
Romain Guye3a9b242013-01-08 11:15:30 -0800507void FontRenderer::setFont(SkPaint* paint, const mat4& matrix) {
508 mCurrentFont = Font::create(this, paint, matrix);
Romain Guy694b5192010-07-21 21:33:20 -0700509}
Romain Guy7975fb62010-10-01 16:36:14 -0700510
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700511FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Raph Levien416a8472012-07-19 22:48:17 -0700512 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700513 checkInit();
514
515 if (!mCurrentFont) {
516 DropShadow image;
517 image.width = 0;
518 image.height = 0;
519 image.image = NULL;
520 image.penX = 0;
521 image.penY = 0;
522 return image;
523 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700524
Romain Guy2d4fd362011-12-13 22:00:19 -0800525 mDrawn = false;
Romain Guyff98fa52011-11-28 09:35:09 -0800526 mClip = NULL;
527 mBounds = NULL;
528
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700529 Rect bounds;
Raph Levien416a8472012-07-19 22:48:17 -0700530 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
Romain Guyff98fa52011-11-28 09:35:09 -0800531
Romain Guy1e45aae2010-08-13 19:39:53 -0700532 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
533 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Romain Guyff98fa52011-11-28 09:35:09 -0800534
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800535 // Align buffers for renderscript usage
536 if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) {
537 paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700538 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700539
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800540 int size = paddedWidth * paddedHeight;
Romain Guy6e200402013-03-08 11:28:22 -0800541 uint8_t* dataBuffer = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, size);
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800542 memset(dataBuffer, 0, size);
543
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700544 int penX = radius - bounds.left;
545 int penY = radius - bounds.bottom;
546
Chris Craikdd8697c2013-02-22 10:41:36 -0800547 if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) {
548 // text has non-whitespace, so draw and blur to create the shadow
549 // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted
550 // TODO: don't draw pure whitespace in the first place, and avoid needing this check
551 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
552 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
553
554 blurImage(&dataBuffer, paddedWidth, paddedHeight, radius);
555 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700556
557 DropShadow image;
558 image.width = paddedWidth;
559 image.height = paddedHeight;
560 image.image = dataBuffer;
561 image.penX = penX;
562 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -0800563
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700564 return image;
565}
Romain Guy694b5192010-07-21 21:33:20 -0700566
Romain Guy257ae352013-03-20 16:31:12 -0700567void FontRenderer::initRender(const Rect* clip, Rect* bounds, Functor* functor) {
Romain Guy694b5192010-07-21 21:33:20 -0700568 checkInit();
569
Romain Guy5b3b3522010-10-27 18:57:51 -0700570 mDrawn = false;
571 mBounds = bounds;
Romain Guy257ae352013-03-20 16:31:12 -0700572 mFunctor = functor;
Romain Guy09147fb2010-07-22 13:08:20 -0700573 mClip = clip;
Romain Guy671d6cf2012-01-18 12:39:17 -0800574}
Romain Guyff98fa52011-11-28 09:35:09 -0800575
Romain Guy671d6cf2012-01-18 12:39:17 -0800576void FontRenderer::finishRender() {
Romain Guy5b3b3522010-10-27 18:57:51 -0700577 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -0800578 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700579
Romain Guy661a87e2013-03-19 15:24:36 -0700580 issueDrawCommand();
Romain Guy671d6cf2012-01-18 12:39:17 -0800581}
582
Romain Guye3a9b242013-01-08 11:15:30 -0800583void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix) {
584 Font* font = Font::create(this, paint, matrix);
Chet Haasee816bae2012-08-09 13:39:02 -0700585 font->precache(paint, text, numGlyphs);
586}
587
Romain Guy671d6cf2012-01-18 12:39:17 -0800588bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
589 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
Romain Guy257ae352013-03-20 16:31:12 -0700590 const float* positions, Rect* bounds, Functor* functor) {
Romain Guy671d6cf2012-01-18 12:39:17 -0800591 if (!mCurrentFont) {
592 ALOGE("No font set");
593 return false;
594 }
595
Romain Guy257ae352013-03-20 16:31:12 -0700596 initRender(clip, bounds, functor);
Romain Guy671d6cf2012-01-18 12:39:17 -0800597 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
598 finishRender();
Romain Guy5b3b3522010-10-27 18:57:51 -0700599
600 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700601}
602
Romain Guy97771732012-02-28 18:17:02 -0800603bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
604 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
605 float hOffset, float vOffset, Rect* bounds) {
606 if (!mCurrentFont) {
607 ALOGE("No font set");
608 return false;
609 }
610
Romain Guy257ae352013-03-20 16:31:12 -0700611 initRender(clip, bounds, NULL);
Romain Guy97771732012-02-28 18:17:02 -0800612 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
613 finishRender();
614
615 return mDrawn;
616}
617
Romain Guy9b1204b2012-09-04 15:22:57 -0700618void FontRenderer::removeFont(const Font* font) {
Romain Guye3a9b242013-01-08 11:15:30 -0800619 mActiveFonts.remove(font->getDescription());
Romain Guy9b1204b2012-09-04 15:22:57 -0700620
621 if (mCurrentFont == font) {
622 mCurrentFont = NULL;
623 }
624}
625
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800626void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) {
627 if (width * height * radius < RS_MIN_INPUT_CUTOFF) {
628 float *gaussian = new float[2 * radius + 1];
Romain Guy6e200402013-03-08 11:28:22 -0800629 Blur::generateGaussianWeights(gaussian, radius);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700630
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800631 uint8_t* scratch = new uint8_t[width * height];
Romain Guy6e200402013-03-08 11:28:22 -0800632 Blur::horizontal(gaussian, radius, *image, scratch, width, height);
633 Blur::vertical(gaussian, radius, scratch, *image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -0800634
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800635 delete[] gaussian;
636 delete[] scratch;
Chris Craikdd8697c2013-02-22 10:41:36 -0800637 return;
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800638 }
Romain Guyd71dd362011-12-12 19:03:35 -0800639
Romain Guy6e200402013-03-08 11:28:22 -0800640 uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800641
642 if (mRs.get() == 0) {
643 mRs = new RSC::RS();
644 if (!mRs->init(true, true)) {
645 ALOGE("blur RS failed to init");
646 }
647
648 mRsElement = RSC::Element::A_8(mRs);
649 mRsScript = new RSC::ScriptIntrinsicBlur(mRs, mRsElement);
650 }
651
652 sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0);
653 sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
654 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, *image);
655 sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
656 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, outImage);
657
658 mRsScript->setRadius(radius);
659 mRsScript->blur(ain, aout);
660
661 // replace the original image's pointer, avoiding a copy back to the original buffer
Ben Cheng15641a612013-02-20 13:20:03 -0800662 free(*image);
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800663 *image = outImage;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700664}
665
Romain Guy694b5192010-07-21 21:33:20 -0700666}; // namespace uirenderer
667}; // namespace android