blob: d5ea0f9c11650143874ad74012f8fcd6957ab5d9 [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 Guy9cccc2b92010-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 Guy9cccc2b92010-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 // Zero out the borders
Romain Guy33fa1f72012-08-07 19:09:57 -0700246 for (cacheX = startX - TEXTURE_BORDER_SIZE; cacheX < endX + TEXTURE_BORDER_SIZE; cacheX++) {
247 cacheBuffer[(startY - TEXTURE_BORDER_SIZE) * cacheWidth + cacheX] = 0;
248 cacheBuffer[(endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + cacheX] = 0;
249 }
250
251 for (cacheY = startY - TEXTURE_BORDER_SIZE + 1;
252 cacheY < endY + TEXTURE_BORDER_SIZE - 1; cacheY++) {
253 cacheBuffer[cacheY * cacheWidth + startX - TEXTURE_BORDER_SIZE] = 0;
254 cacheBuffer[cacheY * cacheWidth + endX + TEXTURE_BORDER_SIZE - 1] = 0;
255 }
256
Romain Guyb969a0d2013-02-05 14:38:40 -0800257 // Copy the glyph image, taking the mask format into account
258 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
259 int stride = glyph.rowBytes();
260
261 switch (format) {
262 case SkMask::kA8_Format: {
263 if (mGammaTable) {
264 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
265 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
266 uint8_t tempCol = bitmapBuffer[bY + bX];
267 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
268 }
269 }
270 } else {
271 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
272 memcpy(&cacheBuffer[cacheY * cacheWidth + startX], &bitmapBuffer[bY],
273 glyph.fWidth);
274 }
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700275 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800276 break;
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700277 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800278 case SkMask::kBW_Format: {
279 static const uint8_t COLORS[2] = { 0, 255 };
280
281 for (cacheY = startY; cacheY < endY; cacheY++) {
282 cacheX = startX;
283 int rowBytes = stride;
284 uint8_t* buffer = bitmapBuffer;
285
286 while (--rowBytes >= 0) {
287 uint8_t b = *buffer++;
288 for (int8_t mask = 7; mask >= 0 && cacheX < endX; mask--) {
289 cacheBuffer[cacheY * cacheWidth + cacheX++] = COLORS[(b >> mask) & 0x1];
290 }
291 }
292
293 bitmapBuffer += stride;
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700294 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800295 break;
Romain Guy694b5192010-07-21 21:33:20 -0700296 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800297 default:
298 ALOGW("Unkown glyph format: 0x%x", format);
299 break;
Romain Guy694b5192010-07-21 21:33:20 -0700300 }
Romain Guy97771732012-02-28 18:17:02 -0800301
Chet Haase7de0cb12011-12-05 16:35:38 -0800302 cachedGlyph->mIsValid = true;
Romain Guy694b5192010-07-21 21:33:20 -0700303}
304
Chet Haase7de0cb12011-12-05 16:35:38 -0800305CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
Romain Guy0aa87bb2012-07-20 11:14:32 -0700306 CacheTexture* cacheTexture = new CacheTexture(width, height);
Romain Guy9d9758a2012-05-14 15:19:58 -0700307
Chet Haase2a47c142011-12-14 15:22:56 -0800308 if (allocate) {
Romain Guy80872462012-09-04 16:42:01 -0700309 Caches::getInstance().activeTexture(0);
310 cacheTexture->allocateTexture();
Chet Haase2a47c142011-12-14 15:22:56 -0800311 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700312
Chet Haase2a47c142011-12-14 15:22:56 -0800313 return cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800314}
315
316void FontRenderer::initTextTexture() {
Chet Haase378e9192012-08-15 15:54:54 -0700317 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
318 delete mCacheTextures[i];
Romain Guy9d9758a2012-05-14 15:19:58 -0700319 }
Chet Haase378e9192012-08-15 15:54:54 -0700320 mCacheTextures.clear();
Romain Guy9d9758a2012-05-14 15:19:58 -0700321
Chet Haase7de0cb12011-12-05 16:35:38 -0800322 mUploadTexture = false;
Chet Haase378e9192012-08-15 15:54:54 -0700323 mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
Chet Haaseeb32a492012-08-31 13:54:03 -0700324 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
325 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
326 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false));
Chet Haase378e9192012-08-15 15:54:54 -0700327 mCurrentCacheTexture = mCacheTextures[0];
Romain Guy694b5192010-07-21 21:33:20 -0700328}
329
330// Avoid having to reallocate memory and render quad by quad
331void FontRenderer::initVertexArrayBuffers() {
Romain Guyd71dd362011-12-12 19:03:35 -0800332 uint32_t numIndices = mMaxNumberOfQuads * 6;
333 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700334 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700335
336 // Four verts, two triangles , six indices per quad
337 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
338 int i6 = i * 6;
339 int i4 = i * 4;
340
341 indexBufferData[i6 + 0] = i4 + 0;
342 indexBufferData[i6 + 1] = i4 + 1;
343 indexBufferData[i6 + 2] = i4 + 2;
344
345 indexBufferData[i6 + 3] = i4 + 0;
346 indexBufferData[i6 + 4] = i4 + 2;
347 indexBufferData[i6 + 5] = i4 + 3;
348 }
349
350 glGenBuffers(1, &mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800351 Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700352 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
Romain Guy694b5192010-07-21 21:33:20 -0700353
354 free(indexBufferData);
355
Romain Guyd71dd362011-12-12 19:03:35 -0800356 uint32_t coordSize = 2;
Romain Guy694b5192010-07-21 21:33:20 -0700357 uint32_t uvSize = 2;
358 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700359 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
Romain Guy9b1204b2012-09-04 15:22:57 -0700360 mTextMesh = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700361}
362
363// We don't want to allocate anything unless we actually draw text
364void FontRenderer::checkInit() {
365 if (mInitialized) {
366 return;
367 }
368
369 initTextTexture();
370 initVertexArrayBuffers();
371
372 mInitialized = true;
373}
374
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900375void FontRenderer::updateDrawParams() {
376 if (mCurrentQuadIndex != mLastQuadIndex) {
377 mDrawOffsets.add((uint16_t*)(mLastQuadIndex * sizeof(uint16_t) * 6));
378 mDrawCounts.add(mCurrentQuadIndex - mLastQuadIndex);
379 mDrawCacheTextures.add(mCurrentCacheTexture);
380 mLastQuadIndex = mCurrentQuadIndex;
381 }
382}
383
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700384void FontRenderer::checkTextureUpdate() {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900385 if (!mUploadTexture) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700386 return;
Romain Guy694b5192010-07-21 21:33:20 -0700387 }
388
Romain Guy2d4fd362011-12-13 22:00:19 -0800389 Caches& caches = Caches::getInstance();
390 GLuint lastTextureId = 0;
Chet Haase378e9192012-08-15 15:54:54 -0700391 // Iterate over all the cache textures and see which ones need to be updated
392 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
393 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guy80872462012-09-04 16:42:01 -0700394 if (cacheTexture->isDirty() && cacheTexture->getTexture()) {
Chet Haaseb92d8f72012-09-21 08:40:46 -0700395 // Can't copy inner rect; glTexSubimage expects pointer to deal with entire buffer
396 // of data. So expand the dirty rect to the encompassing horizontal stripe.
397 const Rect* dirtyRect = cacheTexture->getDirtyRect();
398 uint32_t x = 0;
399 uint32_t y = dirtyRect->top;
Romain Guy80872462012-09-04 16:42:01 -0700400 uint32_t width = cacheTexture->getWidth();
Chet Haaseb92d8f72012-09-21 08:40:46 -0700401 uint32_t height = dirtyRect->getHeight();
402 void* textureData = cacheTexture->getTexture() + y * width;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700403
Romain Guy80872462012-09-04 16:42:01 -0700404 if (cacheTexture->getTextureId() != lastTextureId) {
405 lastTextureId = cacheTexture->getTextureId();
Romain Guy2d4fd362011-12-13 22:00:19 -0800406 caches.activeTexture(0);
Romain Guy80872462012-09-04 16:42:01 -0700407 glBindTexture(GL_TEXTURE_2D, lastTextureId);
Romain Guy2d4fd362011-12-13 22:00:19 -0800408 }
Chet Haasee816bae2012-08-09 13:39:02 -0700409#if DEBUG_FONT_RENDERER
Chet Haaseb92d8f72012-09-21 08:40:46 -0700410 ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d",
411 i, x, y, width, height);
Chet Haasee816bae2012-08-09 13:39:02 -0700412#endif
Chet Haaseb92d8f72012-09-21 08:40:46 -0700413 glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700414 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Romain Guy80872462012-09-04 16:42:01 -0700415 cacheTexture->setDirty(false);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700416 }
417 }
418
419 mUploadTexture = false;
420}
421
422void FontRenderer::issueDrawCommand() {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900423 updateDrawParams();
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700424 checkTextureUpdate();
425
Romain Guy15bc6432011-12-13 13:11:32 -0800426 Caches& caches = Caches::getInstance();
Romain Guy2d4fd362011-12-13 22:00:19 -0800427 caches.bindIndicesBuffer(mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800428 if (!mDrawn) {
Romain Guy9b1204b2012-09-04 15:22:57 -0700429 float* buffer = mTextMesh;
Romain Guy15bc6432011-12-13 13:11:32 -0800430 int offset = 2;
431
432 bool force = caches.unbindMeshBuffer();
Chris Craikcb4d6002012-09-25 12:00:29 -0700433 caches.bindPositionVertexPointer(force, buffer);
434 caches.bindTexCoordsVertexPointer(force, buffer + offset);
Romain Guy15bc6432011-12-13 13:11:32 -0800435 }
436
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900437 for (uint32_t i = 0; i < mDrawOffsets.size(); i++) {
438 uint16_t* offset = mDrawOffsets[i];
439 uint32_t count = mDrawCounts[i];
440 CacheTexture* texture = mDrawCacheTextures[i];
441
442 caches.activeTexture(0);
443 glBindTexture(GL_TEXTURE_2D, texture->getTextureId());
444
445 texture->setLinearFiltering(mLinearFiltering, false);
446
447 glDrawElements(GL_TRIANGLES, count * 6, GL_UNSIGNED_SHORT, offset);
448 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700449
450 mDrawn = true;
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900451
452 mCurrentQuadIndex = 0;
453 mLastQuadIndex = 0;
454 mDrawOffsets.clear();
455 mDrawCounts.clear();
456 mDrawCacheTextures.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700457}
458
Romain Guy97771732012-02-28 18:17:02 -0800459void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
460 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800461 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800462 if (texture != mCurrentCacheTexture) {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900463 updateDrawParams();
Chet Haase7de0cb12011-12-05 16:35:38 -0800464 // Now use the new texture id
465 mCurrentCacheTexture = texture;
466 }
Romain Guy09147fb2010-07-22 13:08:20 -0700467
Romain Guy694b5192010-07-21 21:33:20 -0700468 const uint32_t vertsPerQuad = 4;
Romain Guyd71dd362011-12-12 19:03:35 -0800469 const uint32_t floatsPerVert = 4;
Romain Guy9b1204b2012-09-04 15:22:57 -0700470 float* currentPos = mTextMesh + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700471
Romain Guy694b5192010-07-21 21:33:20 -0700472 (*currentPos++) = x1;
473 (*currentPos++) = y1;
Romain Guy694b5192010-07-21 21:33:20 -0700474 (*currentPos++) = u1;
475 (*currentPos++) = v1;
476
477 (*currentPos++) = x2;
478 (*currentPos++) = y2;
Romain Guy694b5192010-07-21 21:33:20 -0700479 (*currentPos++) = u2;
480 (*currentPos++) = v2;
481
482 (*currentPos++) = x3;
483 (*currentPos++) = y3;
Romain Guy694b5192010-07-21 21:33:20 -0700484 (*currentPos++) = u3;
485 (*currentPos++) = v3;
486
487 (*currentPos++) = x4;
488 (*currentPos++) = y4;
Romain Guy694b5192010-07-21 21:33:20 -0700489 (*currentPos++) = u4;
490 (*currentPos++) = v4;
491
492 mCurrentQuadIndex++;
Romain Guy97771732012-02-28 18:17:02 -0800493}
494
495void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
496 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
497 float x4, float y4, float u4, float v4, CacheTexture* texture) {
498
499 if (mClip &&
500 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
501 return;
502 }
503
504 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 -0700505
Romain Guy5b3b3522010-10-27 18:57:51 -0700506 if (mBounds) {
507 mBounds->left = fmin(mBounds->left, x1);
508 mBounds->top = fmin(mBounds->top, y3);
509 mBounds->right = fmax(mBounds->right, x3);
510 mBounds->bottom = fmax(mBounds->bottom, y1);
511 }
512
Romain Guy694b5192010-07-21 21:33:20 -0700513 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
514 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700515 }
516}
517
Romain Guy97771732012-02-28 18:17:02 -0800518void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
519 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
520 float x4, float y4, float u4, float v4, CacheTexture* texture) {
521
522 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
523
524 if (mBounds) {
525 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
526 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
527 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
528 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
529 }
530
531 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
532 issueDrawCommand();
Romain Guy97771732012-02-28 18:17:02 -0800533 }
534}
535
Romain Guye3a9b242013-01-08 11:15:30 -0800536void FontRenderer::setFont(SkPaint* paint, const mat4& matrix) {
537 mCurrentFont = Font::create(this, paint, matrix);
Romain Guy694b5192010-07-21 21:33:20 -0700538}
Romain Guy7975fb62010-10-01 16:36:14 -0700539
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700540FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Raph Levien416a8472012-07-19 22:48:17 -0700541 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700542 checkInit();
543
544 if (!mCurrentFont) {
545 DropShadow image;
546 image.width = 0;
547 image.height = 0;
548 image.image = NULL;
549 image.penX = 0;
550 image.penY = 0;
551 return image;
552 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700553
Romain Guy2d4fd362011-12-13 22:00:19 -0800554 mDrawn = false;
Romain Guyff98fa52011-11-28 09:35:09 -0800555 mClip = NULL;
556 mBounds = NULL;
557
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700558 Rect bounds;
Raph Levien416a8472012-07-19 22:48:17 -0700559 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
Romain Guyff98fa52011-11-28 09:35:09 -0800560
Romain Guy1e45aae2010-08-13 19:39:53 -0700561 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
562 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Romain Guyff98fa52011-11-28 09:35:09 -0800563
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800564 // Align buffers for renderscript usage
565 if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) {
566 paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700567 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700568
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800569 int size = paddedWidth * paddedHeight;
570 uint8_t* dataBuffer = (uint8_t*)memalign(RS_CPU_ALLOCATION_ALIGNMENT, size);
571 memset(dataBuffer, 0, size);
572
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700573 int penX = radius - bounds.left;
574 int penY = radius - bounds.bottom;
575
Chris Craikdd8697c2013-02-22 10:41:36 -0800576 if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) {
577 // text has non-whitespace, so draw and blur to create the shadow
578 // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted
579 // TODO: don't draw pure whitespace in the first place, and avoid needing this check
580 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
581 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
582
583 blurImage(&dataBuffer, paddedWidth, paddedHeight, radius);
584 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700585
586 DropShadow image;
587 image.width = paddedWidth;
588 image.height = paddedHeight;
589 image.image = dataBuffer;
590 image.penX = penX;
591 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -0800592
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700593 return image;
594}
Romain Guy694b5192010-07-21 21:33:20 -0700595
Romain Guy671d6cf2012-01-18 12:39:17 -0800596void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700597 checkInit();
598
Romain Guy5b3b3522010-10-27 18:57:51 -0700599 mDrawn = false;
600 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -0700601 mClip = clip;
Romain Guy671d6cf2012-01-18 12:39:17 -0800602}
Romain Guyff98fa52011-11-28 09:35:09 -0800603
Romain Guy671d6cf2012-01-18 12:39:17 -0800604void FontRenderer::finishRender() {
Romain Guy5b3b3522010-10-27 18:57:51 -0700605 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -0800606 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700607
608 if (mCurrentQuadIndex != 0) {
609 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700610 }
Romain Guy671d6cf2012-01-18 12:39:17 -0800611}
612
Romain Guye3a9b242013-01-08 11:15:30 -0800613void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix) {
614 Font* font = Font::create(this, paint, matrix);
Chet Haasee816bae2012-08-09 13:39:02 -0700615 font->precache(paint, text, numGlyphs);
616}
617
Romain Guy671d6cf2012-01-18 12:39:17 -0800618bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
619 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
620 const float* positions, Rect* bounds) {
621 if (!mCurrentFont) {
622 ALOGE("No font set");
623 return false;
624 }
625
626 initRender(clip, bounds);
627 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
628 finishRender();
Romain Guy5b3b3522010-10-27 18:57:51 -0700629
630 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700631}
632
Romain Guy97771732012-02-28 18:17:02 -0800633bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
634 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
635 float hOffset, float vOffset, Rect* bounds) {
636 if (!mCurrentFont) {
637 ALOGE("No font set");
638 return false;
639 }
640
641 initRender(clip, bounds);
642 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
643 finishRender();
644
645 return mDrawn;
646}
647
Romain Guy9b1204b2012-09-04 15:22:57 -0700648void FontRenderer::removeFont(const Font* font) {
Romain Guye3a9b242013-01-08 11:15:30 -0800649 mActiveFonts.remove(font->getDescription());
Romain Guy9b1204b2012-09-04 15:22:57 -0700650
651 if (mCurrentFont == font) {
652 mCurrentFont = NULL;
653 }
654}
655
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700656void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
657 // Compute gaussian weights for the blur
658 // e is the euler's number
659 float e = 2.718281828459045f;
660 float pi = 3.1415926535897932f;
661 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
662 // x is of the form [-radius .. 0 .. radius]
663 // and sigma varies with radius.
664 // Based on some experimental radius values and sigma's
665 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700666 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700667 // The larger the radius gets, the more our gaussian blur
668 // will resemble a box blur since with large sigma
669 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -0800670 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700671
672 // Now compute the coefficints
673 // We will store some redundant values to save some math during
674 // the blur calculations
675 // precompute some values
676 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
677 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
678
679 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -0800680 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700681 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700682 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
683 normalizeFactor += weights[r + radius];
684 }
685
686 //Now we need to normalize the weights because all our coefficients need to add up to one
687 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -0800688 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700689 weights[r + radius] *= normalizeFactor;
690 }
691}
692
693void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700694 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700695 float blurredPixel = 0.0f;
696 float currentPixel = 0.0f;
697
Romain Guy325a0f92011-01-05 15:26:55 -0800698 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700699
700 const uint8_t* input = source + y * width;
701 uint8_t* output = dest + y * width;
702
Romain Guy325a0f92011-01-05 15:26:55 -0800703 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700704 blurredPixel = 0.0f;
705 const float* gPtr = weights;
706 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800707 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700708 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -0800709 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700710 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700711 blurredPixel += currentPixel * gPtr[0];
712 gPtr++;
713 i++;
714 }
715 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800716 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700717 // Stepping left and right away from the pixel
718 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -0800719 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700720 validW = 0;
721 }
Romain Guy325a0f92011-01-05 15:26:55 -0800722 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700723 validW = width - 1;
724 }
725
Romain Guy325a0f92011-01-05 15:26:55 -0800726 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700727 blurredPixel += currentPixel * gPtr[0];
728 gPtr++;
729 }
730 }
731 *output = (uint8_t)blurredPixel;
732 output ++;
733 }
734 }
735}
736
737void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700738 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700739 float blurredPixel = 0.0f;
740 float currentPixel = 0.0f;
741
Romain Guy325a0f92011-01-05 15:26:55 -0800742 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700743 uint8_t* output = dest + y * width;
744
Romain Guy325a0f92011-01-05 15:26:55 -0800745 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700746 blurredPixel = 0.0f;
747 const float* gPtr = weights;
748 const uint8_t* input = source + x;
749 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800750 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700751 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -0800752 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700753 currentPixel = (float)(*i);
754 blurredPixel += currentPixel * gPtr[0];
755 gPtr++;
756 i += width;
757 }
758 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800759 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700760 int validH = y + r;
761 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -0800762 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700763 validH = 0;
764 }
Romain Guy325a0f92011-01-05 15:26:55 -0800765 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700766 validH = height - 1;
767 }
768
769 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -0800770 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700771 blurredPixel += currentPixel * gPtr[0];
772 gPtr++;
773 }
774 }
Romain Guy325a0f92011-01-05 15:26:55 -0800775 *output = (uint8_t) blurredPixel;
Romain Guy9b1204b2012-09-04 15:22:57 -0700776 output++;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700777 }
778 }
779}
780
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800781void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) {
782 if (width * height * radius < RS_MIN_INPUT_CUTOFF) {
783 float *gaussian = new float[2 * radius + 1];
784 computeGaussianWeights(gaussian, radius);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700785
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800786 uint8_t* scratch = new uint8_t[width * height];
Romain Guyd71dd362011-12-12 19:03:35 -0800787
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800788 horizontalBlur(gaussian, radius, *image, scratch, width, height);
789 verticalBlur(gaussian, radius, scratch, *image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -0800790
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800791 delete[] gaussian;
792 delete[] scratch;
Chris Craikdd8697c2013-02-22 10:41:36 -0800793 return;
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800794 }
Romain Guyd71dd362011-12-12 19:03:35 -0800795
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800796 uint8_t* outImage = (uint8_t*)memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
797
798 if (mRs.get() == 0) {
799 mRs = new RSC::RS();
800 if (!mRs->init(true, true)) {
801 ALOGE("blur RS failed to init");
802 }
803
804 mRsElement = RSC::Element::A_8(mRs);
805 mRsScript = new RSC::ScriptIntrinsicBlur(mRs, mRsElement);
806 }
807
808 sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0);
809 sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
810 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, *image);
811 sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
812 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, outImage);
813
814 mRsScript->setRadius(radius);
815 mRsScript->blur(ain, aout);
816
817 // replace the original image's pointer, avoiding a copy back to the original buffer
Ben Cheng15641a612013-02-20 13:20:03 -0800818 free(*image);
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800819 *image = outImage;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700820}
821
Romain Guy694b5192010-07-21 21:33:20 -0700822}; // namespace uirenderer
823}; // namespace android