blob: 44dc731c6be765b5a6e1806b6f2e5c1ef06bd8c8 [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 Guy9f5dab32012-09-04 12:55:44 -070036#include "Rect.h"
Romain Guy51769a62010-07-23 00:28:00 -070037
Romain Guy694b5192010-07-21 21:33:20 -070038namespace android {
39namespace uirenderer {
40
Chris Craikf2d8ccc2013-02-13 16:14:17 -080041// blur inputs smaller than this constant will bypass renderscript
42#define RS_MIN_INPUT_CUTOFF 10000
43
Romain Guy694b5192010-07-21 21:33:20 -070044///////////////////////////////////////////////////////////////////////////////
Romain Guy694b5192010-07-21 21:33:20 -070045// FontRenderer
46///////////////////////////////////////////////////////////////////////////////
47
Romain Guy514fb182011-01-19 14:38:29 -080048static bool sLogFontRendererCreate = true;
49
Romain Guye3a9b242013-01-08 11:15:30 -080050FontRenderer::FontRenderer() :
51 mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity) {
52
Romain Guyc9855a52011-01-21 21:14:15 -080053 if (sLogFontRendererCreate) {
54 INIT_LOGD("Creating FontRenderer");
55 }
Romain Guy51769a62010-07-23 00:28:00 -070056
Romain Guyb45c0c92010-08-26 20:35:23 -070057 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -070058 mInitialized = false;
59 mMaxNumberOfQuads = 1024;
Romain Guy694b5192010-07-21 21:33:20 -070060
Chet Haase7de0cb12011-12-05 16:35:38 -080061 mCurrentCacheTexture = NULL;
Romain Guy9cccc2b92010-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 Guy9cccc2b92010-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 }
Chet Haasee816bae2012-08-09 13:39:02 -0700136
Romain Guy80872462012-09-04 16:42:01 -0700137#if DEBUG_FONT_RENDERER
Chet Haase378e9192012-08-15 15:54:54 -0700138 uint16_t totalGlyphs = 0;
139 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
Romain Guy80872462012-09-04 16:42:01 -0700140 totalGlyphs += mCacheTextures[i]->getGlyphCount();
Chet Haase378e9192012-08-15 15:54:54 -0700141 // Erase caches, just as a debugging facility
Romain Guy80872462012-09-04 16:42:01 -0700142 if (mCacheTextures[i]->getTexture()) {
143 memset(mCacheTextures[i]->getTexture(), 0,
144 mCacheTextures[i]->getWidth() * mCacheTextures[i]->getHeight());
Chet Haase378e9192012-08-15 15:54:54 -0700145 }
Chet Haasee816bae2012-08-09 13:39:02 -0700146 }
147 ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
148#endif
Romain Guy694b5192010-07-21 21:33:20 -0700149}
150
Chet Haase9a824562011-12-16 15:44:59 -0800151void FontRenderer::flushLargeCaches() {
Chet Haase378e9192012-08-15 15:54:54 -0700152 // Start from 1; don't deallocate smallest/default texture
153 for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
154 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guy80872462012-09-04 16:42:01 -0700155 if (cacheTexture->getTexture()) {
Chet Haase378e9192012-08-15 15:54:54 -0700156 cacheTexture->init();
Romain Guye3a9b242013-01-08 11:15:30 -0800157 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
158 while (it.next()) {
159 it.value()->invalidateTextureCache(cacheTexture);
Chet Haasee816bae2012-08-09 13:39:02 -0700160 }
Romain Guy80872462012-09-04 16:42:01 -0700161 cacheTexture->releaseTexture();
Chet Haase9a824562011-12-16 15:44:59 -0800162 }
163 }
Chet Haase9a824562011-12-16 15:44:59 -0800164}
165
Chet Haase378e9192012-08-15 15:54:54 -0700166CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
167 uint32_t* startX, uint32_t* startY) {
168 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
169 if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
170 return mCacheTextures[i];
171 }
172 }
173 // Could not fit glyph into current cache textures
174 return NULL;
175}
176
Chet Haase7de0cb12011-12-05 16:35:38 -0800177void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
Chet Haasef942cf12012-08-30 09:06:46 -0700178 uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700179 checkInit();
Romain Guya4adcf02013-02-28 12:15:35 -0800180
181 // If the glyph bitmap is empty let's assum the glyph is valid
182 // so we can avoid doing extra work later on
183 if (glyph.fWidth == 0 || glyph.fHeight == 0) {
184 cachedGlyph->mIsValid = true;
185 cachedGlyph->mCacheTexture = NULL;
186 return;
187 }
188
Chet Haase7de0cb12011-12-05 16:35:38 -0800189 cachedGlyph->mIsValid = false;
Romain Guya4adcf02013-02-28 12:15:35 -0800190
Romain Guy694b5192010-07-21 21:33:20 -0700191 // If the glyph is too tall, don't cache it
Chet Haase378e9192012-08-15 15:54:54 -0700192 if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
Romain Guy80872462012-09-04 16:42:01 -0700193 mCacheTextures[mCacheTextures.size() - 1]->getHeight()) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700194 ALOGE("Font size too large to fit in cache. width, height = %i, %i",
195 (int) glyph.fWidth, (int) glyph.fHeight);
Chet Haase7de0cb12011-12-05 16:35:38 -0800196 return;
Romain Guy694b5192010-07-21 21:33:20 -0700197 }
198
199 // Now copy the bitmap into the cache texture
200 uint32_t startX = 0;
201 uint32_t startY = 0;
202
Chet Haase378e9192012-08-15 15:54:54 -0700203 CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700204
Chet Haase378e9192012-08-15 15:54:54 -0700205 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700206 if (!precaching) {
207 // If the new glyph didn't fit and we are not just trying to precache it,
208 // clear out the cache and try again
209 flushAllAndInvalidate();
210 cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
211 }
Romain Guy694b5192010-07-21 21:33:20 -0700212
Chet Haase378e9192012-08-15 15:54:54 -0700213 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700214 // either the glyph didn't fit or we're precaching and will cache it when we draw
Chet Haase7de0cb12011-12-05 16:35:38 -0800215 return;
Romain Guy694b5192010-07-21 21:33:20 -0700216 }
217 }
218
Chet Haase378e9192012-08-15 15:54:54 -0700219 cachedGlyph->mCacheTexture = cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800220
Romain Guy694b5192010-07-21 21:33:20 -0700221 *retOriginX = startX;
222 *retOriginY = startY;
223
224 uint32_t endX = startX + glyph.fWidth;
225 uint32_t endY = startY + glyph.fHeight;
226
Romain Guy80872462012-09-04 16:42:01 -0700227 uint32_t cacheWidth = cacheTexture->getWidth();
Romain Guy694b5192010-07-21 21:33:20 -0700228
Romain Guy80872462012-09-04 16:42:01 -0700229 if (!cacheTexture->getTexture()) {
230 Caches::getInstance().activeTexture(0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800231 // Large-glyph texture memory is allocated only as needed
Romain Guy80872462012-09-04 16:42:01 -0700232 cacheTexture->allocateTexture();
Chet Haase7de0cb12011-12-05 16:35:38 -0800233 }
Romain Guy661a87e2013-03-19 15:24:36 -0700234 if (!cacheTexture->mesh()) {
235 cacheTexture->allocateMesh();
236 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700237
Romain Guyb969a0d2013-02-05 14:38:40 -0800238 // Tells us whether the glyphs is B&W (1 bit per pixel)
239 // or anti-aliased (8 bits per pixel)
240 SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
Romain Guy694b5192010-07-21 21:33:20 -0700241
Romain Guyb969a0d2013-02-05 14:38:40 -0800242 uint8_t* cacheBuffer = cacheTexture->getTexture();
Romain Guy694b5192010-07-21 21:33:20 -0700243 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
Romain Guy33fa1f72012-08-07 19:09:57 -0700244
Romain Guyb969a0d2013-02-05 14:38:40 -0800245 // Copy the glyph image, taking the mask format into account
246 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
247 int stride = glyph.rowBytes();
248
Romain Guy0b58a3d2013-03-05 12:16:27 -0800249 uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
250 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
251
Romain Guyb969a0d2013-02-05 14:38:40 -0800252 switch (format) {
253 case SkMask::kA8_Format: {
254 if (mGammaTable) {
255 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
Romain Guy0b58a3d2013-03-05 12:16:27 -0800256 row = cacheY * cacheWidth;
257 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800258 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
259 uint8_t tempCol = bitmapBuffer[bY + bX];
Romain Guy0b58a3d2013-03-05 12:16:27 -0800260 cacheBuffer[row + cacheX] = mGammaTable[tempCol];
Romain Guyb969a0d2013-02-05 14:38:40 -0800261 }
Romain Guy0b58a3d2013-03-05 12:16:27 -0800262 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800263 }
264 } else {
265 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
Romain Guy0b58a3d2013-03-05 12:16:27 -0800266 row = cacheY * cacheWidth;
267 memcpy(&cacheBuffer[row + startX], &bitmapBuffer[bY], glyph.fWidth);
268 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
269 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800270 }
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700271 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800272 break;
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700273 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800274 case SkMask::kBW_Format: {
275 static const uint8_t COLORS[2] = { 0, 255 };
276
277 for (cacheY = startY; cacheY < endY; cacheY++) {
278 cacheX = startX;
279 int rowBytes = stride;
280 uint8_t* buffer = bitmapBuffer;
281
Romain Guy0b58a3d2013-03-05 12:16:27 -0800282 row = cacheY * cacheWidth;
283 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800284 while (--rowBytes >= 0) {
285 uint8_t b = *buffer++;
286 for (int8_t mask = 7; mask >= 0 && cacheX < endX; mask--) {
287 cacheBuffer[cacheY * cacheWidth + cacheX++] = COLORS[(b >> mask) & 0x1];
288 }
289 }
Romain Guy0b58a3d2013-03-05 12:16:27 -0800290 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
Romain Guyb969a0d2013-02-05 14:38:40 -0800291
292 bitmapBuffer += stride;
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700293 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800294 break;
Romain Guy694b5192010-07-21 21:33:20 -0700295 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800296 default:
297 ALOGW("Unkown glyph format: 0x%x", format);
298 break;
Romain Guy694b5192010-07-21 21:33:20 -0700299 }
Romain Guy97771732012-02-28 18:17:02 -0800300
Romain Guy0b58a3d2013-03-05 12:16:27 -0800301 row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
302 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
303
Chet Haase7de0cb12011-12-05 16:35:38 -0800304 cachedGlyph->mIsValid = true;
Romain Guy694b5192010-07-21 21:33:20 -0700305}
306
Chet Haase7de0cb12011-12-05 16:35:38 -0800307CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
Romain Guy661a87e2013-03-19 15:24:36 -0700308 CacheTexture* cacheTexture = new CacheTexture(width, height, mMaxNumberOfQuads);
Romain Guy9d9758a2012-05-14 15:19:58 -0700309
Chet Haase2a47c142011-12-14 15:22:56 -0800310 if (allocate) {
Romain Guy80872462012-09-04 16:42:01 -0700311 Caches::getInstance().activeTexture(0);
312 cacheTexture->allocateTexture();
Romain Guy661a87e2013-03-19 15:24:36 -0700313 cacheTexture->allocateMesh();
Chet Haase2a47c142011-12-14 15:22:56 -0800314 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700315
Chet Haase2a47c142011-12-14 15:22:56 -0800316 return cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800317}
318
319void FontRenderer::initTextTexture() {
Chet Haase378e9192012-08-15 15:54:54 -0700320 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
321 delete mCacheTextures[i];
Romain Guy9d9758a2012-05-14 15:19:58 -0700322 }
Chet Haase378e9192012-08-15 15:54:54 -0700323 mCacheTextures.clear();
Romain Guy9d9758a2012-05-14 15:19:58 -0700324
Chet Haase7de0cb12011-12-05 16:35:38 -0800325 mUploadTexture = false;
Chet Haase378e9192012-08-15 15:54:54 -0700326 mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
Chet Haaseeb32a492012-08-31 13:54:03 -0700327 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
328 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
329 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false));
Chet Haase378e9192012-08-15 15:54:54 -0700330 mCurrentCacheTexture = mCacheTextures[0];
Romain Guy694b5192010-07-21 21:33:20 -0700331}
332
333// Avoid having to reallocate memory and render quad by quad
334void FontRenderer::initVertexArrayBuffers() {
Romain Guyd71dd362011-12-12 19:03:35 -0800335 uint32_t numIndices = mMaxNumberOfQuads * 6;
336 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700337 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700338
339 // Four verts, two triangles , six indices per quad
340 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
341 int i6 = i * 6;
342 int i4 = i * 4;
343
344 indexBufferData[i6 + 0] = i4 + 0;
345 indexBufferData[i6 + 1] = i4 + 1;
346 indexBufferData[i6 + 2] = i4 + 2;
347
348 indexBufferData[i6 + 3] = i4 + 0;
349 indexBufferData[i6 + 4] = i4 + 2;
350 indexBufferData[i6 + 5] = i4 + 3;
351 }
352
353 glGenBuffers(1, &mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800354 Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700355 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
Romain Guy694b5192010-07-21 21:33:20 -0700356
357 free(indexBufferData);
Romain Guy694b5192010-07-21 21:33:20 -0700358}
359
360// We don't want to allocate anything unless we actually draw text
361void FontRenderer::checkInit() {
362 if (mInitialized) {
363 return;
364 }
365
366 initTextTexture();
367 initVertexArrayBuffers();
368
369 mInitialized = true;
370}
371
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700372void FontRenderer::checkTextureUpdate() {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900373 if (!mUploadTexture) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700374 return;
Romain Guy694b5192010-07-21 21:33:20 -0700375 }
376
Romain Guy2d4fd362011-12-13 22:00:19 -0800377 Caches& caches = Caches::getInstance();
378 GLuint lastTextureId = 0;
Romain Guy09087642013-04-04 12:27:54 -0700379
380 // OpenGL ES 3.0+ lets us specify the row length for unpack operations such
381 // as glTexSubImage2D(). This allows us to upload a sub-rectangle of a texture.
382 // With OpenGL ES 2.0 we have to upload entire stripes instead.
383 const bool hasUnpackRowLength = Extensions::getInstance().getMajorGlVersion() >= 3;
384 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
385
Chet Haase378e9192012-08-15 15:54:54 -0700386 // Iterate over all the cache textures and see which ones need to be updated
387 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
388 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guy80872462012-09-04 16:42:01 -0700389 if (cacheTexture->isDirty() && cacheTexture->getTexture()) {
Chet Haaseb92d8f72012-09-21 08:40:46 -0700390 const Rect* dirtyRect = cacheTexture->getDirtyRect();
Romain Guy09087642013-04-04 12:27:54 -0700391 uint32_t x = hasUnpackRowLength ? dirtyRect->left : 0;
Chet Haaseb92d8f72012-09-21 08:40:46 -0700392 uint32_t y = dirtyRect->top;
Romain Guy80872462012-09-04 16:42:01 -0700393 uint32_t width = cacheTexture->getWidth();
Chet Haaseb92d8f72012-09-21 08:40:46 -0700394 uint32_t height = dirtyRect->getHeight();
Romain Guy09087642013-04-04 12:27:54 -0700395 void* textureData = cacheTexture->getTexture() + y * width + x;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700396
Romain Guy80872462012-09-04 16:42:01 -0700397 if (cacheTexture->getTextureId() != lastTextureId) {
398 lastTextureId = cacheTexture->getTextureId();
Romain Guy2d4fd362011-12-13 22:00:19 -0800399 caches.activeTexture(0);
Romain Guy80872462012-09-04 16:42:01 -0700400 glBindTexture(GL_TEXTURE_2D, lastTextureId);
Romain Guy09087642013-04-04 12:27:54 -0700401
402 // The unpack row length only needs to be specified when a new
403 // texture is bound
404 if (hasUnpackRowLength) {
405 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
406 }
Romain Guy2d4fd362011-12-13 22:00:19 -0800407 }
Romain Guy09087642013-04-04 12:27:54 -0700408
409 // If we can upload a sub-rectangle, use the dirty rect width
410 // instead of the width of the entire texture
411 if (hasUnpackRowLength) {
412 width = dirtyRect->getWidth();
413 }
414
Chet Haasee816bae2012-08-09 13:39:02 -0700415#if DEBUG_FONT_RENDERER
Chet Haaseb92d8f72012-09-21 08:40:46 -0700416 ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d",
417 i, x, y, width, height);
Chet Haasee816bae2012-08-09 13:39:02 -0700418#endif
Romain Guy09087642013-04-04 12:27:54 -0700419
Chet Haaseb92d8f72012-09-21 08:40:46 -0700420 glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700421 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Romain Guy09087642013-04-04 12:27:54 -0700422
Romain Guy80872462012-09-04 16:42:01 -0700423 cacheTexture->setDirty(false);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700424 }
425 }
426
Romain Guy09087642013-04-04 12:27:54 -0700427 // Reset to default unpack row length to avoid affecting texture
428 // uploads in other parts of the renderer
429 if (hasUnpackRowLength) {
430 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
431 }
432
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700433 mUploadTexture = false;
434}
435
436void FontRenderer::issueDrawCommand() {
Romain Guy661a87e2013-03-19 15:24:36 -0700437 bool first = true;
438 bool force = false;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700439
Romain Guy115096f2013-03-19 11:32:41 -0700440 GLuint lastId = 0;
Romain Guy661a87e2013-03-19 15:24:36 -0700441 Caches& caches = Caches::getInstance();
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900442
Romain Guy661a87e2013-03-19 15:24:36 -0700443 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
444 CacheTexture* texture = mCacheTextures[i];
445 if (texture->canDraw()) {
446 if (first) {
Romain Guy257ae352013-03-20 16:31:12 -0700447 if (mFunctor) (*mFunctor)(0, NULL);
448
Romain Guy661a87e2013-03-19 15:24:36 -0700449 checkTextureUpdate();
450 caches.bindIndicesBuffer(mIndexBufferID);
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900451
Romain Guy661a87e2013-03-19 15:24:36 -0700452 if (!mDrawn) {
453 // If returns true, a VBO was bound and we must
454 // rebind our vertex attrib pointers even if
455 // they have the same values as the current pointers
456 force = caches.unbindMeshBuffer();
457 }
458
459 caches.activeTexture(0);
460 first = false;
461 }
462
463 glBindTexture(GL_TEXTURE_2D, texture->getTextureId());
464 texture->setLinearFiltering(mLinearFiltering, false);
465
466 TextureVertex* mesh = texture->mesh();
467 caches.bindPositionVertexPointer(force, &mesh[0].position[0]);
468 caches.bindTexCoordsVertexPointer(force, &mesh[0].texture[0]);
469 force = false;
470
471 glDrawElements(GL_TRIANGLES, texture->meshElementCount(),
472 GL_UNSIGNED_SHORT, texture->indices());
473
474 texture->resetMesh();
Romain Guy115096f2013-03-19 11:32:41 -0700475 }
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900476 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700477
478 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700479}
480
Romain Guy97771732012-02-28 18:17:02 -0800481void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
482 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800483 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800484 if (texture != mCurrentCacheTexture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800485 // Now use the new texture id
486 mCurrentCacheTexture = texture;
487 }
Romain Guy09147fb2010-07-22 13:08:20 -0700488
Romain Guy661a87e2013-03-19 15:24:36 -0700489 mCurrentCacheTexture->addQuad(x1, y1, u1, v1, x2, y2, u2, v2,
490 x3, y3, u3, v3, x4, y4, u4, v4);
Romain Guy97771732012-02-28 18:17:02 -0800491}
492
493void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
494 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
495 float x4, float y4, float u4, float v4, CacheTexture* texture) {
496
497 if (mClip &&
498 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
499 return;
500 }
501
502 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 -0700503
Romain Guy5b3b3522010-10-27 18:57:51 -0700504 if (mBounds) {
505 mBounds->left = fmin(mBounds->left, x1);
506 mBounds->top = fmin(mBounds->top, y3);
507 mBounds->right = fmax(mBounds->right, x3);
508 mBounds->bottom = fmax(mBounds->bottom, y1);
509 }
510
Romain Guy661a87e2013-03-19 15:24:36 -0700511 if (mCurrentCacheTexture->endOfMesh()) {
Romain Guy694b5192010-07-21 21:33:20 -0700512 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700513 }
514}
515
Romain Guy97771732012-02-28 18:17:02 -0800516void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
517 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
518 float x4, float y4, float u4, float v4, CacheTexture* texture) {
519
520 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
521
522 if (mBounds) {
523 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
524 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
525 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
526 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
527 }
528
Romain Guy661a87e2013-03-19 15:24:36 -0700529 if (mCurrentCacheTexture->endOfMesh()) {
Romain Guy97771732012-02-28 18:17:02 -0800530 issueDrawCommand();
Romain Guy97771732012-02-28 18:17:02 -0800531 }
532}
533
Romain Guye3a9b242013-01-08 11:15:30 -0800534void FontRenderer::setFont(SkPaint* paint, const mat4& matrix) {
535 mCurrentFont = Font::create(this, paint, matrix);
Romain Guy694b5192010-07-21 21:33:20 -0700536}
Romain Guy7975fb62010-10-01 16:36:14 -0700537
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700538FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Raph Levien416a8472012-07-19 22:48:17 -0700539 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700540 checkInit();
541
542 if (!mCurrentFont) {
543 DropShadow image;
544 image.width = 0;
545 image.height = 0;
546 image.image = NULL;
547 image.penX = 0;
548 image.penY = 0;
549 return image;
550 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700551
Romain Guy2d4fd362011-12-13 22:00:19 -0800552 mDrawn = false;
Romain Guyff98fa52011-11-28 09:35:09 -0800553 mClip = NULL;
554 mBounds = NULL;
555
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700556 Rect bounds;
Raph Levien416a8472012-07-19 22:48:17 -0700557 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
Romain Guyff98fa52011-11-28 09:35:09 -0800558
Romain Guy1e45aae2010-08-13 19:39:53 -0700559 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
560 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Romain Guyff98fa52011-11-28 09:35:09 -0800561
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800562 // Align buffers for renderscript usage
563 if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) {
564 paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700565 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700566
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800567 int size = paddedWidth * paddedHeight;
Romain Guy6e200402013-03-08 11:28:22 -0800568 uint8_t* dataBuffer = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, size);
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800569 memset(dataBuffer, 0, size);
570
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700571 int penX = radius - bounds.left;
572 int penY = radius - bounds.bottom;
573
Chris Craikdd8697c2013-02-22 10:41:36 -0800574 if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) {
575 // text has non-whitespace, so draw and blur to create the shadow
576 // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted
577 // TODO: don't draw pure whitespace in the first place, and avoid needing this check
578 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
579 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
580
581 blurImage(&dataBuffer, paddedWidth, paddedHeight, radius);
582 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700583
584 DropShadow image;
585 image.width = paddedWidth;
586 image.height = paddedHeight;
587 image.image = dataBuffer;
588 image.penX = penX;
589 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -0800590
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700591 return image;
592}
Romain Guy694b5192010-07-21 21:33:20 -0700593
Romain Guy257ae352013-03-20 16:31:12 -0700594void FontRenderer::initRender(const Rect* clip, Rect* bounds, Functor* functor) {
Romain Guy694b5192010-07-21 21:33:20 -0700595 checkInit();
596
Romain Guy5b3b3522010-10-27 18:57:51 -0700597 mDrawn = false;
598 mBounds = bounds;
Romain Guy257ae352013-03-20 16:31:12 -0700599 mFunctor = functor;
Romain Guy09147fb2010-07-22 13:08:20 -0700600 mClip = clip;
Romain Guy671d6cf2012-01-18 12:39:17 -0800601}
Romain Guyff98fa52011-11-28 09:35:09 -0800602
Romain Guy671d6cf2012-01-18 12:39:17 -0800603void FontRenderer::finishRender() {
Romain Guy5b3b3522010-10-27 18:57:51 -0700604 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -0800605 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700606
Romain Guy661a87e2013-03-19 15:24:36 -0700607 issueDrawCommand();
Romain Guy671d6cf2012-01-18 12:39:17 -0800608}
609
Romain Guye3a9b242013-01-08 11:15:30 -0800610void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix) {
611 Font* font = Font::create(this, paint, matrix);
Chet Haasee816bae2012-08-09 13:39:02 -0700612 font->precache(paint, text, numGlyphs);
613}
614
Romain Guy671d6cf2012-01-18 12:39:17 -0800615bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
616 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
Romain Guy257ae352013-03-20 16:31:12 -0700617 const float* positions, Rect* bounds, Functor* functor) {
Romain Guy671d6cf2012-01-18 12:39:17 -0800618 if (!mCurrentFont) {
619 ALOGE("No font set");
620 return false;
621 }
622
Romain Guy257ae352013-03-20 16:31:12 -0700623 initRender(clip, bounds, functor);
Romain Guy671d6cf2012-01-18 12:39:17 -0800624 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
625 finishRender();
Romain Guy5b3b3522010-10-27 18:57:51 -0700626
627 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700628}
629
Romain Guy97771732012-02-28 18:17:02 -0800630bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
631 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
632 float hOffset, float vOffset, Rect* bounds) {
633 if (!mCurrentFont) {
634 ALOGE("No font set");
635 return false;
636 }
637
Romain Guy257ae352013-03-20 16:31:12 -0700638 initRender(clip, bounds, NULL);
Romain Guy97771732012-02-28 18:17:02 -0800639 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
640 finishRender();
641
642 return mDrawn;
643}
644
Romain Guy9b1204b2012-09-04 15:22:57 -0700645void FontRenderer::removeFont(const Font* font) {
Romain Guye3a9b242013-01-08 11:15:30 -0800646 mActiveFonts.remove(font->getDescription());
Romain Guy9b1204b2012-09-04 15:22:57 -0700647
648 if (mCurrentFont == font) {
649 mCurrentFont = NULL;
650 }
651}
652
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800653void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) {
654 if (width * height * radius < RS_MIN_INPUT_CUTOFF) {
655 float *gaussian = new float[2 * radius + 1];
Romain Guy6e200402013-03-08 11:28:22 -0800656 Blur::generateGaussianWeights(gaussian, radius);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700657
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800658 uint8_t* scratch = new uint8_t[width * height];
Romain Guy6e200402013-03-08 11:28:22 -0800659 Blur::horizontal(gaussian, radius, *image, scratch, width, height);
660 Blur::vertical(gaussian, radius, scratch, *image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -0800661
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800662 delete[] gaussian;
663 delete[] scratch;
Chris Craikdd8697c2013-02-22 10:41:36 -0800664 return;
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800665 }
Romain Guyd71dd362011-12-12 19:03:35 -0800666
Romain Guy6e200402013-03-08 11:28:22 -0800667 uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800668
669 if (mRs.get() == 0) {
670 mRs = new RSC::RS();
671 if (!mRs->init(true, true)) {
672 ALOGE("blur RS failed to init");
673 }
674
675 mRsElement = RSC::Element::A_8(mRs);
676 mRsScript = new RSC::ScriptIntrinsicBlur(mRs, mRsElement);
677 }
678
679 sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0);
680 sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
681 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, *image);
682 sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
683 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, outImage);
684
685 mRsScript->setRadius(radius);
686 mRsScript->blur(ain, aout);
687
688 // replace the original image's pointer, avoiding a copy back to the original buffer
Ben Cheng15641a612013-02-20 13:20:03 -0800689 free(*image);
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800690 *image = outImage;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700691}
692
Romain Guy694b5192010-07-21 21:33:20 -0700693}; // namespace uirenderer
694}; // namespace android