blob: 5ab89e2065065f6e9ff13083e66d9367f55f3fd1 [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
19#include "FontRenderer.h"
20
21#include <SkUtils.h>
22
23namespace android {
24namespace uirenderer {
25
26///////////////////////////////////////////////////////////////////////////////
27// Font
28///////////////////////////////////////////////////////////////////////////////
29
30Font::Font(FontRenderer* state, uint32_t fontId, float fontSize) :
31 mState(state), mFontId(fontId), mFontSize(fontSize) {
32}
33
34
35Font::~Font() {
36 for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
37 if (mState->mActiveFonts[ct] == this) {
38 mState->mActiveFonts.removeAt(ct);
39 break;
40 }
41 }
42
43 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
44 CachedGlyphInfo *glyph = mCachedGlyphs.valueAt(i);
45 delete glyph;
46 }
47}
48
49void Font::invalidateTextureCache() {
50 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
51 mCachedGlyphs.valueAt(i)->mIsValid = false;
52 }
53}
54
55void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
56 FontRenderer *state = mState;
57
58 int nPenX = x + glyph->mBitmapLeft;
59 int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
60
61 state->appendMeshQuad(nPenX, nPenY, 0, glyph->mBitmapMinU, glyph->mBitmapMaxV,
62 nPenX + (int) glyph->mBitmapWidth, nPenY, 0, glyph->mBitmapMaxU, glyph->mBitmapMaxV,
63 nPenX + (int) glyph->mBitmapWidth, nPenY - (int) glyph->mBitmapHeight,
64 0, glyph->mBitmapMaxU, glyph->mBitmapMinV, nPenX, nPenY - (int) glyph->mBitmapHeight,
65 0, glyph->mBitmapMinU, glyph->mBitmapMinV);
66}
67
Romain Guy09147fb2010-07-22 13:08:20 -070068void Font::renderUTF(SkPaint* paint, const char *text, uint32_t len, uint32_t start,
69 int numGlyphs, int x, int y) {
Romain Guy694b5192010-07-21 21:33:20 -070070 if (numGlyphs == 0 || text == NULL || len == 0) {
71 return;
72 }
73
74 int penX = x, penY = y;
75 int glyphsLeft = 1;
76 if (numGlyphs > 0) {
77 glyphsLeft = numGlyphs;
78 }
79
80 //size_t index = start;
81 //size_t nextIndex = 0;
82
83 text += start;
84
85 while (glyphsLeft > 0) {
86 //int32_t utfChar = utf32_at(text, len, index, &nextIndex);
87 int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text);
88
89 // Reached the end of the string or encountered
90 if (utfChar < 0) {
91 break;
92 }
93
94 // Move to the next character in the array
95 //index = nextIndex;
96
97 CachedGlyphInfo *cachedGlyph = mCachedGlyphs.valueFor(utfChar);
98
99 if (cachedGlyph == NULL) {
100 cachedGlyph = cacheGlyph(paint, utfChar);
101 }
102 // Is the glyph still in texture cache?
103 if (!cachedGlyph->mIsValid) {
104 const SkGlyph& skiaGlyph = paint->getUnicharMetrics(utfChar);
105 updateGlyphCache(paint, skiaGlyph, cachedGlyph);
106 }
107
108 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
109 if (cachedGlyph->mIsValid) {
110 drawCachedGlyph(cachedGlyph, penX, penY);
111 }
112
Romain Guy09147fb2010-07-22 13:08:20 -0700113 penX += SkFixedFloor(cachedGlyph->mAdvanceX);
Romain Guy694b5192010-07-21 21:33:20 -0700114
115 // If we were given a specific number of glyphs, decrement
116 if (numGlyphs > 0) {
117 glyphsLeft--;
118 }
119 }
120}
121
122void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo *glyph) {
123 glyph->mAdvanceX = skiaGlyph.fAdvanceX;
124 glyph->mAdvanceY = skiaGlyph.fAdvanceY;
125 glyph->mBitmapLeft = skiaGlyph.fLeft;
126 glyph->mBitmapTop = skiaGlyph.fTop;
127
128 uint32_t startX = 0;
129 uint32_t startY = 0;
130
131 // Let the font state figure out where to put the bitmap
132 FontRenderer *state = mState;
133 // Get the bitmap for the glyph
134 paint->findImage(skiaGlyph);
135 glyph->mIsValid = state->cacheBitmap(skiaGlyph, &startX, &startY);
136
137 if (!glyph->mIsValid) {
138 return;
139 }
140
141 uint32_t endX = startX + skiaGlyph.fWidth;
142 uint32_t endY = startY + skiaGlyph.fHeight;
143
144 glyph->mBitmapWidth = skiaGlyph.fWidth;
145 glyph->mBitmapHeight = skiaGlyph.fHeight;
146
147 uint32_t cacheWidth = state->getCacheWidth();
148 uint32_t cacheHeight = state->getCacheHeight();
149
150 glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
151 glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
152 glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
153 glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
154
155 state->mUploadTexture = true;
156}
157
158Font::CachedGlyphInfo *Font::cacheGlyph(SkPaint* paint, int32_t glyph) {
159 CachedGlyphInfo *newGlyph = new CachedGlyphInfo();
160 mCachedGlyphs.add(glyph, newGlyph);
161
162 const SkGlyph& skiaGlyph = paint->getUnicharMetrics(glyph);
163 newGlyph->mGlyphIndex = skiaGlyph.fID;
164 newGlyph->mIsValid = false;
165
166 updateGlyphCache(paint, skiaGlyph, newGlyph);
167
168 return newGlyph;
169}
170
171Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize) {
172 Vector<Font*> &activeFonts = state->mActiveFonts;
173
174 for (uint32_t i = 0; i < activeFonts.size(); i++) {
175 Font *ithFont = activeFonts[i];
176 if (ithFont->mFontId == fontId && ithFont->mFontSize == fontSize) {
177 return ithFont;
178 }
179 }
180
181 Font* newFont = new Font(state, fontId, fontSize);
182 activeFonts.push(newFont);
183 return newFont;
184}
185
186///////////////////////////////////////////////////////////////////////////////
187// FontRenderer
188///////////////////////////////////////////////////////////////////////////////
189
190FontRenderer::FontRenderer() {
191 mInitialized = false;
192 mMaxNumberOfQuads = 1024;
193 mCurrentQuadIndex = 0;
194
195 mIndexBufferID = 0;
196
197 mCacheWidth = 1024;
198 mCacheHeight = 256;
199}
200
201FontRenderer::~FontRenderer() {
202 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
203 delete mCacheLines[i];
204 }
205 mCacheLines.clear();
206
207 delete mTextTexture;
208
209 Vector<Font*> fontsToDereference = mActiveFonts;
210 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
211 delete fontsToDereference[i];
212 }
213}
214
215void FontRenderer::flushAllAndInvalidate() {
216 if (mCurrentQuadIndex != 0) {
217 issueDrawCommand();
218 mCurrentQuadIndex = 0;
219 }
220 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
221 mActiveFonts[i]->invalidateTextureCache();
222 }
223 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
224 mCacheLines[i]->mCurrentCol = 0;
225 }
226}
227
228bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
229 // If the glyph is too tall, don't cache it
230 if (glyph.fWidth > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
231 LOGE("Font size to large to fit in cache. width, height = %i, %i",
232 (int) glyph.fWidth, (int) glyph.fHeight);
233 return false;
234 }
235
236 // Now copy the bitmap into the cache texture
237 uint32_t startX = 0;
238 uint32_t startY = 0;
239
240 bool bitmapFit = false;
241 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
242 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
243 if (bitmapFit) {
244 break;
245 }
246 }
247
248 // If the new glyph didn't fit, flush the state so far and invalidate everything
249 if (!bitmapFit) {
250 flushAllAndInvalidate();
251
252 // Try to fit it again
253 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
254 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
255 if (bitmapFit) {
256 break;
257 }
258 }
259
260 // if we still don't fit, something is wrong and we shouldn't draw
261 if (!bitmapFit) {
262 LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
263 (int) glyph.fWidth, (int) glyph.fHeight);
264 return false;
265 }
266 }
267
268 *retOriginX = startX;
269 *retOriginY = startY;
270
271 uint32_t endX = startX + glyph.fWidth;
272 uint32_t endY = startY + glyph.fHeight;
273
274 uint32_t cacheWidth = mCacheWidth;
275
276 unsigned char *cacheBuffer = mTextTexture;
277 unsigned char *bitmapBuffer = (unsigned char*) glyph.fImage;
278 unsigned int stride = glyph.rowBytes();
279
280 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
281 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
282 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
283 unsigned char tempCol = bitmapBuffer[bY * stride + bX];
284 cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
285 }
286 }
287
288 return true;
289}
290
291void FontRenderer::initTextTexture() {
292 mTextTexture = new unsigned char[mCacheWidth * mCacheHeight];
293 mUploadTexture = false;
294
295 glGenTextures(1, &mTextureId);
296 glBindTexture(GL_TEXTURE_2D, mTextureId);
297 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
298
299 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
300 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
301
302 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
303 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
304
305 // Split up our cache texture into lines of certain widths
306 int nextLine = 0;
307 mCacheLines.push(new CacheTextureLine(16, mCacheWidth, nextLine, 0));
308 nextLine += mCacheLines.top()->mMaxHeight;
309 mCacheLines.push(new CacheTextureLine(24, mCacheWidth, nextLine, 0));
310 nextLine += mCacheLines.top()->mMaxHeight;
311 mCacheLines.push(new CacheTextureLine(32, mCacheWidth, nextLine, 0));
312 nextLine += mCacheLines.top()->mMaxHeight;
313 mCacheLines.push(new CacheTextureLine(32, mCacheWidth, nextLine, 0));
314 nextLine += mCacheLines.top()->mMaxHeight;
315 mCacheLines.push(new CacheTextureLine(40, mCacheWidth, nextLine, 0));
316 nextLine += mCacheLines.top()->mMaxHeight;
317 mCacheLines.push(new CacheTextureLine(mCacheHeight - nextLine, mCacheWidth, nextLine, 0));
318}
319
320// Avoid having to reallocate memory and render quad by quad
321void FontRenderer::initVertexArrayBuffers() {
322 uint32_t numIndicies = mMaxNumberOfQuads * 6;
323 uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
324 uint16_t *indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
325
326 // Four verts, two triangles , six indices per quad
327 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
328 int i6 = i * 6;
329 int i4 = i * 4;
330
331 indexBufferData[i6 + 0] = i4 + 0;
332 indexBufferData[i6 + 1] = i4 + 1;
333 indexBufferData[i6 + 2] = i4 + 2;
334
335 indexBufferData[i6 + 3] = i4 + 0;
336 indexBufferData[i6 + 4] = i4 + 2;
337 indexBufferData[i6 + 5] = i4 + 3;
338 }
339
340 glGenBuffers(1, &mIndexBufferID);
341 glBindBuffer(GL_ARRAY_BUFFER, mIndexBufferID);
342 glBufferData(GL_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_DYNAMIC_DRAW);
343 glBindBuffer(GL_ARRAY_BUFFER, 0);
344
345 free(indexBufferData);
346
347 uint32_t coordSize = 3;
348 uint32_t uvSize = 2;
349 uint32_t vertsPerQuad = 4;
350 uint32_t vertexBufferSizeBytes = mMaxNumberOfQuads * vertsPerQuad * coordSize *
351 uvSize * sizeof(float);
352 mTextMeshPtr = (float*) malloc(vertexBufferSizeBytes);
353}
354
355// We don't want to allocate anything unless we actually draw text
356void FontRenderer::checkInit() {
357 if (mInitialized) {
358 return;
359 }
360
361 initTextTexture();
362 initVertexArrayBuffers();
363
364 mInitialized = true;
365}
366
367void FontRenderer::issueDrawCommand() {
368 if (mUploadTexture) {
369 glBindTexture(GL_TEXTURE_2D, mTextureId);
370 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0, GL_ALPHA,
371 GL_UNSIGNED_BYTE, mTextTexture);
372 mUploadTexture = false;
373 }
374
375 float *vtx = mTextMeshPtr;
376 float *tex = vtx + 3;
377
378 // position is slot 0
379 uint32_t slot = 0;
380 glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx);
381
382 // texture0 is slot 1
383 slot = 1;
384 glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex);
385
386 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
387 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
388}
389
390void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
391 float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
392 float x4, float y4, float z4, float u4, float v4) {
Romain Guy09147fb2010-07-22 13:08:20 -0700393 if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) {
394 return;
395 }
396
Romain Guy694b5192010-07-21 21:33:20 -0700397 const uint32_t vertsPerQuad = 4;
398 const uint32_t floatsPerVert = 5;
399 float *currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
400
Romain Guy694b5192010-07-21 21:33:20 -0700401 (*currentPos++) = x1;
402 (*currentPos++) = y1;
403 (*currentPos++) = z1;
404 (*currentPos++) = u1;
405 (*currentPos++) = v1;
406
407 (*currentPos++) = x2;
408 (*currentPos++) = y2;
409 (*currentPos++) = z2;
410 (*currentPos++) = u2;
411 (*currentPos++) = v2;
412
413 (*currentPos++) = x3;
414 (*currentPos++) = y3;
415 (*currentPos++) = z3;
416 (*currentPos++) = u3;
417 (*currentPos++) = v3;
418
419 (*currentPos++) = x4;
420 (*currentPos++) = y4;
421 (*currentPos++) = z4;
422 (*currentPos++) = u4;
423 (*currentPos++) = v4;
424
425 mCurrentQuadIndex++;
426
427 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
428 issueDrawCommand();
429 mCurrentQuadIndex = 0;
430 }
431}
432
433void FontRenderer::setFont(uint32_t fontId, float fontSize) {
434 mCurrentFont = Font::create(this, fontId, fontSize);
435}
436
Romain Guy09147fb2010-07-22 13:08:20 -0700437void FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text, uint32_t len,
438 uint32_t startIndex, int numGlyphs, int x, int y) {
Romain Guy694b5192010-07-21 21:33:20 -0700439 checkInit();
440
Romain Guy09147fb2010-07-22 13:08:20 -0700441 if (!mCurrentFont) {
442 LOGE("No font set");
Romain Guy694b5192010-07-21 21:33:20 -0700443 return;
444 }
445
Romain Guy09147fb2010-07-22 13:08:20 -0700446 mClip = clip;
447 mCurrentFont->renderUTF(paint, text, len, startIndex, numGlyphs, x, y);
Romain Guy694b5192010-07-21 21:33:20 -0700448
449 if (mCurrentQuadIndex != 0) {
450 issueDrawCommand();
451 mCurrentQuadIndex = 0;
452 }
453}
454
Romain Guy694b5192010-07-21 21:33:20 -0700455}; // namespace uirenderer
456}; // namespace android