blob: 8557b87ec924b7f0645f8223fc3a0ed186092672 [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
68void Font::renderUTF(SkPaint* paint, const char *text, uint32_t len, uint32_t start, int numGlyphs,
69 int x, int y) {
70 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
113 // TODO: Check how to do this conversion
114 penX += SkFixedRound(cachedGlyph->mAdvanceX);
115
116 // If we were given a specific number of glyphs, decrement
117 if (numGlyphs > 0) {
118 glyphsLeft--;
119 }
120 }
121}
122
123void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo *glyph) {
124 glyph->mAdvanceX = skiaGlyph.fAdvanceX;
125 glyph->mAdvanceY = skiaGlyph.fAdvanceY;
126 glyph->mBitmapLeft = skiaGlyph.fLeft;
127 glyph->mBitmapTop = skiaGlyph.fTop;
128
129 uint32_t startX = 0;
130 uint32_t startY = 0;
131
132 // Let the font state figure out where to put the bitmap
133 FontRenderer *state = mState;
134 // Get the bitmap for the glyph
135 paint->findImage(skiaGlyph);
136 glyph->mIsValid = state->cacheBitmap(skiaGlyph, &startX, &startY);
137
138 if (!glyph->mIsValid) {
139 return;
140 }
141
142 uint32_t endX = startX + skiaGlyph.fWidth;
143 uint32_t endY = startY + skiaGlyph.fHeight;
144
145 glyph->mBitmapWidth = skiaGlyph.fWidth;
146 glyph->mBitmapHeight = skiaGlyph.fHeight;
147
148 uint32_t cacheWidth = state->getCacheWidth();
149 uint32_t cacheHeight = state->getCacheHeight();
150
151 glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
152 glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
153 glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
154 glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
155
156 state->mUploadTexture = true;
157}
158
159Font::CachedGlyphInfo *Font::cacheGlyph(SkPaint* paint, int32_t glyph) {
160 CachedGlyphInfo *newGlyph = new CachedGlyphInfo();
161 mCachedGlyphs.add(glyph, newGlyph);
162
163 const SkGlyph& skiaGlyph = paint->getUnicharMetrics(glyph);
164 newGlyph->mGlyphIndex = skiaGlyph.fID;
165 newGlyph->mIsValid = false;
166
167 updateGlyphCache(paint, skiaGlyph, newGlyph);
168
169 return newGlyph;
170}
171
172Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize) {
173 Vector<Font*> &activeFonts = state->mActiveFonts;
174
175 for (uint32_t i = 0; i < activeFonts.size(); i++) {
176 Font *ithFont = activeFonts[i];
177 if (ithFont->mFontId == fontId && ithFont->mFontSize == fontSize) {
178 return ithFont;
179 }
180 }
181
182 Font* newFont = new Font(state, fontId, fontSize);
183 activeFonts.push(newFont);
184 return newFont;
185}
186
187///////////////////////////////////////////////////////////////////////////////
188// FontRenderer
189///////////////////////////////////////////////////////////////////////////////
190
191FontRenderer::FontRenderer() {
192 mInitialized = false;
193 mMaxNumberOfQuads = 1024;
194 mCurrentQuadIndex = 0;
195
196 mIndexBufferID = 0;
197
198 mCacheWidth = 1024;
199 mCacheHeight = 256;
200}
201
202FontRenderer::~FontRenderer() {
203 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
204 delete mCacheLines[i];
205 }
206 mCacheLines.clear();
207
208 delete mTextTexture;
209
210 Vector<Font*> fontsToDereference = mActiveFonts;
211 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
212 delete fontsToDereference[i];
213 }
214}
215
216void FontRenderer::flushAllAndInvalidate() {
217 if (mCurrentQuadIndex != 0) {
218 issueDrawCommand();
219 mCurrentQuadIndex = 0;
220 }
221 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
222 mActiveFonts[i]->invalidateTextureCache();
223 }
224 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
225 mCacheLines[i]->mCurrentCol = 0;
226 }
227}
228
229bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
230 // If the glyph is too tall, don't cache it
231 if (glyph.fWidth > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
232 LOGE("Font size to large to fit in cache. width, height = %i, %i",
233 (int) glyph.fWidth, (int) glyph.fHeight);
234 return false;
235 }
236
237 // Now copy the bitmap into the cache texture
238 uint32_t startX = 0;
239 uint32_t startY = 0;
240
241 bool bitmapFit = false;
242 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
243 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
244 if (bitmapFit) {
245 break;
246 }
247 }
248
249 // If the new glyph didn't fit, flush the state so far and invalidate everything
250 if (!bitmapFit) {
251 flushAllAndInvalidate();
252
253 // Try to fit it again
254 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
255 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
256 if (bitmapFit) {
257 break;
258 }
259 }
260
261 // if we still don't fit, something is wrong and we shouldn't draw
262 if (!bitmapFit) {
263 LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
264 (int) glyph.fWidth, (int) glyph.fHeight);
265 return false;
266 }
267 }
268
269 *retOriginX = startX;
270 *retOriginY = startY;
271
272 uint32_t endX = startX + glyph.fWidth;
273 uint32_t endY = startY + glyph.fHeight;
274
275 uint32_t cacheWidth = mCacheWidth;
276
277 unsigned char *cacheBuffer = mTextTexture;
278 unsigned char *bitmapBuffer = (unsigned char*) glyph.fImage;
279 unsigned int stride = glyph.rowBytes();
280
281 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
282 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
283 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
284 unsigned char tempCol = bitmapBuffer[bY * stride + bX];
285 cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
286 }
287 }
288
289 return true;
290}
291
292void FontRenderer::initTextTexture() {
293 mTextTexture = new unsigned char[mCacheWidth * mCacheHeight];
294 mUploadTexture = false;
295
296 glGenTextures(1, &mTextureId);
297 glBindTexture(GL_TEXTURE_2D, mTextureId);
298 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
299
300 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
301 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
302
303 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
304 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
305
306 // Split up our cache texture into lines of certain widths
307 int nextLine = 0;
308 mCacheLines.push(new CacheTextureLine(16, mCacheWidth, nextLine, 0));
309 nextLine += mCacheLines.top()->mMaxHeight;
310 mCacheLines.push(new CacheTextureLine(24, mCacheWidth, nextLine, 0));
311 nextLine += mCacheLines.top()->mMaxHeight;
312 mCacheLines.push(new CacheTextureLine(32, mCacheWidth, nextLine, 0));
313 nextLine += mCacheLines.top()->mMaxHeight;
314 mCacheLines.push(new CacheTextureLine(32, mCacheWidth, nextLine, 0));
315 nextLine += mCacheLines.top()->mMaxHeight;
316 mCacheLines.push(new CacheTextureLine(40, mCacheWidth, nextLine, 0));
317 nextLine += mCacheLines.top()->mMaxHeight;
318 mCacheLines.push(new CacheTextureLine(mCacheHeight - nextLine, mCacheWidth, nextLine, 0));
319}
320
321// Avoid having to reallocate memory and render quad by quad
322void FontRenderer::initVertexArrayBuffers() {
323 uint32_t numIndicies = mMaxNumberOfQuads * 6;
324 uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
325 uint16_t *indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
326
327 // Four verts, two triangles , six indices per quad
328 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
329 int i6 = i * 6;
330 int i4 = i * 4;
331
332 indexBufferData[i6 + 0] = i4 + 0;
333 indexBufferData[i6 + 1] = i4 + 1;
334 indexBufferData[i6 + 2] = i4 + 2;
335
336 indexBufferData[i6 + 3] = i4 + 0;
337 indexBufferData[i6 + 4] = i4 + 2;
338 indexBufferData[i6 + 5] = i4 + 3;
339 }
340
341 glGenBuffers(1, &mIndexBufferID);
342 glBindBuffer(GL_ARRAY_BUFFER, mIndexBufferID);
343 glBufferData(GL_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_DYNAMIC_DRAW);
344 glBindBuffer(GL_ARRAY_BUFFER, 0);
345
346 free(indexBufferData);
347
348 uint32_t coordSize = 3;
349 uint32_t uvSize = 2;
350 uint32_t vertsPerQuad = 4;
351 uint32_t vertexBufferSizeBytes = mMaxNumberOfQuads * vertsPerQuad * coordSize *
352 uvSize * sizeof(float);
353 mTextMeshPtr = (float*) malloc(vertexBufferSizeBytes);
354}
355
356// We don't want to allocate anything unless we actually draw text
357void FontRenderer::checkInit() {
358 if (mInitialized) {
359 return;
360 }
361
362 initTextTexture();
363 initVertexArrayBuffers();
364
365 mInitialized = true;
366}
367
368void FontRenderer::issueDrawCommand() {
369 if (mUploadTexture) {
370 glBindTexture(GL_TEXTURE_2D, mTextureId);
371 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0, GL_ALPHA,
372 GL_UNSIGNED_BYTE, mTextTexture);
373 mUploadTexture = false;
374 }
375
376 float *vtx = mTextMeshPtr;
377 float *tex = vtx + 3;
378
379 // position is slot 0
380 uint32_t slot = 0;
381 glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx);
382
383 // texture0 is slot 1
384 slot = 1;
385 glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex);
386
387 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
388 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
389}
390
391void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
392 float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
393 float x4, float y4, float z4, float u4, float v4) {
394 const uint32_t vertsPerQuad = 4;
395 const uint32_t floatsPerVert = 5;
396 float *currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
397
398 // TODO: Cull things that are off the screen
399 // float width = (float)mRSC->getWidth();
400 // float height = (float)mRSC->getHeight();
401 //
402 // if(x1 > width || y1 < 0.0f || x2 < 0 || y4 > height) {
403 // return;
404 // }
405
406 (*currentPos++) = x1;
407 (*currentPos++) = y1;
408 (*currentPos++) = z1;
409 (*currentPos++) = u1;
410 (*currentPos++) = v1;
411
412 (*currentPos++) = x2;
413 (*currentPos++) = y2;
414 (*currentPos++) = z2;
415 (*currentPos++) = u2;
416 (*currentPos++) = v2;
417
418 (*currentPos++) = x3;
419 (*currentPos++) = y3;
420 (*currentPos++) = z3;
421 (*currentPos++) = u3;
422 (*currentPos++) = v3;
423
424 (*currentPos++) = x4;
425 (*currentPos++) = y4;
426 (*currentPos++) = z4;
427 (*currentPos++) = u4;
428 (*currentPos++) = v4;
429
430 mCurrentQuadIndex++;
431
432 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
433 issueDrawCommand();
434 mCurrentQuadIndex = 0;
435 }
436}
437
438void FontRenderer::setFont(uint32_t fontId, float fontSize) {
439 mCurrentFont = Font::create(this, fontId, fontSize);
440}
441
442void FontRenderer::renderText(SkPaint* paint, const char *text, uint32_t len, uint32_t startIndex,
443 int numGlyphs, int x, int y) {
444 checkInit();
445
446 // Render code here
447 Font *currentFont = mCurrentFont;
448 if (!currentFont) {
449 LOGE("Unable to initialize any fonts");
450 return;
451 }
452
453 currentFont->renderUTF(paint, text, len, startIndex, numGlyphs, x, y);
454
455 if (mCurrentQuadIndex != 0) {
456 issueDrawCommand();
457 mCurrentQuadIndex = 0;
458 }
459}
460
461void FontRenderer::renderText(SkPaint* paint, const char *text, int x, int y) {
462 size_t textLen = strlen(text);
463 renderText(paint, text, textLen, 0, -1, x, y);
464}
465
466}; // namespace uirenderer
467}; // namespace android