blob: db65b8875982c732b5b0613b5a408cdc15e19f49 [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();
Chet Haase7de0cb12011-12-05 16:35:38 -0800183 cachedGlyph->mIsValid = false;
Romain Guy694b5192010-07-21 21:33:20 -0700184 // If the glyph is too tall, don't cache it
Chet Haase378e9192012-08-15 15:54:54 -0700185 if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
Romain Guy80872462012-09-04 16:42:01 -0700186 mCacheTextures[mCacheTextures.size() - 1]->getHeight()) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700187 ALOGE("Font size too large to fit in cache. width, height = %i, %i",
188 (int) glyph.fWidth, (int) glyph.fHeight);
Chet Haase7de0cb12011-12-05 16:35:38 -0800189 return;
Romain Guy694b5192010-07-21 21:33:20 -0700190 }
191
192 // Now copy the bitmap into the cache texture
193 uint32_t startX = 0;
194 uint32_t startY = 0;
195
Chet Haase378e9192012-08-15 15:54:54 -0700196 CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700197
Chet Haase378e9192012-08-15 15:54:54 -0700198 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700199 if (!precaching) {
200 // If the new glyph didn't fit and we are not just trying to precache it,
201 // clear out the cache and try again
202 flushAllAndInvalidate();
203 cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
204 }
Romain Guy694b5192010-07-21 21:33:20 -0700205
Chet Haase378e9192012-08-15 15:54:54 -0700206 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700207 // either the glyph didn't fit or we're precaching and will cache it when we draw
Chet Haase7de0cb12011-12-05 16:35:38 -0800208 return;
Romain Guy694b5192010-07-21 21:33:20 -0700209 }
210 }
211
Chet Haase378e9192012-08-15 15:54:54 -0700212 cachedGlyph->mCacheTexture = cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800213
Romain Guy694b5192010-07-21 21:33:20 -0700214 *retOriginX = startX;
215 *retOriginY = startY;
216
217 uint32_t endX = startX + glyph.fWidth;
218 uint32_t endY = startY + glyph.fHeight;
219
Romain Guy80872462012-09-04 16:42:01 -0700220 uint32_t cacheWidth = cacheTexture->getWidth();
Romain Guy694b5192010-07-21 21:33:20 -0700221
Romain Guy80872462012-09-04 16:42:01 -0700222 if (!cacheTexture->getTexture()) {
223 Caches::getInstance().activeTexture(0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800224 // Large-glyph texture memory is allocated only as needed
Romain Guy80872462012-09-04 16:42:01 -0700225 cacheTexture->allocateTexture();
Chet Haase7de0cb12011-12-05 16:35:38 -0800226 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700227
Romain Guyb969a0d2013-02-05 14:38:40 -0800228 // Tells us whether the glyphs is B&W (1 bit per pixel)
229 // or anti-aliased (8 bits per pixel)
230 SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
Romain Guy694b5192010-07-21 21:33:20 -0700231
Romain Guyb969a0d2013-02-05 14:38:40 -0800232 uint8_t* cacheBuffer = cacheTexture->getTexture();
Romain Guy694b5192010-07-21 21:33:20 -0700233 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
Romain Guy33fa1f72012-08-07 19:09:57 -0700234
Romain Guyb969a0d2013-02-05 14:38:40 -0800235 // Zero out the borders
Romain Guy33fa1f72012-08-07 19:09:57 -0700236 for (cacheX = startX - TEXTURE_BORDER_SIZE; cacheX < endX + TEXTURE_BORDER_SIZE; cacheX++) {
237 cacheBuffer[(startY - TEXTURE_BORDER_SIZE) * cacheWidth + cacheX] = 0;
238 cacheBuffer[(endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + cacheX] = 0;
239 }
240
241 for (cacheY = startY - TEXTURE_BORDER_SIZE + 1;
242 cacheY < endY + TEXTURE_BORDER_SIZE - 1; cacheY++) {
243 cacheBuffer[cacheY * cacheWidth + startX - TEXTURE_BORDER_SIZE] = 0;
244 cacheBuffer[cacheY * cacheWidth + endX + TEXTURE_BORDER_SIZE - 1] = 0;
245 }
246
Romain Guyb969a0d2013-02-05 14:38:40 -0800247 // Copy the glyph image, taking the mask format into account
248 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
249 int stride = glyph.rowBytes();
250
251 switch (format) {
252 case SkMask::kA8_Format: {
253 if (mGammaTable) {
254 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
255 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
256 uint8_t tempCol = bitmapBuffer[bY + bX];
257 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
258 }
259 }
260 } else {
261 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
262 memcpy(&cacheBuffer[cacheY * cacheWidth + startX], &bitmapBuffer[bY],
263 glyph.fWidth);
264 }
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700265 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800266 break;
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700267 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800268 case SkMask::kBW_Format: {
269 static const uint8_t COLORS[2] = { 0, 255 };
270
271 for (cacheY = startY; cacheY < endY; cacheY++) {
272 cacheX = startX;
273 int rowBytes = stride;
274 uint8_t* buffer = bitmapBuffer;
275
276 while (--rowBytes >= 0) {
277 uint8_t b = *buffer++;
278 for (int8_t mask = 7; mask >= 0 && cacheX < endX; mask--) {
279 cacheBuffer[cacheY * cacheWidth + cacheX++] = COLORS[(b >> mask) & 0x1];
280 }
281 }
282
283 bitmapBuffer += stride;
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700284 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800285 break;
Romain Guy694b5192010-07-21 21:33:20 -0700286 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800287 default:
288 ALOGW("Unkown glyph format: 0x%x", format);
289 break;
Romain Guy694b5192010-07-21 21:33:20 -0700290 }
Romain Guy97771732012-02-28 18:17:02 -0800291
Chet Haase7de0cb12011-12-05 16:35:38 -0800292 cachedGlyph->mIsValid = true;
Romain Guy694b5192010-07-21 21:33:20 -0700293}
294
Chet Haase7de0cb12011-12-05 16:35:38 -0800295CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
Romain Guy0aa87bb2012-07-20 11:14:32 -0700296 CacheTexture* cacheTexture = new CacheTexture(width, height);
Romain Guy9d9758a2012-05-14 15:19:58 -0700297
Chet Haase2a47c142011-12-14 15:22:56 -0800298 if (allocate) {
Romain Guy80872462012-09-04 16:42:01 -0700299 Caches::getInstance().activeTexture(0);
300 cacheTexture->allocateTexture();
Chet Haase2a47c142011-12-14 15:22:56 -0800301 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700302
Chet Haase2a47c142011-12-14 15:22:56 -0800303 return cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800304}
305
306void FontRenderer::initTextTexture() {
Chet Haase378e9192012-08-15 15:54:54 -0700307 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
308 delete mCacheTextures[i];
Romain Guy9d9758a2012-05-14 15:19:58 -0700309 }
Chet Haase378e9192012-08-15 15:54:54 -0700310 mCacheTextures.clear();
Romain Guy9d9758a2012-05-14 15:19:58 -0700311
Chet Haase7de0cb12011-12-05 16:35:38 -0800312 mUploadTexture = false;
Chet Haase378e9192012-08-15 15:54:54 -0700313 mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
Chet Haaseeb32a492012-08-31 13:54:03 -0700314 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
315 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
316 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false));
Chet Haase378e9192012-08-15 15:54:54 -0700317 mCurrentCacheTexture = mCacheTextures[0];
Romain Guy694b5192010-07-21 21:33:20 -0700318}
319
320// Avoid having to reallocate memory and render quad by quad
321void FontRenderer::initVertexArrayBuffers() {
Romain Guyd71dd362011-12-12 19:03:35 -0800322 uint32_t numIndices = mMaxNumberOfQuads * 6;
323 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700324 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700325
326 // Four verts, two triangles , six indices per quad
327 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
328 int i6 = i * 6;
329 int i4 = i * 4;
330
331 indexBufferData[i6 + 0] = i4 + 0;
332 indexBufferData[i6 + 1] = i4 + 1;
333 indexBufferData[i6 + 2] = i4 + 2;
334
335 indexBufferData[i6 + 3] = i4 + 0;
336 indexBufferData[i6 + 4] = i4 + 2;
337 indexBufferData[i6 + 5] = i4 + 3;
338 }
339
340 glGenBuffers(1, &mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800341 Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700342 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
Romain Guy694b5192010-07-21 21:33:20 -0700343
344 free(indexBufferData);
345
Romain Guyd71dd362011-12-12 19:03:35 -0800346 uint32_t coordSize = 2;
Romain Guy694b5192010-07-21 21:33:20 -0700347 uint32_t uvSize = 2;
348 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700349 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
Romain Guy9b1204b2012-09-04 15:22:57 -0700350 mTextMesh = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700351}
352
353// We don't want to allocate anything unless we actually draw text
354void FontRenderer::checkInit() {
355 if (mInitialized) {
356 return;
357 }
358
359 initTextTexture();
360 initVertexArrayBuffers();
361
362 mInitialized = true;
363}
364
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900365void FontRenderer::updateDrawParams() {
366 if (mCurrentQuadIndex != mLastQuadIndex) {
367 mDrawOffsets.add((uint16_t*)(mLastQuadIndex * sizeof(uint16_t) * 6));
368 mDrawCounts.add(mCurrentQuadIndex - mLastQuadIndex);
369 mDrawCacheTextures.add(mCurrentCacheTexture);
370 mLastQuadIndex = mCurrentQuadIndex;
371 }
372}
373
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700374void FontRenderer::checkTextureUpdate() {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900375 if (!mUploadTexture) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700376 return;
Romain Guy694b5192010-07-21 21:33:20 -0700377 }
378
Romain Guy2d4fd362011-12-13 22:00:19 -0800379 Caches& caches = Caches::getInstance();
380 GLuint lastTextureId = 0;
Chet Haase378e9192012-08-15 15:54:54 -0700381 // Iterate over all the cache textures and see which ones need to be updated
382 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
383 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guy80872462012-09-04 16:42:01 -0700384 if (cacheTexture->isDirty() && cacheTexture->getTexture()) {
Chet Haaseb92d8f72012-09-21 08:40:46 -0700385 // Can't copy inner rect; glTexSubimage expects pointer to deal with entire buffer
386 // of data. So expand the dirty rect to the encompassing horizontal stripe.
387 const Rect* dirtyRect = cacheTexture->getDirtyRect();
388 uint32_t x = 0;
389 uint32_t y = dirtyRect->top;
Romain Guy80872462012-09-04 16:42:01 -0700390 uint32_t width = cacheTexture->getWidth();
Chet Haaseb92d8f72012-09-21 08:40:46 -0700391 uint32_t height = dirtyRect->getHeight();
392 void* textureData = cacheTexture->getTexture() + y * width;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700393
Romain Guy80872462012-09-04 16:42:01 -0700394 if (cacheTexture->getTextureId() != lastTextureId) {
395 lastTextureId = cacheTexture->getTextureId();
Romain Guy2d4fd362011-12-13 22:00:19 -0800396 caches.activeTexture(0);
Romain Guy80872462012-09-04 16:42:01 -0700397 glBindTexture(GL_TEXTURE_2D, lastTextureId);
Romain Guy2d4fd362011-12-13 22:00:19 -0800398 }
Chet Haasee816bae2012-08-09 13:39:02 -0700399#if DEBUG_FONT_RENDERER
Chet Haaseb92d8f72012-09-21 08:40:46 -0700400 ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d",
401 i, x, y, width, height);
Chet Haasee816bae2012-08-09 13:39:02 -0700402#endif
Chet Haaseb92d8f72012-09-21 08:40:46 -0700403 glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700404 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Romain Guy80872462012-09-04 16:42:01 -0700405 cacheTexture->setDirty(false);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700406 }
407 }
408
409 mUploadTexture = false;
410}
411
412void FontRenderer::issueDrawCommand() {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900413 updateDrawParams();
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700414 checkTextureUpdate();
415
Romain Guy15bc6432011-12-13 13:11:32 -0800416 Caches& caches = Caches::getInstance();
Romain Guy2d4fd362011-12-13 22:00:19 -0800417 caches.bindIndicesBuffer(mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800418 if (!mDrawn) {
Romain Guy9b1204b2012-09-04 15:22:57 -0700419 float* buffer = mTextMesh;
Romain Guy15bc6432011-12-13 13:11:32 -0800420 int offset = 2;
421
422 bool force = caches.unbindMeshBuffer();
Chris Craikcb4d6002012-09-25 12:00:29 -0700423 caches.bindPositionVertexPointer(force, buffer);
424 caches.bindTexCoordsVertexPointer(force, buffer + offset);
Romain Guy15bc6432011-12-13 13:11:32 -0800425 }
426
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900427 for (uint32_t i = 0; i < mDrawOffsets.size(); i++) {
428 uint16_t* offset = mDrawOffsets[i];
429 uint32_t count = mDrawCounts[i];
430 CacheTexture* texture = mDrawCacheTextures[i];
431
432 caches.activeTexture(0);
433 glBindTexture(GL_TEXTURE_2D, texture->getTextureId());
434
435 texture->setLinearFiltering(mLinearFiltering, false);
436
437 glDrawElements(GL_TRIANGLES, count * 6, GL_UNSIGNED_SHORT, offset);
438 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700439
440 mDrawn = true;
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900441
442 mCurrentQuadIndex = 0;
443 mLastQuadIndex = 0;
444 mDrawOffsets.clear();
445 mDrawCounts.clear();
446 mDrawCacheTextures.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700447}
448
Romain Guy97771732012-02-28 18:17:02 -0800449void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
450 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800451 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800452 if (texture != mCurrentCacheTexture) {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900453 updateDrawParams();
Chet Haase7de0cb12011-12-05 16:35:38 -0800454 // Now use the new texture id
455 mCurrentCacheTexture = texture;
456 }
Romain Guy09147fb2010-07-22 13:08:20 -0700457
Romain Guy694b5192010-07-21 21:33:20 -0700458 const uint32_t vertsPerQuad = 4;
Romain Guyd71dd362011-12-12 19:03:35 -0800459 const uint32_t floatsPerVert = 4;
Romain Guy9b1204b2012-09-04 15:22:57 -0700460 float* currentPos = mTextMesh + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700461
Romain Guy694b5192010-07-21 21:33:20 -0700462 (*currentPos++) = x1;
463 (*currentPos++) = y1;
Romain Guy694b5192010-07-21 21:33:20 -0700464 (*currentPos++) = u1;
465 (*currentPos++) = v1;
466
467 (*currentPos++) = x2;
468 (*currentPos++) = y2;
Romain Guy694b5192010-07-21 21:33:20 -0700469 (*currentPos++) = u2;
470 (*currentPos++) = v2;
471
472 (*currentPos++) = x3;
473 (*currentPos++) = y3;
Romain Guy694b5192010-07-21 21:33:20 -0700474 (*currentPos++) = u3;
475 (*currentPos++) = v3;
476
477 (*currentPos++) = x4;
478 (*currentPos++) = y4;
Romain Guy694b5192010-07-21 21:33:20 -0700479 (*currentPos++) = u4;
480 (*currentPos++) = v4;
481
482 mCurrentQuadIndex++;
Romain Guy97771732012-02-28 18:17:02 -0800483}
484
485void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
486 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
487 float x4, float y4, float u4, float v4, CacheTexture* texture) {
488
489 if (mClip &&
490 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
491 return;
492 }
493
494 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 -0700495
Romain Guy5b3b3522010-10-27 18:57:51 -0700496 if (mBounds) {
497 mBounds->left = fmin(mBounds->left, x1);
498 mBounds->top = fmin(mBounds->top, y3);
499 mBounds->right = fmax(mBounds->right, x3);
500 mBounds->bottom = fmax(mBounds->bottom, y1);
501 }
502
Romain Guy694b5192010-07-21 21:33:20 -0700503 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
504 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700505 }
506}
507
Romain Guy97771732012-02-28 18:17:02 -0800508void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
509 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
510 float x4, float y4, float u4, float v4, CacheTexture* texture) {
511
512 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
513
514 if (mBounds) {
515 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
516 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
517 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
518 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
519 }
520
521 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
522 issueDrawCommand();
Romain Guy97771732012-02-28 18:17:02 -0800523 }
524}
525
Romain Guye3a9b242013-01-08 11:15:30 -0800526void FontRenderer::setFont(SkPaint* paint, const mat4& matrix) {
527 mCurrentFont = Font::create(this, paint, matrix);
Romain Guy694b5192010-07-21 21:33:20 -0700528}
Romain Guy7975fb62010-10-01 16:36:14 -0700529
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700530FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Raph Levien416a8472012-07-19 22:48:17 -0700531 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700532 checkInit();
533
534 if (!mCurrentFont) {
535 DropShadow image;
536 image.width = 0;
537 image.height = 0;
538 image.image = NULL;
539 image.penX = 0;
540 image.penY = 0;
541 return image;
542 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700543
Romain Guy2d4fd362011-12-13 22:00:19 -0800544 mDrawn = false;
Romain Guyff98fa52011-11-28 09:35:09 -0800545 mClip = NULL;
546 mBounds = NULL;
547
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700548 Rect bounds;
Raph Levien416a8472012-07-19 22:48:17 -0700549 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
Romain Guyff98fa52011-11-28 09:35:09 -0800550
Romain Guy1e45aae2010-08-13 19:39:53 -0700551 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
552 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Romain Guyff98fa52011-11-28 09:35:09 -0800553
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800554 // Align buffers for renderscript usage
555 if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) {
556 paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700557 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700558
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800559 int size = paddedWidth * paddedHeight;
560 uint8_t* dataBuffer = (uint8_t*)memalign(RS_CPU_ALLOCATION_ALIGNMENT, size);
561 memset(dataBuffer, 0, size);
562
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700563 int penX = radius - bounds.left;
564 int penY = radius - bounds.bottom;
565
Chris Craikdd8697c2013-02-22 10:41:36 -0800566 if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) {
567 // text has non-whitespace, so draw and blur to create the shadow
568 // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted
569 // TODO: don't draw pure whitespace in the first place, and avoid needing this check
570 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
571 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
572
573 blurImage(&dataBuffer, paddedWidth, paddedHeight, radius);
574 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700575
576 DropShadow image;
577 image.width = paddedWidth;
578 image.height = paddedHeight;
579 image.image = dataBuffer;
580 image.penX = penX;
581 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -0800582
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700583 return image;
584}
Romain Guy694b5192010-07-21 21:33:20 -0700585
Romain Guy671d6cf2012-01-18 12:39:17 -0800586void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700587 checkInit();
588
Romain Guy5b3b3522010-10-27 18:57:51 -0700589 mDrawn = false;
590 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -0700591 mClip = clip;
Romain Guy671d6cf2012-01-18 12:39:17 -0800592}
Romain Guyff98fa52011-11-28 09:35:09 -0800593
Romain Guy671d6cf2012-01-18 12:39:17 -0800594void FontRenderer::finishRender() {
Romain Guy5b3b3522010-10-27 18:57:51 -0700595 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -0800596 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700597
598 if (mCurrentQuadIndex != 0) {
599 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700600 }
Romain Guy671d6cf2012-01-18 12:39:17 -0800601}
602
Romain Guye3a9b242013-01-08 11:15:30 -0800603void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix) {
604 Font* font = Font::create(this, paint, matrix);
Chet Haasee816bae2012-08-09 13:39:02 -0700605 font->precache(paint, text, numGlyphs);
606}
607
Romain Guy671d6cf2012-01-18 12:39:17 -0800608bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
609 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
610 const float* positions, Rect* bounds) {
611 if (!mCurrentFont) {
612 ALOGE("No font set");
613 return false;
614 }
615
616 initRender(clip, bounds);
617 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
618 finishRender();
Romain Guy5b3b3522010-10-27 18:57:51 -0700619
620 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700621}
622
Romain Guy97771732012-02-28 18:17:02 -0800623bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
624 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
625 float hOffset, float vOffset, Rect* bounds) {
626 if (!mCurrentFont) {
627 ALOGE("No font set");
628 return false;
629 }
630
631 initRender(clip, bounds);
632 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
633 finishRender();
634
635 return mDrawn;
636}
637
Romain Guy9b1204b2012-09-04 15:22:57 -0700638void FontRenderer::removeFont(const Font* font) {
Romain Guye3a9b242013-01-08 11:15:30 -0800639 mActiveFonts.remove(font->getDescription());
Romain Guy9b1204b2012-09-04 15:22:57 -0700640
641 if (mCurrentFont == font) {
642 mCurrentFont = NULL;
643 }
644}
645
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700646void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
647 // Compute gaussian weights for the blur
648 // e is the euler's number
649 float e = 2.718281828459045f;
650 float pi = 3.1415926535897932f;
651 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
652 // x is of the form [-radius .. 0 .. radius]
653 // and sigma varies with radius.
654 // Based on some experimental radius values and sigma's
655 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700656 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700657 // The larger the radius gets, the more our gaussian blur
658 // will resemble a box blur since with large sigma
659 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -0800660 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700661
662 // Now compute the coefficints
663 // We will store some redundant values to save some math during
664 // the blur calculations
665 // precompute some values
666 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
667 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
668
669 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -0800670 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700671 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700672 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
673 normalizeFactor += weights[r + radius];
674 }
675
676 //Now we need to normalize the weights because all our coefficients need to add up to one
677 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -0800678 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700679 weights[r + radius] *= normalizeFactor;
680 }
681}
682
683void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700684 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700685 float blurredPixel = 0.0f;
686 float currentPixel = 0.0f;
687
Romain Guy325a0f92011-01-05 15:26:55 -0800688 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700689
690 const uint8_t* input = source + y * width;
691 uint8_t* output = dest + y * width;
692
Romain Guy325a0f92011-01-05 15:26:55 -0800693 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700694 blurredPixel = 0.0f;
695 const float* gPtr = weights;
696 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800697 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700698 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -0800699 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700700 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700701 blurredPixel += currentPixel * gPtr[0];
702 gPtr++;
703 i++;
704 }
705 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800706 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700707 // Stepping left and right away from the pixel
708 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -0800709 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700710 validW = 0;
711 }
Romain Guy325a0f92011-01-05 15:26:55 -0800712 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700713 validW = width - 1;
714 }
715
Romain Guy325a0f92011-01-05 15:26:55 -0800716 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700717 blurredPixel += currentPixel * gPtr[0];
718 gPtr++;
719 }
720 }
721 *output = (uint8_t)blurredPixel;
722 output ++;
723 }
724 }
725}
726
727void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700728 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700729 float blurredPixel = 0.0f;
730 float currentPixel = 0.0f;
731
Romain Guy325a0f92011-01-05 15:26:55 -0800732 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700733 uint8_t* output = dest + y * width;
734
Romain Guy325a0f92011-01-05 15:26:55 -0800735 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700736 blurredPixel = 0.0f;
737 const float* gPtr = weights;
738 const uint8_t* input = source + x;
739 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800740 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700741 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -0800742 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700743 currentPixel = (float)(*i);
744 blurredPixel += currentPixel * gPtr[0];
745 gPtr++;
746 i += width;
747 }
748 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800749 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700750 int validH = y + r;
751 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -0800752 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700753 validH = 0;
754 }
Romain Guy325a0f92011-01-05 15:26:55 -0800755 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700756 validH = height - 1;
757 }
758
759 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -0800760 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700761 blurredPixel += currentPixel * gPtr[0];
762 gPtr++;
763 }
764 }
Romain Guy325a0f92011-01-05 15:26:55 -0800765 *output = (uint8_t) blurredPixel;
Romain Guy9b1204b2012-09-04 15:22:57 -0700766 output++;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700767 }
768 }
769}
770
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800771void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) {
772 if (width * height * radius < RS_MIN_INPUT_CUTOFF) {
773 float *gaussian = new float[2 * radius + 1];
774 computeGaussianWeights(gaussian, radius);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700775
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800776 uint8_t* scratch = new uint8_t[width * height];
Romain Guyd71dd362011-12-12 19:03:35 -0800777
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800778 horizontalBlur(gaussian, radius, *image, scratch, width, height);
779 verticalBlur(gaussian, radius, scratch, *image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -0800780
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800781 delete[] gaussian;
782 delete[] scratch;
Chris Craikdd8697c2013-02-22 10:41:36 -0800783 return;
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800784 }
Romain Guyd71dd362011-12-12 19:03:35 -0800785
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800786 uint8_t* outImage = (uint8_t*)memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
787
788 if (mRs.get() == 0) {
789 mRs = new RSC::RS();
790 if (!mRs->init(true, true)) {
791 ALOGE("blur RS failed to init");
792 }
793
794 mRsElement = RSC::Element::A_8(mRs);
795 mRsScript = new RSC::ScriptIntrinsicBlur(mRs, mRsElement);
796 }
797
798 sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0);
799 sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
800 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, *image);
801 sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
802 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, outImage);
803
804 mRsScript->setRadius(radius);
805 mRsScript->blur(ain, aout);
806
807 // replace the original image's pointer, avoiding a copy back to the original buffer
Ben Cheng15641a612013-02-20 13:20:03 -0800808 free(*image);
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800809 *image = outImage;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700810}
811
Romain Guy694b5192010-07-21 21:33:20 -0700812}; // namespace uirenderer
813}; // namespace android