blob: 0c70e272d0a53095872996284fc1e017aca5fc59 [file] [log] [blame]
Romain Guy694b5192010-07-21 21:33:20 -07001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "OpenGLRenderer"
18
Derek Sollenbergerca79cf62012-08-14 16:44:52 -040019#include <SkGlyph.h>
Romain Guy694b5192010-07-21 21:33:20 -070020#include <SkUtils.h>
21
Romain Guy51769a62010-07-23 00:28:00 -070022#include <cutils/properties.h>
Romain Guye2d345e2010-09-24 18:39:22 -070023
Romain Guy51769a62010-07-23 00:28:00 -070024#include <utils/Log.h>
25
Romain Guy6e200402013-03-08 11:28:22 -080026#include <RenderScript.h>
Chris Craikf2d8ccc2013-02-13 16:14:17 -080027
Romain Guy6e200402013-03-08 11:28:22 -080028#include "utils/Blur.h"
Chris Craikf2d8ccc2013-02-13 16:14:17 -080029#include "utils/Timing.h"
Romain Guy6e200402013-03-08 11:28:22 -080030
Romain Guy15bc6432011-12-13 13:11:32 -080031#include "Caches.h"
Romain Guyc9855a52011-01-21 21:14:15 -080032#include "Debug.h"
Romain Guy51769a62010-07-23 00:28:00 -070033#include "FontRenderer.h"
Romain Guy9f5dab32012-09-04 12:55:44 -070034#include "Rect.h"
Romain Guy51769a62010-07-23 00:28:00 -070035
Romain Guy694b5192010-07-21 21:33:20 -070036namespace android {
37namespace uirenderer {
38
Chris Craikf2d8ccc2013-02-13 16:14:17 -080039// blur inputs smaller than this constant will bypass renderscript
40#define RS_MIN_INPUT_CUTOFF 10000
41
Romain Guy694b5192010-07-21 21:33:20 -070042///////////////////////////////////////////////////////////////////////////////
Romain Guy694b5192010-07-21 21:33:20 -070043// FontRenderer
44///////////////////////////////////////////////////////////////////////////////
45
Romain Guy514fb182011-01-19 14:38:29 -080046static bool sLogFontRendererCreate = true;
47
Romain Guye3a9b242013-01-08 11:15:30 -080048FontRenderer::FontRenderer() :
49 mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity) {
50
Romain Guyc9855a52011-01-21 21:14:15 -080051 if (sLogFontRendererCreate) {
52 INIT_LOGD("Creating FontRenderer");
53 }
Romain Guy51769a62010-07-23 00:28:00 -070054
Romain Guyb45c0c92010-08-26 20:35:23 -070055 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -070056 mInitialized = false;
57 mMaxNumberOfQuads = 1024;
58 mCurrentQuadIndex = 0;
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +090059 mLastQuadIndex = 0;
Romain Guy694b5192010-07-21 21:33:20 -070060
Romain Guy9b1204b2012-09-04 15:22:57 -070061 mTextMesh = NULL;
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);
117
Romain Guy9b1204b2012-09-04 15:22:57 -0700118 delete[] mTextMesh;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700119 }
Romain Guy694b5192010-07-21 21:33:20 -0700120
Romain Guye3a9b242013-01-08 11:15:30 -0800121 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
122 while (it.next()) {
123 delete it.value();
Romain Guy694b5192010-07-21 21:33:20 -0700124 }
Romain Guye3a9b242013-01-08 11:15:30 -0800125 mActiveFonts.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700126}
127
128void FontRenderer::flushAllAndInvalidate() {
129 if (mCurrentQuadIndex != 0) {
130 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700131 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700132
Romain Guye3a9b242013-01-08 11:15:30 -0800133 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
134 while (it.next()) {
135 it.value()->invalidateTextureCache();
Romain Guy694b5192010-07-21 21:33:20 -0700136 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700137
Chet Haase378e9192012-08-15 15:54:54 -0700138 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
139 mCacheTextures[i]->init();
Romain Guy694b5192010-07-21 21:33:20 -0700140 }
Chet Haasee816bae2012-08-09 13:39:02 -0700141
Romain Guy80872462012-09-04 16:42:01 -0700142#if DEBUG_FONT_RENDERER
Chet Haase378e9192012-08-15 15:54:54 -0700143 uint16_t totalGlyphs = 0;
144 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
Romain Guy80872462012-09-04 16:42:01 -0700145 totalGlyphs += mCacheTextures[i]->getGlyphCount();
Chet Haase378e9192012-08-15 15:54:54 -0700146 // Erase caches, just as a debugging facility
Romain Guy80872462012-09-04 16:42:01 -0700147 if (mCacheTextures[i]->getTexture()) {
148 memset(mCacheTextures[i]->getTexture(), 0,
149 mCacheTextures[i]->getWidth() * mCacheTextures[i]->getHeight());
Chet Haase378e9192012-08-15 15:54:54 -0700150 }
Chet Haasee816bae2012-08-09 13:39:02 -0700151 }
152 ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
153#endif
Romain Guy694b5192010-07-21 21:33:20 -0700154}
155
Chet Haase9a824562011-12-16 15:44:59 -0800156void FontRenderer::flushLargeCaches() {
Chet Haase378e9192012-08-15 15:54:54 -0700157 // Start from 1; don't deallocate smallest/default texture
158 for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
159 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guy80872462012-09-04 16:42:01 -0700160 if (cacheTexture->getTexture()) {
Chet Haase378e9192012-08-15 15:54:54 -0700161 cacheTexture->init();
Romain Guye3a9b242013-01-08 11:15:30 -0800162 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
163 while (it.next()) {
164 it.value()->invalidateTextureCache(cacheTexture);
Chet Haasee816bae2012-08-09 13:39:02 -0700165 }
Romain Guy80872462012-09-04 16:42:01 -0700166 cacheTexture->releaseTexture();
Chet Haase9a824562011-12-16 15:44:59 -0800167 }
168 }
Chet Haase9a824562011-12-16 15:44:59 -0800169}
170
Chet Haase378e9192012-08-15 15:54:54 -0700171CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
172 uint32_t* startX, uint32_t* startY) {
173 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
174 if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
175 return mCacheTextures[i];
176 }
177 }
178 // Could not fit glyph into current cache textures
179 return NULL;
180}
181
Chet Haase7de0cb12011-12-05 16:35:38 -0800182void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
Chet Haasef942cf12012-08-30 09:06:46 -0700183 uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700184 checkInit();
Romain Guya4adcf02013-02-28 12:15:35 -0800185
186 // If the glyph bitmap is empty let's assum the glyph is valid
187 // so we can avoid doing extra work later on
188 if (glyph.fWidth == 0 || glyph.fHeight == 0) {
189 cachedGlyph->mIsValid = true;
190 cachedGlyph->mCacheTexture = NULL;
191 return;
192 }
193
Chet Haase7de0cb12011-12-05 16:35:38 -0800194 cachedGlyph->mIsValid = false;
Romain Guya4adcf02013-02-28 12:15:35 -0800195
Romain Guy694b5192010-07-21 21:33:20 -0700196 // If the glyph is too tall, don't cache it
Chet Haase378e9192012-08-15 15:54:54 -0700197 if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
Romain Guy80872462012-09-04 16:42:01 -0700198 mCacheTextures[mCacheTextures.size() - 1]->getHeight()) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700199 ALOGE("Font size too large to fit in cache. width, height = %i, %i",
200 (int) glyph.fWidth, (int) glyph.fHeight);
Chet Haase7de0cb12011-12-05 16:35:38 -0800201 return;
Romain Guy694b5192010-07-21 21:33:20 -0700202 }
203
204 // Now copy the bitmap into the cache texture
205 uint32_t startX = 0;
206 uint32_t startY = 0;
207
Chet Haase378e9192012-08-15 15:54:54 -0700208 CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700209
Chet Haase378e9192012-08-15 15:54:54 -0700210 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700211 if (!precaching) {
212 // If the new glyph didn't fit and we are not just trying to precache it,
213 // clear out the cache and try again
214 flushAllAndInvalidate();
215 cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
216 }
Romain Guy694b5192010-07-21 21:33:20 -0700217
Chet Haase378e9192012-08-15 15:54:54 -0700218 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700219 // either the glyph didn't fit or we're precaching and will cache it when we draw
Chet Haase7de0cb12011-12-05 16:35:38 -0800220 return;
Romain Guy694b5192010-07-21 21:33:20 -0700221 }
222 }
223
Chet Haase378e9192012-08-15 15:54:54 -0700224 cachedGlyph->mCacheTexture = cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800225
Romain Guy694b5192010-07-21 21:33:20 -0700226 *retOriginX = startX;
227 *retOriginY = startY;
228
229 uint32_t endX = startX + glyph.fWidth;
230 uint32_t endY = startY + glyph.fHeight;
231
Romain Guy80872462012-09-04 16:42:01 -0700232 uint32_t cacheWidth = cacheTexture->getWidth();
Romain Guy694b5192010-07-21 21:33:20 -0700233
Romain Guy80872462012-09-04 16:42:01 -0700234 if (!cacheTexture->getTexture()) {
235 Caches::getInstance().activeTexture(0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800236 // Large-glyph texture memory is allocated only as needed
Romain Guy80872462012-09-04 16:42:01 -0700237 cacheTexture->allocateTexture();
Chet Haase7de0cb12011-12-05 16:35:38 -0800238 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700239
Romain Guyb969a0d2013-02-05 14:38:40 -0800240 // Tells us whether the glyphs is B&W (1 bit per pixel)
241 // or anti-aliased (8 bits per pixel)
242 SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
Romain Guy694b5192010-07-21 21:33:20 -0700243
Romain Guyb969a0d2013-02-05 14:38:40 -0800244 uint8_t* cacheBuffer = cacheTexture->getTexture();
Romain Guy694b5192010-07-21 21:33:20 -0700245 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
Romain Guy33fa1f72012-08-07 19:09:57 -0700246
Romain Guyb969a0d2013-02-05 14:38:40 -0800247 // Copy the glyph image, taking the mask format into account
248 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
249 int stride = glyph.rowBytes();
250
Romain Guy0b58a3d2013-03-05 12:16:27 -0800251 uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
252 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
253
Romain Guyb969a0d2013-02-05 14:38:40 -0800254 switch (format) {
255 case SkMask::kA8_Format: {
256 if (mGammaTable) {
257 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
Romain Guy0b58a3d2013-03-05 12:16:27 -0800258 row = cacheY * cacheWidth;
259 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800260 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
261 uint8_t tempCol = bitmapBuffer[bY + bX];
Romain Guy0b58a3d2013-03-05 12:16:27 -0800262 cacheBuffer[row + cacheX] = mGammaTable[tempCol];
Romain Guyb969a0d2013-02-05 14:38:40 -0800263 }
Romain Guy0b58a3d2013-03-05 12:16:27 -0800264 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800265 }
266 } else {
267 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
Romain Guy0b58a3d2013-03-05 12:16:27 -0800268 row = cacheY * cacheWidth;
269 memcpy(&cacheBuffer[row + startX], &bitmapBuffer[bY], glyph.fWidth);
270 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
271 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800272 }
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700273 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800274 break;
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700275 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800276 case SkMask::kBW_Format: {
277 static const uint8_t COLORS[2] = { 0, 255 };
278
279 for (cacheY = startY; cacheY < endY; cacheY++) {
280 cacheX = startX;
281 int rowBytes = stride;
282 uint8_t* buffer = bitmapBuffer;
283
Romain Guy0b58a3d2013-03-05 12:16:27 -0800284 row = cacheY * cacheWidth;
285 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800286 while (--rowBytes >= 0) {
287 uint8_t b = *buffer++;
288 for (int8_t mask = 7; mask >= 0 && cacheX < endX; mask--) {
289 cacheBuffer[cacheY * cacheWidth + cacheX++] = COLORS[(b >> mask) & 0x1];
290 }
291 }
Romain Guy0b58a3d2013-03-05 12:16:27 -0800292 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800293
294 bitmapBuffer += stride;
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700295 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800296 break;
Romain Guy694b5192010-07-21 21:33:20 -0700297 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800298 default:
299 ALOGW("Unkown glyph format: 0x%x", format);
300 break;
Romain Guy694b5192010-07-21 21:33:20 -0700301 }
Romain Guy97771732012-02-28 18:17:02 -0800302
Romain Guy0b58a3d2013-03-05 12:16:27 -0800303 row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
304 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
305
Chet Haase7de0cb12011-12-05 16:35:38 -0800306 cachedGlyph->mIsValid = true;
Romain Guy694b5192010-07-21 21:33:20 -0700307}
308
Chet Haase7de0cb12011-12-05 16:35:38 -0800309CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
Romain Guy0aa87bb2012-07-20 11:14:32 -0700310 CacheTexture* cacheTexture = new CacheTexture(width, height);
Romain Guy9d9758a2012-05-14 15:19:58 -0700311
Chet Haase2a47c142011-12-14 15:22:56 -0800312 if (allocate) {
Romain Guy80872462012-09-04 16:42:01 -0700313 Caches::getInstance().activeTexture(0);
314 cacheTexture->allocateTexture();
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);
359
Romain Guyd71dd362011-12-12 19:03:35 -0800360 uint32_t coordSize = 2;
Romain Guy694b5192010-07-21 21:33:20 -0700361 uint32_t uvSize = 2;
362 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700363 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
Romain Guy9b1204b2012-09-04 15:22:57 -0700364 mTextMesh = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700365}
366
367// We don't want to allocate anything unless we actually draw text
368void FontRenderer::checkInit() {
369 if (mInitialized) {
370 return;
371 }
372
373 initTextTexture();
374 initVertexArrayBuffers();
375
376 mInitialized = true;
377}
378
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900379void FontRenderer::updateDrawParams() {
380 if (mCurrentQuadIndex != mLastQuadIndex) {
Romain Guy115096f2013-03-19 11:32:41 -0700381 uint16_t* offset = (uint16_t*)(mLastQuadIndex * sizeof(uint16_t) * 6);
382 uint32_t count = mCurrentQuadIndex - mLastQuadIndex;
383 mDrawBatch.add(TextBatch(offset, count, mCurrentCacheTexture));
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900384 mLastQuadIndex = mCurrentQuadIndex;
385 }
386}
387
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700388void FontRenderer::checkTextureUpdate() {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900389 if (!mUploadTexture) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700390 return;
Romain Guy694b5192010-07-21 21:33:20 -0700391 }
392
Romain Guy2d4fd362011-12-13 22:00:19 -0800393 Caches& caches = Caches::getInstance();
394 GLuint lastTextureId = 0;
Chet Haase378e9192012-08-15 15:54:54 -0700395 // Iterate over all the cache textures and see which ones need to be updated
396 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
397 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guy80872462012-09-04 16:42:01 -0700398 if (cacheTexture->isDirty() && cacheTexture->getTexture()) {
Chet Haaseb92d8f72012-09-21 08:40:46 -0700399 // Can't copy inner rect; glTexSubimage expects pointer to deal with entire buffer
400 // of data. So expand the dirty rect to the encompassing horizontal stripe.
401 const Rect* dirtyRect = cacheTexture->getDirtyRect();
402 uint32_t x = 0;
403 uint32_t y = dirtyRect->top;
Romain Guy80872462012-09-04 16:42:01 -0700404 uint32_t width = cacheTexture->getWidth();
Chet Haaseb92d8f72012-09-21 08:40:46 -0700405 uint32_t height = dirtyRect->getHeight();
406 void* textureData = cacheTexture->getTexture() + y * width;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700407
Romain Guy80872462012-09-04 16:42:01 -0700408 if (cacheTexture->getTextureId() != lastTextureId) {
409 lastTextureId = cacheTexture->getTextureId();
Romain Guy2d4fd362011-12-13 22:00:19 -0800410 caches.activeTexture(0);
Romain Guy80872462012-09-04 16:42:01 -0700411 glBindTexture(GL_TEXTURE_2D, lastTextureId);
Romain Guy2d4fd362011-12-13 22:00:19 -0800412 }
Chet Haasee816bae2012-08-09 13:39:02 -0700413#if DEBUG_FONT_RENDERER
Chet Haaseb92d8f72012-09-21 08:40:46 -0700414 ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d",
415 i, x, y, width, height);
Chet Haasee816bae2012-08-09 13:39:02 -0700416#endif
Chet Haaseb92d8f72012-09-21 08:40:46 -0700417 glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700418 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Romain Guy80872462012-09-04 16:42:01 -0700419 cacheTexture->setDirty(false);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700420 }
421 }
422
423 mUploadTexture = false;
424}
425
426void FontRenderer::issueDrawCommand() {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900427 updateDrawParams();
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700428 checkTextureUpdate();
429
Romain Guy15bc6432011-12-13 13:11:32 -0800430 Caches& caches = Caches::getInstance();
Romain Guy2d4fd362011-12-13 22:00:19 -0800431 caches.bindIndicesBuffer(mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800432 if (!mDrawn) {
Romain Guy9b1204b2012-09-04 15:22:57 -0700433 float* buffer = mTextMesh;
Romain Guy15bc6432011-12-13 13:11:32 -0800434 int offset = 2;
435
436 bool force = caches.unbindMeshBuffer();
Chris Craikcb4d6002012-09-25 12:00:29 -0700437 caches.bindPositionVertexPointer(force, buffer);
438 caches.bindTexCoordsVertexPointer(force, buffer + offset);
Romain Guy15bc6432011-12-13 13:11:32 -0800439 }
440
Romain Guy115096f2013-03-19 11:32:41 -0700441 caches.activeTexture(0);
442 GLuint lastId = 0;
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900443
Romain Guy115096f2013-03-19 11:32:41 -0700444 for (uint32_t i = 0; i < mDrawBatch.size(); i++) {
445 const TextBatch& batch = mDrawBatch[i];
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900446
Romain Guy115096f2013-03-19 11:32:41 -0700447 GLuint id = batch.texture->getTextureId();
448 if (id != lastId) {
449 glBindTexture(GL_TEXTURE_2D, id);
450 batch.texture->setLinearFiltering(mLinearFiltering, false);
451 lastId = id;
452 }
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900453
Romain Guy115096f2013-03-19 11:32:41 -0700454 glDrawElements(GL_TRIANGLES, batch.count * 6, GL_UNSIGNED_SHORT, batch.offset);
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900455 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700456
457 mDrawn = true;
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900458
459 mCurrentQuadIndex = 0;
460 mLastQuadIndex = 0;
Romain Guy115096f2013-03-19 11:32:41 -0700461 mDrawBatch.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700462}
463
Romain Guy97771732012-02-28 18:17:02 -0800464void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
465 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800466 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800467 if (texture != mCurrentCacheTexture) {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900468 updateDrawParams();
Chet Haase7de0cb12011-12-05 16:35:38 -0800469 // Now use the new texture id
470 mCurrentCacheTexture = texture;
471 }
Romain Guy09147fb2010-07-22 13:08:20 -0700472
Romain Guy694b5192010-07-21 21:33:20 -0700473 const uint32_t vertsPerQuad = 4;
Romain Guyd71dd362011-12-12 19:03:35 -0800474 const uint32_t floatsPerVert = 4;
Romain Guy9b1204b2012-09-04 15:22:57 -0700475 float* currentPos = mTextMesh + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700476
Romain Guy694b5192010-07-21 21:33:20 -0700477 (*currentPos++) = x1;
478 (*currentPos++) = y1;
Romain Guy694b5192010-07-21 21:33:20 -0700479 (*currentPos++) = u1;
480 (*currentPos++) = v1;
481
482 (*currentPos++) = x2;
483 (*currentPos++) = y2;
Romain Guy694b5192010-07-21 21:33:20 -0700484 (*currentPos++) = u2;
485 (*currentPos++) = v2;
486
487 (*currentPos++) = x3;
488 (*currentPos++) = y3;
Romain Guy694b5192010-07-21 21:33:20 -0700489 (*currentPos++) = u3;
490 (*currentPos++) = v3;
491
492 (*currentPos++) = x4;
493 (*currentPos++) = y4;
Romain Guy694b5192010-07-21 21:33:20 -0700494 (*currentPos++) = u4;
495 (*currentPos++) = v4;
496
497 mCurrentQuadIndex++;
Romain Guy97771732012-02-28 18:17:02 -0800498}
499
500void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
501 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
502 float x4, float y4, float u4, float v4, CacheTexture* texture) {
503
504 if (mClip &&
505 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
506 return;
507 }
508
509 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 -0700510
Romain Guy5b3b3522010-10-27 18:57:51 -0700511 if (mBounds) {
512 mBounds->left = fmin(mBounds->left, x1);
513 mBounds->top = fmin(mBounds->top, y3);
514 mBounds->right = fmax(mBounds->right, x3);
515 mBounds->bottom = fmax(mBounds->bottom, y1);
516 }
517
Romain Guy694b5192010-07-21 21:33:20 -0700518 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
519 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700520 }
521}
522
Romain Guy97771732012-02-28 18:17:02 -0800523void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
524 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
525 float x4, float y4, float u4, float v4, CacheTexture* texture) {
526
527 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
528
529 if (mBounds) {
530 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
531 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
532 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
533 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
534 }
535
536 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
537 issueDrawCommand();
Romain Guy97771732012-02-28 18:17:02 -0800538 }
539}
540
Romain Guye3a9b242013-01-08 11:15:30 -0800541void FontRenderer::setFont(SkPaint* paint, const mat4& matrix) {
542 mCurrentFont = Font::create(this, paint, matrix);
Romain Guy694b5192010-07-21 21:33:20 -0700543}
Romain Guy7975fb62010-10-01 16:36:14 -0700544
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700545FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Raph Levien416a8472012-07-19 22:48:17 -0700546 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700547 checkInit();
548
549 if (!mCurrentFont) {
550 DropShadow image;
551 image.width = 0;
552 image.height = 0;
553 image.image = NULL;
554 image.penX = 0;
555 image.penY = 0;
556 return image;
557 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700558
Romain Guy2d4fd362011-12-13 22:00:19 -0800559 mDrawn = false;
Romain Guyff98fa52011-11-28 09:35:09 -0800560 mClip = NULL;
561 mBounds = NULL;
562
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700563 Rect bounds;
Raph Levien416a8472012-07-19 22:48:17 -0700564 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
Romain Guyff98fa52011-11-28 09:35:09 -0800565
Romain Guy1e45aae2010-08-13 19:39:53 -0700566 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
567 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Romain Guyff98fa52011-11-28 09:35:09 -0800568
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800569 // Align buffers for renderscript usage
570 if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) {
571 paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700572 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700573
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800574 int size = paddedWidth * paddedHeight;
Romain Guy6e200402013-03-08 11:28:22 -0800575 uint8_t* dataBuffer = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, size);
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800576 memset(dataBuffer, 0, size);
577
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700578 int penX = radius - bounds.left;
579 int penY = radius - bounds.bottom;
580
Chris Craikdd8697c2013-02-22 10:41:36 -0800581 if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) {
582 // text has non-whitespace, so draw and blur to create the shadow
583 // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted
584 // TODO: don't draw pure whitespace in the first place, and avoid needing this check
585 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
586 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
587
588 blurImage(&dataBuffer, paddedWidth, paddedHeight, radius);
589 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700590
591 DropShadow image;
592 image.width = paddedWidth;
593 image.height = paddedHeight;
594 image.image = dataBuffer;
595 image.penX = penX;
596 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -0800597
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700598 return image;
599}
Romain Guy694b5192010-07-21 21:33:20 -0700600
Romain Guy671d6cf2012-01-18 12:39:17 -0800601void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700602 checkInit();
603
Romain Guy5b3b3522010-10-27 18:57:51 -0700604 mDrawn = false;
605 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -0700606 mClip = clip;
Romain Guy671d6cf2012-01-18 12:39:17 -0800607}
Romain Guyff98fa52011-11-28 09:35:09 -0800608
Romain Guy671d6cf2012-01-18 12:39:17 -0800609void FontRenderer::finishRender() {
Romain Guy5b3b3522010-10-27 18:57:51 -0700610 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -0800611 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700612
613 if (mCurrentQuadIndex != 0) {
614 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700615 }
Romain Guy671d6cf2012-01-18 12:39:17 -0800616}
617
Romain Guye3a9b242013-01-08 11:15:30 -0800618void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix) {
619 Font* font = Font::create(this, paint, matrix);
Chet Haasee816bae2012-08-09 13:39:02 -0700620 font->precache(paint, text, numGlyphs);
621}
622
Romain Guy671d6cf2012-01-18 12:39:17 -0800623bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
624 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
625 const float* positions, Rect* bounds) {
626 if (!mCurrentFont) {
627 ALOGE("No font set");
628 return false;
629 }
630
631 initRender(clip, bounds);
632 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
633 finishRender();
Romain Guy5b3b3522010-10-27 18:57:51 -0700634
635 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700636}
637
Romain Guy97771732012-02-28 18:17:02 -0800638bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
639 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
640 float hOffset, float vOffset, Rect* bounds) {
641 if (!mCurrentFont) {
642 ALOGE("No font set");
643 return false;
644 }
645
646 initRender(clip, bounds);
647 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
648 finishRender();
649
650 return mDrawn;
651}
652
Romain Guy9b1204b2012-09-04 15:22:57 -0700653void FontRenderer::removeFont(const Font* font) {
Romain Guye3a9b242013-01-08 11:15:30 -0800654 mActiveFonts.remove(font->getDescription());
Romain Guy9b1204b2012-09-04 15:22:57 -0700655
656 if (mCurrentFont == font) {
657 mCurrentFont = NULL;
658 }
659}
660
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800661void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) {
662 if (width * height * radius < RS_MIN_INPUT_CUTOFF) {
663 float *gaussian = new float[2 * radius + 1];
Romain Guy6e200402013-03-08 11:28:22 -0800664 Blur::generateGaussianWeights(gaussian, radius);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700665
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800666 uint8_t* scratch = new uint8_t[width * height];
Romain Guy6e200402013-03-08 11:28:22 -0800667 Blur::horizontal(gaussian, radius, *image, scratch, width, height);
668 Blur::vertical(gaussian, radius, scratch, *image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -0800669
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800670 delete[] gaussian;
671 delete[] scratch;
Chris Craikdd8697c2013-02-22 10:41:36 -0800672 return;
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800673 }
Romain Guyd71dd362011-12-12 19:03:35 -0800674
Romain Guy6e200402013-03-08 11:28:22 -0800675 uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800676
677 if (mRs.get() == 0) {
678 mRs = new RSC::RS();
679 if (!mRs->init(true, true)) {
680 ALOGE("blur RS failed to init");
681 }
682
683 mRsElement = RSC::Element::A_8(mRs);
684 mRsScript = new RSC::ScriptIntrinsicBlur(mRs, mRsElement);
685 }
686
687 sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0);
688 sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
689 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, *image);
690 sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
691 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, outImage);
692
693 mRsScript->setRadius(radius);
694 mRsScript->blur(ain, aout);
695
696 // replace the original image's pointer, avoiding a copy back to the original buffer
Ben Cheng15641a612013-02-20 13:20:03 -0800697 free(*image);
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800698 *image = outImage;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700699}
700
Romain Guy694b5192010-07-21 21:33:20 -0700701}; // namespace uirenderer
702}; // namespace android