blob: 5d5e6a58001ec47997e02da9429077b7de6dd4d7 [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
Romain Guy694b5192010-07-21 21:33:20 -070019#include <SkUtils.h>
20
Romain Guy51769a62010-07-23 00:28:00 -070021#include <cutils/properties.h>
Romain Guye2d345e2010-09-24 18:39:22 -070022
Romain Guy51769a62010-07-23 00:28:00 -070023#include <utils/Log.h>
24
Chris Craikf2d8ccc2013-02-13 16:14:17 -080025#include "RenderScript.h"
26
27#include "utils/Timing.h"
Romain Guy15bc6432011-12-13 13:11:32 -080028#include "Caches.h"
Romain Guyc9855a52011-01-21 21:14:15 -080029#include "Debug.h"
Romain Guy51769a62010-07-23 00:28:00 -070030#include "FontRenderer.h"
Romain Guy9f5dab32012-09-04 12:55:44 -070031#include "Rect.h"
Romain Guy51769a62010-07-23 00:28:00 -070032
Romain Guy694b5192010-07-21 21:33:20 -070033namespace android {
34namespace uirenderer {
35
Chris Craikf2d8ccc2013-02-13 16:14:17 -080036// blur inputs smaller than this constant will bypass renderscript
37#define RS_MIN_INPUT_CUTOFF 10000
38
Romain Guy694b5192010-07-21 21:33:20 -070039///////////////////////////////////////////////////////////////////////////////
Romain Guy694b5192010-07-21 21:33:20 -070040// FontRenderer
41///////////////////////////////////////////////////////////////////////////////
42
Romain Guy514fb182011-01-19 14:38:29 -080043static bool sLogFontRendererCreate = true;
44
Romain Guye3a9b242013-01-08 11:15:30 -080045FontRenderer::FontRenderer() :
46 mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity) {
47
Romain Guyc9855a52011-01-21 21:14:15 -080048 if (sLogFontRendererCreate) {
49 INIT_LOGD("Creating FontRenderer");
50 }
Romain Guy51769a62010-07-23 00:28:00 -070051
Romain Guyb45c0c92010-08-26 20:35:23 -070052 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -070053 mInitialized = false;
54 mMaxNumberOfQuads = 1024;
55 mCurrentQuadIndex = 0;
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +090056 mLastQuadIndex = 0;
Romain Guy694b5192010-07-21 21:33:20 -070057
Romain Guy9b1204b2012-09-04 15:22:57 -070058 mTextMesh = NULL;
Chet Haase7de0cb12011-12-05 16:35:38 -080059 mCurrentCacheTexture = NULL;
Romain Guy9cccc2b92010-08-07 23:46:15 -070060
Chet Haase2a47c142011-12-14 15:22:56 -080061 mLinearFiltering = false;
62
Romain Guy694b5192010-07-21 21:33:20 -070063 mIndexBufferID = 0;
64
Chet Haaseeb32a492012-08-31 13:54:03 -070065 mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH;
66 mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT;
67 mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH;
68 mLargeCacheHeight = DEFAULT_TEXT_LARGE_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -070069
70 char property[PROPERTY_VALUE_MAX];
Chet Haaseeb32a492012-08-31 13:54:03 -070071 if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, NULL) > 0) {
Chet Haase7de0cb12011-12-05 16:35:38 -080072 mSmallCacheWidth = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -070073 }
Romain Guy9f5dab32012-09-04 12:55:44 -070074
Chet Haaseeb32a492012-08-31 13:54:03 -070075 if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, NULL) > 0) {
Chet Haase7de0cb12011-12-05 16:35:38 -080076 mSmallCacheHeight = atoi(property);
Chet Haaseeb32a492012-08-31 13:54:03 -070077 }
Romain Guy9f5dab32012-09-04 12:55:44 -070078
Chet Haaseeb32a492012-08-31 13:54:03 -070079 if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, NULL) > 0) {
80 mLargeCacheWidth = atoi(property);
81 }
Romain Guy9f5dab32012-09-04 12:55:44 -070082
Chet Haaseeb32a492012-08-31 13:54:03 -070083 if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, NULL) > 0) {
84 mLargeCacheHeight = atoi(property);
85 }
Romain Guy9f5dab32012-09-04 12:55:44 -070086
87 uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize;
88 mSmallCacheWidth = mSmallCacheWidth > maxTextureSize ? maxTextureSize : mSmallCacheWidth;
89 mSmallCacheHeight = mSmallCacheHeight > maxTextureSize ? maxTextureSize : mSmallCacheHeight;
90 mLargeCacheWidth = mLargeCacheWidth > maxTextureSize ? maxTextureSize : mLargeCacheWidth;
91 mLargeCacheHeight = mLargeCacheHeight > maxTextureSize ? maxTextureSize : mLargeCacheHeight;
92
Chet Haaseeb32a492012-08-31 13:54:03 -070093 if (sLogFontRendererCreate) {
94 INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
95 mSmallCacheWidth, mSmallCacheHeight,
96 mLargeCacheWidth, mLargeCacheHeight >> 1,
97 mLargeCacheWidth, mLargeCacheHeight >> 1,
98 mLargeCacheWidth, mLargeCacheHeight);
Romain Guy51769a62010-07-23 00:28:00 -070099 }
Romain Guy514fb182011-01-19 14:38:29 -0800100
101 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -0700102}
103
104FontRenderer::~FontRenderer() {
Chet Haase378e9192012-08-15 15:54:54 -0700105 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
106 delete mCacheTextures[i];
Romain Guy694b5192010-07-21 21:33:20 -0700107 }
Chet Haase378e9192012-08-15 15:54:54 -0700108 mCacheTextures.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700109
Romain Guy9cccc2b92010-08-07 23:46:15 -0700110 if (mInitialized) {
Romain Guya9dd8202012-03-26 14:52:00 -0700111 // Unbinding the buffer shouldn't be necessary but it crashes with some drivers
112 Caches::getInstance().unbindIndicesBuffer();
Romain Guyb0317982012-03-21 11:52:52 -0700113 glDeleteBuffers(1, &mIndexBufferID);
114
Romain Guy9b1204b2012-09-04 15:22:57 -0700115 delete[] mTextMesh;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700116 }
Romain Guy694b5192010-07-21 21:33:20 -0700117
Romain Guye3a9b242013-01-08 11:15:30 -0800118 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
119 while (it.next()) {
120 delete it.value();
Romain Guy694b5192010-07-21 21:33:20 -0700121 }
Romain Guye3a9b242013-01-08 11:15:30 -0800122 mActiveFonts.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700123}
124
125void FontRenderer::flushAllAndInvalidate() {
126 if (mCurrentQuadIndex != 0) {
127 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700128 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700129
Romain Guye3a9b242013-01-08 11:15:30 -0800130 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
131 while (it.next()) {
132 it.value()->invalidateTextureCache();
Romain Guy694b5192010-07-21 21:33:20 -0700133 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700134
Chet Haase378e9192012-08-15 15:54:54 -0700135 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
136 mCacheTextures[i]->init();
Romain Guy694b5192010-07-21 21:33:20 -0700137 }
Chet Haasee816bae2012-08-09 13:39:02 -0700138
Romain Guy80872462012-09-04 16:42:01 -0700139#if DEBUG_FONT_RENDERER
Chet Haase378e9192012-08-15 15:54:54 -0700140 uint16_t totalGlyphs = 0;
141 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
Romain Guy80872462012-09-04 16:42:01 -0700142 totalGlyphs += mCacheTextures[i]->getGlyphCount();
Chet Haase378e9192012-08-15 15:54:54 -0700143 // Erase caches, just as a debugging facility
Romain Guy80872462012-09-04 16:42:01 -0700144 if (mCacheTextures[i]->getTexture()) {
145 memset(mCacheTextures[i]->getTexture(), 0,
146 mCacheTextures[i]->getWidth() * mCacheTextures[i]->getHeight());
Chet Haase378e9192012-08-15 15:54:54 -0700147 }
Chet Haasee816bae2012-08-09 13:39:02 -0700148 }
149 ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
150#endif
Romain Guy694b5192010-07-21 21:33:20 -0700151}
152
Chet Haase9a824562011-12-16 15:44:59 -0800153void FontRenderer::flushLargeCaches() {
Chet Haase378e9192012-08-15 15:54:54 -0700154 // Start from 1; don't deallocate smallest/default texture
155 for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
156 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guy80872462012-09-04 16:42:01 -0700157 if (cacheTexture->getTexture()) {
Chet Haase378e9192012-08-15 15:54:54 -0700158 cacheTexture->init();
Romain Guye3a9b242013-01-08 11:15:30 -0800159 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
160 while (it.next()) {
161 it.value()->invalidateTextureCache(cacheTexture);
Chet Haasee816bae2012-08-09 13:39:02 -0700162 }
Romain Guy80872462012-09-04 16:42:01 -0700163 cacheTexture->releaseTexture();
Chet Haase9a824562011-12-16 15:44:59 -0800164 }
165 }
Chet Haase9a824562011-12-16 15:44:59 -0800166}
167
Chet Haase378e9192012-08-15 15:54:54 -0700168CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
169 uint32_t* startX, uint32_t* startY) {
170 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
171 if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
172 return mCacheTextures[i];
173 }
174 }
175 // Could not fit glyph into current cache textures
176 return NULL;
177}
178
Chet Haase7de0cb12011-12-05 16:35:38 -0800179void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
Chet Haasef942cf12012-08-30 09:06:46 -0700180 uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700181 checkInit();
Chet Haase7de0cb12011-12-05 16:35:38 -0800182 cachedGlyph->mIsValid = false;
Romain Guy694b5192010-07-21 21:33:20 -0700183 // If the glyph is too tall, don't cache it
Chet Haase378e9192012-08-15 15:54:54 -0700184 if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
Romain Guy80872462012-09-04 16:42:01 -0700185 mCacheTextures[mCacheTextures.size() - 1]->getHeight()) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700186 ALOGE("Font size too large to fit in cache. width, height = %i, %i",
187 (int) glyph.fWidth, (int) glyph.fHeight);
Chet Haase7de0cb12011-12-05 16:35:38 -0800188 return;
Romain Guy694b5192010-07-21 21:33:20 -0700189 }
190
191 // Now copy the bitmap into the cache texture
192 uint32_t startX = 0;
193 uint32_t startY = 0;
194
Chet Haase378e9192012-08-15 15:54:54 -0700195 CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700196
Chet Haase378e9192012-08-15 15:54:54 -0700197 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700198 if (!precaching) {
199 // If the new glyph didn't fit and we are not just trying to precache it,
200 // clear out the cache and try again
201 flushAllAndInvalidate();
202 cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
203 }
Romain Guy694b5192010-07-21 21:33:20 -0700204
Chet Haase378e9192012-08-15 15:54:54 -0700205 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700206 // either the glyph didn't fit or we're precaching and will cache it when we draw
Chet Haase7de0cb12011-12-05 16:35:38 -0800207 return;
Romain Guy694b5192010-07-21 21:33:20 -0700208 }
209 }
210
Chet Haase378e9192012-08-15 15:54:54 -0700211 cachedGlyph->mCacheTexture = cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800212
Romain Guy694b5192010-07-21 21:33:20 -0700213 *retOriginX = startX;
214 *retOriginY = startY;
215
216 uint32_t endX = startX + glyph.fWidth;
217 uint32_t endY = startY + glyph.fHeight;
218
Romain Guy80872462012-09-04 16:42:01 -0700219 uint32_t cacheWidth = cacheTexture->getWidth();
Romain Guy694b5192010-07-21 21:33:20 -0700220
Romain Guy80872462012-09-04 16:42:01 -0700221 if (!cacheTexture->getTexture()) {
222 Caches::getInstance().activeTexture(0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800223 // Large-glyph texture memory is allocated only as needed
Romain Guy80872462012-09-04 16:42:01 -0700224 cacheTexture->allocateTexture();
Chet Haase7de0cb12011-12-05 16:35:38 -0800225 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700226
Romain Guyb969a0d2013-02-05 14:38:40 -0800227 // Tells us whether the glyphs is B&W (1 bit per pixel)
228 // or anti-aliased (8 bits per pixel)
229 SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
Romain Guy694b5192010-07-21 21:33:20 -0700230
Romain Guyb969a0d2013-02-05 14:38:40 -0800231 uint8_t* cacheBuffer = cacheTexture->getTexture();
Romain Guy694b5192010-07-21 21:33:20 -0700232 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
Romain Guy33fa1f72012-08-07 19:09:57 -0700233
Romain Guyb969a0d2013-02-05 14:38:40 -0800234 // Zero out the borders
Romain Guy33fa1f72012-08-07 19:09:57 -0700235 for (cacheX = startX - TEXTURE_BORDER_SIZE; cacheX < endX + TEXTURE_BORDER_SIZE; cacheX++) {
236 cacheBuffer[(startY - TEXTURE_BORDER_SIZE) * cacheWidth + cacheX] = 0;
237 cacheBuffer[(endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + cacheX] = 0;
238 }
239
240 for (cacheY = startY - TEXTURE_BORDER_SIZE + 1;
241 cacheY < endY + TEXTURE_BORDER_SIZE - 1; cacheY++) {
242 cacheBuffer[cacheY * cacheWidth + startX - TEXTURE_BORDER_SIZE] = 0;
243 cacheBuffer[cacheY * cacheWidth + endX + TEXTURE_BORDER_SIZE - 1] = 0;
244 }
245
Romain Guyb969a0d2013-02-05 14:38:40 -0800246 // Copy the glyph image, taking the mask format into account
247 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
248 int stride = glyph.rowBytes();
249
250 switch (format) {
251 case SkMask::kA8_Format: {
252 if (mGammaTable) {
253 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
254 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
255 uint8_t tempCol = bitmapBuffer[bY + bX];
256 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
257 }
258 }
259 } else {
260 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
261 memcpy(&cacheBuffer[cacheY * cacheWidth + startX], &bitmapBuffer[bY],
262 glyph.fWidth);
263 }
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700264 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800265 break;
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700266 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800267 case SkMask::kBW_Format: {
268 static const uint8_t COLORS[2] = { 0, 255 };
269
270 for (cacheY = startY; cacheY < endY; cacheY++) {
271 cacheX = startX;
272 int rowBytes = stride;
273 uint8_t* buffer = bitmapBuffer;
274
275 while (--rowBytes >= 0) {
276 uint8_t b = *buffer++;
277 for (int8_t mask = 7; mask >= 0 && cacheX < endX; mask--) {
278 cacheBuffer[cacheY * cacheWidth + cacheX++] = COLORS[(b >> mask) & 0x1];
279 }
280 }
281
282 bitmapBuffer += stride;
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700283 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800284 break;
Romain Guy694b5192010-07-21 21:33:20 -0700285 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800286 default:
287 ALOGW("Unkown glyph format: 0x%x", format);
288 break;
Romain Guy694b5192010-07-21 21:33:20 -0700289 }
Romain Guy97771732012-02-28 18:17:02 -0800290
Chet Haase7de0cb12011-12-05 16:35:38 -0800291 cachedGlyph->mIsValid = true;
Romain Guy694b5192010-07-21 21:33:20 -0700292}
293
Chet Haase7de0cb12011-12-05 16:35:38 -0800294CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
Romain Guy0aa87bb2012-07-20 11:14:32 -0700295 CacheTexture* cacheTexture = new CacheTexture(width, height);
Romain Guy9d9758a2012-05-14 15:19:58 -0700296
Chet Haase2a47c142011-12-14 15:22:56 -0800297 if (allocate) {
Romain Guy80872462012-09-04 16:42:01 -0700298 Caches::getInstance().activeTexture(0);
299 cacheTexture->allocateTexture();
Chet Haase2a47c142011-12-14 15:22:56 -0800300 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700301
Chet Haase2a47c142011-12-14 15:22:56 -0800302 return cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800303}
304
305void FontRenderer::initTextTexture() {
Chet Haase378e9192012-08-15 15:54:54 -0700306 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
307 delete mCacheTextures[i];
Romain Guy9d9758a2012-05-14 15:19:58 -0700308 }
Chet Haase378e9192012-08-15 15:54:54 -0700309 mCacheTextures.clear();
Romain Guy9d9758a2012-05-14 15:19:58 -0700310
Chet Haase7de0cb12011-12-05 16:35:38 -0800311 mUploadTexture = false;
Chet Haase378e9192012-08-15 15:54:54 -0700312 mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
Chet Haaseeb32a492012-08-31 13:54:03 -0700313 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
314 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
315 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false));
Chet Haase378e9192012-08-15 15:54:54 -0700316 mCurrentCacheTexture = mCacheTextures[0];
Romain Guy694b5192010-07-21 21:33:20 -0700317}
318
319// Avoid having to reallocate memory and render quad by quad
320void FontRenderer::initVertexArrayBuffers() {
Romain Guyd71dd362011-12-12 19:03:35 -0800321 uint32_t numIndices = mMaxNumberOfQuads * 6;
322 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700323 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700324
325 // Four verts, two triangles , six indices per quad
326 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
327 int i6 = i * 6;
328 int i4 = i * 4;
329
330 indexBufferData[i6 + 0] = i4 + 0;
331 indexBufferData[i6 + 1] = i4 + 1;
332 indexBufferData[i6 + 2] = i4 + 2;
333
334 indexBufferData[i6 + 3] = i4 + 0;
335 indexBufferData[i6 + 4] = i4 + 2;
336 indexBufferData[i6 + 5] = i4 + 3;
337 }
338
339 glGenBuffers(1, &mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800340 Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700341 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
Romain Guy694b5192010-07-21 21:33:20 -0700342
343 free(indexBufferData);
344
Romain Guyd71dd362011-12-12 19:03:35 -0800345 uint32_t coordSize = 2;
Romain Guy694b5192010-07-21 21:33:20 -0700346 uint32_t uvSize = 2;
347 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700348 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
Romain Guy9b1204b2012-09-04 15:22:57 -0700349 mTextMesh = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700350}
351
352// We don't want to allocate anything unless we actually draw text
353void FontRenderer::checkInit() {
354 if (mInitialized) {
355 return;
356 }
357
358 initTextTexture();
359 initVertexArrayBuffers();
360
361 mInitialized = true;
362}
363
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900364void FontRenderer::updateDrawParams() {
365 if (mCurrentQuadIndex != mLastQuadIndex) {
366 mDrawOffsets.add((uint16_t*)(mLastQuadIndex * sizeof(uint16_t) * 6));
367 mDrawCounts.add(mCurrentQuadIndex - mLastQuadIndex);
368 mDrawCacheTextures.add(mCurrentCacheTexture);
369 mLastQuadIndex = mCurrentQuadIndex;
370 }
371}
372
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700373void FontRenderer::checkTextureUpdate() {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900374 if (!mUploadTexture) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700375 return;
Romain Guy694b5192010-07-21 21:33:20 -0700376 }
377
Romain Guy2d4fd362011-12-13 22:00:19 -0800378 Caches& caches = Caches::getInstance();
379 GLuint lastTextureId = 0;
Chet Haase378e9192012-08-15 15:54:54 -0700380 // Iterate over all the cache textures and see which ones need to be updated
381 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
382 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guy80872462012-09-04 16:42:01 -0700383 if (cacheTexture->isDirty() && cacheTexture->getTexture()) {
Chet Haaseb92d8f72012-09-21 08:40:46 -0700384 // Can't copy inner rect; glTexSubimage expects pointer to deal with entire buffer
385 // of data. So expand the dirty rect to the encompassing horizontal stripe.
386 const Rect* dirtyRect = cacheTexture->getDirtyRect();
387 uint32_t x = 0;
388 uint32_t y = dirtyRect->top;
Romain Guy80872462012-09-04 16:42:01 -0700389 uint32_t width = cacheTexture->getWidth();
Chet Haaseb92d8f72012-09-21 08:40:46 -0700390 uint32_t height = dirtyRect->getHeight();
391 void* textureData = cacheTexture->getTexture() + y * width;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700392
Romain Guy80872462012-09-04 16:42:01 -0700393 if (cacheTexture->getTextureId() != lastTextureId) {
394 lastTextureId = cacheTexture->getTextureId();
Romain Guy2d4fd362011-12-13 22:00:19 -0800395 caches.activeTexture(0);
Romain Guy80872462012-09-04 16:42:01 -0700396 glBindTexture(GL_TEXTURE_2D, lastTextureId);
Romain Guy2d4fd362011-12-13 22:00:19 -0800397 }
Chet Haasee816bae2012-08-09 13:39:02 -0700398#if DEBUG_FONT_RENDERER
Chet Haaseb92d8f72012-09-21 08:40:46 -0700399 ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d",
400 i, x, y, width, height);
Chet Haasee816bae2012-08-09 13:39:02 -0700401#endif
Chet Haaseb92d8f72012-09-21 08:40:46 -0700402 glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700403 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Romain Guy80872462012-09-04 16:42:01 -0700404 cacheTexture->setDirty(false);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700405 }
406 }
407
408 mUploadTexture = false;
409}
410
411void FontRenderer::issueDrawCommand() {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900412 updateDrawParams();
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700413 checkTextureUpdate();
414
Romain Guy15bc6432011-12-13 13:11:32 -0800415 Caches& caches = Caches::getInstance();
Romain Guy2d4fd362011-12-13 22:00:19 -0800416 caches.bindIndicesBuffer(mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800417 if (!mDrawn) {
Romain Guy9b1204b2012-09-04 15:22:57 -0700418 float* buffer = mTextMesh;
Romain Guy15bc6432011-12-13 13:11:32 -0800419 int offset = 2;
420
421 bool force = caches.unbindMeshBuffer();
Chris Craikcb4d6002012-09-25 12:00:29 -0700422 caches.bindPositionVertexPointer(force, buffer);
423 caches.bindTexCoordsVertexPointer(force, buffer + offset);
Romain Guy15bc6432011-12-13 13:11:32 -0800424 }
425
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900426 for (uint32_t i = 0; i < mDrawOffsets.size(); i++) {
427 uint16_t* offset = mDrawOffsets[i];
428 uint32_t count = mDrawCounts[i];
429 CacheTexture* texture = mDrawCacheTextures[i];
430
431 caches.activeTexture(0);
432 glBindTexture(GL_TEXTURE_2D, texture->getTextureId());
433
434 texture->setLinearFiltering(mLinearFiltering, false);
435
436 glDrawElements(GL_TRIANGLES, count * 6, GL_UNSIGNED_SHORT, offset);
437 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700438
439 mDrawn = true;
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900440
441 mCurrentQuadIndex = 0;
442 mLastQuadIndex = 0;
443 mDrawOffsets.clear();
444 mDrawCounts.clear();
445 mDrawCacheTextures.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700446}
447
Romain Guy97771732012-02-28 18:17:02 -0800448void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
449 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800450 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800451 if (texture != mCurrentCacheTexture) {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900452 updateDrawParams();
Chet Haase7de0cb12011-12-05 16:35:38 -0800453 // Now use the new texture id
454 mCurrentCacheTexture = texture;
455 }
Romain Guy09147fb2010-07-22 13:08:20 -0700456
Romain Guy694b5192010-07-21 21:33:20 -0700457 const uint32_t vertsPerQuad = 4;
Romain Guyd71dd362011-12-12 19:03:35 -0800458 const uint32_t floatsPerVert = 4;
Romain Guy9b1204b2012-09-04 15:22:57 -0700459 float* currentPos = mTextMesh + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700460
Romain Guy694b5192010-07-21 21:33:20 -0700461 (*currentPos++) = x1;
462 (*currentPos++) = y1;
Romain Guy694b5192010-07-21 21:33:20 -0700463 (*currentPos++) = u1;
464 (*currentPos++) = v1;
465
466 (*currentPos++) = x2;
467 (*currentPos++) = y2;
Romain Guy694b5192010-07-21 21:33:20 -0700468 (*currentPos++) = u2;
469 (*currentPos++) = v2;
470
471 (*currentPos++) = x3;
472 (*currentPos++) = y3;
Romain Guy694b5192010-07-21 21:33:20 -0700473 (*currentPos++) = u3;
474 (*currentPos++) = v3;
475
476 (*currentPos++) = x4;
477 (*currentPos++) = y4;
Romain Guy694b5192010-07-21 21:33:20 -0700478 (*currentPos++) = u4;
479 (*currentPos++) = v4;
480
481 mCurrentQuadIndex++;
Romain Guy97771732012-02-28 18:17:02 -0800482}
483
484void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
485 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
486 float x4, float y4, float u4, float v4, CacheTexture* texture) {
487
488 if (mClip &&
489 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
490 return;
491 }
492
493 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 -0700494
Romain Guy5b3b3522010-10-27 18:57:51 -0700495 if (mBounds) {
496 mBounds->left = fmin(mBounds->left, x1);
497 mBounds->top = fmin(mBounds->top, y3);
498 mBounds->right = fmax(mBounds->right, x3);
499 mBounds->bottom = fmax(mBounds->bottom, y1);
500 }
501
Romain Guy694b5192010-07-21 21:33:20 -0700502 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
503 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700504 }
505}
506
Romain Guy97771732012-02-28 18:17:02 -0800507void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
508 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
509 float x4, float y4, float u4, float v4, CacheTexture* texture) {
510
511 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
512
513 if (mBounds) {
514 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
515 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
516 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
517 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
518 }
519
520 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
521 issueDrawCommand();
Romain Guy97771732012-02-28 18:17:02 -0800522 }
523}
524
Romain Guye3a9b242013-01-08 11:15:30 -0800525void FontRenderer::setFont(SkPaint* paint, const mat4& matrix) {
526 mCurrentFont = Font::create(this, paint, matrix);
Romain Guy694b5192010-07-21 21:33:20 -0700527}
Romain Guy7975fb62010-10-01 16:36:14 -0700528
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700529FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Raph Levien416a8472012-07-19 22:48:17 -0700530 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700531 checkInit();
532
533 if (!mCurrentFont) {
534 DropShadow image;
535 image.width = 0;
536 image.height = 0;
537 image.image = NULL;
538 image.penX = 0;
539 image.penY = 0;
540 return image;
541 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700542
Romain Guy2d4fd362011-12-13 22:00:19 -0800543 mDrawn = false;
Romain Guyff98fa52011-11-28 09:35:09 -0800544 mClip = NULL;
545 mBounds = NULL;
546
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700547 Rect bounds;
Raph Levien416a8472012-07-19 22:48:17 -0700548 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
Romain Guyff98fa52011-11-28 09:35:09 -0800549
Romain Guy1e45aae2010-08-13 19:39:53 -0700550 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
551 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Romain Guyff98fa52011-11-28 09:35:09 -0800552
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800553 // Align buffers for renderscript usage
554 if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) {
555 paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700556 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700557
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800558 int size = paddedWidth * paddedHeight;
559 uint8_t* dataBuffer = (uint8_t*)memalign(RS_CPU_ALLOCATION_ALIGNMENT, size);
560 memset(dataBuffer, 0, size);
561
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700562 int penX = radius - bounds.left;
563 int penY = radius - bounds.bottom;
564
Romain Guy726aeba2011-06-01 14:52:00 -0700565 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
Raph Levien416a8472012-07-19 22:48:17 -0700566 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800567 blurImage(&dataBuffer, paddedWidth, paddedHeight, radius);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700568
569 DropShadow image;
570 image.width = paddedWidth;
571 image.height = paddedHeight;
572 image.image = dataBuffer;
573 image.penX = penX;
574 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -0800575
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700576 return image;
577}
Romain Guy694b5192010-07-21 21:33:20 -0700578
Romain Guy671d6cf2012-01-18 12:39:17 -0800579void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700580 checkInit();
581
Romain Guy5b3b3522010-10-27 18:57:51 -0700582 mDrawn = false;
583 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -0700584 mClip = clip;
Romain Guy671d6cf2012-01-18 12:39:17 -0800585}
Romain Guyff98fa52011-11-28 09:35:09 -0800586
Romain Guy671d6cf2012-01-18 12:39:17 -0800587void FontRenderer::finishRender() {
Romain Guy5b3b3522010-10-27 18:57:51 -0700588 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -0800589 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700590
591 if (mCurrentQuadIndex != 0) {
592 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700593 }
Romain Guy671d6cf2012-01-18 12:39:17 -0800594}
595
Romain Guye3a9b242013-01-08 11:15:30 -0800596void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix) {
597 Font* font = Font::create(this, paint, matrix);
Chet Haasee816bae2012-08-09 13:39:02 -0700598 font->precache(paint, text, numGlyphs);
599}
600
Romain Guy671d6cf2012-01-18 12:39:17 -0800601bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
602 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
603 const float* positions, Rect* bounds) {
604 if (!mCurrentFont) {
605 ALOGE("No font set");
606 return false;
607 }
608
609 initRender(clip, bounds);
610 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
611 finishRender();
Romain Guy5b3b3522010-10-27 18:57:51 -0700612
613 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700614}
615
Romain Guy97771732012-02-28 18:17:02 -0800616bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
617 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
618 float hOffset, float vOffset, Rect* bounds) {
619 if (!mCurrentFont) {
620 ALOGE("No font set");
621 return false;
622 }
623
624 initRender(clip, bounds);
625 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
626 finishRender();
627
628 return mDrawn;
629}
630
Romain Guy9b1204b2012-09-04 15:22:57 -0700631void FontRenderer::removeFont(const Font* font) {
Romain Guye3a9b242013-01-08 11:15:30 -0800632 mActiveFonts.remove(font->getDescription());
Romain Guy9b1204b2012-09-04 15:22:57 -0700633
634 if (mCurrentFont == font) {
635 mCurrentFont = NULL;
636 }
637}
638
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700639void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
640 // Compute gaussian weights for the blur
641 // e is the euler's number
642 float e = 2.718281828459045f;
643 float pi = 3.1415926535897932f;
644 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
645 // x is of the form [-radius .. 0 .. radius]
646 // and sigma varies with radius.
647 // Based on some experimental radius values and sigma's
648 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700649 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700650 // The larger the radius gets, the more our gaussian blur
651 // will resemble a box blur since with large sigma
652 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -0800653 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700654
655 // Now compute the coefficints
656 // We will store some redundant values to save some math during
657 // the blur calculations
658 // precompute some values
659 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
660 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
661
662 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -0800663 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700664 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700665 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
666 normalizeFactor += weights[r + radius];
667 }
668
669 //Now we need to normalize the weights because all our coefficients need to add up to one
670 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -0800671 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700672 weights[r + radius] *= normalizeFactor;
673 }
674}
675
676void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700677 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700678 float blurredPixel = 0.0f;
679 float currentPixel = 0.0f;
680
Romain Guy325a0f92011-01-05 15:26:55 -0800681 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700682
683 const uint8_t* input = source + y * width;
684 uint8_t* output = dest + y * width;
685
Romain Guy325a0f92011-01-05 15:26:55 -0800686 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700687 blurredPixel = 0.0f;
688 const float* gPtr = weights;
689 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800690 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700691 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -0800692 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700693 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700694 blurredPixel += currentPixel * gPtr[0];
695 gPtr++;
696 i++;
697 }
698 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800699 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700700 // Stepping left and right away from the pixel
701 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -0800702 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700703 validW = 0;
704 }
Romain Guy325a0f92011-01-05 15:26:55 -0800705 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700706 validW = width - 1;
707 }
708
Romain Guy325a0f92011-01-05 15:26:55 -0800709 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700710 blurredPixel += currentPixel * gPtr[0];
711 gPtr++;
712 }
713 }
714 *output = (uint8_t)blurredPixel;
715 output ++;
716 }
717 }
718}
719
720void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700721 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700722 float blurredPixel = 0.0f;
723 float currentPixel = 0.0f;
724
Romain Guy325a0f92011-01-05 15:26:55 -0800725 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700726 uint8_t* output = dest + y * width;
727
Romain Guy325a0f92011-01-05 15:26:55 -0800728 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700729 blurredPixel = 0.0f;
730 const float* gPtr = weights;
731 const uint8_t* input = source + x;
732 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800733 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700734 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -0800735 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700736 currentPixel = (float)(*i);
737 blurredPixel += currentPixel * gPtr[0];
738 gPtr++;
739 i += width;
740 }
741 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800742 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700743 int validH = y + r;
744 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -0800745 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700746 validH = 0;
747 }
Romain Guy325a0f92011-01-05 15:26:55 -0800748 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700749 validH = height - 1;
750 }
751
752 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -0800753 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700754 blurredPixel += currentPixel * gPtr[0];
755 gPtr++;
756 }
757 }
Romain Guy325a0f92011-01-05 15:26:55 -0800758 *output = (uint8_t) blurredPixel;
Romain Guy9b1204b2012-09-04 15:22:57 -0700759 output++;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700760 }
761 }
762}
763
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800764void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) {
765 if (width * height * radius < RS_MIN_INPUT_CUTOFF) {
766 float *gaussian = new float[2 * radius + 1];
767 computeGaussianWeights(gaussian, radius);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700768
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800769 uint8_t* scratch = new uint8_t[width * height];
Romain Guyd71dd362011-12-12 19:03:35 -0800770
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800771 horizontalBlur(gaussian, radius, *image, scratch, width, height);
772 verticalBlur(gaussian, radius, scratch, *image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -0800773
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800774 delete[] gaussian;
775 delete[] scratch;
776 }
Romain Guyd71dd362011-12-12 19:03:35 -0800777
Chris Craikf2d8ccc2013-02-13 16:14:17 -0800778 uint8_t* outImage = (uint8_t*)memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
779
780 if (mRs.get() == 0) {
781 mRs = new RSC::RS();
782 if (!mRs->init(true, true)) {
783 ALOGE("blur RS failed to init");
784 }
785
786 mRsElement = RSC::Element::A_8(mRs);
787 mRsScript = new RSC::ScriptIntrinsicBlur(mRs, mRsElement);
788 }
789
790 sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0);
791 sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
792 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, *image);
793 sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
794 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, outImage);
795
796 mRsScript->setRadius(radius);
797 mRsScript->blur(ain, aout);
798
799 // replace the original image's pointer, avoiding a copy back to the original buffer
800 delete *image;
801 *image = outImage;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700802}
803
Romain Guy694b5192010-07-21 21:33:20 -0700804}; // namespace uirenderer
805}; // namespace android