blob: 543cfa2cc792eaef4382c74b697ad2dc968f4633 [file] [log] [blame]
Romain Guy694b5192010-07-21 21:33:20 -07001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "OpenGLRenderer"
18
Derek Sollenbergerca79cf62012-08-14 16:44:52 -040019#include <SkGlyph.h>
Romain Guy694b5192010-07-21 21:33:20 -070020#include <SkUtils.h>
21
Romain Guy51769a62010-07-23 00:28:00 -070022#include <cutils/properties.h>
Romain Guye2d345e2010-09-24 18:39:22 -070023
Romain Guy257ae352013-03-20 16:31:12 -070024#include <utils/Functor.h>
Romain Guy51769a62010-07-23 00:28:00 -070025#include <utils/Log.h>
26
Romain Guy6e200402013-03-08 11:28:22 -080027#include <RenderScript.h>
Chris Craikf2d8ccc2013-02-13 16:14:17 -080028
Romain Guy6e200402013-03-08 11:28:22 -080029#include "utils/Blur.h"
Chris Craikf2d8ccc2013-02-13 16:14:17 -080030#include "utils/Timing.h"
Romain Guy6e200402013-03-08 11:28:22 -080031
Romain Guy15bc6432011-12-13 13:11:32 -080032#include "Caches.h"
Romain Guyc9855a52011-01-21 21:14:15 -080033#include "Debug.h"
Romain Guy09087642013-04-04 12:27:54 -070034#include "Extensions.h"
Romain Guy51769a62010-07-23 00:28:00 -070035#include "FontRenderer.h"
Romain Guycf51a412013-04-08 19:40:31 -070036#include "PixelBuffer.h"
Romain Guy9f5dab32012-09-04 12:55:44 -070037#include "Rect.h"
Romain Guy51769a62010-07-23 00:28:00 -070038
Romain Guy694b5192010-07-21 21:33:20 -070039namespace android {
40namespace uirenderer {
41
Chris Craikf2d8ccc2013-02-13 16:14:17 -080042// blur inputs smaller than this constant will bypass renderscript
43#define RS_MIN_INPUT_CUTOFF 10000
44
Romain Guy694b5192010-07-21 21:33:20 -070045///////////////////////////////////////////////////////////////////////////////
Romain Guy694b5192010-07-21 21:33:20 -070046// FontRenderer
47///////////////////////////////////////////////////////////////////////////////
48
Romain Guy514fb182011-01-19 14:38:29 -080049static bool sLogFontRendererCreate = true;
50
Romain Guye3a9b242013-01-08 11:15:30 -080051FontRenderer::FontRenderer() :
52 mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity) {
53
Romain Guyc9855a52011-01-21 21:14:15 -080054 if (sLogFontRendererCreate) {
55 INIT_LOGD("Creating FontRenderer");
56 }
Romain Guy51769a62010-07-23 00:28:00 -070057
Romain Guyb45c0c92010-08-26 20:35:23 -070058 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -070059 mInitialized = false;
Romain Guy694b5192010-07-21 21:33:20 -070060
Chet Haase7de0cb12011-12-05 16:35:38 -080061 mCurrentCacheTexture = NULL;
Romain Guy9cccc2b2010-08-07 23:46:15 -070062
Chet Haase2a47c142011-12-14 15:22:56 -080063 mLinearFiltering = false;
64
Romain Guy694b5192010-07-21 21:33:20 -070065 mIndexBufferID = 0;
66
Chet Haaseeb32a492012-08-31 13:54:03 -070067 mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH;
68 mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT;
69 mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH;
70 mLargeCacheHeight = DEFAULT_TEXT_LARGE_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -070071
72 char property[PROPERTY_VALUE_MAX];
Chet Haaseeb32a492012-08-31 13:54:03 -070073 if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, NULL) > 0) {
Chet Haase7de0cb12011-12-05 16:35:38 -080074 mSmallCacheWidth = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -070075 }
Romain Guy9f5dab32012-09-04 12:55:44 -070076
Chet Haaseeb32a492012-08-31 13:54:03 -070077 if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, NULL) > 0) {
Chet Haase7de0cb12011-12-05 16:35:38 -080078 mSmallCacheHeight = atoi(property);
Chet Haaseeb32a492012-08-31 13:54:03 -070079 }
Romain Guy9f5dab32012-09-04 12:55:44 -070080
Chet Haaseeb32a492012-08-31 13:54:03 -070081 if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, NULL) > 0) {
82 mLargeCacheWidth = atoi(property);
83 }
Romain Guy9f5dab32012-09-04 12:55:44 -070084
Chet Haaseeb32a492012-08-31 13:54:03 -070085 if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, NULL) > 0) {
86 mLargeCacheHeight = atoi(property);
87 }
Romain Guy9f5dab32012-09-04 12:55:44 -070088
89 uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize;
90 mSmallCacheWidth = mSmallCacheWidth > maxTextureSize ? maxTextureSize : mSmallCacheWidth;
91 mSmallCacheHeight = mSmallCacheHeight > maxTextureSize ? maxTextureSize : mSmallCacheHeight;
92 mLargeCacheWidth = mLargeCacheWidth > maxTextureSize ? maxTextureSize : mLargeCacheWidth;
93 mLargeCacheHeight = mLargeCacheHeight > maxTextureSize ? maxTextureSize : mLargeCacheHeight;
94
Chet Haaseeb32a492012-08-31 13:54:03 -070095 if (sLogFontRendererCreate) {
96 INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
97 mSmallCacheWidth, mSmallCacheHeight,
98 mLargeCacheWidth, mLargeCacheHeight >> 1,
99 mLargeCacheWidth, mLargeCacheHeight >> 1,
100 mLargeCacheWidth, mLargeCacheHeight);
Romain Guy51769a62010-07-23 00:28:00 -0700101 }
Romain Guy514fb182011-01-19 14:38:29 -0800102
103 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -0700104}
105
106FontRenderer::~FontRenderer() {
Chet Haase378e9192012-08-15 15:54:54 -0700107 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
108 delete mCacheTextures[i];
Romain Guy694b5192010-07-21 21:33:20 -0700109 }
Chet Haase378e9192012-08-15 15:54:54 -0700110 mCacheTextures.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700111
Romain Guy9cccc2b2010-08-07 23:46:15 -0700112 if (mInitialized) {
Romain Guya9dd8202012-03-26 14:52:00 -0700113 // Unbinding the buffer shouldn't be necessary but it crashes with some drivers
114 Caches::getInstance().unbindIndicesBuffer();
Romain Guyb0317982012-03-21 11:52:52 -0700115 glDeleteBuffers(1, &mIndexBufferID);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700116 }
Romain Guy694b5192010-07-21 21:33:20 -0700117
Romain Guye3a9b242013-01-08 11:15:30 -0800118 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
119 while (it.next()) {
120 delete it.value();
Romain Guy694b5192010-07-21 21:33:20 -0700121 }
Romain Guye3a9b242013-01-08 11:15:30 -0800122 mActiveFonts.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700123}
124
125void FontRenderer::flushAllAndInvalidate() {
Romain Guy661a87e2013-03-19 15:24:36 -0700126 issueDrawCommand();
Romain Guy9d9758a2012-05-14 15:19:58 -0700127
Romain Guye3a9b242013-01-08 11:15:30 -0800128 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
129 while (it.next()) {
130 it.value()->invalidateTextureCache();
Romain Guy694b5192010-07-21 21:33:20 -0700131 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700132
Chet Haase378e9192012-08-15 15:54:54 -0700133 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
134 mCacheTextures[i]->init();
Romain Guy694b5192010-07-21 21:33:20 -0700135 }
136}
137
Chet Haase9a824562011-12-16 15:44:59 -0800138void FontRenderer::flushLargeCaches() {
Chet Haase378e9192012-08-15 15:54:54 -0700139 // Start from 1; don't deallocate smallest/default texture
140 for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
141 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guycf51a412013-04-08 19:40:31 -0700142 if (cacheTexture->getPixelBuffer()) {
Chet Haase378e9192012-08-15 15:54:54 -0700143 cacheTexture->init();
Romain Guye3a9b242013-01-08 11:15:30 -0800144 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
145 while (it.next()) {
146 it.value()->invalidateTextureCache(cacheTexture);
Chet Haasee816bae2012-08-09 13:39:02 -0700147 }
Romain Guy80872462012-09-04 16:42:01 -0700148 cacheTexture->releaseTexture();
Chet Haase9a824562011-12-16 15:44:59 -0800149 }
150 }
Chet Haase9a824562011-12-16 15:44:59 -0800151}
152
Chet Haase378e9192012-08-15 15:54:54 -0700153CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
154 uint32_t* startX, uint32_t* startY) {
155 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
156 if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
157 return mCacheTextures[i];
158 }
159 }
160 // Could not fit glyph into current cache textures
161 return NULL;
162}
163
Chet Haase7de0cb12011-12-05 16:35:38 -0800164void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
Chet Haasef942cf12012-08-30 09:06:46 -0700165 uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700166 checkInit();
Romain Guya4adcf02013-02-28 12:15:35 -0800167
168 // If the glyph bitmap is empty let's assum the glyph is valid
169 // so we can avoid doing extra work later on
170 if (glyph.fWidth == 0 || glyph.fHeight == 0) {
171 cachedGlyph->mIsValid = true;
172 cachedGlyph->mCacheTexture = NULL;
173 return;
174 }
175
Chet Haase7de0cb12011-12-05 16:35:38 -0800176 cachedGlyph->mIsValid = false;
Romain Guya4adcf02013-02-28 12:15:35 -0800177
Romain Guy694b5192010-07-21 21:33:20 -0700178 // If the glyph is too tall, don't cache it
Chet Haase378e9192012-08-15 15:54:54 -0700179 if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
Romain Guy80872462012-09-04 16:42:01 -0700180 mCacheTextures[mCacheTextures.size() - 1]->getHeight()) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700181 ALOGE("Font size too large to fit in cache. width, height = %i, %i",
182 (int) glyph.fWidth, (int) glyph.fHeight);
Chet Haase7de0cb12011-12-05 16:35:38 -0800183 return;
Romain Guy694b5192010-07-21 21:33:20 -0700184 }
185
186 // Now copy the bitmap into the cache texture
187 uint32_t startX = 0;
188 uint32_t startY = 0;
189
Chet Haase378e9192012-08-15 15:54:54 -0700190 CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700191
Chet Haase378e9192012-08-15 15:54:54 -0700192 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700193 if (!precaching) {
194 // If the new glyph didn't fit and we are not just trying to precache it,
195 // clear out the cache and try again
196 flushAllAndInvalidate();
197 cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
198 }
Romain Guy694b5192010-07-21 21:33:20 -0700199
Chet Haase378e9192012-08-15 15:54:54 -0700200 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700201 // either the glyph didn't fit or we're precaching and will cache it when we draw
Chet Haase7de0cb12011-12-05 16:35:38 -0800202 return;
Romain Guy694b5192010-07-21 21:33:20 -0700203 }
204 }
205
Chet Haase378e9192012-08-15 15:54:54 -0700206 cachedGlyph->mCacheTexture = cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800207
Romain Guy694b5192010-07-21 21:33:20 -0700208 *retOriginX = startX;
209 *retOriginY = startY;
210
211 uint32_t endX = startX + glyph.fWidth;
212 uint32_t endY = startY + glyph.fHeight;
213
Romain Guy80872462012-09-04 16:42:01 -0700214 uint32_t cacheWidth = cacheTexture->getWidth();
Romain Guy694b5192010-07-21 21:33:20 -0700215
Romain Guycf51a412013-04-08 19:40:31 -0700216 if (!cacheTexture->getPixelBuffer()) {
Romain Guy80872462012-09-04 16:42:01 -0700217 Caches::getInstance().activeTexture(0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800218 // Large-glyph texture memory is allocated only as needed
Romain Guy80872462012-09-04 16:42:01 -0700219 cacheTexture->allocateTexture();
Chet Haase7de0cb12011-12-05 16:35:38 -0800220 }
Romain Guy661a87e2013-03-19 15:24:36 -0700221 if (!cacheTexture->mesh()) {
222 cacheTexture->allocateMesh();
223 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700224
Romain Guyb969a0d2013-02-05 14:38:40 -0800225 // Tells us whether the glyphs is B&W (1 bit per pixel)
226 // or anti-aliased (8 bits per pixel)
227 SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
Romain Guy694b5192010-07-21 21:33:20 -0700228
Romain Guycf51a412013-04-08 19:40:31 -0700229 uint8_t* cacheBuffer = cacheTexture->getPixelBuffer()->map();
Romain Guy694b5192010-07-21 21:33:20 -0700230 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
Romain Guy33fa1f72012-08-07 19:09:57 -0700231
Romain Guyb969a0d2013-02-05 14:38:40 -0800232 // Copy the glyph image, taking the mask format into account
233 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
234 int stride = glyph.rowBytes();
235
Romain Guy0b58a3d2013-03-05 12:16:27 -0800236 uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
237 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
238
Romain Guyb969a0d2013-02-05 14:38:40 -0800239 switch (format) {
240 case SkMask::kA8_Format: {
241 if (mGammaTable) {
242 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
Romain Guy0b58a3d2013-03-05 12:16:27 -0800243 row = cacheY * cacheWidth;
244 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800245 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
246 uint8_t tempCol = bitmapBuffer[bY + bX];
Romain Guy0b58a3d2013-03-05 12:16:27 -0800247 cacheBuffer[row + cacheX] = mGammaTable[tempCol];
Romain Guyb969a0d2013-02-05 14:38:40 -0800248 }
Romain Guy0b58a3d2013-03-05 12:16:27 -0800249 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800250 }
251 } else {
252 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
Romain Guy0b58a3d2013-03-05 12:16:27 -0800253 row = cacheY * cacheWidth;
254 memcpy(&cacheBuffer[row + startX], &bitmapBuffer[bY], glyph.fWidth);
255 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
256 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800257 }
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700258 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800259 break;
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700260 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800261 case SkMask::kBW_Format: {
262 static const uint8_t COLORS[2] = { 0, 255 };
263
264 for (cacheY = startY; cacheY < endY; cacheY++) {
265 cacheX = startX;
266 int rowBytes = stride;
267 uint8_t* buffer = bitmapBuffer;
268
Romain Guy0b58a3d2013-03-05 12:16:27 -0800269 row = cacheY * cacheWidth;
270 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800271 while (--rowBytes >= 0) {
272 uint8_t b = *buffer++;
273 for (int8_t mask = 7; mask >= 0 && cacheX < endX; mask--) {
274 cacheBuffer[cacheY * cacheWidth + cacheX++] = COLORS[(b >> mask) & 0x1];
275 }
276 }
Romain Guy0b58a3d2013-03-05 12:16:27 -0800277 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800278
279 bitmapBuffer += stride;
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700280 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800281 break;
Romain Guy694b5192010-07-21 21:33:20 -0700282 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800283 default:
284 ALOGW("Unkown glyph format: 0x%x", format);
285 break;
Romain Guy694b5192010-07-21 21:33:20 -0700286 }
Romain Guy97771732012-02-28 18:17:02 -0800287
Romain Guy0b58a3d2013-03-05 12:16:27 -0800288 row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
289 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
290
Chet Haase7de0cb12011-12-05 16:35:38 -0800291 cachedGlyph->mIsValid = true;
Romain Guy694b5192010-07-21 21:33:20 -0700292}
293
Chet Haase7de0cb12011-12-05 16:35:38 -0800294CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
Chris Craik527a3aa2013-03-04 10:19:31 -0800295 CacheTexture* cacheTexture = new CacheTexture(width, height, gMaxNumberOfQuads);
Romain Guy9d9758a2012-05-14 15:19:58 -0700296
Chet Haase2a47c142011-12-14 15:22:56 -0800297 if (allocate) {
Romain Guy80872462012-09-04 16:42:01 -0700298 Caches::getInstance().activeTexture(0);
299 cacheTexture->allocateTexture();
Romain Guy661a87e2013-03-19 15:24:36 -0700300 cacheTexture->allocateMesh();
Chet Haase2a47c142011-12-14 15:22:56 -0800301 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700302
Chet Haase2a47c142011-12-14 15:22:56 -0800303 return cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800304}
305
306void FontRenderer::initTextTexture() {
Chet Haase378e9192012-08-15 15:54:54 -0700307 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
308 delete mCacheTextures[i];
Romain Guy9d9758a2012-05-14 15:19:58 -0700309 }
Chet Haase378e9192012-08-15 15:54:54 -0700310 mCacheTextures.clear();
Romain Guy9d9758a2012-05-14 15:19:58 -0700311
Chet Haase7de0cb12011-12-05 16:35:38 -0800312 mUploadTexture = false;
Chet Haase378e9192012-08-15 15:54:54 -0700313 mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
Chet Haaseeb32a492012-08-31 13:54:03 -0700314 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
315 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
316 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false));
Chet Haase378e9192012-08-15 15:54:54 -0700317 mCurrentCacheTexture = mCacheTextures[0];
Romain Guy694b5192010-07-21 21:33:20 -0700318}
319
320// Avoid having to reallocate memory and render quad by quad
321void FontRenderer::initVertexArrayBuffers() {
Chris Craik527a3aa2013-03-04 10:19:31 -0800322 uint32_t numIndices = gMaxNumberOfQuads * 6;
Romain Guyd71dd362011-12-12 19:03:35 -0800323 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700324 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700325
326 // Four verts, two triangles , six indices per quad
Chris Craik527a3aa2013-03-04 10:19:31 -0800327 for (uint32_t i = 0; i < gMaxNumberOfQuads; i++) {
Romain Guy694b5192010-07-21 21:33:20 -0700328 int i6 = i * 6;
329 int i4 = i * 4;
330
331 indexBufferData[i6 + 0] = i4 + 0;
332 indexBufferData[i6 + 1] = i4 + 1;
333 indexBufferData[i6 + 2] = i4 + 2;
334
335 indexBufferData[i6 + 3] = i4 + 0;
336 indexBufferData[i6 + 4] = i4 + 2;
337 indexBufferData[i6 + 5] = i4 + 3;
338 }
339
340 glGenBuffers(1, &mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800341 Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700342 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
Romain Guy694b5192010-07-21 21:33:20 -0700343
344 free(indexBufferData);
Romain Guy694b5192010-07-21 21:33:20 -0700345}
346
347// We don't want to allocate anything unless we actually draw text
348void FontRenderer::checkInit() {
349 if (mInitialized) {
350 return;
351 }
352
353 initTextTexture();
354 initVertexArrayBuffers();
355
356 mInitialized = true;
357}
358
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700359void FontRenderer::checkTextureUpdate() {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900360 if (!mUploadTexture) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700361 return;
Romain Guy694b5192010-07-21 21:33:20 -0700362 }
363
Romain Guy2d4fd362011-12-13 22:00:19 -0800364 Caches& caches = Caches::getInstance();
365 GLuint lastTextureId = 0;
Romain Guy09087642013-04-04 12:27:54 -0700366
Romain Guycf51a412013-04-08 19:40:31 -0700367 bool resetPixelStore = false;
Romain Guy09087642013-04-04 12:27:54 -0700368 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
369
Chet Haase378e9192012-08-15 15:54:54 -0700370 // Iterate over all the cache textures and see which ones need to be updated
371 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
372 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guycf51a412013-04-08 19:40:31 -0700373 if (cacheTexture->isDirty() && cacheTexture->getPixelBuffer()) {
Romain Guy80872462012-09-04 16:42:01 -0700374 if (cacheTexture->getTextureId() != lastTextureId) {
375 lastTextureId = cacheTexture->getTextureId();
Romain Guy2d4fd362011-12-13 22:00:19 -0800376 caches.activeTexture(0);
Romain Guy80872462012-09-04 16:42:01 -0700377 glBindTexture(GL_TEXTURE_2D, lastTextureId);
Romain Guy2d4fd362011-12-13 22:00:19 -0800378 }
Romain Guy09087642013-04-04 12:27:54 -0700379
Romain Guycf51a412013-04-08 19:40:31 -0700380 if (cacheTexture->upload()) {
381 resetPixelStore = true;
Romain Guy09087642013-04-04 12:27:54 -0700382 }
383
Chet Haasee816bae2012-08-09 13:39:02 -0700384#if DEBUG_FONT_RENDERER
Chet Haaseb92d8f72012-09-21 08:40:46 -0700385 ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d",
386 i, x, y, width, height);
Chet Haasee816bae2012-08-09 13:39:02 -0700387#endif
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700388 }
389 }
390
Romain Guycf51a412013-04-08 19:40:31 -0700391 // Unbind any PBO we might have used to update textures
392 caches.unbindPixelBuffer();
393
Romain Guy09087642013-04-04 12:27:54 -0700394 // Reset to default unpack row length to avoid affecting texture
395 // uploads in other parts of the renderer
Romain Guycf51a412013-04-08 19:40:31 -0700396 if (resetPixelStore) {
Romain Guy09087642013-04-04 12:27:54 -0700397 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
398 }
399
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700400 mUploadTexture = false;
401}
402
403void FontRenderer::issueDrawCommand() {
Romain Guy661a87e2013-03-19 15:24:36 -0700404 bool first = true;
405 bool force = false;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700406
Romain Guy115096f2013-03-19 11:32:41 -0700407 GLuint lastId = 0;
Romain Guy661a87e2013-03-19 15:24:36 -0700408 Caches& caches = Caches::getInstance();
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900409
Romain Guy661a87e2013-03-19 15:24:36 -0700410 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
411 CacheTexture* texture = mCacheTextures[i];
412 if (texture->canDraw()) {
413 if (first) {
Romain Guy257ae352013-03-20 16:31:12 -0700414 if (mFunctor) (*mFunctor)(0, NULL);
415
Romain Guy661a87e2013-03-19 15:24:36 -0700416 checkTextureUpdate();
417 caches.bindIndicesBuffer(mIndexBufferID);
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900418
Romain Guy661a87e2013-03-19 15:24:36 -0700419 if (!mDrawn) {
420 // If returns true, a VBO was bound and we must
421 // rebind our vertex attrib pointers even if
422 // they have the same values as the current pointers
423 force = caches.unbindMeshBuffer();
424 }
425
426 caches.activeTexture(0);
427 first = false;
428 }
429
430 glBindTexture(GL_TEXTURE_2D, texture->getTextureId());
431 texture->setLinearFiltering(mLinearFiltering, false);
432
433 TextureVertex* mesh = texture->mesh();
434 caches.bindPositionVertexPointer(force, &mesh[0].position[0]);
435 caches.bindTexCoordsVertexPointer(force, &mesh[0].texture[0]);
436 force = false;
437
438 glDrawElements(GL_TRIANGLES, texture->meshElementCount(),
439 GL_UNSIGNED_SHORT, texture->indices());
440
441 texture->resetMesh();
Romain Guy115096f2013-03-19 11:32:41 -0700442 }
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900443 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700444
445 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700446}
447
Romain Guy97771732012-02-28 18:17:02 -0800448void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
449 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800450 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800451 if (texture != mCurrentCacheTexture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800452 // Now use the new texture id
453 mCurrentCacheTexture = texture;
454 }
Romain Guy09147fb2010-07-22 13:08:20 -0700455
Romain Guy661a87e2013-03-19 15:24:36 -0700456 mCurrentCacheTexture->addQuad(x1, y1, u1, v1, x2, y2, u2, v2,
457 x3, y3, u3, v3, x4, y4, u4, v4);
Romain Guy97771732012-02-28 18:17:02 -0800458}
459
460void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
461 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
462 float x4, float y4, float u4, float v4, CacheTexture* texture) {
463
464 if (mClip &&
465 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
466 return;
467 }
468
469 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 -0700470
Romain Guy5b3b3522010-10-27 18:57:51 -0700471 if (mBounds) {
472 mBounds->left = fmin(mBounds->left, x1);
473 mBounds->top = fmin(mBounds->top, y3);
474 mBounds->right = fmax(mBounds->right, x3);
475 mBounds->bottom = fmax(mBounds->bottom, y1);
476 }
477
Romain Guy661a87e2013-03-19 15:24:36 -0700478 if (mCurrentCacheTexture->endOfMesh()) {
Romain Guy694b5192010-07-21 21:33:20 -0700479 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700480 }
481}
482
Romain Guy97771732012-02-28 18:17:02 -0800483void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
484 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
485 float x4, float y4, float u4, float v4, CacheTexture* texture) {
486
487 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
488
489 if (mBounds) {
490 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
491 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
492 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
493 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
494 }
495
Romain Guy661a87e2013-03-19 15:24:36 -0700496 if (mCurrentCacheTexture->endOfMesh()) {
Romain Guy97771732012-02-28 18:17:02 -0800497 issueDrawCommand();
Romain Guy97771732012-02-28 18:17:02 -0800498 }
499}
500
Romain Guye3a9b242013-01-08 11:15:30 -0800501void FontRenderer::setFont(SkPaint* paint, const mat4& matrix) {
502 mCurrentFont = Font::create(this, paint, matrix);
Romain Guy694b5192010-07-21 21:33:20 -0700503}
Romain Guy7975fb62010-10-01 16:36:14 -0700504
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700505FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Raph Levien416a8472012-07-19 22:48:17 -0700506 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700507 checkInit();
508
Romain Guycf51a412013-04-08 19:40:31 -0700509 DropShadow image;
510 image.width = 0;
511 image.height = 0;
512 image.image = NULL;
513 image.penX = 0;
514 image.penY = 0;
515
Romain Guy1e45aae2010-08-13 19:39:53 -0700516 if (!mCurrentFont) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700517 return image;
518 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700519
Romain Guy2d4fd362011-12-13 22:00:19 -0800520 mDrawn = false;
Romain Guyff98fa52011-11-28 09:35:09 -0800521 mClip = NULL;
522 mBounds = NULL;
523
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700524 Rect bounds;
Raph Levien416a8472012-07-19 22:48:17 -0700525 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
Romain Guyff98fa52011-11-28 09:35:09 -0800526
Romain Guy1e45aae2010-08-13 19:39:53 -0700527 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
528 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Romain Guyff98fa52011-11-28 09:35:09 -0800529
Romain Guycf51a412013-04-08 19:40:31 -0700530 uint32_t maxSize = Caches::getInstance().maxTextureSize;
531 if (paddedWidth > maxSize || paddedHeight > maxSize) {
532 return image;
533 }
534
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
Romain Guycf51a412013-04-08 19:40:31 -0700554 // Unbind any PBO we might have used
555 Caches::getInstance().unbindPixelBuffer();
556
Chris Craikdd8697c2013-02-22 10:41:36 -0800557 blurImage(&dataBuffer, paddedWidth, paddedHeight, radius);
558 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700559
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700560 image.width = paddedWidth;
561 image.height = paddedHeight;
562 image.image = dataBuffer;
563 image.penX = penX;
564 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -0800565
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700566 return image;
567}
Romain Guy694b5192010-07-21 21:33:20 -0700568
Romain Guy257ae352013-03-20 16:31:12 -0700569void FontRenderer::initRender(const Rect* clip, Rect* bounds, Functor* functor) {
Romain Guy694b5192010-07-21 21:33:20 -0700570 checkInit();
571
Romain Guy5b3b3522010-10-27 18:57:51 -0700572 mDrawn = false;
573 mBounds = bounds;
Romain Guy257ae352013-03-20 16:31:12 -0700574 mFunctor = functor;
Romain Guy09147fb2010-07-22 13:08:20 -0700575 mClip = clip;
Romain Guy671d6cf2012-01-18 12:39:17 -0800576}
Romain Guyff98fa52011-11-28 09:35:09 -0800577
Romain Guy671d6cf2012-01-18 12:39:17 -0800578void FontRenderer::finishRender() {
Romain Guy5b3b3522010-10-27 18:57:51 -0700579 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -0800580 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700581
Romain Guy661a87e2013-03-19 15:24:36 -0700582 issueDrawCommand();
Romain Guy671d6cf2012-01-18 12:39:17 -0800583}
584
Romain Guye3a9b242013-01-08 11:15:30 -0800585void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix) {
586 Font* font = Font::create(this, paint, matrix);
Chet Haasee816bae2012-08-09 13:39:02 -0700587 font->precache(paint, text, numGlyphs);
588}
589
Romain Guycf51a412013-04-08 19:40:31 -0700590void FontRenderer::endPrecaching() {
591 checkTextureUpdate();
592}
593
Romain Guy671d6cf2012-01-18 12:39:17 -0800594bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
595 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
Chris Craik527a3aa2013-03-04 10:19:31 -0800596 const float* positions, Rect* bounds, Functor* functor, bool forceFinish) {
Romain Guy671d6cf2012-01-18 12:39:17 -0800597 if (!mCurrentFont) {
598 ALOGE("No font set");
599 return false;
600 }
601
Romain Guy257ae352013-03-20 16:31:12 -0700602 initRender(clip, bounds, functor);
Romain Guy671d6cf2012-01-18 12:39:17 -0800603 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
Chris Craik527a3aa2013-03-04 10:19:31 -0800604
605 if (forceFinish) {
606 finishRender();
607 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700608
609 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700610}
611
Romain Guy97771732012-02-28 18:17:02 -0800612bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
613 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
614 float hOffset, float vOffset, Rect* bounds) {
615 if (!mCurrentFont) {
616 ALOGE("No font set");
617 return false;
618 }
619
Romain Guy257ae352013-03-20 16:31:12 -0700620 initRender(clip, bounds, NULL);
Romain Guy97771732012-02-28 18:17:02 -0800621 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
622 finishRender();
623
624 return mDrawn;
625}
626
Romain Guy9b1204b2012-09-04 15:22:57 -0700627void FontRenderer::removeFont(const Font* font) {
Romain Guye3a9b242013-01-08 11:15:30 -0800628 mActiveFonts.remove(font->getDescription());
Romain Guy9b1204b2012-09-04 15:22:57 -0700629
630 if (mCurrentFont == font) {
631 mCurrentFont = NULL;
632 }
633}
634
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800635void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) {
636 if (width * height * radius < RS_MIN_INPUT_CUTOFF) {
637 float *gaussian = new float[2 * radius + 1];
Romain Guy6e200402013-03-08 11:28:22 -0800638 Blur::generateGaussianWeights(gaussian, radius);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700639
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800640 uint8_t* scratch = new uint8_t[width * height];
Romain Guy6e200402013-03-08 11:28:22 -0800641 Blur::horizontal(gaussian, radius, *image, scratch, width, height);
642 Blur::vertical(gaussian, radius, scratch, *image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -0800643
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800644 delete[] gaussian;
645 delete[] scratch;
Chris Craikdd8697c2013-02-22 10:41:36 -0800646 return;
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800647 }
Romain Guyd71dd362011-12-12 19:03:35 -0800648
Romain Guy6e200402013-03-08 11:28:22 -0800649 uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800650
651 if (mRs.get() == 0) {
652 mRs = new RSC::RS();
653 if (!mRs->init(true, true)) {
654 ALOGE("blur RS failed to init");
655 }
656
657 mRsElement = RSC::Element::A_8(mRs);
658 mRsScript = new RSC::ScriptIntrinsicBlur(mRs, mRsElement);
659 }
660
661 sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0);
662 sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
663 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, *image);
664 sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
665 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, outImage);
666
667 mRsScript->setRadius(radius);
668 mRsScript->blur(ain, aout);
669
670 // replace the original image's pointer, avoiding a copy back to the original buffer
Ben Cheng15641a62013-02-20 13:20:03 -0800671 free(*image);
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800672 *image = outImage;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700673}
674
Romain Guycf51a412013-04-08 19:40:31 -0700675uint32_t FontRenderer::getCacheSize() const {
676 uint32_t size = 0;
677 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
678 CacheTexture* cacheTexture = mCacheTextures[i];
679 if (cacheTexture && cacheTexture->getPixelBuffer()) {
680 size += cacheTexture->getPixelBuffer()->getSize();
681 }
682 }
683 return size;
684}
685
Romain Guy694b5192010-07-21 21:33:20 -0700686}; // namespace uirenderer
687}; // namespace android