blob: f0dcb30db549b5856d4f701437f962184c3779ae [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) {
381 mDrawOffsets.add((uint16_t*)(mLastQuadIndex * sizeof(uint16_t) * 6));
382 mDrawCounts.add(mCurrentQuadIndex - mLastQuadIndex);
383 mDrawCacheTextures.add(mCurrentCacheTexture);
384 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
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900441 for (uint32_t i = 0; i < mDrawOffsets.size(); i++) {
442 uint16_t* offset = mDrawOffsets[i];
443 uint32_t count = mDrawCounts[i];
444 CacheTexture* texture = mDrawCacheTextures[i];
445
446 caches.activeTexture(0);
447 glBindTexture(GL_TEXTURE_2D, texture->getTextureId());
448
449 texture->setLinearFiltering(mLinearFiltering, false);
450
451 glDrawElements(GL_TRIANGLES, count * 6, GL_UNSIGNED_SHORT, offset);
452 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700453
454 mDrawn = true;
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900455
456 mCurrentQuadIndex = 0;
457 mLastQuadIndex = 0;
458 mDrawOffsets.clear();
459 mDrawCounts.clear();
460 mDrawCacheTextures.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700461}
462
Romain Guy97771732012-02-28 18:17:02 -0800463void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
464 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800465 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800466 if (texture != mCurrentCacheTexture) {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900467 updateDrawParams();
Chet Haase7de0cb12011-12-05 16:35:38 -0800468 // Now use the new texture id
469 mCurrentCacheTexture = texture;
470 }
Romain Guy09147fb2010-07-22 13:08:20 -0700471
Romain Guy694b5192010-07-21 21:33:20 -0700472 const uint32_t vertsPerQuad = 4;
Romain Guyd71dd362011-12-12 19:03:35 -0800473 const uint32_t floatsPerVert = 4;
Romain Guy9b1204b2012-09-04 15:22:57 -0700474 float* currentPos = mTextMesh + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700475
Romain Guy694b5192010-07-21 21:33:20 -0700476 (*currentPos++) = x1;
477 (*currentPos++) = y1;
Romain Guy694b5192010-07-21 21:33:20 -0700478 (*currentPos++) = u1;
479 (*currentPos++) = v1;
480
481 (*currentPos++) = x2;
482 (*currentPos++) = y2;
Romain Guy694b5192010-07-21 21:33:20 -0700483 (*currentPos++) = u2;
484 (*currentPos++) = v2;
485
486 (*currentPos++) = x3;
487 (*currentPos++) = y3;
Romain Guy694b5192010-07-21 21:33:20 -0700488 (*currentPos++) = u3;
489 (*currentPos++) = v3;
490
491 (*currentPos++) = x4;
492 (*currentPos++) = y4;
Romain Guy694b5192010-07-21 21:33:20 -0700493 (*currentPos++) = u4;
494 (*currentPos++) = v4;
495
496 mCurrentQuadIndex++;
Romain Guy97771732012-02-28 18:17:02 -0800497}
498
499void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
500 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
501 float x4, float y4, float u4, float v4, CacheTexture* texture) {
502
503 if (mClip &&
504 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
505 return;
506 }
507
508 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 -0700509
Romain Guy5b3b3522010-10-27 18:57:51 -0700510 if (mBounds) {
511 mBounds->left = fmin(mBounds->left, x1);
512 mBounds->top = fmin(mBounds->top, y3);
513 mBounds->right = fmax(mBounds->right, x3);
514 mBounds->bottom = fmax(mBounds->bottom, y1);
515 }
516
Romain Guy694b5192010-07-21 21:33:20 -0700517 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
518 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700519 }
520}
521
Romain Guy97771732012-02-28 18:17:02 -0800522void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
523 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
524 float x4, float y4, float u4, float v4, CacheTexture* texture) {
525
526 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
527
528 if (mBounds) {
529 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
530 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
531 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
532 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
533 }
534
535 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
536 issueDrawCommand();
Romain Guy97771732012-02-28 18:17:02 -0800537 }
538}
539
Romain Guye3a9b242013-01-08 11:15:30 -0800540void FontRenderer::setFont(SkPaint* paint, const mat4& matrix) {
541 mCurrentFont = Font::create(this, paint, matrix);
Romain Guy694b5192010-07-21 21:33:20 -0700542}
Romain Guy7975fb62010-10-01 16:36:14 -0700543
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700544FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Raph Levien416a8472012-07-19 22:48:17 -0700545 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700546 checkInit();
547
548 if (!mCurrentFont) {
549 DropShadow image;
550 image.width = 0;
551 image.height = 0;
552 image.image = NULL;
553 image.penX = 0;
554 image.penY = 0;
555 return image;
556 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700557
Romain Guy2d4fd362011-12-13 22:00:19 -0800558 mDrawn = false;
Romain Guyff98fa52011-11-28 09:35:09 -0800559 mClip = NULL;
560 mBounds = NULL;
561
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700562 Rect bounds;
Raph Levien416a8472012-07-19 22:48:17 -0700563 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
Romain Guyff98fa52011-11-28 09:35:09 -0800564
Romain Guy1e45aae2010-08-13 19:39:53 -0700565 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
566 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Romain Guyff98fa52011-11-28 09:35:09 -0800567
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800568 // Align buffers for renderscript usage
569 if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) {
570 paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700571 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700572
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800573 int size = paddedWidth * paddedHeight;
Romain Guy6e200402013-03-08 11:28:22 -0800574 uint8_t* dataBuffer = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, size);
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800575 memset(dataBuffer, 0, size);
576
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700577 int penX = radius - bounds.left;
578 int penY = radius - bounds.bottom;
579
Chris Craikdd8697c2013-02-22 10:41:36 -0800580 if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) {
581 // text has non-whitespace, so draw and blur to create the shadow
582 // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted
583 // TODO: don't draw pure whitespace in the first place, and avoid needing this check
584 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
585 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
586
587 blurImage(&dataBuffer, paddedWidth, paddedHeight, radius);
588 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700589
590 DropShadow image;
591 image.width = paddedWidth;
592 image.height = paddedHeight;
593 image.image = dataBuffer;
594 image.penX = penX;
595 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -0800596
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700597 return image;
598}
Romain Guy694b5192010-07-21 21:33:20 -0700599
Romain Guy671d6cf2012-01-18 12:39:17 -0800600void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700601 checkInit();
602
Romain Guy5b3b3522010-10-27 18:57:51 -0700603 mDrawn = false;
604 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -0700605 mClip = clip;
Romain Guy671d6cf2012-01-18 12:39:17 -0800606}
Romain Guyff98fa52011-11-28 09:35:09 -0800607
Romain Guy671d6cf2012-01-18 12:39:17 -0800608void FontRenderer::finishRender() {
Romain Guy5b3b3522010-10-27 18:57:51 -0700609 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -0800610 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700611
612 if (mCurrentQuadIndex != 0) {
613 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700614 }
Romain Guy671d6cf2012-01-18 12:39:17 -0800615}
616
Romain Guye3a9b242013-01-08 11:15:30 -0800617void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix) {
618 Font* font = Font::create(this, paint, matrix);
Chet Haasee816bae2012-08-09 13:39:02 -0700619 font->precache(paint, text, numGlyphs);
620}
621
Romain Guy671d6cf2012-01-18 12:39:17 -0800622bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
623 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
624 const float* positions, Rect* bounds) {
625 if (!mCurrentFont) {
626 ALOGE("No font set");
627 return false;
628 }
629
630 initRender(clip, bounds);
631 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
632 finishRender();
Romain Guy5b3b3522010-10-27 18:57:51 -0700633
634 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700635}
636
Romain Guy97771732012-02-28 18:17:02 -0800637bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
638 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
639 float hOffset, float vOffset, Rect* bounds) {
640 if (!mCurrentFont) {
641 ALOGE("No font set");
642 return false;
643 }
644
645 initRender(clip, bounds);
646 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
647 finishRender();
648
649 return mDrawn;
650}
651
Romain Guy9b1204b2012-09-04 15:22:57 -0700652void FontRenderer::removeFont(const Font* font) {
Romain Guye3a9b242013-01-08 11:15:30 -0800653 mActiveFonts.remove(font->getDescription());
Romain Guy9b1204b2012-09-04 15:22:57 -0700654
655 if (mCurrentFont == font) {
656 mCurrentFont = NULL;
657 }
658}
659
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800660void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) {
661 if (width * height * radius < RS_MIN_INPUT_CUTOFF) {
662 float *gaussian = new float[2 * radius + 1];
Romain Guy6e200402013-03-08 11:28:22 -0800663 Blur::generateGaussianWeights(gaussian, radius);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700664
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800665 uint8_t* scratch = new uint8_t[width * height];
Romain Guy6e200402013-03-08 11:28:22 -0800666 Blur::horizontal(gaussian, radius, *image, scratch, width, height);
667 Blur::vertical(gaussian, radius, scratch, *image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -0800668
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800669 delete[] gaussian;
670 delete[] scratch;
Chris Craikdd8697c2013-02-22 10:41:36 -0800671 return;
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800672 }
Romain Guyd71dd362011-12-12 19:03:35 -0800673
Romain Guy6e200402013-03-08 11:28:22 -0800674 uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800675
676 if (mRs.get() == 0) {
677 mRs = new RSC::RS();
678 if (!mRs->init(true, true)) {
679 ALOGE("blur RS failed to init");
680 }
681
682 mRsElement = RSC::Element::A_8(mRs);
683 mRsScript = new RSC::ScriptIntrinsicBlur(mRs, mRsElement);
684 }
685
686 sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0);
687 sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
688 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, *image);
689 sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
690 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, outImage);
691
692 mRsScript->setRadius(radius);
693 mRsScript->blur(ain, aout);
694
695 // replace the original image's pointer, avoiding a copy back to the original buffer
Ben Cheng15641a612013-02-20 13:20:03 -0800696 free(*image);
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800697 *image = outImage;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700698}
699
Romain Guy694b5192010-07-21 21:33:20 -0700700}; // namespace uirenderer
701}; // namespace android