blob: 5595ab0220d6d75dc9d1aaebb27f3575800bd905 [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>
22#include <utils/Log.h>
23
24#include "FontRenderer.h"
25
Romain Guy694b5192010-07-21 21:33:20 -070026namespace android {
27namespace uirenderer {
28
29///////////////////////////////////////////////////////////////////////////////
Romain Guy51769a62010-07-23 00:28:00 -070030// Defines
31///////////////////////////////////////////////////////////////////////////////
32
33#define DEFAULT_TEXT_CACHE_WIDTH 1024
34#define DEFAULT_TEXT_CACHE_HEIGHT 256
35
36///////////////////////////////////////////////////////////////////////////////
Romain Guy694b5192010-07-21 21:33:20 -070037// Font
38///////////////////////////////////////////////////////////////////////////////
39
40Font::Font(FontRenderer* state, uint32_t fontId, float fontSize) :
41 mState(state), mFontId(fontId), mFontSize(fontSize) {
42}
43
44
45Font::~Font() {
46 for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
47 if (mState->mActiveFonts[ct] == this) {
48 mState->mActiveFonts.removeAt(ct);
49 break;
50 }
51 }
52
53 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
Romain Guy51769a62010-07-23 00:28:00 -070054 CachedGlyphInfo* glyph = mCachedGlyphs.valueAt(i);
Romain Guy694b5192010-07-21 21:33:20 -070055 delete glyph;
56 }
57}
58
59void Font::invalidateTextureCache() {
60 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
61 mCachedGlyphs.valueAt(i)->mIsValid = false;
62 }
63}
64
65void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
Romain Guy694b5192010-07-21 21:33:20 -070066 int nPenX = x + glyph->mBitmapLeft;
67 int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
68
Romain Guy51769a62010-07-23 00:28:00 -070069 float u1 = glyph->mBitmapMinU;
70 float u2 = glyph->mBitmapMaxU;
71 float v1 = glyph->mBitmapMinV;
72 float v2 = glyph->mBitmapMaxV;
73
74 int width = (int) glyph->mBitmapWidth;
75 int height = (int) glyph->mBitmapHeight;
76
77 mState->appendMeshQuad(nPenX, nPenY, 0, u1, v2,
78 nPenX + width, nPenY, 0, u2, v2,
79 nPenX + width, nPenY - height, 0, u2, v1,
80 nPenX, nPenY - height, 0, u1, v1);
Romain Guy694b5192010-07-21 21:33:20 -070081}
82
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -070083Font::CachedGlyphInfo* Font::getCachedUTFChar(SkPaint* paint, int32_t utfChar) {
84 CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueFor(utfChar);
85 if (cachedGlyph == NULL) {
86 cachedGlyph = cacheGlyph(paint, utfChar);
87 }
88
89 // Is the glyph still in texture cache?
90 if (!cachedGlyph->mIsValid) {
91 const SkGlyph& skiaGlyph = paint->getUnicharMetrics(utfChar);
92 updateGlyphCache(paint, skiaGlyph, cachedGlyph);
93 }
94
95 return cachedGlyph;
96}
97
Romain Guy51769a62010-07-23 00:28:00 -070098void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Romain Guy09147fb2010-07-22 13:08:20 -070099 int numGlyphs, int x, int y) {
Romain Guy694b5192010-07-21 21:33:20 -0700100 if (numGlyphs == 0 || text == NULL || len == 0) {
101 return;
102 }
103
104 int penX = x, penY = y;
105 int glyphsLeft = 1;
106 if (numGlyphs > 0) {
107 glyphsLeft = numGlyphs;
108 }
109
Romain Guy694b5192010-07-21 21:33:20 -0700110 text += start;
111
112 while (glyphsLeft > 0) {
Romain Guy694b5192010-07-21 21:33:20 -0700113 int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text);
114
115 // Reached the end of the string or encountered
116 if (utfChar < 0) {
117 break;
118 }
119
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700120 CachedGlyphInfo* cachedGlyph = getCachedUTFChar(paint, utfChar);
Romain Guy694b5192010-07-21 21:33:20 -0700121
122 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
123 if (cachedGlyph->mIsValid) {
124 drawCachedGlyph(cachedGlyph, penX, penY);
125 }
126
Romain Guy09147fb2010-07-22 13:08:20 -0700127 penX += SkFixedFloor(cachedGlyph->mAdvanceX);
Romain Guy694b5192010-07-21 21:33:20 -0700128
129 // If we were given a specific number of glyphs, decrement
130 if (numGlyphs > 0) {
131 glyphsLeft--;
132 }
133 }
134}
135
Romain Guy51769a62010-07-23 00:28:00 -0700136void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
Romain Guy694b5192010-07-21 21:33:20 -0700137 glyph->mAdvanceX = skiaGlyph.fAdvanceX;
138 glyph->mAdvanceY = skiaGlyph.fAdvanceY;
139 glyph->mBitmapLeft = skiaGlyph.fLeft;
140 glyph->mBitmapTop = skiaGlyph.fTop;
141
142 uint32_t startX = 0;
143 uint32_t startY = 0;
144
Romain Guy694b5192010-07-21 21:33:20 -0700145 // Get the bitmap for the glyph
146 paint->findImage(skiaGlyph);
Romain Guy51769a62010-07-23 00:28:00 -0700147 glyph->mIsValid = mState->cacheBitmap(skiaGlyph, &startX, &startY);
Romain Guy694b5192010-07-21 21:33:20 -0700148
149 if (!glyph->mIsValid) {
150 return;
151 }
152
153 uint32_t endX = startX + skiaGlyph.fWidth;
154 uint32_t endY = startY + skiaGlyph.fHeight;
155
156 glyph->mBitmapWidth = skiaGlyph.fWidth;
157 glyph->mBitmapHeight = skiaGlyph.fHeight;
158
Romain Guy51769a62010-07-23 00:28:00 -0700159 uint32_t cacheWidth = mState->getCacheWidth();
160 uint32_t cacheHeight = mState->getCacheHeight();
Romain Guy694b5192010-07-21 21:33:20 -0700161
162 glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
163 glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
164 glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
165 glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
166
Romain Guy51769a62010-07-23 00:28:00 -0700167 mState->mUploadTexture = true;
Romain Guy694b5192010-07-21 21:33:20 -0700168}
169
Romain Guy51769a62010-07-23 00:28:00 -0700170Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, int32_t glyph) {
171 CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
Romain Guy694b5192010-07-21 21:33:20 -0700172 mCachedGlyphs.add(glyph, newGlyph);
173
174 const SkGlyph& skiaGlyph = paint->getUnicharMetrics(glyph);
175 newGlyph->mGlyphIndex = skiaGlyph.fID;
176 newGlyph->mIsValid = false;
177
178 updateGlyphCache(paint, skiaGlyph, newGlyph);
179
180 return newGlyph;
181}
182
183Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize) {
184 Vector<Font*> &activeFonts = state->mActiveFonts;
185
186 for (uint32_t i = 0; i < activeFonts.size(); i++) {
Romain Guy51769a62010-07-23 00:28:00 -0700187 Font* font = activeFonts[i];
188 if (font->mFontId == fontId && font->mFontSize == fontSize) {
189 return font;
Romain Guy694b5192010-07-21 21:33:20 -0700190 }
191 }
192
193 Font* newFont = new Font(state, fontId, fontSize);
194 activeFonts.push(newFont);
195 return newFont;
196}
197
198///////////////////////////////////////////////////////////////////////////////
199// FontRenderer
200///////////////////////////////////////////////////////////////////////////////
201
202FontRenderer::FontRenderer() {
Romain Guy51769a62010-07-23 00:28:00 -0700203 LOGD("Creating FontRenderer");
204
Romain Guy694b5192010-07-21 21:33:20 -0700205 mInitialized = false;
206 mMaxNumberOfQuads = 1024;
207 mCurrentQuadIndex = 0;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700208 mTextureId = 0;
Romain Guy694b5192010-07-21 21:33:20 -0700209
210 mIndexBufferID = 0;
211
Romain Guy51769a62010-07-23 00:28:00 -0700212 mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700213 mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
Romain Guy51769a62010-07-23 00:28:00 -0700214
215 char property[PROPERTY_VALUE_MAX];
216 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
217 LOGD(" Setting text cache width to %s pixels", property);
218 mCacheWidth = atoi(property);
219 } else {
220 LOGD(" Using default text cache width of %i pixels", mCacheWidth);
221 }
222
223 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
224 LOGD(" Setting text cache width to %s pixels", property);
225 mCacheHeight = atoi(property);
226 } else {
227 LOGD(" Using default text cache height of %i pixels", mCacheHeight);
228 }
Romain Guy694b5192010-07-21 21:33:20 -0700229}
230
231FontRenderer::~FontRenderer() {
232 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
233 delete mCacheLines[i];
234 }
235 mCacheLines.clear();
236
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700237 delete mTextMeshPtr;
238
Romain Guy694b5192010-07-21 21:33:20 -0700239 delete mTextTexture;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700240 if(mTextureId) {
241 glDeleteTextures(1, &mTextureId);
242 }
Romain Guy694b5192010-07-21 21:33:20 -0700243
244 Vector<Font*> fontsToDereference = mActiveFonts;
245 for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
246 delete fontsToDereference[i];
247 }
248}
249
250void FontRenderer::flushAllAndInvalidate() {
251 if (mCurrentQuadIndex != 0) {
252 issueDrawCommand();
253 mCurrentQuadIndex = 0;
254 }
255 for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
256 mActiveFonts[i]->invalidateTextureCache();
257 }
258 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
259 mCacheLines[i]->mCurrentCol = 0;
260 }
261}
262
Romain Guy51769a62010-07-23 00:28:00 -0700263bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
Romain Guy694b5192010-07-21 21:33:20 -0700264 // If the glyph is too tall, don't cache it
265 if (glyph.fWidth > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
266 LOGE("Font size to large to fit in cache. width, height = %i, %i",
267 (int) glyph.fWidth, (int) glyph.fHeight);
268 return false;
269 }
270
271 // Now copy the bitmap into the cache texture
272 uint32_t startX = 0;
273 uint32_t startY = 0;
274
275 bool bitmapFit = false;
276 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
277 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
278 if (bitmapFit) {
279 break;
280 }
281 }
282
283 // If the new glyph didn't fit, flush the state so far and invalidate everything
284 if (!bitmapFit) {
285 flushAllAndInvalidate();
286
287 // Try to fit it again
288 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
289 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
290 if (bitmapFit) {
291 break;
292 }
293 }
294
295 // if we still don't fit, something is wrong and we shouldn't draw
296 if (!bitmapFit) {
297 LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
298 (int) glyph.fWidth, (int) glyph.fHeight);
299 return false;
300 }
301 }
302
303 *retOriginX = startX;
304 *retOriginY = startY;
305
306 uint32_t endX = startX + glyph.fWidth;
307 uint32_t endY = startY + glyph.fHeight;
308
309 uint32_t cacheWidth = mCacheWidth;
310
Romain Guy51769a62010-07-23 00:28:00 -0700311 unsigned char* cacheBuffer = mTextTexture;
312 unsigned char* bitmapBuffer = (unsigned char*) glyph.fImage;
Romain Guy694b5192010-07-21 21:33:20 -0700313 unsigned int stride = glyph.rowBytes();
314
315 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
316 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
317 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
318 unsigned char tempCol = bitmapBuffer[bY * stride + bX];
319 cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
320 }
321 }
322
323 return true;
324}
325
326void FontRenderer::initTextTexture() {
327 mTextTexture = new unsigned char[mCacheWidth * mCacheHeight];
328 mUploadTexture = false;
329
330 glGenTextures(1, &mTextureId);
331 glBindTexture(GL_TEXTURE_2D, mTextureId);
332 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700333 // Initialize texture dimentions
334 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
335 GL_ALPHA, GL_UNSIGNED_BYTE, 0);
Romain Guy694b5192010-07-21 21:33:20 -0700336
337 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
338 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
339
340 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
341 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
342
343 // Split up our cache texture into lines of certain widths
344 int nextLine = 0;
Romain Guy51769a62010-07-23 00:28:00 -0700345 mCacheLines.push(new CacheTextureLine(mCacheWidth, 16, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700346 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700347 mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700348 nextLine += mCacheLines.top()->mMaxHeight;
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700349 mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0));
350 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700351 mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700352 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700353 mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700354 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700355 mCacheLines.push(new CacheTextureLine(mCacheWidth, 40, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700356 nextLine += mCacheLines.top()->mMaxHeight;
Romain Guy51769a62010-07-23 00:28:00 -0700357 mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
Romain Guy694b5192010-07-21 21:33:20 -0700358}
359
360// Avoid having to reallocate memory and render quad by quad
361void FontRenderer::initVertexArrayBuffers() {
362 uint32_t numIndicies = mMaxNumberOfQuads * 6;
363 uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
Romain Guy51769a62010-07-23 00:28:00 -0700364 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
Romain Guy694b5192010-07-21 21:33:20 -0700365
366 // Four verts, two triangles , six indices per quad
367 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
368 int i6 = i * 6;
369 int i4 = i * 4;
370
371 indexBufferData[i6 + 0] = i4 + 0;
372 indexBufferData[i6 + 1] = i4 + 1;
373 indexBufferData[i6 + 2] = i4 + 2;
374
375 indexBufferData[i6 + 3] = i4 + 0;
376 indexBufferData[i6 + 4] = i4 + 2;
377 indexBufferData[i6 + 5] = i4 + 3;
378 }
379
380 glGenBuffers(1, &mIndexBufferID);
381 glBindBuffer(GL_ARRAY_BUFFER, mIndexBufferID);
382 glBufferData(GL_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_DYNAMIC_DRAW);
383 glBindBuffer(GL_ARRAY_BUFFER, 0);
384
385 free(indexBufferData);
386
387 uint32_t coordSize = 3;
388 uint32_t uvSize = 2;
389 uint32_t vertsPerQuad = 4;
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700390 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
391 mTextMeshPtr = new float[vertexBufferSize];
Romain Guy694b5192010-07-21 21:33:20 -0700392}
393
394// We don't want to allocate anything unless we actually draw text
395void FontRenderer::checkInit() {
396 if (mInitialized) {
397 return;
398 }
399
400 initTextTexture();
401 initVertexArrayBuffers();
402
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700403 // We store a string with letters in a rough frequency of occurrence
404 mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
405 mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
406 mLatinPrecache += String16(",.?!()-+@;:`'");
407 mLatinPrecache += String16("0123456789");
408
Romain Guy694b5192010-07-21 21:33:20 -0700409 mInitialized = true;
410}
411
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700412void FontRenderer::checkTextureUpdate() {
413 if (!mUploadTexture) {
414 return;
Romain Guy694b5192010-07-21 21:33:20 -0700415 }
416
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700417 glBindTexture(GL_TEXTURE_2D, mTextureId);
418
419 // Iterate over all the cache lines and see which ones need to be updated
420 for (uint32_t i = 0; i < mCacheLines.size(); i++) {
421 CacheTextureLine* cl = mCacheLines[i];
422 if(cl->mDirty) {
423 uint32_t xOffset = 0;
424 uint32_t yOffset = cl->mCurrentRow;
425 uint32_t width = mCacheWidth;
426 uint32_t height = cl->mMaxHeight;
427 void* textureData = mTextTexture + yOffset*width;
428
429 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
430 GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
431
432 cl->mDirty = false;
433 }
434 }
435
436 mUploadTexture = false;
437}
438
439void FontRenderer::issueDrawCommand() {
440
441 checkTextureUpdate();
442
Romain Guy51769a62010-07-23 00:28:00 -0700443 float* vtx = mTextMeshPtr;
444 float* tex = vtx + 3;
Romain Guy694b5192010-07-21 21:33:20 -0700445
446 // position is slot 0
447 uint32_t slot = 0;
448 glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx);
449
450 // texture0 is slot 1
451 slot = 1;
452 glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex);
453
454 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
455 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
456}
457
458void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
459 float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
460 float x4, float y4, float z4, float u4, float v4) {
Romain Guy09147fb2010-07-22 13:08:20 -0700461 if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) {
462 return;
463 }
464
Romain Guy694b5192010-07-21 21:33:20 -0700465 const uint32_t vertsPerQuad = 4;
466 const uint32_t floatsPerVert = 5;
Romain Guy51769a62010-07-23 00:28:00 -0700467 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
Romain Guy694b5192010-07-21 21:33:20 -0700468
Romain Guy694b5192010-07-21 21:33:20 -0700469 (*currentPos++) = x1;
470 (*currentPos++) = y1;
471 (*currentPos++) = z1;
472 (*currentPos++) = u1;
473 (*currentPos++) = v1;
474
475 (*currentPos++) = x2;
476 (*currentPos++) = y2;
477 (*currentPos++) = z2;
478 (*currentPos++) = u2;
479 (*currentPos++) = v2;
480
481 (*currentPos++) = x3;
482 (*currentPos++) = y3;
483 (*currentPos++) = z3;
484 (*currentPos++) = u3;
485 (*currentPos++) = v3;
486
487 (*currentPos++) = x4;
488 (*currentPos++) = y4;
489 (*currentPos++) = z4;
490 (*currentPos++) = u4;
491 (*currentPos++) = v4;
492
493 mCurrentQuadIndex++;
494
495 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
496 issueDrawCommand();
497 mCurrentQuadIndex = 0;
498 }
499}
500
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700501uint32_t FontRenderer::getRemainingCacheCapacity() {
502 uint32_t remainingCapacity = 0;
503 float totalPixels = 0;
504 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
505 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
506 totalPixels += mCacheLines[i]->mMaxWidth;
507 }
508 remainingCapacity = (remainingCapacity * 100) / totalPixels;
509 return remainingCapacity;
510}
511
512void FontRenderer::precacheLatin(SkPaint* paint) {
513 // Remaining capacity is measured in %
514 uint32_t remainingCapacity = getRemainingCacheCapacity();
515 uint32_t precacheIdx = 0;
516 while(remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
517 mCurrentFont->getCachedUTFChar(paint, (int32_t)mLatinPrecache[precacheIdx]);
518 remainingCapacity = getRemainingCacheCapacity();
519 precacheIdx ++;
520 }
521}
522
523void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
524 uint32_t currentNumFonts = mActiveFonts.size();
Romain Guy694b5192010-07-21 21:33:20 -0700525 mCurrentFont = Font::create(this, fontId, fontSize);
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700526
527 const float maxPrecacheFontSize = 40.0f;
528 bool isNewFont = currentNumFonts != mActiveFonts.size();
529
530 if(isNewFont && fontSize <= maxPrecacheFontSize ){
531 precacheLatin(paint);
532 }
Romain Guy694b5192010-07-21 21:33:20 -0700533}
534
Romain Guy51769a62010-07-23 00:28:00 -0700535void FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
536 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y) {
Romain Guy694b5192010-07-21 21:33:20 -0700537 checkInit();
538
Romain Guy09147fb2010-07-22 13:08:20 -0700539 if (!mCurrentFont) {
540 LOGE("No font set");
Romain Guy694b5192010-07-21 21:33:20 -0700541 return;
542 }
543
Romain Guy09147fb2010-07-22 13:08:20 -0700544 mClip = clip;
Romain Guy51769a62010-07-23 00:28:00 -0700545 mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y);
Romain Guy694b5192010-07-21 21:33:20 -0700546
547 if (mCurrentQuadIndex != 0) {
548 issueDrawCommand();
549 mCurrentQuadIndex = 0;
550 }
551}
552
Romain Guy694b5192010-07-21 21:33:20 -0700553}; // namespace uirenderer
554}; // namespace android