blob: d8297daeb636b356da24185c3c71d8f6b33da719 [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
Romain Guy15bc6432011-12-13 13:11:32 -080026#include "Caches.h"
Romain Guyc9855a52011-01-21 21:14:15 -080027#include "Debug.h"
Romain Guy51769a62010-07-23 00:28:00 -070028#include "FontRenderer.h"
Romain Guy9f5dab32012-09-04 12:55:44 -070029#include "Rect.h"
Romain Guy51769a62010-07-23 00:28:00 -070030
Romain Guy694b5192010-07-21 21:33:20 -070031namespace android {
32namespace uirenderer {
33
34///////////////////////////////////////////////////////////////////////////////
Romain Guy694b5192010-07-21 21:33:20 -070035// FontRenderer
36///////////////////////////////////////////////////////////////////////////////
37
Romain Guy514fb182011-01-19 14:38:29 -080038static bool sLogFontRendererCreate = true;
39
Romain Guye3a9b242013-01-08 11:15:30 -080040FontRenderer::FontRenderer() :
41 mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity) {
42
Romain Guyc9855a52011-01-21 21:14:15 -080043 if (sLogFontRendererCreate) {
44 INIT_LOGD("Creating FontRenderer");
45 }
Romain Guy51769a62010-07-23 00:28:00 -070046
Romain Guyb45c0c92010-08-26 20:35:23 -070047 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -070048 mInitialized = false;
49 mMaxNumberOfQuads = 1024;
50 mCurrentQuadIndex = 0;
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +090051 mLastQuadIndex = 0;
Romain Guy694b5192010-07-21 21:33:20 -070052
Romain Guy9b1204b2012-09-04 15:22:57 -070053 mTextMesh = NULL;
Chet Haase7de0cb12011-12-05 16:35:38 -080054 mCurrentCacheTexture = NULL;
Romain Guy9cccc2b92010-08-07 23:46:15 -070055
Chet Haase2a47c142011-12-14 15:22:56 -080056 mLinearFiltering = false;
57
Romain Guy694b5192010-07-21 21:33:20 -070058 mIndexBufferID = 0;
59
Chet Haaseeb32a492012-08-31 13:54:03 -070060 mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH;
61 mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT;
62 mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH;
63 mLargeCacheHeight = DEFAULT_TEXT_LARGE_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -070064
65 char property[PROPERTY_VALUE_MAX];
Chet Haaseeb32a492012-08-31 13:54:03 -070066 if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, NULL) > 0) {
Chet Haase7de0cb12011-12-05 16:35:38 -080067 mSmallCacheWidth = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -070068 }
Romain Guy9f5dab32012-09-04 12:55:44 -070069
Chet Haaseeb32a492012-08-31 13:54:03 -070070 if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, NULL) > 0) {
Chet Haase7de0cb12011-12-05 16:35:38 -080071 mSmallCacheHeight = atoi(property);
Chet Haaseeb32a492012-08-31 13:54:03 -070072 }
Romain Guy9f5dab32012-09-04 12:55:44 -070073
Chet Haaseeb32a492012-08-31 13:54:03 -070074 if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, NULL) > 0) {
75 mLargeCacheWidth = atoi(property);
76 }
Romain Guy9f5dab32012-09-04 12:55:44 -070077
Chet Haaseeb32a492012-08-31 13:54:03 -070078 if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, NULL) > 0) {
79 mLargeCacheHeight = atoi(property);
80 }
Romain Guy9f5dab32012-09-04 12:55:44 -070081
82 uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize;
83 mSmallCacheWidth = mSmallCacheWidth > maxTextureSize ? maxTextureSize : mSmallCacheWidth;
84 mSmallCacheHeight = mSmallCacheHeight > maxTextureSize ? maxTextureSize : mSmallCacheHeight;
85 mLargeCacheWidth = mLargeCacheWidth > maxTextureSize ? maxTextureSize : mLargeCacheWidth;
86 mLargeCacheHeight = mLargeCacheHeight > maxTextureSize ? maxTextureSize : mLargeCacheHeight;
87
Chet Haaseeb32a492012-08-31 13:54:03 -070088 if (sLogFontRendererCreate) {
89 INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
90 mSmallCacheWidth, mSmallCacheHeight,
91 mLargeCacheWidth, mLargeCacheHeight >> 1,
92 mLargeCacheWidth, mLargeCacheHeight >> 1,
93 mLargeCacheWidth, mLargeCacheHeight);
Romain Guy51769a62010-07-23 00:28:00 -070094 }
Romain Guy514fb182011-01-19 14:38:29 -080095
96 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -070097}
98
99FontRenderer::~FontRenderer() {
Chet Haase378e9192012-08-15 15:54:54 -0700100 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
101 delete mCacheTextures[i];
Romain Guy694b5192010-07-21 21:33:20 -0700102 }
Chet Haase378e9192012-08-15 15:54:54 -0700103 mCacheTextures.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700104
Romain Guy9cccc2b92010-08-07 23:46:15 -0700105 if (mInitialized) {
Romain Guya9dd8202012-03-26 14:52:00 -0700106 // Unbinding the buffer shouldn't be necessary but it crashes with some drivers
107 Caches::getInstance().unbindIndicesBuffer();
Romain Guyb0317982012-03-21 11:52:52 -0700108 glDeleteBuffers(1, &mIndexBufferID);
109
Romain Guy9b1204b2012-09-04 15:22:57 -0700110 delete[] mTextMesh;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700111 }
Romain Guy694b5192010-07-21 21:33:20 -0700112
Romain Guye3a9b242013-01-08 11:15:30 -0800113 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
114 while (it.next()) {
115 delete it.value();
Romain Guy694b5192010-07-21 21:33:20 -0700116 }
Romain Guye3a9b242013-01-08 11:15:30 -0800117 mActiveFonts.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700118}
119
120void FontRenderer::flushAllAndInvalidate() {
121 if (mCurrentQuadIndex != 0) {
122 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700123 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700124
Romain Guye3a9b242013-01-08 11:15:30 -0800125 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
126 while (it.next()) {
127 it.value()->invalidateTextureCache();
Romain Guy694b5192010-07-21 21:33:20 -0700128 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700129
Chet Haase378e9192012-08-15 15:54:54 -0700130 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
131 mCacheTextures[i]->init();
Romain Guy694b5192010-07-21 21:33:20 -0700132 }
Chet Haasee816bae2012-08-09 13:39:02 -0700133
Romain Guy80872462012-09-04 16:42:01 -0700134#if DEBUG_FONT_RENDERER
Chet Haase378e9192012-08-15 15:54:54 -0700135 uint16_t totalGlyphs = 0;
136 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
Romain Guy80872462012-09-04 16:42:01 -0700137 totalGlyphs += mCacheTextures[i]->getGlyphCount();
Chet Haase378e9192012-08-15 15:54:54 -0700138 // Erase caches, just as a debugging facility
Romain Guy80872462012-09-04 16:42:01 -0700139 if (mCacheTextures[i]->getTexture()) {
140 memset(mCacheTextures[i]->getTexture(), 0,
141 mCacheTextures[i]->getWidth() * mCacheTextures[i]->getHeight());
Chet Haase378e9192012-08-15 15:54:54 -0700142 }
Chet Haasee816bae2012-08-09 13:39:02 -0700143 }
144 ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
145#endif
Romain Guy694b5192010-07-21 21:33:20 -0700146}
147
Chet Haase9a824562011-12-16 15:44:59 -0800148void FontRenderer::flushLargeCaches() {
Chet Haase378e9192012-08-15 15:54:54 -0700149 // Start from 1; don't deallocate smallest/default texture
150 for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
151 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guy80872462012-09-04 16:42:01 -0700152 if (cacheTexture->getTexture()) {
Chet Haase378e9192012-08-15 15:54:54 -0700153 cacheTexture->init();
Romain Guye3a9b242013-01-08 11:15:30 -0800154 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
155 while (it.next()) {
156 it.value()->invalidateTextureCache(cacheTexture);
Chet Haasee816bae2012-08-09 13:39:02 -0700157 }
Romain Guy80872462012-09-04 16:42:01 -0700158 cacheTexture->releaseTexture();
Chet Haase9a824562011-12-16 15:44:59 -0800159 }
160 }
Chet Haase9a824562011-12-16 15:44:59 -0800161}
162
Chet Haase378e9192012-08-15 15:54:54 -0700163CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
164 uint32_t* startX, uint32_t* startY) {
165 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
166 if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
167 return mCacheTextures[i];
168 }
169 }
170 // Could not fit glyph into current cache textures
171 return NULL;
172}
173
Chet Haase7de0cb12011-12-05 16:35:38 -0800174void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
Chet Haasef942cf12012-08-30 09:06:46 -0700175 uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700176 checkInit();
Chet Haase7de0cb12011-12-05 16:35:38 -0800177 cachedGlyph->mIsValid = false;
Romain Guy694b5192010-07-21 21:33:20 -0700178 // If the glyph is too tall, don't cache it
Chet Haase378e9192012-08-15 15:54:54 -0700179 if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
Romain Guy80872462012-09-04 16:42:01 -0700180 mCacheTextures[mCacheTextures.size() - 1]->getHeight()) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700181 ALOGE("Font size too large to fit in cache. width, height = %i, %i",
182 (int) glyph.fWidth, (int) glyph.fHeight);
Chet Haase7de0cb12011-12-05 16:35:38 -0800183 return;
Romain Guy694b5192010-07-21 21:33:20 -0700184 }
185
186 // Now copy the bitmap into the cache texture
187 uint32_t startX = 0;
188 uint32_t startY = 0;
189
Chet Haase378e9192012-08-15 15:54:54 -0700190 CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700191
Chet Haase378e9192012-08-15 15:54:54 -0700192 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700193 if (!precaching) {
194 // If the new glyph didn't fit and we are not just trying to precache it,
195 // clear out the cache and try again
196 flushAllAndInvalidate();
197 cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
198 }
Romain Guy694b5192010-07-21 21:33:20 -0700199
Chet Haase378e9192012-08-15 15:54:54 -0700200 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700201 // either the glyph didn't fit or we're precaching and will cache it when we draw
Chet Haase7de0cb12011-12-05 16:35:38 -0800202 return;
Romain Guy694b5192010-07-21 21:33:20 -0700203 }
204 }
205
Chet Haase378e9192012-08-15 15:54:54 -0700206 cachedGlyph->mCacheTexture = cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800207
Romain Guy694b5192010-07-21 21:33:20 -0700208 *retOriginX = startX;
209 *retOriginY = startY;
210
211 uint32_t endX = startX + glyph.fWidth;
212 uint32_t endY = startY + glyph.fHeight;
213
Romain Guy80872462012-09-04 16:42:01 -0700214 uint32_t cacheWidth = cacheTexture->getWidth();
Romain Guy694b5192010-07-21 21:33:20 -0700215
Romain Guy80872462012-09-04 16:42:01 -0700216 if (!cacheTexture->getTexture()) {
217 Caches::getInstance().activeTexture(0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800218 // Large-glyph texture memory is allocated only as needed
Romain Guy80872462012-09-04 16:42:01 -0700219 cacheTexture->allocateTexture();
Chet Haase7de0cb12011-12-05 16:35:38 -0800220 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700221
Romain Guyb969a0d2013-02-05 14:38:40 -0800222 // Tells us whether the glyphs is B&W (1 bit per pixel)
223 // or anti-aliased (8 bits per pixel)
224 SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
Romain Guy694b5192010-07-21 21:33:20 -0700225
Romain Guyb969a0d2013-02-05 14:38:40 -0800226 uint8_t* cacheBuffer = cacheTexture->getTexture();
Romain Guy694b5192010-07-21 21:33:20 -0700227 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
Romain Guy33fa1f72012-08-07 19:09:57 -0700228
Romain Guyb969a0d2013-02-05 14:38:40 -0800229 // Zero out the borders
Romain Guy33fa1f72012-08-07 19:09:57 -0700230 for (cacheX = startX - TEXTURE_BORDER_SIZE; cacheX < endX + TEXTURE_BORDER_SIZE; cacheX++) {
231 cacheBuffer[(startY - TEXTURE_BORDER_SIZE) * cacheWidth + cacheX] = 0;
232 cacheBuffer[(endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + cacheX] = 0;
233 }
234
235 for (cacheY = startY - TEXTURE_BORDER_SIZE + 1;
236 cacheY < endY + TEXTURE_BORDER_SIZE - 1; cacheY++) {
237 cacheBuffer[cacheY * cacheWidth + startX - TEXTURE_BORDER_SIZE] = 0;
238 cacheBuffer[cacheY * cacheWidth + endX + TEXTURE_BORDER_SIZE - 1] = 0;
239 }
240
Romain Guyb969a0d2013-02-05 14:38:40 -0800241 // Copy the glyph image, taking the mask format into account
242 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
243 int stride = glyph.rowBytes();
244
245 switch (format) {
246 case SkMask::kA8_Format: {
247 if (mGammaTable) {
248 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
249 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
250 uint8_t tempCol = bitmapBuffer[bY + bX];
251 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
252 }
253 }
254 } else {
255 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
256 memcpy(&cacheBuffer[cacheY * cacheWidth + startX], &bitmapBuffer[bY],
257 glyph.fWidth);
258 }
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700259 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800260 break;
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700261 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800262 case SkMask::kBW_Format: {
263 static const uint8_t COLORS[2] = { 0, 255 };
264
265 for (cacheY = startY; cacheY < endY; cacheY++) {
266 cacheX = startX;
267 int rowBytes = stride;
268 uint8_t* buffer = bitmapBuffer;
269
270 while (--rowBytes >= 0) {
271 uint8_t b = *buffer++;
272 for (int8_t mask = 7; mask >= 0 && cacheX < endX; mask--) {
273 cacheBuffer[cacheY * cacheWidth + cacheX++] = COLORS[(b >> mask) & 0x1];
274 }
275 }
276
277 bitmapBuffer += stride;
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700278 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800279 break;
Romain Guy694b5192010-07-21 21:33:20 -0700280 }
Romain Guyb969a0d2013-02-05 14:38:40 -0800281 default:
282 ALOGW("Unkown glyph format: 0x%x", format);
283 break;
Romain Guy694b5192010-07-21 21:33:20 -0700284 }
Romain Guy97771732012-02-28 18:17:02 -0800285
Chet Haase7de0cb12011-12-05 16:35:38 -0800286 cachedGlyph->mIsValid = true;
Romain Guy694b5192010-07-21 21:33:20 -0700287}
288
Chet Haase7de0cb12011-12-05 16:35:38 -0800289CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
Romain Guy0aa87bb2012-07-20 11:14:32 -0700290 CacheTexture* cacheTexture = new CacheTexture(width, height);
Romain Guy9d9758a2012-05-14 15:19:58 -0700291
Chet Haase2a47c142011-12-14 15:22:56 -0800292 if (allocate) {
Romain Guy80872462012-09-04 16:42:01 -0700293 Caches::getInstance().activeTexture(0);
294 cacheTexture->allocateTexture();
Chet Haase2a47c142011-12-14 15:22:56 -0800295 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700296
Chet Haase2a47c142011-12-14 15:22:56 -0800297 return cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800298}
299
300void FontRenderer::initTextTexture() {
Chet Haase378e9192012-08-15 15:54:54 -0700301 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
302 delete mCacheTextures[i];
Romain Guy9d9758a2012-05-14 15:19:58 -0700303 }
Chet Haase378e9192012-08-15 15:54:54 -0700304 mCacheTextures.clear();
Romain Guy9d9758a2012-05-14 15:19:58 -0700305
Chet Haase7de0cb12011-12-05 16:35:38 -0800306 mUploadTexture = false;
Chet Haase378e9192012-08-15 15:54:54 -0700307 mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
Chet Haaseeb32a492012-08-31 13:54:03 -0700308 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
309 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
310 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false));
Chet Haase378e9192012-08-15 15:54:54 -0700311 mCurrentCacheTexture = mCacheTextures[0];
Romain Guy694b5192010-07-21 21:33:20 -0700312}
313
314// Avoid having to reallocate memory and render quad by quad
315void FontRenderer::initVertexArrayBuffers() {
Romain Guyd71dd362011-12-12 19:03:35 -0800316 uint32_t numIndices = mMaxNumberOfQuads * 6;
317 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700318 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700319
320 // Four verts, two triangles , six indices per quad
321 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
322 int i6 = i * 6;
323 int i4 = i * 4;
324
325 indexBufferData[i6 + 0] = i4 + 0;
326 indexBufferData[i6 + 1] = i4 + 1;
327 indexBufferData[i6 + 2] = i4 + 2;
328
329 indexBufferData[i6 + 3] = i4 + 0;
330 indexBufferData[i6 + 4] = i4 + 2;
331 indexBufferData[i6 + 5] = i4 + 3;
332 }
333
334 glGenBuffers(1, &mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800335 Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700336 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
Romain Guy694b5192010-07-21 21:33:20 -0700337
338 free(indexBufferData);
339
Romain Guyd71dd362011-12-12 19:03:35 -0800340 uint32_t coordSize = 2;
Romain Guy694b5192010-07-21 21:33:20 -0700341 uint32_t uvSize = 2;
342 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700343 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
Romain Guy9b1204b2012-09-04 15:22:57 -0700344 mTextMesh = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700345}
346
347// We don't want to allocate anything unless we actually draw text
348void FontRenderer::checkInit() {
349 if (mInitialized) {
350 return;
351 }
352
353 initTextTexture();
354 initVertexArrayBuffers();
355
356 mInitialized = true;
357}
358
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900359void FontRenderer::updateDrawParams() {
360 if (mCurrentQuadIndex != mLastQuadIndex) {
361 mDrawOffsets.add((uint16_t*)(mLastQuadIndex * sizeof(uint16_t) * 6));
362 mDrawCounts.add(mCurrentQuadIndex - mLastQuadIndex);
363 mDrawCacheTextures.add(mCurrentCacheTexture);
364 mLastQuadIndex = mCurrentQuadIndex;
365 }
366}
367
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700368void FontRenderer::checkTextureUpdate() {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900369 if (!mUploadTexture) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700370 return;
Romain Guy694b5192010-07-21 21:33:20 -0700371 }
372
Romain Guy2d4fd362011-12-13 22:00:19 -0800373 Caches& caches = Caches::getInstance();
374 GLuint lastTextureId = 0;
Chet Haase378e9192012-08-15 15:54:54 -0700375 // Iterate over all the cache textures and see which ones need to be updated
376 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
377 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guy80872462012-09-04 16:42:01 -0700378 if (cacheTexture->isDirty() && cacheTexture->getTexture()) {
Chet Haaseb92d8f72012-09-21 08:40:46 -0700379 // Can't copy inner rect; glTexSubimage expects pointer to deal with entire buffer
380 // of data. So expand the dirty rect to the encompassing horizontal stripe.
381 const Rect* dirtyRect = cacheTexture->getDirtyRect();
382 uint32_t x = 0;
383 uint32_t y = dirtyRect->top;
Romain Guy80872462012-09-04 16:42:01 -0700384 uint32_t width = cacheTexture->getWidth();
Chet Haaseb92d8f72012-09-21 08:40:46 -0700385 uint32_t height = dirtyRect->getHeight();
386 void* textureData = cacheTexture->getTexture() + y * width;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700387
Romain Guy80872462012-09-04 16:42:01 -0700388 if (cacheTexture->getTextureId() != lastTextureId) {
389 lastTextureId = cacheTexture->getTextureId();
Romain Guy2d4fd362011-12-13 22:00:19 -0800390 caches.activeTexture(0);
Romain Guy80872462012-09-04 16:42:01 -0700391 glBindTexture(GL_TEXTURE_2D, lastTextureId);
Romain Guy2d4fd362011-12-13 22:00:19 -0800392 }
Chet Haasee816bae2012-08-09 13:39:02 -0700393#if DEBUG_FONT_RENDERER
Chet Haaseb92d8f72012-09-21 08:40:46 -0700394 ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d",
395 i, x, y, width, height);
Chet Haasee816bae2012-08-09 13:39:02 -0700396#endif
Chet Haaseb92d8f72012-09-21 08:40:46 -0700397 glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700398 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Romain Guy80872462012-09-04 16:42:01 -0700399 cacheTexture->setDirty(false);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700400 }
401 }
402
403 mUploadTexture = false;
404}
405
406void FontRenderer::issueDrawCommand() {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900407 updateDrawParams();
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700408 checkTextureUpdate();
409
Romain Guy15bc6432011-12-13 13:11:32 -0800410 Caches& caches = Caches::getInstance();
Romain Guy2d4fd362011-12-13 22:00:19 -0800411 caches.bindIndicesBuffer(mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800412 if (!mDrawn) {
Romain Guy9b1204b2012-09-04 15:22:57 -0700413 float* buffer = mTextMesh;
Romain Guy15bc6432011-12-13 13:11:32 -0800414 int offset = 2;
415
416 bool force = caches.unbindMeshBuffer();
Chris Craikcb4d6002012-09-25 12:00:29 -0700417 caches.bindPositionVertexPointer(force, buffer);
418 caches.bindTexCoordsVertexPointer(force, buffer + offset);
Romain Guy15bc6432011-12-13 13:11:32 -0800419 }
420
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900421 for (uint32_t i = 0; i < mDrawOffsets.size(); i++) {
422 uint16_t* offset = mDrawOffsets[i];
423 uint32_t count = mDrawCounts[i];
424 CacheTexture* texture = mDrawCacheTextures[i];
425
426 caches.activeTexture(0);
427 glBindTexture(GL_TEXTURE_2D, texture->getTextureId());
428
429 texture->setLinearFiltering(mLinearFiltering, false);
430
431 glDrawElements(GL_TRIANGLES, count * 6, GL_UNSIGNED_SHORT, offset);
432 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700433
434 mDrawn = true;
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900435
436 mCurrentQuadIndex = 0;
437 mLastQuadIndex = 0;
438 mDrawOffsets.clear();
439 mDrawCounts.clear();
440 mDrawCacheTextures.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700441}
442
Romain Guy97771732012-02-28 18:17:02 -0800443void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
444 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800445 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800446 if (texture != mCurrentCacheTexture) {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900447 updateDrawParams();
Chet Haase7de0cb12011-12-05 16:35:38 -0800448 // Now use the new texture id
449 mCurrentCacheTexture = texture;
450 }
Romain Guy09147fb2010-07-22 13:08:20 -0700451
Romain Guy694b5192010-07-21 21:33:20 -0700452 const uint32_t vertsPerQuad = 4;
Romain Guyd71dd362011-12-12 19:03:35 -0800453 const uint32_t floatsPerVert = 4;
Romain Guy9b1204b2012-09-04 15:22:57 -0700454 float* currentPos = mTextMesh + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700455
Romain Guy694b5192010-07-21 21:33:20 -0700456 (*currentPos++) = x1;
457 (*currentPos++) = y1;
Romain Guy694b5192010-07-21 21:33:20 -0700458 (*currentPos++) = u1;
459 (*currentPos++) = v1;
460
461 (*currentPos++) = x2;
462 (*currentPos++) = y2;
Romain Guy694b5192010-07-21 21:33:20 -0700463 (*currentPos++) = u2;
464 (*currentPos++) = v2;
465
466 (*currentPos++) = x3;
467 (*currentPos++) = y3;
Romain Guy694b5192010-07-21 21:33:20 -0700468 (*currentPos++) = u3;
469 (*currentPos++) = v3;
470
471 (*currentPos++) = x4;
472 (*currentPos++) = y4;
Romain Guy694b5192010-07-21 21:33:20 -0700473 (*currentPos++) = u4;
474 (*currentPos++) = v4;
475
476 mCurrentQuadIndex++;
Romain Guy97771732012-02-28 18:17:02 -0800477}
478
479void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
480 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
481 float x4, float y4, float u4, float v4, CacheTexture* texture) {
482
483 if (mClip &&
484 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
485 return;
486 }
487
488 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 -0700489
Romain Guy5b3b3522010-10-27 18:57:51 -0700490 if (mBounds) {
491 mBounds->left = fmin(mBounds->left, x1);
492 mBounds->top = fmin(mBounds->top, y3);
493 mBounds->right = fmax(mBounds->right, x3);
494 mBounds->bottom = fmax(mBounds->bottom, y1);
495 }
496
Romain Guy694b5192010-07-21 21:33:20 -0700497 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
498 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700499 }
500}
501
Romain Guy97771732012-02-28 18:17:02 -0800502void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
503 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
504 float x4, float y4, float u4, float v4, CacheTexture* texture) {
505
506 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
507
508 if (mBounds) {
509 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
510 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
511 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
512 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
513 }
514
515 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
516 issueDrawCommand();
Romain Guy97771732012-02-28 18:17:02 -0800517 }
518}
519
Romain Guye3a9b242013-01-08 11:15:30 -0800520void FontRenderer::setFont(SkPaint* paint, const mat4& matrix) {
521 mCurrentFont = Font::create(this, paint, matrix);
Romain Guy694b5192010-07-21 21:33:20 -0700522}
Romain Guy7975fb62010-10-01 16:36:14 -0700523
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700524FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Raph Levien416a8472012-07-19 22:48:17 -0700525 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700526 checkInit();
527
528 if (!mCurrentFont) {
529 DropShadow image;
530 image.width = 0;
531 image.height = 0;
532 image.image = NULL;
533 image.penX = 0;
534 image.penY = 0;
535 return image;
536 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700537
Romain Guy2d4fd362011-12-13 22:00:19 -0800538 mDrawn = false;
Romain Guyff98fa52011-11-28 09:35:09 -0800539 mClip = NULL;
540 mBounds = NULL;
541
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700542 Rect bounds;
Raph Levien416a8472012-07-19 22:48:17 -0700543 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
Romain Guyff98fa52011-11-28 09:35:09 -0800544
Romain Guy1e45aae2010-08-13 19:39:53 -0700545 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
546 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Chris Craikbf5703e2013-02-13 19:47:24 +0000547 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guyff98fa52011-11-28 09:35:09 -0800548
Chris Craikbf5703e2013-02-13 19:47:24 +0000549 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
550 dataBuffer[i] = 0;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700551 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700552
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700553 int penX = radius - bounds.left;
554 int penY = radius - bounds.bottom;
555
Romain Guy726aeba2011-06-01 14:52:00 -0700556 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
Raph Levien416a8472012-07-19 22:48:17 -0700557 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
Chris Craikbf5703e2013-02-13 19:47:24 +0000558 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700559
560 DropShadow image;
561 image.width = paddedWidth;
562 image.height = paddedHeight;
563 image.image = dataBuffer;
564 image.penX = penX;
565 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -0800566
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700567 return image;
568}
Romain Guy694b5192010-07-21 21:33:20 -0700569
Romain Guy671d6cf2012-01-18 12:39:17 -0800570void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700571 checkInit();
572
Romain Guy5b3b3522010-10-27 18:57:51 -0700573 mDrawn = false;
574 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -0700575 mClip = clip;
Romain Guy671d6cf2012-01-18 12:39:17 -0800576}
Romain Guyff98fa52011-11-28 09:35:09 -0800577
Romain Guy671d6cf2012-01-18 12:39:17 -0800578void FontRenderer::finishRender() {
Romain Guy5b3b3522010-10-27 18:57:51 -0700579 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -0800580 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700581
582 if (mCurrentQuadIndex != 0) {
583 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700584 }
Romain Guy671d6cf2012-01-18 12:39:17 -0800585}
586
Romain Guye3a9b242013-01-08 11:15:30 -0800587void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix) {
588 Font* font = Font::create(this, paint, matrix);
Chet Haasee816bae2012-08-09 13:39:02 -0700589 font->precache(paint, text, numGlyphs);
590}
591
Romain Guy671d6cf2012-01-18 12:39:17 -0800592bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
593 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
594 const float* positions, Rect* bounds) {
595 if (!mCurrentFont) {
596 ALOGE("No font set");
597 return false;
598 }
599
600 initRender(clip, bounds);
601 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
602 finishRender();
Romain Guy5b3b3522010-10-27 18:57:51 -0700603
604 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700605}
606
Romain Guy97771732012-02-28 18:17:02 -0800607bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
608 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
609 float hOffset, float vOffset, Rect* bounds) {
610 if (!mCurrentFont) {
611 ALOGE("No font set");
612 return false;
613 }
614
615 initRender(clip, bounds);
616 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
617 finishRender();
618
619 return mDrawn;
620}
621
Romain Guy9b1204b2012-09-04 15:22:57 -0700622void FontRenderer::removeFont(const Font* font) {
Romain Guye3a9b242013-01-08 11:15:30 -0800623 mActiveFonts.remove(font->getDescription());
Romain Guy9b1204b2012-09-04 15:22:57 -0700624
625 if (mCurrentFont == font) {
626 mCurrentFont = NULL;
627 }
628}
629
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700630void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
631 // Compute gaussian weights for the blur
632 // e is the euler's number
633 float e = 2.718281828459045f;
634 float pi = 3.1415926535897932f;
635 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
636 // x is of the form [-radius .. 0 .. radius]
637 // and sigma varies with radius.
638 // Based on some experimental radius values and sigma's
639 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700640 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700641 // The larger the radius gets, the more our gaussian blur
642 // will resemble a box blur since with large sigma
643 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -0800644 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700645
646 // Now compute the coefficints
647 // We will store some redundant values to save some math during
648 // the blur calculations
649 // precompute some values
650 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
651 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
652
653 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -0800654 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700655 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700656 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
657 normalizeFactor += weights[r + radius];
658 }
659
660 //Now we need to normalize the weights because all our coefficients need to add up to one
661 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -0800662 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700663 weights[r + radius] *= normalizeFactor;
664 }
665}
666
667void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700668 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700669 float blurredPixel = 0.0f;
670 float currentPixel = 0.0f;
671
Romain Guy325a0f92011-01-05 15:26:55 -0800672 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700673
674 const uint8_t* input = source + y * width;
675 uint8_t* output = dest + y * width;
676
Romain Guy325a0f92011-01-05 15:26:55 -0800677 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700678 blurredPixel = 0.0f;
679 const float* gPtr = weights;
680 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800681 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700682 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -0800683 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700684 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700685 blurredPixel += currentPixel * gPtr[0];
686 gPtr++;
687 i++;
688 }
689 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800690 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700691 // Stepping left and right away from the pixel
692 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -0800693 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700694 validW = 0;
695 }
Romain Guy325a0f92011-01-05 15:26:55 -0800696 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700697 validW = width - 1;
698 }
699
Romain Guy325a0f92011-01-05 15:26:55 -0800700 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700701 blurredPixel += currentPixel * gPtr[0];
702 gPtr++;
703 }
704 }
705 *output = (uint8_t)blurredPixel;
706 output ++;
707 }
708 }
709}
710
711void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700712 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700713 float blurredPixel = 0.0f;
714 float currentPixel = 0.0f;
715
Romain Guy325a0f92011-01-05 15:26:55 -0800716 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700717 uint8_t* output = dest + y * width;
718
Romain Guy325a0f92011-01-05 15:26:55 -0800719 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700720 blurredPixel = 0.0f;
721 const float* gPtr = weights;
722 const uint8_t* input = source + x;
723 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800724 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700725 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -0800726 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700727 currentPixel = (float)(*i);
728 blurredPixel += currentPixel * gPtr[0];
729 gPtr++;
730 i += width;
731 }
732 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800733 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700734 int validH = y + r;
735 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -0800736 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700737 validH = 0;
738 }
Romain Guy325a0f92011-01-05 15:26:55 -0800739 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700740 validH = height - 1;
741 }
742
743 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -0800744 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700745 blurredPixel += currentPixel * gPtr[0];
746 gPtr++;
747 }
748 }
Romain Guy325a0f92011-01-05 15:26:55 -0800749 *output = (uint8_t) blurredPixel;
Romain Guy9b1204b2012-09-04 15:22:57 -0700750 output++;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700751 }
752 }
753}
754
755
Chris Craikbf5703e2013-02-13 19:47:24 +0000756void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
757 float *gaussian = new float[2 * radius + 1];
758 computeGaussianWeights(gaussian, radius);
Romain Guyd71dd362011-12-12 19:03:35 -0800759
Chris Craikbf5703e2013-02-13 19:47:24 +0000760 uint8_t* scratch = new uint8_t[width * height];
Romain Guyd71dd362011-12-12 19:03:35 -0800761
Chris Craikbf5703e2013-02-13 19:47:24 +0000762 horizontalBlur(gaussian, radius, image, scratch, width, height);
763 verticalBlur(gaussian, radius, scratch, image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -0800764
Chris Craikbf5703e2013-02-13 19:47:24 +0000765 delete[] gaussian;
766 delete[] scratch;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700767}
768
Romain Guy694b5192010-07-21 21:33:20 -0700769}; // namespace uirenderer
770}; // namespace android