blob: 97988f78454bfeb6bf612b5f23571b77ca639ef2 [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
Romain Guy15bc6432011-12-13 13:11:32 -080025#include "Caches.h"
Romain Guyc9855a52011-01-21 21:14:15 -080026#include "Debug.h"
Romain Guy51769a62010-07-23 00:28:00 -070027#include "FontRenderer.h"
Romain Guy9f5dab32012-09-04 12:55:44 -070028#include "Rect.h"
Romain Guy51769a62010-07-23 00:28:00 -070029
Romain Guy694b5192010-07-21 21:33:20 -070030namespace android {
31namespace uirenderer {
32
33///////////////////////////////////////////////////////////////////////////////
Romain Guy694b5192010-07-21 21:33:20 -070034// FontRenderer
35///////////////////////////////////////////////////////////////////////////////
36
Romain Guy514fb182011-01-19 14:38:29 -080037static bool sLogFontRendererCreate = true;
38
Romain Guye3a9b242013-01-08 11:15:30 -080039FontRenderer::FontRenderer() :
40 mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity) {
41
Romain Guyc9855a52011-01-21 21:14:15 -080042 if (sLogFontRendererCreate) {
43 INIT_LOGD("Creating FontRenderer");
44 }
Romain Guy51769a62010-07-23 00:28:00 -070045
Romain Guyb45c0c92010-08-26 20:35:23 -070046 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -070047 mInitialized = false;
48 mMaxNumberOfQuads = 1024;
49 mCurrentQuadIndex = 0;
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +090050 mLastQuadIndex = 0;
Romain Guy694b5192010-07-21 21:33:20 -070051
Romain Guy9b1204b2012-09-04 15:22:57 -070052 mTextMesh = NULL;
Chet Haase7de0cb12011-12-05 16:35:38 -080053 mCurrentCacheTexture = NULL;
Romain Guy9cccc2b92010-08-07 23:46:15 -070054
Chet Haase2a47c142011-12-14 15:22:56 -080055 mLinearFiltering = false;
56
Romain Guy694b5192010-07-21 21:33:20 -070057 mIndexBufferID = 0;
58
Chet Haaseeb32a492012-08-31 13:54:03 -070059 mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH;
60 mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT;
61 mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH;
62 mLargeCacheHeight = DEFAULT_TEXT_LARGE_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -070063
64 char property[PROPERTY_VALUE_MAX];
Chet Haaseeb32a492012-08-31 13:54:03 -070065 if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, NULL) > 0) {
Chet Haase7de0cb12011-12-05 16:35:38 -080066 mSmallCacheWidth = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -070067 }
Romain Guy9f5dab32012-09-04 12:55:44 -070068
Chet Haaseeb32a492012-08-31 13:54:03 -070069 if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, NULL) > 0) {
Chet Haase7de0cb12011-12-05 16:35:38 -080070 mSmallCacheHeight = atoi(property);
Chet Haaseeb32a492012-08-31 13:54:03 -070071 }
Romain Guy9f5dab32012-09-04 12:55:44 -070072
Chet Haaseeb32a492012-08-31 13:54:03 -070073 if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, NULL) > 0) {
74 mLargeCacheWidth = atoi(property);
75 }
Romain Guy9f5dab32012-09-04 12:55:44 -070076
Chet Haaseeb32a492012-08-31 13:54:03 -070077 if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, NULL) > 0) {
78 mLargeCacheHeight = atoi(property);
79 }
Romain Guy9f5dab32012-09-04 12:55:44 -070080
81 uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize;
82 mSmallCacheWidth = mSmallCacheWidth > maxTextureSize ? maxTextureSize : mSmallCacheWidth;
83 mSmallCacheHeight = mSmallCacheHeight > maxTextureSize ? maxTextureSize : mSmallCacheHeight;
84 mLargeCacheWidth = mLargeCacheWidth > maxTextureSize ? maxTextureSize : mLargeCacheWidth;
85 mLargeCacheHeight = mLargeCacheHeight > maxTextureSize ? maxTextureSize : mLargeCacheHeight;
86
Chet Haaseeb32a492012-08-31 13:54:03 -070087 if (sLogFontRendererCreate) {
88 INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
89 mSmallCacheWidth, mSmallCacheHeight,
90 mLargeCacheWidth, mLargeCacheHeight >> 1,
91 mLargeCacheWidth, mLargeCacheHeight >> 1,
92 mLargeCacheWidth, mLargeCacheHeight);
Romain Guy51769a62010-07-23 00:28:00 -070093 }
Romain Guy514fb182011-01-19 14:38:29 -080094
95 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -070096}
97
98FontRenderer::~FontRenderer() {
Chet Haase378e9192012-08-15 15:54:54 -070099 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
100 delete mCacheTextures[i];
Romain Guy694b5192010-07-21 21:33:20 -0700101 }
Chet Haase378e9192012-08-15 15:54:54 -0700102 mCacheTextures.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700103
Romain Guy9cccc2b92010-08-07 23:46:15 -0700104 if (mInitialized) {
Romain Guya9dd8202012-03-26 14:52:00 -0700105 // Unbinding the buffer shouldn't be necessary but it crashes with some drivers
106 Caches::getInstance().unbindIndicesBuffer();
Romain Guyb0317982012-03-21 11:52:52 -0700107 glDeleteBuffers(1, &mIndexBufferID);
108
Romain Guy9b1204b2012-09-04 15:22:57 -0700109 delete[] mTextMesh;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700110 }
Romain Guy694b5192010-07-21 21:33:20 -0700111
Romain Guye3a9b242013-01-08 11:15:30 -0800112 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
113 while (it.next()) {
114 delete it.value();
Romain Guy694b5192010-07-21 21:33:20 -0700115 }
Romain Guye3a9b242013-01-08 11:15:30 -0800116 mActiveFonts.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700117}
118
119void FontRenderer::flushAllAndInvalidate() {
120 if (mCurrentQuadIndex != 0) {
121 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700122 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700123
Romain Guye3a9b242013-01-08 11:15:30 -0800124 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
125 while (it.next()) {
126 it.value()->invalidateTextureCache();
Romain Guy694b5192010-07-21 21:33:20 -0700127 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700128
Chet Haase378e9192012-08-15 15:54:54 -0700129 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
130 mCacheTextures[i]->init();
Romain Guy694b5192010-07-21 21:33:20 -0700131 }
Chet Haasee816bae2012-08-09 13:39:02 -0700132
Romain Guy80872462012-09-04 16:42:01 -0700133#if DEBUG_FONT_RENDERER
Chet Haase378e9192012-08-15 15:54:54 -0700134 uint16_t totalGlyphs = 0;
135 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
Romain Guy80872462012-09-04 16:42:01 -0700136 totalGlyphs += mCacheTextures[i]->getGlyphCount();
Chet Haase378e9192012-08-15 15:54:54 -0700137 // Erase caches, just as a debugging facility
Romain Guy80872462012-09-04 16:42:01 -0700138 if (mCacheTextures[i]->getTexture()) {
139 memset(mCacheTextures[i]->getTexture(), 0,
140 mCacheTextures[i]->getWidth() * mCacheTextures[i]->getHeight());
Chet Haase378e9192012-08-15 15:54:54 -0700141 }
Chet Haasee816bae2012-08-09 13:39:02 -0700142 }
143 ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
144#endif
Romain Guy694b5192010-07-21 21:33:20 -0700145}
146
Chet Haase9a824562011-12-16 15:44:59 -0800147void FontRenderer::flushLargeCaches() {
Chet Haase378e9192012-08-15 15:54:54 -0700148 // Start from 1; don't deallocate smallest/default texture
149 for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
150 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guy80872462012-09-04 16:42:01 -0700151 if (cacheTexture->getTexture()) {
Chet Haase378e9192012-08-15 15:54:54 -0700152 cacheTexture->init();
Romain Guye3a9b242013-01-08 11:15:30 -0800153 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
154 while (it.next()) {
155 it.value()->invalidateTextureCache(cacheTexture);
Chet Haasee816bae2012-08-09 13:39:02 -0700156 }
Romain Guy80872462012-09-04 16:42:01 -0700157 cacheTexture->releaseTexture();
Chet Haase9a824562011-12-16 15:44:59 -0800158 }
159 }
Chet Haase9a824562011-12-16 15:44:59 -0800160}
161
Chet Haase378e9192012-08-15 15:54:54 -0700162CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
163 uint32_t* startX, uint32_t* startY) {
164 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
165 if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
166 return mCacheTextures[i];
167 }
168 }
169 // Could not fit glyph into current cache textures
170 return NULL;
171}
172
Chet Haase7de0cb12011-12-05 16:35:38 -0800173void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
Chet Haasef942cf12012-08-30 09:06:46 -0700174 uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700175 checkInit();
Chet Haase7de0cb12011-12-05 16:35:38 -0800176 cachedGlyph->mIsValid = false;
Romain Guy694b5192010-07-21 21:33:20 -0700177 // If the glyph is too tall, don't cache it
Chet Haase378e9192012-08-15 15:54:54 -0700178 if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
Romain Guy80872462012-09-04 16:42:01 -0700179 mCacheTextures[mCacheTextures.size() - 1]->getHeight()) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700180 ALOGE("Font size too large to fit in cache. width, height = %i, %i",
181 (int) glyph.fWidth, (int) glyph.fHeight);
Chet Haase7de0cb12011-12-05 16:35:38 -0800182 return;
Romain Guy694b5192010-07-21 21:33:20 -0700183 }
184
185 // Now copy the bitmap into the cache texture
186 uint32_t startX = 0;
187 uint32_t startY = 0;
188
Chet Haase378e9192012-08-15 15:54:54 -0700189 CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700190
Chet Haase378e9192012-08-15 15:54:54 -0700191 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700192 if (!precaching) {
193 // If the new glyph didn't fit and we are not just trying to precache it,
194 // clear out the cache and try again
195 flushAllAndInvalidate();
196 cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
197 }
Romain Guy694b5192010-07-21 21:33:20 -0700198
Chet Haase378e9192012-08-15 15:54:54 -0700199 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700200 // either the glyph didn't fit or we're precaching and will cache it when we draw
Chet Haase7de0cb12011-12-05 16:35:38 -0800201 return;
Romain Guy694b5192010-07-21 21:33:20 -0700202 }
203 }
204
Chet Haase378e9192012-08-15 15:54:54 -0700205 cachedGlyph->mCacheTexture = cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800206
Romain Guy694b5192010-07-21 21:33:20 -0700207 *retOriginX = startX;
208 *retOriginY = startY;
209
210 uint32_t endX = startX + glyph.fWidth;
211 uint32_t endY = startY + glyph.fHeight;
212
Romain Guy80872462012-09-04 16:42:01 -0700213 uint32_t cacheWidth = cacheTexture->getWidth();
Romain Guy694b5192010-07-21 21:33:20 -0700214
Romain Guy80872462012-09-04 16:42:01 -0700215 if (!cacheTexture->getTexture()) {
216 Caches::getInstance().activeTexture(0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800217 // Large-glyph texture memory is allocated only as needed
Romain Guy80872462012-09-04 16:42:01 -0700218 cacheTexture->allocateTexture();
Chet Haase7de0cb12011-12-05 16:35:38 -0800219 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700220
Romain Guyb969a0d2013-02-05 14:38:40 -0800221 // Tells us whether the glyphs is B&W (1 bit per pixel)
222 // or anti-aliased (8 bits per pixel)
223 SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
Romain Guy694b5192010-07-21 21:33:20 -0700224
Romain Guyb969a0d2013-02-05 14:38:40 -0800225 uint8_t* cacheBuffer = cacheTexture->getTexture();
Romain Guy694b5192010-07-21 21:33:20 -0700226 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
Romain Guy33fa1f72012-08-07 19:09:57 -0700227
Romain Guyb969a0d2013-02-05 14:38:40 -0800228 // Zero out the borders
Romain Guy33fa1f72012-08-07 19:09:57 -0700229 for (cacheX = startX - TEXTURE_BORDER_SIZE; cacheX < endX + TEXTURE_BORDER_SIZE; cacheX++) {
230 cacheBuffer[(startY - TEXTURE_BORDER_SIZE) * cacheWidth + cacheX] = 0;
231 cacheBuffer[(endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + cacheX] = 0;
232 }
233
234 for (cacheY = startY - TEXTURE_BORDER_SIZE + 1;
235 cacheY < endY + TEXTURE_BORDER_SIZE - 1; cacheY++) {
236 cacheBuffer[cacheY * cacheWidth + startX - TEXTURE_BORDER_SIZE] = 0;
237 cacheBuffer[cacheY * cacheWidth + endX + TEXTURE_BORDER_SIZE - 1] = 0;
238 }
239
Romain Guyb969a0d2013-02-05 14:38:40 -0800240 // Copy the glyph image, taking the mask format into account
241 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
242 int stride = glyph.rowBytes();
243
244 switch (format) {
245 case SkMask::kA8_Format: {
246 if (mGammaTable) {
247 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
248 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
249 uint8_t tempCol = bitmapBuffer[bY + bX];
250 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
251 }
252 }
253 } else {
254 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
255 memcpy(&cacheBuffer[cacheY * cacheWidth + startX], &bitmapBuffer[bY],
256 glyph.fWidth);
257 }
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700258 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800259 break;
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700260 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800261 case SkMask::kBW_Format: {
262 static const uint8_t COLORS[2] = { 0, 255 };
263
264 for (cacheY = startY; cacheY < endY; cacheY++) {
265 cacheX = startX;
266 int rowBytes = stride;
267 uint8_t* buffer = bitmapBuffer;
268
269 while (--rowBytes >= 0) {
270 uint8_t b = *buffer++;
271 for (int8_t mask = 7; mask >= 0 && cacheX < endX; mask--) {
272 cacheBuffer[cacheY * cacheWidth + cacheX++] = COLORS[(b >> mask) & 0x1];
273 }
274 }
275
276 bitmapBuffer += stride;
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700277 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800278 break;
Romain Guy694b5192010-07-21 21:33:20 -0700279 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800280 default:
281 ALOGW("Unkown glyph format: 0x%x", format);
282 break;
Romain Guy694b5192010-07-21 21:33:20 -0700283 }
Romain Guy97771732012-02-28 18:17:02 -0800284
Chet Haase7de0cb12011-12-05 16:35:38 -0800285 cachedGlyph->mIsValid = true;
Romain Guy694b5192010-07-21 21:33:20 -0700286}
287
Chet Haase7de0cb12011-12-05 16:35:38 -0800288CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
Romain Guy0aa87bb2012-07-20 11:14:32 -0700289 CacheTexture* cacheTexture = new CacheTexture(width, height);
Romain Guy9d9758a2012-05-14 15:19:58 -0700290
Chet Haase2a47c142011-12-14 15:22:56 -0800291 if (allocate) {
Romain Guy80872462012-09-04 16:42:01 -0700292 Caches::getInstance().activeTexture(0);
293 cacheTexture->allocateTexture();
Chet Haase2a47c142011-12-14 15:22:56 -0800294 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700295
Chet Haase2a47c142011-12-14 15:22:56 -0800296 return cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800297}
298
299void FontRenderer::initTextTexture() {
Chet Haase378e9192012-08-15 15:54:54 -0700300 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
301 delete mCacheTextures[i];
Romain Guy9d9758a2012-05-14 15:19:58 -0700302 }
Chet Haase378e9192012-08-15 15:54:54 -0700303 mCacheTextures.clear();
Romain Guy9d9758a2012-05-14 15:19:58 -0700304
Chet Haase7de0cb12011-12-05 16:35:38 -0800305 mUploadTexture = false;
Chet Haase378e9192012-08-15 15:54:54 -0700306 mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
Chet Haaseeb32a492012-08-31 13:54:03 -0700307 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
308 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
309 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false));
Chet Haase378e9192012-08-15 15:54:54 -0700310 mCurrentCacheTexture = mCacheTextures[0];
Romain Guy694b5192010-07-21 21:33:20 -0700311}
312
313// Avoid having to reallocate memory and render quad by quad
314void FontRenderer::initVertexArrayBuffers() {
Romain Guyd71dd362011-12-12 19:03:35 -0800315 uint32_t numIndices = mMaxNumberOfQuads * 6;
316 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700317 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700318
319 // Four verts, two triangles , six indices per quad
320 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
321 int i6 = i * 6;
322 int i4 = i * 4;
323
324 indexBufferData[i6 + 0] = i4 + 0;
325 indexBufferData[i6 + 1] = i4 + 1;
326 indexBufferData[i6 + 2] = i4 + 2;
327
328 indexBufferData[i6 + 3] = i4 + 0;
329 indexBufferData[i6 + 4] = i4 + 2;
330 indexBufferData[i6 + 5] = i4 + 3;
331 }
332
333 glGenBuffers(1, &mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800334 Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700335 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
Romain Guy694b5192010-07-21 21:33:20 -0700336
337 free(indexBufferData);
338
Romain Guyd71dd362011-12-12 19:03:35 -0800339 uint32_t coordSize = 2;
Romain Guy694b5192010-07-21 21:33:20 -0700340 uint32_t uvSize = 2;
341 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700342 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
Romain Guy9b1204b2012-09-04 15:22:57 -0700343 mTextMesh = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700344}
345
346// We don't want to allocate anything unless we actually draw text
347void FontRenderer::checkInit() {
348 if (mInitialized) {
349 return;
350 }
351
352 initTextTexture();
353 initVertexArrayBuffers();
354
355 mInitialized = true;
356}
357
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900358void FontRenderer::updateDrawParams() {
359 if (mCurrentQuadIndex != mLastQuadIndex) {
360 mDrawOffsets.add((uint16_t*)(mLastQuadIndex * sizeof(uint16_t) * 6));
361 mDrawCounts.add(mCurrentQuadIndex - mLastQuadIndex);
362 mDrawCacheTextures.add(mCurrentCacheTexture);
363 mLastQuadIndex = mCurrentQuadIndex;
364 }
365}
366
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700367void FontRenderer::checkTextureUpdate() {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900368 if (!mUploadTexture) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700369 return;
Romain Guy694b5192010-07-21 21:33:20 -0700370 }
371
Romain Guy2d4fd362011-12-13 22:00:19 -0800372 Caches& caches = Caches::getInstance();
373 GLuint lastTextureId = 0;
Chet Haase378e9192012-08-15 15:54:54 -0700374 // Iterate over all the cache textures and see which ones need to be updated
375 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
376 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guy80872462012-09-04 16:42:01 -0700377 if (cacheTexture->isDirty() && cacheTexture->getTexture()) {
Chet Haaseb92d8f72012-09-21 08:40:46 -0700378 // Can't copy inner rect; glTexSubimage expects pointer to deal with entire buffer
379 // of data. So expand the dirty rect to the encompassing horizontal stripe.
380 const Rect* dirtyRect = cacheTexture->getDirtyRect();
381 uint32_t x = 0;
382 uint32_t y = dirtyRect->top;
Romain Guy80872462012-09-04 16:42:01 -0700383 uint32_t width = cacheTexture->getWidth();
Chet Haaseb92d8f72012-09-21 08:40:46 -0700384 uint32_t height = dirtyRect->getHeight();
385 void* textureData = cacheTexture->getTexture() + y * width;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700386
Romain Guy80872462012-09-04 16:42:01 -0700387 if (cacheTexture->getTextureId() != lastTextureId) {
388 lastTextureId = cacheTexture->getTextureId();
Romain Guy2d4fd362011-12-13 22:00:19 -0800389 caches.activeTexture(0);
Romain Guy80872462012-09-04 16:42:01 -0700390 glBindTexture(GL_TEXTURE_2D, lastTextureId);
Romain Guy2d4fd362011-12-13 22:00:19 -0800391 }
Chet Haasee816bae2012-08-09 13:39:02 -0700392#if DEBUG_FONT_RENDERER
Chet Haaseb92d8f72012-09-21 08:40:46 -0700393 ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d",
394 i, x, y, width, height);
Chet Haasee816bae2012-08-09 13:39:02 -0700395#endif
Chet Haaseb92d8f72012-09-21 08:40:46 -0700396 glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700397 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Romain Guy80872462012-09-04 16:42:01 -0700398 cacheTexture->setDirty(false);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700399 }
400 }
401
402 mUploadTexture = false;
403}
404
405void FontRenderer::issueDrawCommand() {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900406 updateDrawParams();
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700407 checkTextureUpdate();
408
Romain Guy15bc6432011-12-13 13:11:32 -0800409 Caches& caches = Caches::getInstance();
Romain Guy2d4fd362011-12-13 22:00:19 -0800410 caches.bindIndicesBuffer(mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800411 if (!mDrawn) {
Romain Guy9b1204b2012-09-04 15:22:57 -0700412 float* buffer = mTextMesh;
Romain Guy15bc6432011-12-13 13:11:32 -0800413 int offset = 2;
414
415 bool force = caches.unbindMeshBuffer();
Chris Craikcb4d6002012-09-25 12:00:29 -0700416 caches.bindPositionVertexPointer(force, buffer);
417 caches.bindTexCoordsVertexPointer(force, buffer + offset);
Romain Guy15bc6432011-12-13 13:11:32 -0800418 }
419
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900420 for (uint32_t i = 0; i < mDrawOffsets.size(); i++) {
421 uint16_t* offset = mDrawOffsets[i];
422 uint32_t count = mDrawCounts[i];
423 CacheTexture* texture = mDrawCacheTextures[i];
424
425 caches.activeTexture(0);
426 glBindTexture(GL_TEXTURE_2D, texture->getTextureId());
427
428 texture->setLinearFiltering(mLinearFiltering, false);
429
430 glDrawElements(GL_TRIANGLES, count * 6, GL_UNSIGNED_SHORT, offset);
431 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700432
433 mDrawn = true;
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900434
435 mCurrentQuadIndex = 0;
436 mLastQuadIndex = 0;
437 mDrawOffsets.clear();
438 mDrawCounts.clear();
439 mDrawCacheTextures.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700440}
441
Romain Guy97771732012-02-28 18:17:02 -0800442void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
443 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800444 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800445 if (texture != mCurrentCacheTexture) {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900446 updateDrawParams();
Chet Haase7de0cb12011-12-05 16:35:38 -0800447 // Now use the new texture id
448 mCurrentCacheTexture = texture;
449 }
Romain Guy09147fb2010-07-22 13:08:20 -0700450
Romain Guy694b5192010-07-21 21:33:20 -0700451 const uint32_t vertsPerQuad = 4;
Romain Guyd71dd362011-12-12 19:03:35 -0800452 const uint32_t floatsPerVert = 4;
Romain Guy9b1204b2012-09-04 15:22:57 -0700453 float* currentPos = mTextMesh + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700454
Romain Guy694b5192010-07-21 21:33:20 -0700455 (*currentPos++) = x1;
456 (*currentPos++) = y1;
Romain Guy694b5192010-07-21 21:33:20 -0700457 (*currentPos++) = u1;
458 (*currentPos++) = v1;
459
460 (*currentPos++) = x2;
461 (*currentPos++) = y2;
Romain Guy694b5192010-07-21 21:33:20 -0700462 (*currentPos++) = u2;
463 (*currentPos++) = v2;
464
465 (*currentPos++) = x3;
466 (*currentPos++) = y3;
Romain Guy694b5192010-07-21 21:33:20 -0700467 (*currentPos++) = u3;
468 (*currentPos++) = v3;
469
470 (*currentPos++) = x4;
471 (*currentPos++) = y4;
Romain Guy694b5192010-07-21 21:33:20 -0700472 (*currentPos++) = u4;
473 (*currentPos++) = v4;
474
475 mCurrentQuadIndex++;
Romain Guy97771732012-02-28 18:17:02 -0800476}
477
478void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
479 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
480 float x4, float y4, float u4, float v4, CacheTexture* texture) {
481
482 if (mClip &&
483 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
484 return;
485 }
486
487 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 -0700488
Romain Guy5b3b3522010-10-27 18:57:51 -0700489 if (mBounds) {
490 mBounds->left = fmin(mBounds->left, x1);
491 mBounds->top = fmin(mBounds->top, y3);
492 mBounds->right = fmax(mBounds->right, x3);
493 mBounds->bottom = fmax(mBounds->bottom, y1);
494 }
495
Romain Guy694b5192010-07-21 21:33:20 -0700496 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
497 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700498 }
499}
500
Romain Guy97771732012-02-28 18:17:02 -0800501void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
502 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
503 float x4, float y4, float u4, float v4, CacheTexture* texture) {
504
505 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
506
507 if (mBounds) {
508 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
509 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
510 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
511 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
512 }
513
514 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
515 issueDrawCommand();
Romain Guy97771732012-02-28 18:17:02 -0800516 }
517}
518
Romain Guye3a9b242013-01-08 11:15:30 -0800519void FontRenderer::setFont(SkPaint* paint, const mat4& matrix) {
520 mCurrentFont = Font::create(this, paint, matrix);
Romain Guy694b5192010-07-21 21:33:20 -0700521}
Romain Guy7975fb62010-10-01 16:36:14 -0700522
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700523FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Raph Levien416a8472012-07-19 22:48:17 -0700524 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700525 checkInit();
526
527 if (!mCurrentFont) {
528 DropShadow image;
529 image.width = 0;
530 image.height = 0;
531 image.image = NULL;
532 image.penX = 0;
533 image.penY = 0;
534 return image;
535 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700536
Romain Guy2d4fd362011-12-13 22:00:19 -0800537 mDrawn = false;
Romain Guyff98fa52011-11-28 09:35:09 -0800538 mClip = NULL;
539 mBounds = NULL;
540
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700541 Rect bounds;
Raph Levien416a8472012-07-19 22:48:17 -0700542 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
Romain Guyff98fa52011-11-28 09:35:09 -0800543
Romain Guy1e45aae2010-08-13 19:39:53 -0700544 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
545 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700546 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guyff98fa52011-11-28 09:35:09 -0800547
Romain Guy1e45aae2010-08-13 19:39:53 -0700548 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700549 dataBuffer[i] = 0;
550 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700551
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700552 int penX = radius - bounds.left;
553 int penY = radius - bounds.bottom;
554
Romain Guy726aeba2011-06-01 14:52:00 -0700555 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
Raph Levien416a8472012-07-19 22:48:17 -0700556 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700557 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
558
559 DropShadow image;
560 image.width = paddedWidth;
561 image.height = paddedHeight;
562 image.image = dataBuffer;
563 image.penX = penX;
564 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -0800565
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700566 return image;
567}
Romain Guy694b5192010-07-21 21:33:20 -0700568
Romain Guy671d6cf2012-01-18 12:39:17 -0800569void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700570 checkInit();
571
Romain Guy5b3b3522010-10-27 18:57:51 -0700572 mDrawn = false;
573 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -0700574 mClip = clip;
Romain Guy671d6cf2012-01-18 12:39:17 -0800575}
Romain Guyff98fa52011-11-28 09:35:09 -0800576
Romain Guy671d6cf2012-01-18 12:39:17 -0800577void FontRenderer::finishRender() {
Romain Guy5b3b3522010-10-27 18:57:51 -0700578 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -0800579 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700580
581 if (mCurrentQuadIndex != 0) {
582 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700583 }
Romain Guy671d6cf2012-01-18 12:39:17 -0800584}
585
Romain Guye3a9b242013-01-08 11:15:30 -0800586void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix) {
587 Font* font = Font::create(this, paint, matrix);
Chet Haasee816bae2012-08-09 13:39:02 -0700588 font->precache(paint, text, numGlyphs);
589}
590
Romain Guy671d6cf2012-01-18 12:39:17 -0800591bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
592 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
593 const float* positions, Rect* bounds) {
594 if (!mCurrentFont) {
595 ALOGE("No font set");
596 return false;
597 }
598
599 initRender(clip, bounds);
600 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
601 finishRender();
Romain Guy5b3b3522010-10-27 18:57:51 -0700602
603 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700604}
605
Romain Guy97771732012-02-28 18:17:02 -0800606bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
607 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
608 float hOffset, float vOffset, Rect* bounds) {
609 if (!mCurrentFont) {
610 ALOGE("No font set");
611 return false;
612 }
613
614 initRender(clip, bounds);
615 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
616 finishRender();
617
618 return mDrawn;
619}
620
Romain Guy9b1204b2012-09-04 15:22:57 -0700621void FontRenderer::removeFont(const Font* font) {
Romain Guye3a9b242013-01-08 11:15:30 -0800622 mActiveFonts.remove(font->getDescription());
Romain Guy9b1204b2012-09-04 15:22:57 -0700623
624 if (mCurrentFont == font) {
625 mCurrentFont = NULL;
626 }
627}
628
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700629void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
630 // Compute gaussian weights for the blur
631 // e is the euler's number
632 float e = 2.718281828459045f;
633 float pi = 3.1415926535897932f;
634 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
635 // x is of the form [-radius .. 0 .. radius]
636 // and sigma varies with radius.
637 // Based on some experimental radius values and sigma's
638 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700639 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700640 // The larger the radius gets, the more our gaussian blur
641 // will resemble a box blur since with large sigma
642 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -0800643 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700644
645 // Now compute the coefficints
646 // We will store some redundant values to save some math during
647 // the blur calculations
648 // precompute some values
649 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
650 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
651
652 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -0800653 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700654 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700655 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
656 normalizeFactor += weights[r + radius];
657 }
658
659 //Now we need to normalize the weights because all our coefficients need to add up to one
660 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -0800661 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700662 weights[r + radius] *= normalizeFactor;
663 }
664}
665
666void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700667 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700668 float blurredPixel = 0.0f;
669 float currentPixel = 0.0f;
670
Romain Guy325a0f92011-01-05 15:26:55 -0800671 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700672
673 const uint8_t* input = source + y * width;
674 uint8_t* output = dest + y * width;
675
Romain Guy325a0f92011-01-05 15:26:55 -0800676 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700677 blurredPixel = 0.0f;
678 const float* gPtr = weights;
679 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800680 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700681 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -0800682 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700683 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700684 blurredPixel += currentPixel * gPtr[0];
685 gPtr++;
686 i++;
687 }
688 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800689 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700690 // Stepping left and right away from the pixel
691 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -0800692 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700693 validW = 0;
694 }
Romain Guy325a0f92011-01-05 15:26:55 -0800695 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700696 validW = width - 1;
697 }
698
Romain Guy325a0f92011-01-05 15:26:55 -0800699 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700700 blurredPixel += currentPixel * gPtr[0];
701 gPtr++;
702 }
703 }
704 *output = (uint8_t)blurredPixel;
705 output ++;
706 }
707 }
708}
709
710void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700711 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700712 float blurredPixel = 0.0f;
713 float currentPixel = 0.0f;
714
Romain Guy325a0f92011-01-05 15:26:55 -0800715 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700716 uint8_t* output = dest + y * width;
717
Romain Guy325a0f92011-01-05 15:26:55 -0800718 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700719 blurredPixel = 0.0f;
720 const float* gPtr = weights;
721 const uint8_t* input = source + x;
722 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800723 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700724 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -0800725 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700726 currentPixel = (float)(*i);
727 blurredPixel += currentPixel * gPtr[0];
728 gPtr++;
729 i += width;
730 }
731 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800732 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700733 int validH = y + r;
734 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -0800735 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700736 validH = 0;
737 }
Romain Guy325a0f92011-01-05 15:26:55 -0800738 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700739 validH = height - 1;
740 }
741
742 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -0800743 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700744 blurredPixel += currentPixel * gPtr[0];
745 gPtr++;
746 }
747 }
Romain Guy325a0f92011-01-05 15:26:55 -0800748 *output = (uint8_t) blurredPixel;
Romain Guy9b1204b2012-09-04 15:22:57 -0700749 output++;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700750 }
751 }
752}
753
754
755void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
756 float *gaussian = new float[2 * radius + 1];
757 computeGaussianWeights(gaussian, radius);
Romain Guyd71dd362011-12-12 19:03:35 -0800758
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700759 uint8_t* scratch = new uint8_t[width * height];
Romain Guyd71dd362011-12-12 19:03:35 -0800760
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700761 horizontalBlur(gaussian, radius, image, scratch, width, height);
762 verticalBlur(gaussian, radius, scratch, image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -0800763
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700764 delete[] gaussian;
765 delete[] scratch;
766}
767
Romain Guy694b5192010-07-21 21:33:20 -0700768}; // namespace uirenderer
769}; // namespace android