blob: 77b8df19551bc9b045ba5511caf45fee6802d53f [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
Chris Craikf2d8ccc2013-02-13 16:14:17 -080026#include "RenderScript.h"
27
28#include "utils/Timing.h"
Romain Guy15bc6432011-12-13 13:11:32 -080029#include "Caches.h"
Romain Guyc9855a52011-01-21 21:14:15 -080030#include "Debug.h"
Romain Guy51769a62010-07-23 00:28:00 -070031#include "FontRenderer.h"
Romain Guy9f5dab32012-09-04 12:55:44 -070032#include "Rect.h"
Romain Guy51769a62010-07-23 00:28:00 -070033
Romain Guy694b5192010-07-21 21:33:20 -070034namespace android {
35namespace uirenderer {
36
Chris Craikf2d8ccc2013-02-13 16:14:17 -080037// blur inputs smaller than this constant will bypass renderscript
38#define RS_MIN_INPUT_CUTOFF 10000
39
Romain Guy694b5192010-07-21 21:33:20 -070040///////////////////////////////////////////////////////////////////////////////
Romain Guy694b5192010-07-21 21:33:20 -070041// FontRenderer
42///////////////////////////////////////////////////////////////////////////////
43
Romain Guy514fb182011-01-19 14:38:29 -080044static bool sLogFontRendererCreate = true;
45
Romain Guye3a9b242013-01-08 11:15:30 -080046FontRenderer::FontRenderer() :
47 mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity) {
48
Romain Guyc9855a52011-01-21 21:14:15 -080049 if (sLogFontRendererCreate) {
50 INIT_LOGD("Creating FontRenderer");
51 }
Romain Guy51769a62010-07-23 00:28:00 -070052
Romain Guyb45c0c92010-08-26 20:35:23 -070053 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -070054 mInitialized = false;
55 mMaxNumberOfQuads = 1024;
56 mCurrentQuadIndex = 0;
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +090057 mLastQuadIndex = 0;
Romain Guy694b5192010-07-21 21:33:20 -070058
Romain Guy9b1204b2012-09-04 15:22:57 -070059 mTextMesh = NULL;
Chet Haase7de0cb12011-12-05 16:35:38 -080060 mCurrentCacheTexture = NULL;
Romain Guy9cccc2b2010-08-07 23:46:15 -070061
Chet Haase2a47c142011-12-14 15:22:56 -080062 mLinearFiltering = false;
63
Romain Guy694b5192010-07-21 21:33:20 -070064 mIndexBufferID = 0;
65
Chet Haaseeb32a492012-08-31 13:54:03 -070066 mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH;
67 mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT;
68 mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH;
69 mLargeCacheHeight = DEFAULT_TEXT_LARGE_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -070070
71 char property[PROPERTY_VALUE_MAX];
Chet Haaseeb32a492012-08-31 13:54:03 -070072 if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, NULL) > 0) {
Chet Haase7de0cb12011-12-05 16:35:38 -080073 mSmallCacheWidth = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -070074 }
Romain Guy9f5dab32012-09-04 12:55:44 -070075
Chet Haaseeb32a492012-08-31 13:54:03 -070076 if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, NULL) > 0) {
Chet Haase7de0cb12011-12-05 16:35:38 -080077 mSmallCacheHeight = atoi(property);
Chet Haaseeb32a492012-08-31 13:54:03 -070078 }
Romain Guy9f5dab32012-09-04 12:55:44 -070079
Chet Haaseeb32a492012-08-31 13:54:03 -070080 if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, NULL) > 0) {
81 mLargeCacheWidth = atoi(property);
82 }
Romain Guy9f5dab32012-09-04 12:55:44 -070083
Chet Haaseeb32a492012-08-31 13:54:03 -070084 if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, NULL) > 0) {
85 mLargeCacheHeight = atoi(property);
86 }
Romain Guy9f5dab32012-09-04 12:55:44 -070087
88 uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize;
89 mSmallCacheWidth = mSmallCacheWidth > maxTextureSize ? maxTextureSize : mSmallCacheWidth;
90 mSmallCacheHeight = mSmallCacheHeight > maxTextureSize ? maxTextureSize : mSmallCacheHeight;
91 mLargeCacheWidth = mLargeCacheWidth > maxTextureSize ? maxTextureSize : mLargeCacheWidth;
92 mLargeCacheHeight = mLargeCacheHeight > maxTextureSize ? maxTextureSize : mLargeCacheHeight;
93
Chet Haaseeb32a492012-08-31 13:54:03 -070094 if (sLogFontRendererCreate) {
95 INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
96 mSmallCacheWidth, mSmallCacheHeight,
97 mLargeCacheWidth, mLargeCacheHeight >> 1,
98 mLargeCacheWidth, mLargeCacheHeight >> 1,
99 mLargeCacheWidth, mLargeCacheHeight);
Romain Guy51769a62010-07-23 00:28:00 -0700100 }
Romain Guy514fb182011-01-19 14:38:29 -0800101
102 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -0700103}
104
105FontRenderer::~FontRenderer() {
Chet Haase378e9192012-08-15 15:54:54 -0700106 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
107 delete mCacheTextures[i];
Romain Guy694b5192010-07-21 21:33:20 -0700108 }
Chet Haase378e9192012-08-15 15:54:54 -0700109 mCacheTextures.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700110
Romain Guy9cccc2b2010-08-07 23:46:15 -0700111 if (mInitialized) {
Romain Guya9dd8202012-03-26 14:52:00 -0700112 // Unbinding the buffer shouldn't be necessary but it crashes with some drivers
113 Caches::getInstance().unbindIndicesBuffer();
Romain Guyb0317982012-03-21 11:52:52 -0700114 glDeleteBuffers(1, &mIndexBufferID);
115
Romain Guy9b1204b2012-09-04 15:22:57 -0700116 delete[] mTextMesh;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700117 }
Romain Guy694b5192010-07-21 21:33:20 -0700118
Romain Guye3a9b242013-01-08 11:15:30 -0800119 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
120 while (it.next()) {
121 delete it.value();
Romain Guy694b5192010-07-21 21:33:20 -0700122 }
Romain Guye3a9b242013-01-08 11:15:30 -0800123 mActiveFonts.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700124}
125
126void FontRenderer::flushAllAndInvalidate() {
127 if (mCurrentQuadIndex != 0) {
128 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700129 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700130
Romain Guye3a9b242013-01-08 11:15:30 -0800131 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
132 while (it.next()) {
133 it.value()->invalidateTextureCache();
Romain Guy694b5192010-07-21 21:33:20 -0700134 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700135
Chet Haase378e9192012-08-15 15:54:54 -0700136 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
137 mCacheTextures[i]->init();
Romain Guy694b5192010-07-21 21:33:20 -0700138 }
Chet Haasee816bae2012-08-09 13:39:02 -0700139
Romain Guy80872462012-09-04 16:42:01 -0700140#if DEBUG_FONT_RENDERER
Chet Haase378e9192012-08-15 15:54:54 -0700141 uint16_t totalGlyphs = 0;
142 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
Romain Guy80872462012-09-04 16:42:01 -0700143 totalGlyphs += mCacheTextures[i]->getGlyphCount();
Chet Haase378e9192012-08-15 15:54:54 -0700144 // Erase caches, just as a debugging facility
Romain Guy80872462012-09-04 16:42:01 -0700145 if (mCacheTextures[i]->getTexture()) {
146 memset(mCacheTextures[i]->getTexture(), 0,
147 mCacheTextures[i]->getWidth() * mCacheTextures[i]->getHeight());
Chet Haase378e9192012-08-15 15:54:54 -0700148 }
Chet Haasee816bae2012-08-09 13:39:02 -0700149 }
150 ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
151#endif
Romain Guy694b5192010-07-21 21:33:20 -0700152}
153
Chet Haase9a824562011-12-16 15:44:59 -0800154void FontRenderer::flushLargeCaches() {
Chet Haase378e9192012-08-15 15:54:54 -0700155 // Start from 1; don't deallocate smallest/default texture
156 for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
157 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guy80872462012-09-04 16:42:01 -0700158 if (cacheTexture->getTexture()) {
Chet Haase378e9192012-08-15 15:54:54 -0700159 cacheTexture->init();
Romain Guye3a9b242013-01-08 11:15:30 -0800160 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
161 while (it.next()) {
162 it.value()->invalidateTextureCache(cacheTexture);
Chet Haasee816bae2012-08-09 13:39:02 -0700163 }
Romain Guy80872462012-09-04 16:42:01 -0700164 cacheTexture->releaseTexture();
Chet Haase9a824562011-12-16 15:44:59 -0800165 }
166 }
Chet Haase9a824562011-12-16 15:44:59 -0800167}
168
Chet Haase378e9192012-08-15 15:54:54 -0700169CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
170 uint32_t* startX, uint32_t* startY) {
171 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
172 if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
173 return mCacheTextures[i];
174 }
175 }
176 // Could not fit glyph into current cache textures
177 return NULL;
178}
179
Chet Haase7de0cb12011-12-05 16:35:38 -0800180void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
Chet Haasef942cf12012-08-30 09:06:46 -0700181 uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700182 checkInit();
Romain Guya4adcf02013-02-28 12:15:35 -0800183
184 // If the glyph bitmap is empty let's assum the glyph is valid
185 // so we can avoid doing extra work later on
186 if (glyph.fWidth == 0 || glyph.fHeight == 0) {
187 cachedGlyph->mIsValid = true;
188 cachedGlyph->mCacheTexture = NULL;
189 return;
190 }
191
Chet Haase7de0cb12011-12-05 16:35:38 -0800192 cachedGlyph->mIsValid = false;
Romain Guya4adcf02013-02-28 12:15:35 -0800193
Romain Guy694b5192010-07-21 21:33:20 -0700194 // If the glyph is too tall, don't cache it
Chet Haase378e9192012-08-15 15:54:54 -0700195 if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
Romain Guy80872462012-09-04 16:42:01 -0700196 mCacheTextures[mCacheTextures.size() - 1]->getHeight()) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700197 ALOGE("Font size too large to fit in cache. width, height = %i, %i",
198 (int) glyph.fWidth, (int) glyph.fHeight);
Chet Haase7de0cb12011-12-05 16:35:38 -0800199 return;
Romain Guy694b5192010-07-21 21:33:20 -0700200 }
201
202 // Now copy the bitmap into the cache texture
203 uint32_t startX = 0;
204 uint32_t startY = 0;
205
Chet Haase378e9192012-08-15 15:54:54 -0700206 CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700207
Chet Haase378e9192012-08-15 15:54:54 -0700208 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700209 if (!precaching) {
210 // If the new glyph didn't fit and we are not just trying to precache it,
211 // clear out the cache and try again
212 flushAllAndInvalidate();
213 cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
214 }
Romain Guy694b5192010-07-21 21:33:20 -0700215
Chet Haase378e9192012-08-15 15:54:54 -0700216 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700217 // either the glyph didn't fit or we're precaching and will cache it when we draw
Chet Haase7de0cb12011-12-05 16:35:38 -0800218 return;
Romain Guy694b5192010-07-21 21:33:20 -0700219 }
220 }
221
Chet Haase378e9192012-08-15 15:54:54 -0700222 cachedGlyph->mCacheTexture = cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800223
Romain Guy694b5192010-07-21 21:33:20 -0700224 *retOriginX = startX;
225 *retOriginY = startY;
226
227 uint32_t endX = startX + glyph.fWidth;
228 uint32_t endY = startY + glyph.fHeight;
229
Romain Guy80872462012-09-04 16:42:01 -0700230 uint32_t cacheWidth = cacheTexture->getWidth();
Romain Guy694b5192010-07-21 21:33:20 -0700231
Romain Guy80872462012-09-04 16:42:01 -0700232 if (!cacheTexture->getTexture()) {
233 Caches::getInstance().activeTexture(0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800234 // Large-glyph texture memory is allocated only as needed
Romain Guy80872462012-09-04 16:42:01 -0700235 cacheTexture->allocateTexture();
Chet Haase7de0cb12011-12-05 16:35:38 -0800236 }
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 Guy0aa87bb2012-07-20 11:14:32 -0700308 CacheTexture* cacheTexture = new CacheTexture(width, height);
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();
Chet Haase2a47c142011-12-14 15:22:56 -0800313 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700314
Chet Haase2a47c142011-12-14 15:22:56 -0800315 return cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800316}
317
318void FontRenderer::initTextTexture() {
Chet Haase378e9192012-08-15 15:54:54 -0700319 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
320 delete mCacheTextures[i];
Romain Guy9d9758a2012-05-14 15:19:58 -0700321 }
Chet Haase378e9192012-08-15 15:54:54 -0700322 mCacheTextures.clear();
Romain Guy9d9758a2012-05-14 15:19:58 -0700323
Chet Haase7de0cb12011-12-05 16:35:38 -0800324 mUploadTexture = false;
Chet Haase378e9192012-08-15 15:54:54 -0700325 mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
Chet Haaseeb32a492012-08-31 13:54:03 -0700326 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
327 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
328 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false));
Chet Haase378e9192012-08-15 15:54:54 -0700329 mCurrentCacheTexture = mCacheTextures[0];
Romain Guy694b5192010-07-21 21:33:20 -0700330}
331
332// Avoid having to reallocate memory and render quad by quad
333void FontRenderer::initVertexArrayBuffers() {
Romain Guyd71dd362011-12-12 19:03:35 -0800334 uint32_t numIndices = mMaxNumberOfQuads * 6;
335 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700336 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700337
338 // Four verts, two triangles , six indices per quad
339 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
340 int i6 = i * 6;
341 int i4 = i * 4;
342
343 indexBufferData[i6 + 0] = i4 + 0;
344 indexBufferData[i6 + 1] = i4 + 1;
345 indexBufferData[i6 + 2] = i4 + 2;
346
347 indexBufferData[i6 + 3] = i4 + 0;
348 indexBufferData[i6 + 4] = i4 + 2;
349 indexBufferData[i6 + 5] = i4 + 3;
350 }
351
352 glGenBuffers(1, &mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800353 Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700354 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
Romain Guy694b5192010-07-21 21:33:20 -0700355
356 free(indexBufferData);
357
Romain Guyd71dd362011-12-12 19:03:35 -0800358 uint32_t coordSize = 2;
Romain Guy694b5192010-07-21 21:33:20 -0700359 uint32_t uvSize = 2;
360 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700361 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
Romain Guy9b1204b2012-09-04 15:22:57 -0700362 mTextMesh = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700363}
364
365// We don't want to allocate anything unless we actually draw text
366void FontRenderer::checkInit() {
367 if (mInitialized) {
368 return;
369 }
370
371 initTextTexture();
372 initVertexArrayBuffers();
373
374 mInitialized = true;
375}
376
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900377void FontRenderer::updateDrawParams() {
378 if (mCurrentQuadIndex != mLastQuadIndex) {
379 mDrawOffsets.add((uint16_t*)(mLastQuadIndex * sizeof(uint16_t) * 6));
380 mDrawCounts.add(mCurrentQuadIndex - mLastQuadIndex);
381 mDrawCacheTextures.add(mCurrentCacheTexture);
382 mLastQuadIndex = mCurrentQuadIndex;
383 }
384}
385
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700386void FontRenderer::checkTextureUpdate() {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900387 if (!mUploadTexture) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700388 return;
Romain Guy694b5192010-07-21 21:33:20 -0700389 }
390
Romain Guy2d4fd362011-12-13 22:00:19 -0800391 Caches& caches = Caches::getInstance();
392 GLuint lastTextureId = 0;
Chet Haase378e9192012-08-15 15:54:54 -0700393 // Iterate over all the cache textures and see which ones need to be updated
394 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
395 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guy80872462012-09-04 16:42:01 -0700396 if (cacheTexture->isDirty() && cacheTexture->getTexture()) {
Chet Haaseb92d8f72012-09-21 08:40:46 -0700397 // Can't copy inner rect; glTexSubimage expects pointer to deal with entire buffer
398 // of data. So expand the dirty rect to the encompassing horizontal stripe.
399 const Rect* dirtyRect = cacheTexture->getDirtyRect();
400 uint32_t x = 0;
401 uint32_t y = dirtyRect->top;
Romain Guy80872462012-09-04 16:42:01 -0700402 uint32_t width = cacheTexture->getWidth();
Chet Haaseb92d8f72012-09-21 08:40:46 -0700403 uint32_t height = dirtyRect->getHeight();
404 void* textureData = cacheTexture->getTexture() + y * width;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700405
Romain Guy80872462012-09-04 16:42:01 -0700406 if (cacheTexture->getTextureId() != lastTextureId) {
407 lastTextureId = cacheTexture->getTextureId();
Romain Guy2d4fd362011-12-13 22:00:19 -0800408 caches.activeTexture(0);
Romain Guy80872462012-09-04 16:42:01 -0700409 glBindTexture(GL_TEXTURE_2D, lastTextureId);
Romain Guy2d4fd362011-12-13 22:00:19 -0800410 }
Chet Haasee816bae2012-08-09 13:39:02 -0700411#if DEBUG_FONT_RENDERER
Chet Haaseb92d8f72012-09-21 08:40:46 -0700412 ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d",
413 i, x, y, width, height);
Chet Haasee816bae2012-08-09 13:39:02 -0700414#endif
Chet Haaseb92d8f72012-09-21 08:40:46 -0700415 glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700416 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Romain Guy80872462012-09-04 16:42:01 -0700417 cacheTexture->setDirty(false);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700418 }
419 }
420
421 mUploadTexture = false;
422}
423
424void FontRenderer::issueDrawCommand() {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900425 updateDrawParams();
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700426 checkTextureUpdate();
427
Romain Guy15bc6432011-12-13 13:11:32 -0800428 Caches& caches = Caches::getInstance();
Romain Guy2d4fd362011-12-13 22:00:19 -0800429 caches.bindIndicesBuffer(mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800430 if (!mDrawn) {
Romain Guy9b1204b2012-09-04 15:22:57 -0700431 float* buffer = mTextMesh;
Romain Guy15bc6432011-12-13 13:11:32 -0800432 int offset = 2;
433
434 bool force = caches.unbindMeshBuffer();
Chris Craikcb4d6002012-09-25 12:00:29 -0700435 caches.bindPositionVertexPointer(force, buffer);
436 caches.bindTexCoordsVertexPointer(force, buffer + offset);
Romain Guy15bc6432011-12-13 13:11:32 -0800437 }
438
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900439 for (uint32_t i = 0; i < mDrawOffsets.size(); i++) {
440 uint16_t* offset = mDrawOffsets[i];
441 uint32_t count = mDrawCounts[i];
442 CacheTexture* texture = mDrawCacheTextures[i];
443
444 caches.activeTexture(0);
445 glBindTexture(GL_TEXTURE_2D, texture->getTextureId());
446
447 texture->setLinearFiltering(mLinearFiltering, false);
448
449 glDrawElements(GL_TRIANGLES, count * 6, GL_UNSIGNED_SHORT, offset);
450 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700451
452 mDrawn = true;
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900453
454 mCurrentQuadIndex = 0;
455 mLastQuadIndex = 0;
456 mDrawOffsets.clear();
457 mDrawCounts.clear();
458 mDrawCacheTextures.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700459}
460
Romain Guy97771732012-02-28 18:17:02 -0800461void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
462 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800463 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800464 if (texture != mCurrentCacheTexture) {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900465 updateDrawParams();
Chet Haase7de0cb12011-12-05 16:35:38 -0800466 // Now use the new texture id
467 mCurrentCacheTexture = texture;
468 }
Romain Guy09147fb2010-07-22 13:08:20 -0700469
Romain Guy694b5192010-07-21 21:33:20 -0700470 const uint32_t vertsPerQuad = 4;
Romain Guyd71dd362011-12-12 19:03:35 -0800471 const uint32_t floatsPerVert = 4;
Romain Guy9b1204b2012-09-04 15:22:57 -0700472 float* currentPos = mTextMesh + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700473
Romain Guy694b5192010-07-21 21:33:20 -0700474 (*currentPos++) = x1;
475 (*currentPos++) = y1;
Romain Guy694b5192010-07-21 21:33:20 -0700476 (*currentPos++) = u1;
477 (*currentPos++) = v1;
478
479 (*currentPos++) = x2;
480 (*currentPos++) = y2;
Romain Guy694b5192010-07-21 21:33:20 -0700481 (*currentPos++) = u2;
482 (*currentPos++) = v2;
483
484 (*currentPos++) = x3;
485 (*currentPos++) = y3;
Romain Guy694b5192010-07-21 21:33:20 -0700486 (*currentPos++) = u3;
487 (*currentPos++) = v3;
488
489 (*currentPos++) = x4;
490 (*currentPos++) = y4;
Romain Guy694b5192010-07-21 21:33:20 -0700491 (*currentPos++) = u4;
492 (*currentPos++) = v4;
493
494 mCurrentQuadIndex++;
Romain Guy97771732012-02-28 18:17:02 -0800495}
496
497void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
498 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
499 float x4, float y4, float u4, float v4, CacheTexture* texture) {
500
501 if (mClip &&
502 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
503 return;
504 }
505
506 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 -0700507
Romain Guy5b3b3522010-10-27 18:57:51 -0700508 if (mBounds) {
509 mBounds->left = fmin(mBounds->left, x1);
510 mBounds->top = fmin(mBounds->top, y3);
511 mBounds->right = fmax(mBounds->right, x3);
512 mBounds->bottom = fmax(mBounds->bottom, y1);
513 }
514
Romain Guy694b5192010-07-21 21:33:20 -0700515 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
516 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700517 }
518}
519
Romain Guy97771732012-02-28 18:17:02 -0800520void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
521 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
522 float x4, float y4, float u4, float v4, CacheTexture* texture) {
523
524 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
525
526 if (mBounds) {
527 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
528 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
529 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
530 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
531 }
532
533 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
534 issueDrawCommand();
Romain Guy97771732012-02-28 18:17:02 -0800535 }
536}
537
Romain Guye3a9b242013-01-08 11:15:30 -0800538void FontRenderer::setFont(SkPaint* paint, const mat4& matrix) {
539 mCurrentFont = Font::create(this, paint, matrix);
Romain Guy694b5192010-07-21 21:33:20 -0700540}
Romain Guy7975fb62010-10-01 16:36:14 -0700541
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700542FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Raph Levien416a8472012-07-19 22:48:17 -0700543 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700544 checkInit();
545
546 if (!mCurrentFont) {
547 DropShadow image;
548 image.width = 0;
549 image.height = 0;
550 image.image = NULL;
551 image.penX = 0;
552 image.penY = 0;
553 return image;
554 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700555
Romain Guy2d4fd362011-12-13 22:00:19 -0800556 mDrawn = false;
Romain Guyff98fa52011-11-28 09:35:09 -0800557 mClip = NULL;
558 mBounds = NULL;
559
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700560 Rect bounds;
Raph Levien416a8472012-07-19 22:48:17 -0700561 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
Romain Guyff98fa52011-11-28 09:35:09 -0800562
Romain Guy1e45aae2010-08-13 19:39:53 -0700563 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
564 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Romain Guyff98fa52011-11-28 09:35:09 -0800565
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800566 // Align buffers for renderscript usage
567 if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) {
568 paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700569 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700570
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800571 int size = paddedWidth * paddedHeight;
572 uint8_t* dataBuffer = (uint8_t*)memalign(RS_CPU_ALLOCATION_ALIGNMENT, size);
573 memset(dataBuffer, 0, size);
574
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700575 int penX = radius - bounds.left;
576 int penY = radius - bounds.bottom;
577
Chris Craikdd8697c2013-02-22 10:41:36 -0800578 if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) {
579 // text has non-whitespace, so draw and blur to create the shadow
580 // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted
581 // TODO: don't draw pure whitespace in the first place, and avoid needing this check
582 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
583 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
584
585 blurImage(&dataBuffer, paddedWidth, paddedHeight, radius);
586 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700587
588 DropShadow image;
589 image.width = paddedWidth;
590 image.height = paddedHeight;
591 image.image = dataBuffer;
592 image.penX = penX;
593 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -0800594
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700595 return image;
596}
Romain Guy694b5192010-07-21 21:33:20 -0700597
Romain Guy671d6cf2012-01-18 12:39:17 -0800598void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700599 checkInit();
600
Romain Guy5b3b3522010-10-27 18:57:51 -0700601 mDrawn = false;
602 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -0700603 mClip = clip;
Romain Guy671d6cf2012-01-18 12:39:17 -0800604}
Romain Guyff98fa52011-11-28 09:35:09 -0800605
Romain Guy671d6cf2012-01-18 12:39:17 -0800606void FontRenderer::finishRender() {
Romain Guy5b3b3522010-10-27 18:57:51 -0700607 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -0800608 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700609
610 if (mCurrentQuadIndex != 0) {
611 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700612 }
Romain Guy671d6cf2012-01-18 12:39:17 -0800613}
614
Romain Guye3a9b242013-01-08 11:15:30 -0800615void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix) {
616 Font* font = Font::create(this, paint, matrix);
Chet Haasee816bae2012-08-09 13:39:02 -0700617 font->precache(paint, text, numGlyphs);
618}
619
Romain Guy671d6cf2012-01-18 12:39:17 -0800620bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
621 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
622 const float* positions, Rect* bounds) {
623 if (!mCurrentFont) {
624 ALOGE("No font set");
625 return false;
626 }
627
628 initRender(clip, bounds);
629 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
630 finishRender();
Romain Guy5b3b3522010-10-27 18:57:51 -0700631
632 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700633}
634
Romain Guy97771732012-02-28 18:17:02 -0800635bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
636 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
637 float hOffset, float vOffset, Rect* bounds) {
638 if (!mCurrentFont) {
639 ALOGE("No font set");
640 return false;
641 }
642
643 initRender(clip, bounds);
644 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
645 finishRender();
646
647 return mDrawn;
648}
649
Romain Guy9b1204b2012-09-04 15:22:57 -0700650void FontRenderer::removeFont(const Font* font) {
Romain Guye3a9b242013-01-08 11:15:30 -0800651 mActiveFonts.remove(font->getDescription());
Romain Guy9b1204b2012-09-04 15:22:57 -0700652
653 if (mCurrentFont == font) {
654 mCurrentFont = NULL;
655 }
656}
657
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700658void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
659 // Compute gaussian weights for the blur
660 // e is the euler's number
661 float e = 2.718281828459045f;
662 float pi = 3.1415926535897932f;
663 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
664 // x is of the form [-radius .. 0 .. radius]
665 // and sigma varies with radius.
666 // Based on some experimental radius values and sigma's
667 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700668 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700669 // The larger the radius gets, the more our gaussian blur
670 // will resemble a box blur since with large sigma
671 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -0800672 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700673
674 // Now compute the coefficints
675 // We will store some redundant values to save some math during
676 // the blur calculations
677 // precompute some values
678 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
679 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
680
681 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -0800682 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700683 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700684 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
685 normalizeFactor += weights[r + radius];
686 }
687
688 //Now we need to normalize the weights because all our coefficients need to add up to one
689 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -0800690 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700691 weights[r + radius] *= normalizeFactor;
692 }
693}
694
695void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700696 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700697 float blurredPixel = 0.0f;
698 float currentPixel = 0.0f;
699
Romain Guy325a0f92011-01-05 15:26:55 -0800700 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700701
702 const uint8_t* input = source + y * width;
703 uint8_t* output = dest + y * width;
704
Romain Guy325a0f92011-01-05 15:26:55 -0800705 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700706 blurredPixel = 0.0f;
707 const float* gPtr = weights;
708 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800709 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700710 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -0800711 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700712 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700713 blurredPixel += currentPixel * gPtr[0];
714 gPtr++;
715 i++;
716 }
717 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800718 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700719 // Stepping left and right away from the pixel
720 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -0800721 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700722 validW = 0;
723 }
Romain Guy325a0f92011-01-05 15:26:55 -0800724 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700725 validW = width - 1;
726 }
727
Romain Guy325a0f92011-01-05 15:26:55 -0800728 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700729 blurredPixel += currentPixel * gPtr[0];
730 gPtr++;
731 }
732 }
733 *output = (uint8_t)blurredPixel;
734 output ++;
735 }
736 }
737}
738
739void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700740 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700741 float blurredPixel = 0.0f;
742 float currentPixel = 0.0f;
743
Romain Guy325a0f92011-01-05 15:26:55 -0800744 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700745 uint8_t* output = dest + y * width;
746
Romain Guy325a0f92011-01-05 15:26:55 -0800747 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700748 blurredPixel = 0.0f;
749 const float* gPtr = weights;
750 const uint8_t* input = source + x;
751 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800752 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700753 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -0800754 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700755 currentPixel = (float)(*i);
756 blurredPixel += currentPixel * gPtr[0];
757 gPtr++;
758 i += width;
759 }
760 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800761 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700762 int validH = y + r;
763 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -0800764 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700765 validH = 0;
766 }
Romain Guy325a0f92011-01-05 15:26:55 -0800767 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700768 validH = height - 1;
769 }
770
771 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -0800772 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700773 blurredPixel += currentPixel * gPtr[0];
774 gPtr++;
775 }
776 }
Romain Guy325a0f92011-01-05 15:26:55 -0800777 *output = (uint8_t) blurredPixel;
Romain Guy9b1204b2012-09-04 15:22:57 -0700778 output++;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700779 }
780 }
781}
782
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800783void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) {
784 if (width * height * radius < RS_MIN_INPUT_CUTOFF) {
785 float *gaussian = new float[2 * radius + 1];
786 computeGaussianWeights(gaussian, radius);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700787
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800788 uint8_t* scratch = new uint8_t[width * height];
Romain Guyd71dd362011-12-12 19:03:35 -0800789
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800790 horizontalBlur(gaussian, radius, *image, scratch, width, height);
791 verticalBlur(gaussian, radius, scratch, *image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -0800792
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800793 delete[] gaussian;
794 delete[] scratch;
Chris Craikdd8697c2013-02-22 10:41:36 -0800795 return;
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800796 }
Romain Guyd71dd362011-12-12 19:03:35 -0800797
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800798 uint8_t* outImage = (uint8_t*)memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
799
800 if (mRs.get() == 0) {
801 mRs = new RSC::RS();
802 if (!mRs->init(true, true)) {
803 ALOGE("blur RS failed to init");
804 }
805
806 mRsElement = RSC::Element::A_8(mRs);
807 mRsScript = new RSC::ScriptIntrinsicBlur(mRs, mRsElement);
808 }
809
810 sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0);
811 sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
812 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, *image);
813 sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
814 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, outImage);
815
816 mRsScript->setRadius(radius);
817 mRsScript->blur(ain, aout);
818
819 // replace the original image's pointer, avoiding a copy back to the original buffer
Ben Cheng15641a62013-02-20 13:20:03 -0800820 free(*image);
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800821 *image = outImage;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700822}
823
Romain Guy694b5192010-07-21 21:33:20 -0700824}; // namespace uirenderer
825}; // namespace android