blob: 5ff4abadbcfc13a81cb0b75fd7b8f3fd2112e2a3 [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;
48
Romain Guy9b1204b2012-09-04 15:22:57 -070049 mTextMesh = NULL;
Chet Haase7de0cb12011-12-05 16:35:38 -080050 mCurrentCacheTexture = NULL;
51 mLastCacheTexture = NULL;
Romain Guy9cccc2b92010-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 Guy9cccc2b92010-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();
119 mCurrentQuadIndex = 0;
120 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700121
Romain Guy694b5192010-07-21 21:33:20 -0700122 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
123 mActiveFonts[i]->invalidateTextureCache();
124 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700125
Chet Haase378e9192012-08-15 15:54:54 -0700126 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
127 mCacheTextures[i]->init();
Romain Guy694b5192010-07-21 21:33:20 -0700128 }
Chet Haasee816bae2012-08-09 13:39:02 -0700129
Chet Haase378e9192012-08-15 15:54:54 -0700130 #if DEBUG_FONT_RENDERER
131 uint16_t totalGlyphs = 0;
132 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
133 totalGlyphs += mCacheTextures[i]->mNumGlyphs;
134 // Erase caches, just as a debugging facility
135 if (mCacheTextures[i]->mTexture) {
136 memset(mCacheTextures[i]->mTexture, 0,
137 mCacheTextures[i]->mWidth * mCacheTextures[i]->mHeight);
138 }
Chet Haasee816bae2012-08-09 13:39:02 -0700139 }
140 ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
141#endif
Romain Guy694b5192010-07-21 21:33:20 -0700142}
143
Chet Haase9a824562011-12-16 15:44:59 -0800144void FontRenderer::deallocateTextureMemory(CacheTexture *cacheTexture) {
145 if (cacheTexture && cacheTexture->mTexture) {
146 glDeleteTextures(1, &cacheTexture->mTextureId);
Romain Guy9d9758a2012-05-14 15:19:58 -0700147 delete[] cacheTexture->mTexture;
Chet Haase9a824562011-12-16 15:44:59 -0800148 cacheTexture->mTexture = NULL;
Romain Guy99a6ddd2012-05-14 15:32:18 -0700149 cacheTexture->mTextureId = 0;
Chet Haase9a824562011-12-16 15:44:59 -0800150 }
151}
152
153void FontRenderer::flushLargeCaches() {
Chet Haase378e9192012-08-15 15:54:54 -0700154 // Start from 1; don't deallocate smallest/default texture
155 for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
156 CacheTexture* cacheTexture = mCacheTextures[i];
157 if (cacheTexture->mTexture != NULL) {
158 cacheTexture->init();
159 for (uint32_t j = 0; j < mActiveFonts.size(); j++) {
160 mActiveFonts[j]->invalidateTextureCache(cacheTexture);
Chet Haasee816bae2012-08-09 13:39:02 -0700161 }
Chet Haase378e9192012-08-15 15:54:54 -0700162 deallocateTextureMemory(cacheTexture);
Chet Haase9a824562011-12-16 15:44:59 -0800163 }
164 }
Chet Haase9a824562011-12-16 15:44:59 -0800165}
166
Romain Guy9d9758a2012-05-14 15:19:58 -0700167void FontRenderer::allocateTextureMemory(CacheTexture* cacheTexture) {
Chet Haase2a47c142011-12-14 15:22:56 -0800168 int width = cacheTexture->mWidth;
169 int height = cacheTexture->mHeight;
Romain Guy9d9758a2012-05-14 15:19:58 -0700170
Chet Haase2a47c142011-12-14 15:22:56 -0800171 cacheTexture->mTexture = new uint8_t[width * height];
Romain Guy99a6ddd2012-05-14 15:32:18 -0700172
173 if (!cacheTexture->mTextureId) {
174 glGenTextures(1, &cacheTexture->mTextureId);
175 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700176
Romain Guy16c88082012-06-11 16:03:47 -0700177 Caches::getInstance().activeTexture(0);
Chet Haase2a47c142011-12-14 15:22:56 -0800178 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
179 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
180 // Initialize texture dimensions
181 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
182 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800183
Chet Haase2a47c142011-12-14 15:22:56 -0800184 const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST;
185 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
186 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
187
188 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
189 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
Chet Haase7de0cb12011-12-05 16:35:38 -0800190}
191
Chet Haase378e9192012-08-15 15:54:54 -0700192CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
193 uint32_t* startX, uint32_t* startY) {
194 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
195 if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
196 return mCacheTextures[i];
197 }
198 }
199 // Could not fit glyph into current cache textures
200 return NULL;
201}
202
Chet Haase7de0cb12011-12-05 16:35:38 -0800203void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
Chet Haasef942cf12012-08-30 09:06:46 -0700204 uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700205 checkInit();
Chet Haase7de0cb12011-12-05 16:35:38 -0800206 cachedGlyph->mIsValid = false;
Romain Guy694b5192010-07-21 21:33:20 -0700207 // If the glyph is too tall, don't cache it
Chet Haase378e9192012-08-15 15:54:54 -0700208 if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
209 mCacheTextures[mCacheTextures.size() - 1]->mHeight) {
Chet Haase2efd5c52012-08-15 13:15:16 -0700210 ALOGE("Font size too large to fit in cache. width, height = %i, %i",
211 (int) glyph.fWidth, (int) glyph.fHeight);
Chet Haase7de0cb12011-12-05 16:35:38 -0800212 return;
Romain Guy694b5192010-07-21 21:33:20 -0700213 }
214
215 // Now copy the bitmap into the cache texture
216 uint32_t startX = 0;
217 uint32_t startY = 0;
218
Chet Haase378e9192012-08-15 15:54:54 -0700219 CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700220
Chet Haase378e9192012-08-15 15:54:54 -0700221 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700222 if (!precaching) {
223 // If the new glyph didn't fit and we are not just trying to precache it,
224 // clear out the cache and try again
225 flushAllAndInvalidate();
226 cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
227 }
Romain Guy694b5192010-07-21 21:33:20 -0700228
Chet Haase378e9192012-08-15 15:54:54 -0700229 if (!cacheTexture) {
Chet Haasef942cf12012-08-30 09:06:46 -0700230 // either the glyph didn't fit or we're precaching and will cache it when we draw
Chet Haase7de0cb12011-12-05 16:35:38 -0800231 return;
Romain Guy694b5192010-07-21 21:33:20 -0700232 }
233 }
234
Chet Haase378e9192012-08-15 15:54:54 -0700235 cachedGlyph->mCacheTexture = cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800236
Romain Guy694b5192010-07-21 21:33:20 -0700237 *retOriginX = startX;
238 *retOriginY = startY;
239
240 uint32_t endX = startX + glyph.fWidth;
241 uint32_t endY = startY + glyph.fHeight;
242
Chet Haase378e9192012-08-15 15:54:54 -0700243 uint32_t cacheWidth = cacheTexture->mWidth;
Romain Guy694b5192010-07-21 21:33:20 -0700244
Romain Guy9d9758a2012-05-14 15:19:58 -0700245 if (!cacheTexture->mTexture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800246 // Large-glyph texture memory is allocated only as needed
Chet Haase2a47c142011-12-14 15:22:56 -0800247 allocateTextureMemory(cacheTexture);
Chet Haase7de0cb12011-12-05 16:35:38 -0800248 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700249
Chet Haase7de0cb12011-12-05 16:35:38 -0800250 uint8_t* cacheBuffer = cacheTexture->mTexture;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700251 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700252 unsigned int stride = glyph.rowBytes();
253
254 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
Romain Guy33fa1f72012-08-07 19:09:57 -0700255
256 for (cacheX = startX - TEXTURE_BORDER_SIZE; cacheX < endX + TEXTURE_BORDER_SIZE; cacheX++) {
257 cacheBuffer[(startY - TEXTURE_BORDER_SIZE) * cacheWidth + cacheX] = 0;
258 cacheBuffer[(endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + cacheX] = 0;
259 }
260
261 for (cacheY = startY - TEXTURE_BORDER_SIZE + 1;
262 cacheY < endY + TEXTURE_BORDER_SIZE - 1; cacheY++) {
263 cacheBuffer[cacheY * cacheWidth + startX - TEXTURE_BORDER_SIZE] = 0;
264 cacheBuffer[cacheY * cacheWidth + endX + TEXTURE_BORDER_SIZE - 1] = 0;
265 }
266
Romain Guyb1d0a4e2012-07-13 18:25:35 -0700267 if (mGammaTable) {
268 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
269 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
270 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
271 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
272 }
273 }
274 } else {
275 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
276 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
277 uint8_t tempCol = bitmapBuffer[bY * stride + bX];
278 cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
279 }
Romain Guy694b5192010-07-21 21:33:20 -0700280 }
281 }
Romain Guy97771732012-02-28 18:17:02 -0800282
Chet Haase7de0cb12011-12-05 16:35:38 -0800283 cachedGlyph->mIsValid = true;
Romain Guy694b5192010-07-21 21:33:20 -0700284}
285
Chet Haase7de0cb12011-12-05 16:35:38 -0800286CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
Romain Guy0aa87bb2012-07-20 11:14:32 -0700287 CacheTexture* cacheTexture = new CacheTexture(width, height);
Romain Guy9d9758a2012-05-14 15:19:58 -0700288
Chet Haase2a47c142011-12-14 15:22:56 -0800289 if (allocate) {
290 allocateTextureMemory(cacheTexture);
291 }
Romain Guy9d9758a2012-05-14 15:19:58 -0700292
Chet Haase2a47c142011-12-14 15:22:56 -0800293 return cacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800294}
295
296void FontRenderer::initTextTexture() {
Chet Haase378e9192012-08-15 15:54:54 -0700297 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
298 delete mCacheTextures[i];
Romain Guy9d9758a2012-05-14 15:19:58 -0700299 }
Chet Haase378e9192012-08-15 15:54:54 -0700300 mCacheTextures.clear();
Romain Guy9d9758a2012-05-14 15:19:58 -0700301
Chet Haase7de0cb12011-12-05 16:35:38 -0800302 mUploadTexture = false;
Chet Haase378e9192012-08-15 15:54:54 -0700303 mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
Chet Haaseeb32a492012-08-31 13:54:03 -0700304 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
305 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
306 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false));
Chet Haase378e9192012-08-15 15:54:54 -0700307 mCurrentCacheTexture = mCacheTextures[0];
Romain Guy694b5192010-07-21 21:33:20 -0700308}
309
310// Avoid having to reallocate memory and render quad by quad
311void FontRenderer::initVertexArrayBuffers() {
Romain Guyd71dd362011-12-12 19:03:35 -0800312 uint32_t numIndices = mMaxNumberOfQuads * 6;
313 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700314 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700315
316 // Four verts, two triangles , six indices per quad
317 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
318 int i6 = i * 6;
319 int i4 = i * 4;
320
321 indexBufferData[i6 + 0] = i4 + 0;
322 indexBufferData[i6 + 1] = i4 + 1;
323 indexBufferData[i6 + 2] = i4 + 2;
324
325 indexBufferData[i6 + 3] = i4 + 0;
326 indexBufferData[i6 + 4] = i4 + 2;
327 indexBufferData[i6 + 5] = i4 + 3;
328 }
329
330 glGenBuffers(1, &mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800331 Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
Romain Guy5d794412010-10-13 16:36:22 -0700332 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
Romain Guy694b5192010-07-21 21:33:20 -0700333
334 free(indexBufferData);
335
Romain Guyd71dd362011-12-12 19:03:35 -0800336 uint32_t coordSize = 2;
Romain Guy694b5192010-07-21 21:33:20 -0700337 uint32_t uvSize = 2;
338 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700339 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
Romain Guy9b1204b2012-09-04 15:22:57 -0700340 mTextMesh = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700341}
342
343// We don't want to allocate anything unless we actually draw text
344void FontRenderer::checkInit() {
345 if (mInitialized) {
346 return;
347 }
348
349 initTextTexture();
350 initVertexArrayBuffers();
351
352 mInitialized = true;
353}
354
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700355void FontRenderer::checkTextureUpdate() {
Chet Haase7de0cb12011-12-05 16:35:38 -0800356 if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700357 return;
Romain Guy694b5192010-07-21 21:33:20 -0700358 }
359
Romain Guy2d4fd362011-12-13 22:00:19 -0800360 Caches& caches = Caches::getInstance();
361 GLuint lastTextureId = 0;
Chet Haase378e9192012-08-15 15:54:54 -0700362 // Iterate over all the cache textures and see which ones need to be updated
363 for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
364 CacheTexture* cacheTexture = mCacheTextures[i];
365 if (cacheTexture->mDirty && cacheTexture->mTexture != NULL) {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700366 uint32_t xOffset = 0;
Chet Haase378e9192012-08-15 15:54:54 -0700367 uint32_t width = cacheTexture->mWidth;
368 uint32_t height = cacheTexture->mHeight;
369 void* textureData = cacheTexture->mTexture;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700370
Romain Guy2d4fd362011-12-13 22:00:19 -0800371 if (cacheTexture->mTextureId != lastTextureId) {
372 caches.activeTexture(0);
373 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
374 lastTextureId = cacheTexture->mTextureId;
375 }
Chet Haasee816bae2012-08-09 13:39:02 -0700376#if DEBUG_FONT_RENDERER
Chet Haase378e9192012-08-15 15:54:54 -0700377 ALOGD("glTextSubimage for cacheTexture %d: xOff, width height = %d, %d, %d",
378 i, xOffset, width, height);
Chet Haasee816bae2012-08-09 13:39:02 -0700379#endif
Chet Haase378e9192012-08-15 15:54:54 -0700380 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, 0, width, height,
Romain Guy1e45aae2010-08-13 19:39:53 -0700381 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700382
Chet Haase378e9192012-08-15 15:54:54 -0700383 cacheTexture->mDirty = false;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700384 }
385 }
386
Romain Guy16c88082012-06-11 16:03:47 -0700387 caches.activeTexture(0);
Chet Haase7de0cb12011-12-05 16:35:38 -0800388 glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
Chet Haase2a47c142011-12-14 15:22:56 -0800389 if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) {
390 const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST;
391 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
392 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
393 mCurrentCacheTexture->mLinearFiltering = mLinearFiltering;
394 }
Chet Haase7de0cb12011-12-05 16:35:38 -0800395 mLastCacheTexture = mCurrentCacheTexture;
396
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700397 mUploadTexture = false;
398}
399
400void FontRenderer::issueDrawCommand() {
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700401 checkTextureUpdate();
402
Romain Guy15bc6432011-12-13 13:11:32 -0800403 Caches& caches = Caches::getInstance();
Romain Guy2d4fd362011-12-13 22:00:19 -0800404 caches.bindIndicesBuffer(mIndexBufferID);
Romain Guy15bc6432011-12-13 13:11:32 -0800405 if (!mDrawn) {
Romain Guy9b1204b2012-09-04 15:22:57 -0700406 float* buffer = mTextMesh;
Romain Guy15bc6432011-12-13 13:11:32 -0800407 int offset = 2;
408
409 bool force = caches.unbindMeshBuffer();
410 caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
411 caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
412 buffer + offset);
413 }
414
Romain Guy694b5192010-07-21 21:33:20 -0700415 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
Romain Guy5b3b3522010-10-27 18:57:51 -0700416
417 mDrawn = true;
Romain Guy694b5192010-07-21 21:33:20 -0700418}
419
Romain Guy97771732012-02-28 18:17:02 -0800420void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
421 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800422 float x4, float y4, float u4, float v4, CacheTexture* texture) {
Chet Haase7de0cb12011-12-05 16:35:38 -0800423 if (texture != mCurrentCacheTexture) {
424 if (mCurrentQuadIndex != 0) {
425 // First, draw everything stored already which uses the previous texture
426 issueDrawCommand();
427 mCurrentQuadIndex = 0;
428 }
429 // Now use the new texture id
430 mCurrentCacheTexture = texture;
431 }
Romain Guy09147fb2010-07-22 13:08:20 -0700432
Romain Guy694b5192010-07-21 21:33:20 -0700433 const uint32_t vertsPerQuad = 4;
Romain Guyd71dd362011-12-12 19:03:35 -0800434 const uint32_t floatsPerVert = 4;
Romain Guy9b1204b2012-09-04 15:22:57 -0700435 float* currentPos = mTextMesh + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700436
Romain Guy694b5192010-07-21 21:33:20 -0700437 (*currentPos++) = x1;
438 (*currentPos++) = y1;
Romain Guy694b5192010-07-21 21:33:20 -0700439 (*currentPos++) = u1;
440 (*currentPos++) = v1;
441
442 (*currentPos++) = x2;
443 (*currentPos++) = y2;
Romain Guy694b5192010-07-21 21:33:20 -0700444 (*currentPos++) = u2;
445 (*currentPos++) = v2;
446
447 (*currentPos++) = x3;
448 (*currentPos++) = y3;
Romain Guy694b5192010-07-21 21:33:20 -0700449 (*currentPos++) = u3;
450 (*currentPos++) = v3;
451
452 (*currentPos++) = x4;
453 (*currentPos++) = y4;
Romain Guy694b5192010-07-21 21:33:20 -0700454 (*currentPos++) = u4;
455 (*currentPos++) = v4;
456
457 mCurrentQuadIndex++;
Romain Guy97771732012-02-28 18:17:02 -0800458}
459
460void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
461 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
462 float x4, float y4, float u4, float v4, CacheTexture* texture) {
463
464 if (mClip &&
465 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
466 return;
467 }
468
469 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 -0700470
Romain Guy5b3b3522010-10-27 18:57:51 -0700471 if (mBounds) {
472 mBounds->left = fmin(mBounds->left, x1);
473 mBounds->top = fmin(mBounds->top, y3);
474 mBounds->right = fmax(mBounds->right, x3);
475 mBounds->bottom = fmax(mBounds->bottom, y1);
476 }
477
Romain Guy694b5192010-07-21 21:33:20 -0700478 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
479 issueDrawCommand();
480 mCurrentQuadIndex = 0;
481 }
482}
483
Romain Guy97771732012-02-28 18:17:02 -0800484void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
485 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
486 float x4, float y4, float u4, float v4, CacheTexture* texture) {
487
488 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
489
490 if (mBounds) {
491 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
492 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
493 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
494 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
495 }
496
497 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
498 issueDrawCommand();
499 mCurrentQuadIndex = 0;
500 }
501}
502
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700503void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
Romain Guy325a0f92011-01-05 15:26:55 -0800504 int flags = 0;
505 if (paint->isFakeBoldText()) {
506 flags |= Font::kFakeBold;
507 }
Romain Guy2577db12011-01-18 13:02:38 -0800508
509 const float skewX = paint->getTextSkewX();
510 uint32_t italicStyle = *(uint32_t*) &skewX;
Chet Haase8668f8a2011-03-02 13:51:36 -0800511 const float scaleXFloat = paint->getTextScaleX();
512 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
Romain Guybd496bc2011-08-02 17:32:41 -0700513 SkPaint::Style style = paint->getStyle();
514 const float strokeWidthFloat = paint->getStrokeWidth();
515 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
516 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
517 scaleX, style, strokeWidth);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700518
Romain Guy694b5192010-07-21 21:33:20 -0700519}
Romain Guy7975fb62010-10-01 16:36:14 -0700520
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700521FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
Raph Levien416a8472012-07-19 22:48:17 -0700522 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
Romain Guy1e45aae2010-08-13 19:39:53 -0700523 checkInit();
524
525 if (!mCurrentFont) {
526 DropShadow image;
527 image.width = 0;
528 image.height = 0;
529 image.image = NULL;
530 image.penX = 0;
531 image.penY = 0;
532 return image;
533 }
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700534
Romain Guy2d4fd362011-12-13 22:00:19 -0800535 mDrawn = false;
Romain Guyff98fa52011-11-28 09:35:09 -0800536 mClip = NULL;
537 mBounds = NULL;
538
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700539 Rect bounds;
Raph Levien416a8472012-07-19 22:48:17 -0700540 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
Romain Guyff98fa52011-11-28 09:35:09 -0800541
Romain Guy1e45aae2010-08-13 19:39:53 -0700542 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
543 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700544 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
Romain Guyff98fa52011-11-28 09:35:09 -0800545
Romain Guy1e45aae2010-08-13 19:39:53 -0700546 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700547 dataBuffer[i] = 0;
548 }
Romain Guy1e45aae2010-08-13 19:39:53 -0700549
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700550 int penX = radius - bounds.left;
551 int penY = radius - bounds.bottom;
552
Romain Guy726aeba2011-06-01 14:52:00 -0700553 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
Raph Levien416a8472012-07-19 22:48:17 -0700554 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700555 blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
556
557 DropShadow image;
558 image.width = paddedWidth;
559 image.height = paddedHeight;
560 image.image = dataBuffer;
561 image.penX = penX;
562 image.penY = penY;
Romain Guy2d4fd362011-12-13 22:00:19 -0800563
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700564 return image;
565}
Romain Guy694b5192010-07-21 21:33:20 -0700566
Romain Guy671d6cf2012-01-18 12:39:17 -0800567void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
Romain Guy694b5192010-07-21 21:33:20 -0700568 checkInit();
569
Romain Guy5b3b3522010-10-27 18:57:51 -0700570 mDrawn = false;
571 mBounds = bounds;
Romain Guy09147fb2010-07-22 13:08:20 -0700572 mClip = clip;
Romain Guy671d6cf2012-01-18 12:39:17 -0800573}
Romain Guyff98fa52011-11-28 09:35:09 -0800574
Romain Guy671d6cf2012-01-18 12:39:17 -0800575void FontRenderer::finishRender() {
Romain Guy5b3b3522010-10-27 18:57:51 -0700576 mBounds = NULL;
Romain Guyff98fa52011-11-28 09:35:09 -0800577 mClip = NULL;
Romain Guy694b5192010-07-21 21:33:20 -0700578
579 if (mCurrentQuadIndex != 0) {
580 issueDrawCommand();
581 mCurrentQuadIndex = 0;
582 }
Romain Guy671d6cf2012-01-18 12:39:17 -0800583}
584
Chet Haasee816bae2012-08-09 13:39:02 -0700585void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs) {
586 int flags = 0;
587 if (paint->isFakeBoldText()) {
588 flags |= Font::kFakeBold;
589 }
590 const float skewX = paint->getTextSkewX();
591 uint32_t italicStyle = *(uint32_t*) &skewX;
592 const float scaleXFloat = paint->getTextScaleX();
593 uint32_t scaleX = *(uint32_t*) &scaleXFloat;
594 SkPaint::Style style = paint->getStyle();
595 const float strokeWidthFloat = paint->getStrokeWidth();
596 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
597 float fontSize = paint->getTextSize();
598 Font* font = Font::create(this, SkTypeface::UniqueID(paint->getTypeface()),
599 fontSize, flags, italicStyle, scaleX, style, strokeWidth);
600
601 font->precache(paint, text, numGlyphs);
602}
603
Romain Guy671d6cf2012-01-18 12:39:17 -0800604bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
605 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
606 if (!mCurrentFont) {
607 ALOGE("No font set");
608 return false;
609 }
610
611 initRender(clip, bounds);
612 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
613 finishRender();
614
615 return mDrawn;
616}
617
618bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
619 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
620 const float* positions, Rect* bounds) {
621 if (!mCurrentFont) {
622 ALOGE("No font set");
623 return false;
624 }
625
626 initRender(clip, bounds);
627 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
628 finishRender();
Romain Guy5b3b3522010-10-27 18:57:51 -0700629
630 return mDrawn;
Romain Guy694b5192010-07-21 21:33:20 -0700631}
632
Romain Guy97771732012-02-28 18:17:02 -0800633bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
634 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
635 float hOffset, float vOffset, Rect* bounds) {
636 if (!mCurrentFont) {
637 ALOGE("No font set");
638 return false;
639 }
640
641 initRender(clip, bounds);
642 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
643 finishRender();
644
645 return mDrawn;
646}
647
Romain Guy9b1204b2012-09-04 15:22:57 -0700648void FontRenderer::removeFont(const Font* font) {
649 for (uint32_t ct = 0; ct < mActiveFonts.size(); ct++) {
650 if (mActiveFonts[ct] == font) {
651 mActiveFonts.removeAt(ct);
652 break;
653 }
654 }
655
656 if (mCurrentFont == font) {
657 mCurrentFont = NULL;
658 }
659}
660
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700661void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
662 // Compute gaussian weights for the blur
663 // e is the euler's number
664 float e = 2.718281828459045f;
665 float pi = 3.1415926535897932f;
666 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
667 // x is of the form [-radius .. 0 .. radius]
668 // and sigma varies with radius.
669 // Based on some experimental radius values and sigma's
670 // we approximately fit sigma = f(radius) as
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700671 // sigma = radius * 0.3 + 0.6
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700672 // The larger the radius gets, the more our gaussian blur
673 // will resemble a box blur since with large sigma
674 // the gaussian curve begins to lose its shape
Romain Guy325a0f92011-01-05 15:26:55 -0800675 float sigma = 0.3f * (float) radius + 0.6f;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700676
677 // Now compute the coefficints
678 // We will store some redundant values to save some math during
679 // the blur calculations
680 // precompute some values
681 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
682 float coeff2 = - 1.0f / (2.0f * sigma * sigma);
683
684 float normalizeFactor = 0.0f;
Romain Guy325a0f92011-01-05 15:26:55 -0800685 for (int32_t r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700686 float floatR = (float) r;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700687 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
688 normalizeFactor += weights[r + radius];
689 }
690
691 //Now we need to normalize the weights because all our coefficients need to add up to one
692 normalizeFactor = 1.0f / normalizeFactor;
Romain Guy325a0f92011-01-05 15:26:55 -0800693 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700694 weights[r + radius] *= normalizeFactor;
695 }
696}
697
698void FontRenderer::horizontalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700699 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700700 float blurredPixel = 0.0f;
701 float currentPixel = 0.0f;
702
Romain Guy325a0f92011-01-05 15:26:55 -0800703 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700704
705 const uint8_t* input = source + y * width;
706 uint8_t* output = dest + y * width;
707
Romain Guy325a0f92011-01-05 15:26:55 -0800708 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700709 blurredPixel = 0.0f;
710 const float* gPtr = weights;
711 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800712 if (x > radius && x < (width - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700713 const uint8_t *i = input + (x - radius);
Romain Guy325a0f92011-01-05 15:26:55 -0800714 for (int r = -radius; r <= radius; r ++) {
Romain Guy7975fb62010-10-01 16:36:14 -0700715 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700716 blurredPixel += currentPixel * gPtr[0];
717 gPtr++;
718 i++;
719 }
720 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800721 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700722 // Stepping left and right away from the pixel
723 int validW = x + r;
Romain Guy325a0f92011-01-05 15:26:55 -0800724 if (validW < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700725 validW = 0;
726 }
Romain Guy325a0f92011-01-05 15:26:55 -0800727 if (validW > width - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700728 validW = width - 1;
729 }
730
Romain Guy325a0f92011-01-05 15:26:55 -0800731 currentPixel = (float) input[validW];
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700732 blurredPixel += currentPixel * gPtr[0];
733 gPtr++;
734 }
735 }
736 *output = (uint8_t)blurredPixel;
737 output ++;
738 }
739 }
740}
741
742void FontRenderer::verticalBlur(float* weights, int32_t radius,
Romain Guy1e45aae2010-08-13 19:39:53 -0700743 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700744 float blurredPixel = 0.0f;
745 float currentPixel = 0.0f;
746
Romain Guy325a0f92011-01-05 15:26:55 -0800747 for (int32_t y = 0; y < height; y ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700748 uint8_t* output = dest + y * width;
749
Romain Guy325a0f92011-01-05 15:26:55 -0800750 for (int32_t x = 0; x < width; x ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700751 blurredPixel = 0.0f;
752 const float* gPtr = weights;
753 const uint8_t* input = source + x;
754 // Optimization for non-border pixels
Romain Guy325a0f92011-01-05 15:26:55 -0800755 if (y > radius && y < (height - radius)) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700756 const uint8_t *i = input + ((y - radius) * width);
Romain Guy325a0f92011-01-05 15:26:55 -0800757 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700758 currentPixel = (float)(*i);
759 blurredPixel += currentPixel * gPtr[0];
760 gPtr++;
761 i += width;
762 }
763 } else {
Romain Guy325a0f92011-01-05 15:26:55 -0800764 for (int32_t r = -radius; r <= radius; r ++) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700765 int validH = y + r;
766 // Clamp to zero and width
Romain Guy325a0f92011-01-05 15:26:55 -0800767 if (validH < 0) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700768 validH = 0;
769 }
Romain Guy325a0f92011-01-05 15:26:55 -0800770 if (validH > height - 1) {
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700771 validH = height - 1;
772 }
773
774 const uint8_t *i = input + validH * width;
Romain Guy325a0f92011-01-05 15:26:55 -0800775 currentPixel = (float) (*i);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700776 blurredPixel += currentPixel * gPtr[0];
777 gPtr++;
778 }
779 }
Romain Guy325a0f92011-01-05 15:26:55 -0800780 *output = (uint8_t) blurredPixel;
Romain Guy9b1204b2012-09-04 15:22:57 -0700781 output++;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700782 }
783 }
784}
785
786
787void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
788 float *gaussian = new float[2 * radius + 1];
789 computeGaussianWeights(gaussian, radius);
Romain Guyd71dd362011-12-12 19:03:35 -0800790
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700791 uint8_t* scratch = new uint8_t[width * height];
Romain Guyd71dd362011-12-12 19:03:35 -0800792
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700793 horizontalBlur(gaussian, radius, image, scratch, width, height);
794 verticalBlur(gaussian, radius, scratch, image, width, height);
Romain Guyd71dd362011-12-12 19:03:35 -0800795
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700796 delete[] gaussian;
797 delete[] scratch;
798}
799
Romain Guy694b5192010-07-21 21:33:20 -0700800}; // namespace uirenderer
801}; // namespace android