blob: 47784a41dcae7df4a4858a827eedda9d30cb8e2f [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 Guy694b5192010-07-21 21:33:20 -070039FontRenderer::FontRenderer() {
Romain Guyc9855a52011-01-21 21:14:15 -080040 if (sLogFontRendererCreate) {
41 INIT_LOGD("Creating FontRenderer");
42 }
Romain Guy51769a62010-07-23 00:28:00 -070043
Romain Guyb45c0c92010-08-26 20:35:23 -070044 mGammaTable = NULL;
Romain Guy694b5192010-07-21 21:33:20 -070045 mInitialized = false;
46 mMaxNumberOfQuads = 1024;
47 mCurrentQuadIndex = 0;
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +090048 mLastQuadIndex = 0;
Romain Guy694b5192010-07-21 21:33:20 -070049
Romain Guy9b1204b2012-09-04 15:22:57 -070050 mTextMesh = NULL;
Chet Haase7de0cb12011-12-05 16:35:38 -080051 mCurrentCacheTexture = NULL;
Romain Guy9cccc2b2010-08-07 23:46:15 -070052
Chet Haase2a47c142011-12-14 15:22:56 -080053 mLinearFiltering = false;
54
Romain Guy694b5192010-07-21 21:33:20 -070055 mIndexBufferID = 0;
56
Chet Haaseeb32a492012-08-31 13:54:03 -070057 mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH;
58 mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT;
59 mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH;
60 mLargeCacheHeight = DEFAULT_TEXT_LARGE_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -070061
62 char property[PROPERTY_VALUE_MAX];
Chet Haaseeb32a492012-08-31 13:54:03 -070063 if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, NULL) > 0) {
Chet Haase7de0cb12011-12-05 16:35:38 -080064 mSmallCacheWidth = atoi(property);
Romain Guy51769a62010-07-23 00:28:00 -070065 }
Romain Guy9f5dab32012-09-04 12:55:44 -070066
Chet Haaseeb32a492012-08-31 13:54:03 -070067 if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, NULL) > 0) {
Chet Haase7de0cb12011-12-05 16:35:38 -080068 mSmallCacheHeight = atoi(property);
Chet Haaseeb32a492012-08-31 13:54:03 -070069 }
Romain Guy9f5dab32012-09-04 12:55:44 -070070
Chet Haaseeb32a492012-08-31 13:54:03 -070071 if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, NULL) > 0) {
72 mLargeCacheWidth = atoi(property);
73 }
Romain Guy9f5dab32012-09-04 12:55:44 -070074
Chet Haaseeb32a492012-08-31 13:54:03 -070075 if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, NULL) > 0) {
76 mLargeCacheHeight = atoi(property);
77 }
Romain Guy9f5dab32012-09-04 12:55:44 -070078
79 uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize;
80 mSmallCacheWidth = mSmallCacheWidth > maxTextureSize ? maxTextureSize : mSmallCacheWidth;
81 mSmallCacheHeight = mSmallCacheHeight > maxTextureSize ? maxTextureSize : mSmallCacheHeight;
82 mLargeCacheWidth = mLargeCacheWidth > maxTextureSize ? maxTextureSize : mLargeCacheWidth;
83 mLargeCacheHeight = mLargeCacheHeight > maxTextureSize ? maxTextureSize : mLargeCacheHeight;
84
Chet Haaseeb32a492012-08-31 13:54:03 -070085 if (sLogFontRendererCreate) {
86 INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
87 mSmallCacheWidth, mSmallCacheHeight,
88 mLargeCacheWidth, mLargeCacheHeight >> 1,
89 mLargeCacheWidth, mLargeCacheHeight >> 1,
90 mLargeCacheWidth, mLargeCacheHeight);
Romain Guy51769a62010-07-23 00:28:00 -070091 }
Romain Guy514fb182011-01-19 14:38:29 -080092
93 sLogFontRendererCreate = false;
Romain Guy694b5192010-07-21 21:33:20 -070094}
95
96FontRenderer::~FontRenderer() {
Chet Haase378e9192012-08-15 15:54:54 -070097 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
98 delete mCacheTextures[i];
Romain Guy694b5192010-07-21 21:33:20 -070099 }
Chet Haase378e9192012-08-15 15:54:54 -0700100 mCacheTextures.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700101
Romain Guy9cccc2b2010-08-07 23:46:15 -0700102 if (mInitialized) {
Romain Guya9dd8202012-03-26 14:52:00 -0700103 // Unbinding the buffer shouldn't be necessary but it crashes with some drivers
104 Caches::getInstance().unbindIndicesBuffer();
Romain Guyb0317982012-03-21 11:52:52 -0700105 glDeleteBuffers(1, &mIndexBufferID);
106
Romain Guy9b1204b2012-09-04 15:22:57 -0700107 delete[] mTextMesh;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700108 }
Romain Guy694b5192010-07-21 21:33:20 -0700109
110 Vector<Font*> fontsToDereference = mActiveFonts;
111 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
112 delete fontsToDereference[i];
113 }
114}
115
116void FontRenderer::flushAllAndInvalidate() {
117 if (mCurrentQuadIndex != 0) {
118 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700119 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700120
Romain Guy694b5192010-07-21 21:33:20 -0700121 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
122 mActiveFonts[i]->invalidateTextureCache();
123 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700124
Chet Haase378e9192012-08-15 15:54:54 -0700125 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
126 mCacheTextures[i]->init();
Romain Guy694b5192010-07-21 21:33:20 -0700127 }
Chet Haasee816bae2012-08-09 13:39:02 -0700128
Romain Guy80872462012-09-04 16:42:01 -0700129#if DEBUG_FONT_RENDERER
Chet Haase378e9192012-08-15 15:54:54 -0700130 uint16_t totalGlyphs = 0;
131 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
Romain Guy80872462012-09-04 16:42:01 -0700132 totalGlyphs += mCacheTextures[i]->getGlyphCount();
Chet Haase378e9192012-08-15 15:54:54 -0700133 // Erase caches, just as a debugging facility
Romain Guy80872462012-09-04 16:42:01 -0700134 if (mCacheTextures[i]->getTexture()) {
135 memset(mCacheTextures[i]->getTexture(), 0,
136 mCacheTextures[i]->getWidth() * mCacheTextures[i]->getHeight());
Chet Haase378e9192012-08-15 15:54:54 -0700137 }
Chet Haasee816bae2012-08-09 13:39:02 -0700138 }
139 ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
140#endif
Romain Guy694b5192010-07-21 21:33:20 -0700141}
142
Chet Haase9a824562011-12-16 15:44:59 -0800143void FontRenderer::flushLargeCaches() {
Chet Haase378e9192012-08-15 15:54:54 -0700144 // Start from 1; don't deallocate smallest/default texture
145 for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
146 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guy80872462012-09-04 16:42:01 -0700147 if (cacheTexture->getTexture()) {
Chet Haase378e9192012-08-15 15:54:54 -0700148 cacheTexture->init();
149 for (uint32_t j = 0; j < mActiveFonts.size(); j++) {
150 mActiveFonts[j]->invalidateTextureCache(cacheTexture);
Chet Haasee816bae2012-08-09 13:39:02 -0700151 }
Romain Guy80872462012-09-04 16:42:01 -0700152 cacheTexture->releaseTexture();
Chet Haase9a824562011-12-16 15:44:59 -0800153 }
154 }
Chet Haase9a824562011-12-16 15:44:59 -0800155}
156
Chet Haase378e9192012-08-15 15:54:54 -0700157CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
158 uint32_t* startX, uint32_t* startY) {
159 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
160 if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
161 return mCacheTextures[i];
162 }
163 }
164 // Could not fit glyph into current cache textures
165 return NULL;
166}
167
Chet Haase7de0cb12011-12-05 16:35:38 -0800168void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
Chet Haasef942cf12012-08-30 09:06:46 -0700169 uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700170 checkInit();
Chet Haase7de0cb12011-12-05 16:35:38 -0800171 cachedGlyph->mIsValid = false;
Romain Guy694b5192010-07-21 21:33:20 -0700172 // If the glyph is too tall, don't cache it
Chet Haase378e9192012-08-15 15:54:54 -0700173 if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
Romain Guy80872462012-09-04 16:42:01 -0700174 mCacheTextures[mCacheTextures.size() - 1]->getHeight()) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700175 ALOGE("Font size too large to fit in cache. width, height = %i, %i",
176 (int) glyph.fWidth, (int) glyph.fHeight);
Chet Haase7de0cb12011-12-05 16:35:38 -0800177 return;
Romain Guy694b5192010-07-21 21:33:20 -0700178 }
179
180 // Now copy the bitmap into the cache texture
181 uint32_t startX = 0;
182 uint32_t startY = 0;
183
Chet Haase378e9192012-08-15 15:54:54 -0700184 CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700185
Chet Haase378e9192012-08-15 15:54:54 -0700186 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700187 if (!precaching) {
188 // If the new glyph didn't fit and we are not just trying to precache it,
189 // clear out the cache and try again
190 flushAllAndInvalidate();
191 cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
192 }
Romain Guy694b5192010-07-21 21:33:20 -0700193
Chet Haase378e9192012-08-15 15:54:54 -0700194 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700195 // either the glyph didn't fit or we're precaching and will cache it when we draw
Chet Haase7de0cb12011-12-05 16:35:38 -0800196 return;
Romain Guy694b5192010-07-21 21:33:20 -0700197 }
198 }
199
Chet Haase378e9192012-08-15 15:54:54 -0700200 cachedGlyph->mCacheTexture = cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800201
Romain Guy694b5192010-07-21 21:33:20 -0700202 *retOriginX = startX;
203 *retOriginY = startY;
204
205 uint32_t endX = startX + glyph.fWidth;
206 uint32_t endY = startY + glyph.fHeight;
207
Romain Guy80872462012-09-04 16:42:01 -0700208 uint32_t cacheWidth = cacheTexture->getWidth();
Romain Guy694b5192010-07-21 21:33:20 -0700209
Romain Guy80872462012-09-04 16:42:01 -0700210 if (!cacheTexture->getTexture()) {
211 Caches::getInstance().activeTexture(0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800212 // Large-glyph texture memory is allocated only as needed
Romain Guy80872462012-09-04 16:42:01 -0700213 cacheTexture->allocateTexture();
Chet Haase7de0cb12011-12-05 16:35:38 -0800214 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700215
Romain Guy80872462012-09-04 16:42:01 -0700216 uint8_t* cacheBuffer = cacheTexture->getTexture();
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700217 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700218 unsigned int stride = glyph.rowBytes();
219
220 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
Romain Guy33fa1f72012-08-07 19:09:57 -0700221
222 for (cacheX = startX - TEXTURE_BORDER_SIZE; cacheX < endX + TEXTURE_BORDER_SIZE; cacheX++) {
223 cacheBuffer[(startY - TEXTURE_BORDER_SIZE) * cacheWidth + cacheX] = 0;
224 cacheBuffer[(endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + cacheX] = 0;
225 }
226
227 for (cacheY = startY - TEXTURE_BORDER_SIZE + 1;
228 cacheY < endY + TEXTURE_BORDER_SIZE - 1; cacheY++) {
229 cacheBuffer[cacheY * cacheWidth + startX - TEXTURE_BORDER_SIZE] = 0;
230 cacheBuffer[cacheY * cacheWidth + endX + TEXTURE_BORDER_SIZE - 1] = 0;
231 }
232
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700233 if (mGammaTable) {
234 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
235 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
236 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
237 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
238 }
239 }
240 } else {
241 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
242 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
243 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
244 cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
245 }
Romain Guy694b5192010-07-21 21:33:20 -0700246 }
247 }
Romain Guy97771732012-02-28 18:17:02 -0800248
Chet Haase7de0cb12011-12-05 16:35:38 -0800249 cachedGlyph->mIsValid = true;
Romain Guy694b5192010-07-21 21:33:20 -0700250}
251
Chet Haase7de0cb12011-12-05 16:35:38 -0800252CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
Romain Guy0aa87bb2012-07-20 11:14:32 -0700253 CacheTexture* cacheTexture = new CacheTexture(width, height);
Romain Guy9d9758a2012-05-14 15:19:58 -0700254
Chet Haase2a47c142011-12-14 15:22:56 -0800255 if (allocate) {
Romain Guy80872462012-09-04 16:42:01 -0700256 Caches::getInstance().activeTexture(0);
257 cacheTexture->allocateTexture();
Chet Haase2a47c142011-12-14 15:22:56 -0800258 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700259
Chet Haase2a47c142011-12-14 15:22:56 -0800260 return cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800261}
262
263void FontRenderer::initTextTexture() {
Chet Haase378e9192012-08-15 15:54:54 -0700264 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
265 delete mCacheTextures[i];
Romain Guy9d9758a2012-05-14 15:19:58 -0700266 }
Chet Haase378e9192012-08-15 15:54:54 -0700267 mCacheTextures.clear();
Romain Guy9d9758a2012-05-14 15:19:58 -0700268
Chet Haase7de0cb12011-12-05 16:35:38 -0800269 mUploadTexture = false;
Chet Haase378e9192012-08-15 15:54:54 -0700270 mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
Chet Haaseeb32a492012-08-31 13:54:03 -0700271 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
272 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
273 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false));
Chet Haase378e9192012-08-15 15:54:54 -0700274 mCurrentCacheTexture = mCacheTextures[0];
Romain Guy694b5192010-07-21 21:33:20 -0700275}
276
277// Avoid having to reallocate memory and render quad by quad
278void FontRenderer::initVertexArrayBuffers() {
Romain Guyd71dd362011-12-12 19:03:35 -0800279 uint32_t numIndices = mMaxNumberOfQuads * 6;
280 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700281 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700282
283 // Four verts, two triangles , six indices per quad
284 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
285 int i6 = i * 6;
286 int i4 = i * 4;
287
288 indexBufferData[i6 + 0] = i4 + 0;
289 indexBufferData[i6 + 1] = i4 + 1;
290 indexBufferData[i6 + 2] = i4 + 2;
291
292 indexBufferData[i6 + 3] = i4 + 0;
293 indexBufferData[i6 + 4] = i4 + 2;
294 indexBufferData[i6 + 5] = i4 + 3;
295 }
296
297 glGenBuffers(1, &mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800298 Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700299 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
Romain Guy694b5192010-07-21 21:33:20 -0700300
301 free(indexBufferData);
302
Romain Guyd71dd362011-12-12 19:03:35 -0800303 uint32_t coordSize = 2;
Romain Guy694b5192010-07-21 21:33:20 -0700304 uint32_t uvSize = 2;
305 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700306 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
Romain Guy9b1204b2012-09-04 15:22:57 -0700307 mTextMesh = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700308}
309
310// We don't want to allocate anything unless we actually draw text
311void FontRenderer::checkInit() {
312 if (mInitialized) {
313 return;
314 }
315
316 initTextTexture();
317 initVertexArrayBuffers();
318
319 mInitialized = true;
320}
321
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900322void FontRenderer::updateDrawParams() {
323 if (mCurrentQuadIndex != mLastQuadIndex) {
324 mDrawOffsets.add((uint16_t*)(mLastQuadIndex * sizeof(uint16_t) * 6));
325 mDrawCounts.add(mCurrentQuadIndex - mLastQuadIndex);
326 mDrawCacheTextures.add(mCurrentCacheTexture);
327 mLastQuadIndex = mCurrentQuadIndex;
328 }
329}
330
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700331void FontRenderer::checkTextureUpdate() {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900332 if (!mUploadTexture) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700333 return;
Romain Guy694b5192010-07-21 21:33:20 -0700334 }
335
Romain Guy2d4fd362011-12-13 22:00:19 -0800336 Caches& caches = Caches::getInstance();
337 GLuint lastTextureId = 0;
Chet Haase378e9192012-08-15 15:54:54 -0700338 // Iterate over all the cache textures and see which ones need to be updated
339 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
340 CacheTexture* cacheTexture = mCacheTextures[i];
Romain Guy80872462012-09-04 16:42:01 -0700341 if (cacheTexture->isDirty() && cacheTexture->getTexture()) {
Chet Haaseb92d8f72012-09-21 08:40:46 -0700342 // Can't copy inner rect; glTexSubimage expects pointer to deal with entire buffer
343 // of data. So expand the dirty rect to the encompassing horizontal stripe.
344 const Rect* dirtyRect = cacheTexture->getDirtyRect();
345 uint32_t x = 0;
346 uint32_t y = dirtyRect->top;
Romain Guy80872462012-09-04 16:42:01 -0700347 uint32_t width = cacheTexture->getWidth();
Chet Haaseb92d8f72012-09-21 08:40:46 -0700348 uint32_t height = dirtyRect->getHeight();
349 void* textureData = cacheTexture->getTexture() + y * width;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700350
Romain Guy80872462012-09-04 16:42:01 -0700351 if (cacheTexture->getTextureId() != lastTextureId) {
352 lastTextureId = cacheTexture->getTextureId();
Romain Guy2d4fd362011-12-13 22:00:19 -0800353 caches.activeTexture(0);
Romain Guy80872462012-09-04 16:42:01 -0700354 glBindTexture(GL_TEXTURE_2D, lastTextureId);
Romain Guy2d4fd362011-12-13 22:00:19 -0800355 }
Chet Haasee816bae2012-08-09 13:39:02 -0700356#if DEBUG_FONT_RENDERER
Chet Haaseb92d8f72012-09-21 08:40:46 -0700357 ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d",
358 i, x, y, width, height);
Chet Haasee816bae2012-08-09 13:39:02 -0700359#endif
Chet Haaseb92d8f72012-09-21 08:40:46 -0700360 glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700361 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Romain Guy80872462012-09-04 16:42:01 -0700362 cacheTexture->setDirty(false);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700363 }
364 }
365
366 mUploadTexture = false;
367}
368
369void FontRenderer::issueDrawCommand() {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900370 updateDrawParams();
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700371 checkTextureUpdate();
372
Romain Guy15bc6432011-12-13 13:11:32 -0800373 Caches& caches = Caches::getInstance();
Romain Guy2d4fd362011-12-13 22:00:19 -0800374 caches.bindIndicesBuffer(mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800375 if (!mDrawn) {
Romain Guy9b1204b2012-09-04 15:22:57 -0700376 float* buffer = mTextMesh;
Romain Guy15bc6432011-12-13 13:11:32 -0800377 int offset = 2;
378
379 bool force = caches.unbindMeshBuffer();
Chris Craikcb4d6002012-09-25 12:00:29 -0700380 caches.bindPositionVertexPointer(force, buffer);
381 caches.bindTexCoordsVertexPointer(force, buffer + offset);
Romain Guy15bc6432011-12-13 13:11:32 -0800382 }
383
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900384 for (uint32_t i = 0; i < mDrawOffsets.size(); i++) {
385 uint16_t* offset = mDrawOffsets[i];
386 uint32_t count = mDrawCounts[i];
387 CacheTexture* texture = mDrawCacheTextures[i];
388
389 caches.activeTexture(0);
390 glBindTexture(GL_TEXTURE_2D, texture->getTextureId());
391
392 texture->setLinearFiltering(mLinearFiltering, false);
393
394 glDrawElements(GL_TRIANGLES, count * 6, GL_UNSIGNED_SHORT, offset);
395 }
Romain Guy5b3b3522010-10-27 18:57:51 -0700396
397 mDrawn = true;
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900398
399 mCurrentQuadIndex = 0;
400 mLastQuadIndex = 0;
401 mDrawOffsets.clear();
402 mDrawCounts.clear();
403 mDrawCacheTextures.clear();
Romain Guy694b5192010-07-21 21:33:20 -0700404}
405
Romain Guy97771732012-02-28 18:17:02 -0800406void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
407 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800408 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800409 if (texture != mCurrentCacheTexture) {
Sangkyu Lee7bb3cfe2012-11-16 00:03:17 +0900410 updateDrawParams();
Chet Haase7de0cb12011-12-05 16:35:38 -0800411 // Now use the new texture id
412 mCurrentCacheTexture = texture;
413 }
Romain Guy09147fb2010-07-22 13:08:20 -0700414
Romain Guy694b5192010-07-21 21:33:20 -0700415 const uint32_t vertsPerQuad = 4;
Romain Guyd71dd362011-12-12 19:03:35 -0800416 const uint32_t floatsPerVert = 4;
Romain Guy9b1204b2012-09-04 15:22:57 -0700417 float* currentPos = mTextMesh + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700418
Romain Guy694b5192010-07-21 21:33:20 -0700419 (*currentPos++) = x1;
420 (*currentPos++) = y1;
Romain Guy694b5192010-07-21 21:33:20 -0700421 (*currentPos++) = u1;
422 (*currentPos++) = v1;
423
424 (*currentPos++) = x2;
425 (*currentPos++) = y2;
Romain Guy694b5192010-07-21 21:33:20 -0700426 (*currentPos++) = u2;
427 (*currentPos++) = v2;
428
429 (*currentPos++) = x3;
430 (*currentPos++) = y3;
Romain Guy694b5192010-07-21 21:33:20 -0700431 (*currentPos++) = u3;
432 (*currentPos++) = v3;
433
434 (*currentPos++) = x4;
435 (*currentPos++) = y4;
Romain Guy694b5192010-07-21 21:33:20 -0700436 (*currentPos++) = u4;
437 (*currentPos++) = v4;
438
439 mCurrentQuadIndex++;
Romain Guy97771732012-02-28 18:17:02 -0800440}
441
442void FontRenderer::appendMeshQuad(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,
444 float x4, float y4, float u4, float v4, CacheTexture* texture) {
445
446 if (mClip &&
447 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
448 return;
449 }
450
451 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 -0700452
Romain Guy5b3b3522010-10-27 18:57:51 -0700453 if (mBounds) {
454 mBounds->left = fmin(mBounds->left, x1);
455 mBounds->top = fmin(mBounds->top, y3);
456 mBounds->right = fmax(mBounds->right, x3);
457 mBounds->bottom = fmax(mBounds->bottom, y1);
458 }
459
Romain Guy694b5192010-07-21 21:33:20 -0700460 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
461 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700462 }
463}
464
Romain Guy97771732012-02-28 18:17:02 -0800465void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
466 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
467 float x4, float y4, float u4, float v4, CacheTexture* texture) {
468
469 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
470
471 if (mBounds) {
472 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
473 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
474 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
475 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
476 }
477
478 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
479 issueDrawCommand();
Romain Guy97771732012-02-28 18:17:02 -0800480 }
481}
482
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700483void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
Romain Guy325a0f92011-01-05 15:26:55 -0800484 int flags = 0;
485 if (paint->isFakeBoldText()) {
486 flags |= Font::kFakeBold;
487 }
Romain Guy2577db12011-01-18 13:02:38 -0800488
489 const float skewX = paint->getTextSkewX();
490 uint32_t italicStyle = *(uint32_t*) &skewX;
Chet Haase8668f8a2011-03-02 13:51:36 -0800491 const float scaleXFloat = paint->getTextScaleX();
492 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
Romain Guybd496bc2011-08-02 17:32:41 -0700493 SkPaint::Style style = paint->getStyle();
494 const float strokeWidthFloat = paint->getStrokeWidth();
495 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
496 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
497 scaleX, style, strokeWidth);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700498
Romain Guy694b5192010-07-21 21:33:20 -0700499}
Romain Guy7975fb62010-10-01 16:36:14 -0700500
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700501FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Raph Levien416a8472012-07-19 22:48:17 -0700502 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700503 checkInit();
504
505 if (!mCurrentFont) {
506 DropShadow image;
507 image.width = 0;
508 image.height = 0;
509 image.image = NULL;
510 image.penX = 0;
511 image.penY = 0;
512 return image;
513 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700514
Romain Guy2d4fd362011-12-13 22:00:19 -0800515 mDrawn = false;
Romain Guyff98fa52011-11-28 09:35:09 -0800516 mClip = NULL;
517 mBounds = NULL;
518
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700519 Rect bounds;
Raph Levien416a8472012-07-19 22:48:17 -0700520 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
Romain Guyff98fa52011-11-28 09:35:09 -0800521
Romain Guy1e45aae2010-08-13 19:39:53 -0700522 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
523 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700524 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guyff98fa52011-11-28 09:35:09 -0800525
Romain Guy1e45aae2010-08-13 19:39:53 -0700526 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700527 dataBuffer[i] = 0;
528 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700529
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700530 int penX = radius - bounds.left;
531 int penY = radius - bounds.bottom;
532
Romain Guy726aeba2011-06-01 14:52:00 -0700533 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
Raph Levien416a8472012-07-19 22:48:17 -0700534 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700535 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
536
537 DropShadow image;
538 image.width = paddedWidth;
539 image.height = paddedHeight;
540 image.image = dataBuffer;
541 image.penX = penX;
542 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -0800543
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700544 return image;
545}
Romain Guy694b5192010-07-21 21:33:20 -0700546
Romain Guy671d6cf2012-01-18 12:39:17 -0800547void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700548 checkInit();
549
Romain Guy5b3b3522010-10-27 18:57:51 -0700550 mDrawn = false;
551 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -0700552 mClip = clip;
Romain Guy671d6cf2012-01-18 12:39:17 -0800553}
Romain Guyff98fa52011-11-28 09:35:09 -0800554
Romain Guy671d6cf2012-01-18 12:39:17 -0800555void FontRenderer::finishRender() {
Romain Guy5b3b3522010-10-27 18:57:51 -0700556 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -0800557 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700558
559 if (mCurrentQuadIndex != 0) {
560 issueDrawCommand();
Romain Guy694b5192010-07-21 21:33:20 -0700561 }
Romain Guy671d6cf2012-01-18 12:39:17 -0800562}
563
Chet Haasee816bae2012-08-09 13:39:02 -0700564void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs) {
565 int flags = 0;
566 if (paint->isFakeBoldText()) {
567 flags |= Font::kFakeBold;
568 }
569 const float skewX = paint->getTextSkewX();
570 uint32_t italicStyle = *(uint32_t*) &skewX;
571 const float scaleXFloat = paint->getTextScaleX();
572 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
573 SkPaint::Style style = paint->getStyle();
574 const float strokeWidthFloat = paint->getStrokeWidth();
575 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
576 float fontSize = paint->getTextSize();
577 Font* font = Font::create(this, SkTypeface::UniqueID(paint->getTypeface()),
578 fontSize, flags, italicStyle, scaleX, style, strokeWidth);
579
580 font->precache(paint, text, numGlyphs);
581}
582
Romain Guy671d6cf2012-01-18 12:39:17 -0800583bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
584 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
585 if (!mCurrentFont) {
586 ALOGE("No font set");
587 return false;
588 }
589
590 initRender(clip, bounds);
591 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
592 finishRender();
593
594 return mDrawn;
595}
596
597bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
598 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
599 const float* positions, Rect* bounds) {
600 if (!mCurrentFont) {
601 ALOGE("No font set");
602 return false;
603 }
604
605 initRender(clip, bounds);
606 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
607 finishRender();
Romain Guy5b3b3522010-10-27 18:57:51 -0700608
609 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700610}
611
Romain Guy97771732012-02-28 18:17:02 -0800612bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
613 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
614 float hOffset, float vOffset, Rect* bounds) {
615 if (!mCurrentFont) {
616 ALOGE("No font set");
617 return false;
618 }
619
620 initRender(clip, bounds);
621 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
622 finishRender();
623
624 return mDrawn;
625}
626
Romain Guy9b1204b2012-09-04 15:22:57 -0700627void FontRenderer::removeFont(const Font* font) {
628 for (uint32_t ct = 0; ct < mActiveFonts.size(); ct++) {
629 if (mActiveFonts[ct] == font) {
630 mActiveFonts.removeAt(ct);
631 break;
632 }
633 }
634
635 if (mCurrentFont == font) {
636 mCurrentFont = NULL;
637 }
638}
639
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700640void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
641 // Compute gaussian weights for the blur
642 // e is the euler's number
643 float e = 2.718281828459045f;
644 float pi = 3.1415926535897932f;
645 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
646 // x is of the form [-radius .. 0 .. radius]
647 // and sigma varies with radius.
648 // Based on some experimental radius values and sigma's
649 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700650 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700651 // The larger the radius gets, the more our gaussian blur
652 // will resemble a box blur since with large sigma
653 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -0800654 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700655
656 // Now compute the coefficints
657 // We will store some redundant values to save some math during
658 // the blur calculations
659 // precompute some values
660 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
661 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
662
663 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -0800664 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700665 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700666 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
667 normalizeFactor += weights[r + radius];
668 }
669
670 //Now we need to normalize the weights because all our coefficients need to add up to one
671 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -0800672 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700673 weights[r + radius] *= normalizeFactor;
674 }
675}
676
677void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700678 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700679 float blurredPixel = 0.0f;
680 float currentPixel = 0.0f;
681
Romain Guy325a0f92011-01-05 15:26:55 -0800682 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700683
684 const uint8_t* input = source + y * width;
685 uint8_t* output = dest + y * width;
686
Romain Guy325a0f92011-01-05 15:26:55 -0800687 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700688 blurredPixel = 0.0f;
689 const float* gPtr = weights;
690 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800691 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700692 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -0800693 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700694 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700695 blurredPixel += currentPixel * gPtr[0];
696 gPtr++;
697 i++;
698 }
699 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800700 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700701 // Stepping left and right away from the pixel
702 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -0800703 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700704 validW = 0;
705 }
Romain Guy325a0f92011-01-05 15:26:55 -0800706 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700707 validW = width - 1;
708 }
709
Romain Guy325a0f92011-01-05 15:26:55 -0800710 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700711 blurredPixel += currentPixel * gPtr[0];
712 gPtr++;
713 }
714 }
715 *output = (uint8_t)blurredPixel;
716 output ++;
717 }
718 }
719}
720
721void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700722 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700723 float blurredPixel = 0.0f;
724 float currentPixel = 0.0f;
725
Romain Guy325a0f92011-01-05 15:26:55 -0800726 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700727 uint8_t* output = dest + y * width;
728
Romain Guy325a0f92011-01-05 15:26:55 -0800729 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700730 blurredPixel = 0.0f;
731 const float* gPtr = weights;
732 const uint8_t* input = source + x;
733 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800734 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700735 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -0800736 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700737 currentPixel = (float)(*i);
738 blurredPixel += currentPixel * gPtr[0];
739 gPtr++;
740 i += width;
741 }
742 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800743 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700744 int validH = y + r;
745 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -0800746 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700747 validH = 0;
748 }
Romain Guy325a0f92011-01-05 15:26:55 -0800749 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700750 validH = height - 1;
751 }
752
753 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -0800754 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700755 blurredPixel += currentPixel * gPtr[0];
756 gPtr++;
757 }
758 }
Romain Guy325a0f92011-01-05 15:26:55 -0800759 *output = (uint8_t) blurredPixel;
Romain Guy9b1204b2012-09-04 15:22:57 -0700760 output++;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700761 }
762 }
763}
764
765
766void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
767 float *gaussian = new float[2 * radius + 1];
768 computeGaussianWeights(gaussian, radius);
Romain Guyd71dd362011-12-12 19:03:35 -0800769
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700770 uint8_t* scratch = new uint8_t[width * height];
Romain Guyd71dd362011-12-12 19:03:35 -0800771
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700772 horizontalBlur(gaussian, radius, image, scratch, width, height);
773 verticalBlur(gaussian, radius, scratch, image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -0800774
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700775 delete[] gaussian;
776 delete[] scratch;
777}
778
Romain Guy694b5192010-07-21 21:33:20 -0700779}; // namespace uirenderer
780}; // namespace android