1
2 /*
3 * Copyright (C) 2009 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 #include "rsContext.h"
19
20 #include "rsFont.h"
21 #include "rsProgramFragment.h"
22 #include <cutils/properties.h>
23
24 #ifndef ANDROID_RS_SERIALIZE
25 #include <ft2build.h>
26 #include FT_FREETYPE_H
27 #include FT_BITMAP_H
28 #endif //ANDROID_RS_SERIALIZE
29
30 using namespace android;
31 using namespace android::renderscript;
32
Font(Context * rsc)33 Font::Font(Context *rsc) : ObjectBase(rsc), mCachedGlyphs(NULL) {
34 mInitialized = false;
35 mHasKerning = false;
36 mFace = NULL;
37 }
38
init(const char * name,float fontSize,uint32_t dpi,const void * data,uint32_t dataLen)39 bool Font::init(const char *name, float fontSize, uint32_t dpi, const void *data, uint32_t dataLen) {
40 #ifndef ANDROID_RS_SERIALIZE
41 if (mInitialized) {
42 LOGE("Reinitialization of fonts not supported");
43 return false;
44 }
45
46 FT_Error error = 0;
47 if (data != NULL && dataLen > 0) {
48 error = FT_New_Memory_Face(mRSC->mStateFont.getLib(), (const FT_Byte*)data, dataLen, 0, &mFace);
49 } else {
50 error = FT_New_Face(mRSC->mStateFont.getLib(), name, 0, &mFace);
51 }
52
53 if (error) {
54 LOGE("Unable to initialize font %s", name);
55 return false;
56 }
57
58 mFontName = name;
59 mFontSize = fontSize;
60 mDpi = dpi;
61
62 error = FT_Set_Char_Size(mFace, (FT_F26Dot6)(fontSize * 64.0f), 0, dpi, 0);
63 if (error) {
64 LOGE("Unable to set font size on %s", name);
65 return false;
66 }
67
68 mHasKerning = FT_HAS_KERNING(mFace);
69
70 mInitialized = true;
71 #endif //ANDROID_RS_SERIALIZE
72 return true;
73 }
74
preDestroy() const75 void Font::preDestroy() const {
76 for (uint32_t ct = 0; ct < mRSC->mStateFont.mActiveFonts.size(); ct++) {
77 if (mRSC->mStateFont.mActiveFonts[ct] == this) {
78 mRSC->mStateFont.mActiveFonts.removeAt(ct);
79 break;
80 }
81 }
82 }
83
invalidateTextureCache()84 void Font::invalidateTextureCache() {
85 for (uint32_t i = 0; i < mCachedGlyphs.size(); i ++) {
86 mCachedGlyphs.valueAt(i)->mIsValid = false;
87 }
88 }
89
drawCachedGlyph(CachedGlyphInfo * glyph,int32_t x,int32_t y)90 void Font::drawCachedGlyph(CachedGlyphInfo *glyph, int32_t x, int32_t y) {
91 FontState *state = &mRSC->mStateFont;
92
93 int32_t nPenX = x + glyph->mBitmapLeft;
94 int32_t nPenY = y - glyph->mBitmapTop + glyph->mBitmapHeight;
95
96 float u1 = glyph->mBitmapMinU;
97 float u2 = glyph->mBitmapMaxU;
98 float v1 = glyph->mBitmapMinV;
99 float v2 = glyph->mBitmapMaxV;
100
101 int32_t width = (int32_t) glyph->mBitmapWidth;
102 int32_t height = (int32_t) glyph->mBitmapHeight;
103
104 state->appendMeshQuad(nPenX, nPenY, 0, u1, v2,
105 nPenX + width, nPenY, 0, u2, v2,
106 nPenX + width, nPenY - height, 0, u2, v1,
107 nPenX, nPenY - height, 0, u1, v1);
108 }
109
drawCachedGlyph(CachedGlyphInfo * glyph,int32_t x,int32_t y,uint8_t * bitmap,uint32_t bitmapW,uint32_t bitmapH)110 void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int32_t x, int32_t y,
111 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) {
112 int32_t nPenX = x + glyph->mBitmapLeft;
113 int32_t nPenY = y + glyph->mBitmapTop;
114
115 uint32_t endX = glyph->mBitmapMinX + glyph->mBitmapWidth;
116 uint32_t endY = glyph->mBitmapMinY + glyph->mBitmapHeight;
117
118 FontState *state = &mRSC->mStateFont;
119 uint32_t cacheWidth = state->getCacheTextureType()->getDimX();
120 const uint8_t* cacheBuffer = state->getTextTextureData();
121
122 uint32_t cacheX = 0, cacheY = 0;
123 int32_t bX = 0, bY = 0;
124 for (cacheX = glyph->mBitmapMinX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
125 for (cacheY = glyph->mBitmapMinY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
126 if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
127 LOGE("Skipping invalid index");
128 continue;
129 }
130 uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
131 bitmap[bY * bitmapW + bX] = tempCol;
132 }
133 }
134 }
135
measureCachedGlyph(CachedGlyphInfo * glyph,int32_t x,int32_t y,Rect * bounds)136 void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int32_t x, int32_t y, Rect *bounds) {
137 int32_t nPenX = x + glyph->mBitmapLeft;
138 int32_t nPenY = y - glyph->mBitmapTop + glyph->mBitmapHeight;
139
140 int32_t width = (int32_t) glyph->mBitmapWidth;
141 int32_t height = (int32_t) glyph->mBitmapHeight;
142
143 // 0, 0 is top left, so bottom is a positive number
144 if (bounds->bottom < nPenY) {
145 bounds->bottom = nPenY;
146 }
147 if (bounds->left > nPenX) {
148 bounds->left = nPenX;
149 }
150 if (bounds->right < nPenX + width) {
151 bounds->right = nPenX + width;
152 }
153 if (bounds->top > nPenY - height) {
154 bounds->top = nPenY - height;
155 }
156 }
157
renderUTF(const char * text,uint32_t len,int32_t x,int32_t y,uint32_t start,int32_t numGlyphs,RenderMode mode,Rect * bounds,uint8_t * bitmap,uint32_t bitmapW,uint32_t bitmapH)158 void Font::renderUTF(const char *text, uint32_t len, int32_t x, int32_t y,
159 uint32_t start, int32_t numGlyphs,
160 RenderMode mode, Rect *bounds,
161 uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
162 if (!mInitialized || numGlyphs == 0 || text == NULL || len == 0) {
163 return;
164 }
165
166 if (mode == Font::MEASURE) {
167 if (bounds == NULL) {
168 LOGE("No return rectangle provided to measure text");
169 return;
170 }
171 // Reset min and max of the bounding box to something large
172 bounds->set(1e6, -1e6, 1e6, -1e6);
173 }
174
175 int32_t penX = x, penY = y;
176 int32_t glyphsLeft = 1;
177 if (numGlyphs > 0) {
178 glyphsLeft = numGlyphs;
179 }
180
181 size_t index = start;
182 size_t nextIndex = 0;
183
184 while (glyphsLeft > 0) {
185
186 int32_t utfChar = utf32_from_utf8_at(text, len, index, &nextIndex);
187
188 // Reached the end of the string or encountered
189 if (utfChar < 0) {
190 break;
191 }
192
193 // Move to the next character in the array
194 index = nextIndex;
195
196 CachedGlyphInfo *cachedGlyph = getCachedUTFChar(utfChar);
197
198 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
199 if (cachedGlyph->mIsValid) {
200 switch (mode) {
201 case FRAMEBUFFER:
202 drawCachedGlyph(cachedGlyph, penX, penY);
203 break;
204 case BITMAP:
205 drawCachedGlyph(cachedGlyph, penX, penY, bitmap, bitmapW, bitmapH);
206 break;
207 case MEASURE:
208 measureCachedGlyph(cachedGlyph, penX, penY, bounds);
209 break;
210 }
211 }
212
213 penX += (cachedGlyph->mAdvanceX >> 6);
214
215 // If we were given a specific number of glyphs, decrement
216 if (numGlyphs > 0) {
217 glyphsLeft --;
218 }
219 }
220 }
221
getCachedUTFChar(int32_t utfChar)222 Font::CachedGlyphInfo* Font::getCachedUTFChar(int32_t utfChar) {
223
224 CachedGlyphInfo *cachedGlyph = mCachedGlyphs.valueFor((uint32_t)utfChar);
225 if (cachedGlyph == NULL) {
226 cachedGlyph = cacheGlyph((uint32_t)utfChar);
227 }
228 // Is the glyph still in texture cache?
229 if (!cachedGlyph->mIsValid) {
230 updateGlyphCache(cachedGlyph);
231 }
232
233 return cachedGlyph;
234 }
235
updateGlyphCache(CachedGlyphInfo * glyph)236 void Font::updateGlyphCache(CachedGlyphInfo *glyph) {
237 #ifndef ANDROID_RS_SERIALIZE
238 FT_Error error = FT_Load_Glyph( mFace, glyph->mGlyphIndex, FT_LOAD_RENDER );
239 if (error) {
240 LOGE("Couldn't load glyph.");
241 return;
242 }
243
244 glyph->mAdvanceX = mFace->glyph->advance.x;
245 glyph->mAdvanceY = mFace->glyph->advance.y;
246 glyph->mBitmapLeft = mFace->glyph->bitmap_left;
247 glyph->mBitmapTop = mFace->glyph->bitmap_top;
248
249 FT_Bitmap *bitmap = &mFace->glyph->bitmap;
250
251 // Now copy the bitmap into the cache texture
252 uint32_t startX = 0;
253 uint32_t startY = 0;
254
255 // Let the font state figure out where to put the bitmap
256 FontState *state = &mRSC->mStateFont;
257 glyph->mIsValid = state->cacheBitmap(bitmap, &startX, &startY);
258
259 if (!glyph->mIsValid) {
260 return;
261 }
262
263 uint32_t endX = startX + bitmap->width;
264 uint32_t endY = startY + bitmap->rows;
265
266 glyph->mBitmapMinX = startX;
267 glyph->mBitmapMinY = startY;
268 glyph->mBitmapWidth = bitmap->width;
269 glyph->mBitmapHeight = bitmap->rows;
270
271 uint32_t cacheWidth = state->getCacheTextureType()->getDimX();
272 uint32_t cacheHeight = state->getCacheTextureType()->getDimY();
273
274 glyph->mBitmapMinU = (float)startX / (float)cacheWidth;
275 glyph->mBitmapMinV = (float)startY / (float)cacheHeight;
276 glyph->mBitmapMaxU = (float)endX / (float)cacheWidth;
277 glyph->mBitmapMaxV = (float)endY / (float)cacheHeight;
278 #endif //ANDROID_RS_SERIALIZE
279 }
280
cacheGlyph(uint32_t glyph)281 Font::CachedGlyphInfo *Font::cacheGlyph(uint32_t glyph) {
282 CachedGlyphInfo *newGlyph = new CachedGlyphInfo();
283 mCachedGlyphs.add(glyph, newGlyph);
284 #ifndef ANDROID_RS_SERIALIZE
285 newGlyph->mGlyphIndex = FT_Get_Char_Index(mFace, glyph);
286 newGlyph->mIsValid = false;
287 #endif //ANDROID_RS_SERIALIZE
288 updateGlyphCache(newGlyph);
289
290 return newGlyph;
291 }
292
create(Context * rsc,const char * name,float fontSize,uint32_t dpi,const void * data,uint32_t dataLen)293 Font * Font::create(Context *rsc, const char *name, float fontSize, uint32_t dpi,
294 const void *data, uint32_t dataLen) {
295 rsc->mStateFont.checkInit();
296 Vector<Font*> &activeFonts = rsc->mStateFont.mActiveFonts;
297
298 for (uint32_t i = 0; i < activeFonts.size(); i ++) {
299 Font *ithFont = activeFonts[i];
300 if (ithFont->mFontName == name && ithFont->mFontSize == fontSize && ithFont->mDpi == dpi) {
301 return ithFont;
302 }
303 }
304
305 Font *newFont = new Font(rsc);
306 bool isInitialized = newFont->init(name, fontSize, dpi, data, dataLen);
307 if (isInitialized) {
308 activeFonts.push(newFont);
309 rsc->mStateFont.precacheLatin(newFont);
310 return newFont;
311 }
312
313 ObjectBase::checkDelete(newFont);
314 return NULL;
315 }
316
~Font()317 Font::~Font() {
318 #ifndef ANDROID_RS_SERIALIZE
319 if (mFace) {
320 FT_Done_Face(mFace);
321 }
322 #endif
323
324 for (uint32_t i = 0; i < mCachedGlyphs.size(); i ++) {
325 CachedGlyphInfo *glyph = mCachedGlyphs.valueAt(i);
326 delete glyph;
327 }
328 }
329
FontState()330 FontState::FontState() {
331 mInitialized = false;
332 mMaxNumberOfQuads = 1024;
333 mCurrentQuadIndex = 0;
334 mRSC = NULL;
335 #ifndef ANDROID_RS_SERIALIZE
336 mLibrary = NULL;
337 #endif //ANDROID_RS_SERIALIZE
338
339 // Get the renderer properties
340 char property[PROPERTY_VALUE_MAX];
341
342 // Get the gamma
343 float gamma = DEFAULT_TEXT_GAMMA;
344 if (property_get(PROPERTY_TEXT_GAMMA, property, NULL) > 0) {
345 gamma = atof(property);
346 }
347
348 // Get the black gamma threshold
349 int32_t blackThreshold = DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD;
350 if (property_get(PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD, property, NULL) > 0) {
351 blackThreshold = atoi(property);
352 }
353 mBlackThreshold = (float)(blackThreshold) / 255.0f;
354
355 // Get the white gamma threshold
356 int32_t whiteThreshold = DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD;
357 if (property_get(PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD, property, NULL) > 0) {
358 whiteThreshold = atoi(property);
359 }
360 mWhiteThreshold = (float)(whiteThreshold) / 255.0f;
361
362 // Compute the gamma tables
363 mBlackGamma = gamma;
364 mWhiteGamma = 1.0f / gamma;
365
366 setFontColor(0.1f, 0.1f, 0.1f, 1.0f);
367 }
368
~FontState()369 FontState::~FontState() {
370 for (uint32_t i = 0; i < mCacheLines.size(); i ++) {
371 delete mCacheLines[i];
372 }
373
374 rsAssert(!mActiveFonts.size());
375 }
376 #ifndef ANDROID_RS_SERIALIZE
getLib()377 FT_Library FontState::getLib() {
378 if (!mLibrary) {
379 FT_Error error = FT_Init_FreeType(&mLibrary);
380 if (error) {
381 LOGE("Unable to initialize freetype");
382 return NULL;
383 }
384 }
385
386 return mLibrary;
387 }
388 #endif //ANDROID_RS_SERIALIZE
389
390
init(Context * rsc)391 void FontState::init(Context *rsc) {
392 mRSC = rsc;
393 }
394
flushAllAndInvalidate()395 void FontState::flushAllAndInvalidate() {
396 if (mCurrentQuadIndex != 0) {
397 issueDrawCommand();
398 mCurrentQuadIndex = 0;
399 }
400 for (uint32_t i = 0; i < mActiveFonts.size(); i ++) {
401 mActiveFonts[i]->invalidateTextureCache();
402 }
403 for (uint32_t i = 0; i < mCacheLines.size(); i ++) {
404 mCacheLines[i]->mCurrentCol = 0;
405 }
406 }
407
408 #ifndef ANDROID_RS_SERIALIZE
cacheBitmap(FT_Bitmap * bitmap,uint32_t * retOriginX,uint32_t * retOriginY)409 bool FontState::cacheBitmap(FT_Bitmap *bitmap, uint32_t *retOriginX, uint32_t *retOriginY) {
410 // If the glyph is too tall, don't cache it
411 if ((uint32_t)bitmap->rows > mCacheLines[mCacheLines.size()-1]->mMaxHeight) {
412 LOGE("Font size to large to fit in cache. width, height = %i, %i", (int)bitmap->width, (int)bitmap->rows);
413 return false;
414 }
415
416 // Now copy the bitmap into the cache texture
417 uint32_t startX = 0;
418 uint32_t startY = 0;
419
420 bool bitmapFit = false;
421 for (uint32_t i = 0; i < mCacheLines.size(); i ++) {
422 bitmapFit = mCacheLines[i]->fitBitmap(bitmap, &startX, &startY);
423 if (bitmapFit) {
424 break;
425 }
426 }
427
428 // If the new glyph didn't fit, flush the state so far and invalidate everything
429 if (!bitmapFit) {
430 flushAllAndInvalidate();
431
432 // Try to fit it again
433 for (uint32_t i = 0; i < mCacheLines.size(); i ++) {
434 bitmapFit = mCacheLines[i]->fitBitmap(bitmap, &startX, &startY);
435 if (bitmapFit) {
436 break;
437 }
438 }
439
440 // if we still don't fit, something is wrong and we shouldn't draw
441 if (!bitmapFit) {
442 LOGE("Bitmap doesn't fit in cache. width, height = %i, %i", (int)bitmap->width, (int)bitmap->rows);
443 return false;
444 }
445 }
446
447 *retOriginX = startX;
448 *retOriginY = startY;
449
450 uint32_t endX = startX + bitmap->width;
451 uint32_t endY = startY + bitmap->rows;
452
453 uint32_t cacheWidth = getCacheTextureType()->getDimX();
454
455 uint8_t *cacheBuffer = (uint8_t*)mTextTexture->getPtr();
456 uint8_t *bitmapBuffer = bitmap->buffer;
457
458 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
459 for (cacheX = startX, bX = 0; cacheX < endX; cacheX ++, bX ++) {
460 for (cacheY = startY, bY = 0; cacheY < endY; cacheY ++, bY ++) {
461 uint8_t tempCol = bitmapBuffer[bY * bitmap->width + bX];
462 cacheBuffer[cacheY*cacheWidth + cacheX] = tempCol;
463 }
464 }
465
466 // This will dirty the texture and the shader so next time
467 // we draw it will upload the data
468
469 mTextTexture->sendDirty(mRSC);
470 mFontShaderF->bindTexture(mRSC, 0, mTextTexture.get());
471
472 // Some debug code
473 /*for (uint32_t i = 0; i < mCacheLines.size(); i ++) {
474 LOGE("Cache Line: H: %u Empty Space: %f",
475 mCacheLines[i]->mMaxHeight,
476 (1.0f - (float)mCacheLines[i]->mCurrentCol/(float)mCacheLines[i]->mMaxWidth)*100.0f);
477
478 }*/
479
480 return true;
481 }
482 #endif //ANDROID_RS_SERIALIZE
483
initRenderState()484 void FontState::initRenderState() {
485 String8 shaderString("varying vec2 varTex0;\n");
486 shaderString.append("void main() {\n");
487 shaderString.append(" lowp vec4 col = UNI_Color;\n");
488 shaderString.append(" col.a = texture2D(UNI_Tex0, varTex0.xy).a;\n");
489 shaderString.append(" col.a = pow(col.a, UNI_Gamma);\n");
490 shaderString.append(" gl_FragColor = col;\n");
491 shaderString.append("}\n");
492
493 ObjectBaseRef<const Element> colorElem = Element::createRef(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 4);
494 ObjectBaseRef<const Element> gammaElem = Element::createRef(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 1);
495 Element::Builder builder;
496 builder.add(colorElem.get(), "Color", 1);
497 builder.add(gammaElem.get(), "Gamma", 1);
498 ObjectBaseRef<const Element> constInput = builder.create(mRSC);
499
500 ObjectBaseRef<Type> inputType = Type::getTypeRef(mRSC, constInput.get(), 1, 0, 0, false, false);
501
502 uint32_t tmp[4];
503 tmp[0] = RS_PROGRAM_PARAM_CONSTANT;
504 tmp[1] = (uint32_t)inputType.get();
505 tmp[2] = RS_PROGRAM_PARAM_TEXTURE_TYPE;
506 tmp[3] = RS_TEXTURE_2D;
507
508 mFontShaderFConstant.set(Allocation::createAllocation(mRSC, inputType.get(),
509 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_GRAPHICS_CONSTANTS));
510 ProgramFragment *pf = new ProgramFragment(mRSC, shaderString.string(),
511 shaderString.length(), tmp, 4);
512 mFontShaderF.set(pf);
513 mFontShaderF->bindAllocation(mRSC, mFontShaderFConstant.get(), 0);
514
515 mFontSampler.set(Sampler::getSampler(mRSC, RS_SAMPLER_NEAREST, RS_SAMPLER_NEAREST,
516 RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP).get());
517 mFontShaderF->bindSampler(mRSC, 0, mFontSampler.get());
518
519 mFontProgramStore.set(ProgramStore::getProgramStore(mRSC, true, true, true, true,
520 false, false,
521 RS_BLEND_SRC_SRC_ALPHA,
522 RS_BLEND_DST_ONE_MINUS_SRC_ALPHA,
523 RS_DEPTH_FUNC_ALWAYS).get());
524 mFontProgramStore->init();
525 }
526
initTextTexture()527 void FontState::initTextTexture() {
528 ObjectBaseRef<const Element> alphaElem = Element::createRef(mRSC, RS_TYPE_UNSIGNED_8, RS_KIND_PIXEL_A, true, 1);
529
530 // We will allocate a texture to initially hold 32 character bitmaps
531 ObjectBaseRef<Type> texType = Type::getTypeRef(mRSC, alphaElem.get(), 1024, 256, 0, false, false);
532
533 Allocation *cacheAlloc = Allocation::createAllocation(mRSC, texType.get(),
534 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_GRAPHICS_TEXTURE);
535 mTextTexture.set(cacheAlloc);
536 mTextTexture->syncAll(mRSC, RS_ALLOCATION_USAGE_SCRIPT);
537
538 // Split up our cache texture into lines of certain widths
539 int32_t nextLine = 0;
540 mCacheLines.push(new CacheTextureLine(16, texType->getDimX(), nextLine, 0));
541 nextLine += mCacheLines.top()->mMaxHeight;
542 mCacheLines.push(new CacheTextureLine(24, texType->getDimX(), nextLine, 0));
543 nextLine += mCacheLines.top()->mMaxHeight;
544 mCacheLines.push(new CacheTextureLine(24, texType->getDimX(), nextLine, 0));
545 nextLine += mCacheLines.top()->mMaxHeight;
546 mCacheLines.push(new CacheTextureLine(32, texType->getDimX(), nextLine, 0));
547 nextLine += mCacheLines.top()->mMaxHeight;
548 mCacheLines.push(new CacheTextureLine(32, texType->getDimX(), nextLine, 0));
549 nextLine += mCacheLines.top()->mMaxHeight;
550 mCacheLines.push(new CacheTextureLine(40, texType->getDimX(), nextLine, 0));
551 nextLine += mCacheLines.top()->mMaxHeight;
552 mCacheLines.push(new CacheTextureLine(texType->getDimY() - nextLine, texType->getDimX(), nextLine, 0));
553 }
554
555 // Avoid having to reallocate memory and render quad by quad
initVertexArrayBuffers()556 void FontState::initVertexArrayBuffers() {
557 // Now lets write index data
558 ObjectBaseRef<const Element> indexElem = Element::createRef(mRSC, RS_TYPE_UNSIGNED_16, RS_KIND_USER, false, 1);
559 uint32_t numIndicies = mMaxNumberOfQuads * 6;
560 ObjectBaseRef<Type> indexType = Type::getTypeRef(mRSC, indexElem.get(), numIndicies, 0, 0, false, false);
561
562 Allocation *indexAlloc = Allocation::createAllocation(mRSC, indexType.get(),
563 RS_ALLOCATION_USAGE_SCRIPT |
564 RS_ALLOCATION_USAGE_GRAPHICS_VERTEX);
565 uint16_t *indexPtr = (uint16_t*)indexAlloc->getPtr();
566
567 // Four verts, two triangles , six indices per quad
568 for (uint32_t i = 0; i < mMaxNumberOfQuads; i ++) {
569 int32_t i6 = i * 6;
570 int32_t i4 = i * 4;
571
572 indexPtr[i6 + 0] = i4 + 0;
573 indexPtr[i6 + 1] = i4 + 1;
574 indexPtr[i6 + 2] = i4 + 2;
575
576 indexPtr[i6 + 3] = i4 + 0;
577 indexPtr[i6 + 4] = i4 + 2;
578 indexPtr[i6 + 5] = i4 + 3;
579 }
580
581 indexAlloc->sendDirty(mRSC);
582
583 ObjectBaseRef<const Element> posElem = Element::createRef(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 3);
584 ObjectBaseRef<const Element> texElem = Element::createRef(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 2);
585
586 Element::Builder builder;
587 builder.add(posElem.get(), "position", 1);
588 builder.add(texElem.get(), "texture0", 1);
589 ObjectBaseRef<const Element> vertexDataElem = builder.create(mRSC);
590
591 ObjectBaseRef<Type> vertexDataType = Type::getTypeRef(mRSC, vertexDataElem.get(),
592 mMaxNumberOfQuads * 4,
593 0, 0, false, false);
594
595 Allocation *vertexAlloc = Allocation::createAllocation(mRSC, vertexDataType.get(),
596 RS_ALLOCATION_USAGE_SCRIPT);
597 mTextMeshPtr = (float*)vertexAlloc->getPtr();
598
599 mMesh.set(new Mesh(mRSC, 1, 1));
600 mMesh->setVertexBuffer(vertexAlloc, 0);
601 mMesh->setPrimitive(indexAlloc, RS_PRIMITIVE_TRIANGLE, 0);
602 mMesh->init();
603 }
604
605 // We don't want to allocate anything unless we actually draw text
checkInit()606 void FontState::checkInit() {
607 if (mInitialized) {
608 return;
609 }
610
611 initTextTexture();
612 initRenderState();
613
614 initVertexArrayBuffers();
615
616 // We store a string with letters in a rough frequency of occurrence
617 mLatinPrecache = String8(" eisarntolcdugpmhbyfvkwzxjq");
618 mLatinPrecache += String8("EISARNTOLCDUGPMHBYFVKWZXJQ");
619 mLatinPrecache += String8(",.?!()-+@;:`'");
620 mLatinPrecache += String8("0123456789");
621
622 mInitialized = true;
623 }
624
issueDrawCommand()625 void FontState::issueDrawCommand() {
626 Context::PushState ps(mRSC);
627
628 mRSC->setProgramVertex(mRSC->getDefaultProgramVertex());
629 mRSC->setProgramRaster(mRSC->getDefaultProgramRaster());
630 mRSC->setProgramFragment(mFontShaderF.get());
631 mRSC->setProgramStore(mFontProgramStore.get());
632
633 if (mConstantsDirty) {
634 mFontShaderFConstant->data(mRSC, 0, 0, 1, &mConstants, sizeof(mConstants));
635 mConstantsDirty = false;
636 }
637
638 if (!mRSC->setupCheck()) {
639 return;
640 }
641
642 mMesh->renderPrimitiveRange(mRSC, 0, 0, mCurrentQuadIndex * 6);
643 }
644
appendMeshQuad(float x1,float y1,float z1,float u1,float v1,float x2,float y2,float z2,float u2,float v2,float x3,float y3,float z3,float u3,float v3,float x4,float y4,float z4,float u4,float v4)645 void FontState::appendMeshQuad(float x1, float y1, float z1,
646 float u1, float v1,
647 float x2, float y2, float z2,
648 float u2, float v2,
649 float x3, float y3, float z3,
650 float u3, float v3,
651 float x4, float y4, float z4,
652 float u4, float v4) {
653 const uint32_t vertsPerQuad = 4;
654 const uint32_t floatsPerVert = 5;
655 float *currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
656
657 // Cull things that are off the screen
658 float width = (float)mRSC->getWidth();
659 float height = (float)mRSC->getHeight();
660
661 if (x1 > width || y1 < 0.0f || x2 < 0 || y4 > height) {
662 return;
663 }
664
665 /*LOGE("V0 x: %f y: %f z: %f", x1, y1, z1);
666 LOGE("V1 x: %f y: %f z: %f", x2, y2, z2);
667 LOGE("V2 x: %f y: %f z: %f", x3, y3, z3);
668 LOGE("V3 x: %f y: %f z: %f", x4, y4, z4);*/
669
670 (*currentPos++) = x1;
671 (*currentPos++) = y1;
672 (*currentPos++) = z1;
673 (*currentPos++) = u1;
674 (*currentPos++) = v1;
675
676 (*currentPos++) = x2;
677 (*currentPos++) = y2;
678 (*currentPos++) = z2;
679 (*currentPos++) = u2;
680 (*currentPos++) = v2;
681
682 (*currentPos++) = x3;
683 (*currentPos++) = y3;
684 (*currentPos++) = z3;
685 (*currentPos++) = u3;
686 (*currentPos++) = v3;
687
688 (*currentPos++) = x4;
689 (*currentPos++) = y4;
690 (*currentPos++) = z4;
691 (*currentPos++) = u4;
692 (*currentPos++) = v4;
693
694 mCurrentQuadIndex ++;
695
696 if (mCurrentQuadIndex == mMaxNumberOfQuads) {
697 issueDrawCommand();
698 mCurrentQuadIndex = 0;
699 }
700 }
701
getRemainingCacheCapacity()702 uint32_t FontState::getRemainingCacheCapacity() {
703 uint32_t remainingCapacity = 0;
704 uint32_t totalPixels = 0;
705 for (uint32_t i = 0; i < mCacheLines.size(); i ++) {
706 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
707 totalPixels += mCacheLines[i]->mMaxWidth;
708 }
709 remainingCapacity = (remainingCapacity * 100) / totalPixels;
710 return remainingCapacity;
711 }
712
precacheLatin(Font * font)713 void FontState::precacheLatin(Font *font) {
714 // Remaining capacity is measured in %
715 uint32_t remainingCapacity = getRemainingCacheCapacity();
716 uint32_t precacheIdx = 0;
717 while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
718 font->getCachedUTFChar((int32_t)mLatinPrecache[precacheIdx]);
719 remainingCapacity = getRemainingCacheCapacity();
720 precacheIdx ++;
721 }
722 }
723
724
renderText(const char * text,uint32_t len,int32_t x,int32_t y,uint32_t startIndex,int32_t numGlyphs,Font::RenderMode mode,Font::Rect * bounds,uint8_t * bitmap,uint32_t bitmapW,uint32_t bitmapH)725 void FontState::renderText(const char *text, uint32_t len, int32_t x, int32_t y,
726 uint32_t startIndex, int32_t numGlyphs,
727 Font::RenderMode mode,
728 Font::Rect *bounds,
729 uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
730 checkInit();
731
732 // Render code here
733 Font *currentFont = mRSC->getFont();
734 if (!currentFont) {
735 if (!mDefault.get()) {
736 String8 fontsDir("/fonts/Roboto-Regular.ttf");
737 String8 fullPath(getenv("ANDROID_ROOT"));
738 fullPath += fontsDir;
739
740 mDefault.set(Font::create(mRSC, fullPath.string(), 8, mRSC->getDPI()));
741 }
742 currentFont = mDefault.get();
743 }
744 if (!currentFont) {
745 LOGE("Unable to initialize any fonts");
746 return;
747 }
748
749 currentFont->renderUTF(text, len, x, y, startIndex, numGlyphs,
750 mode, bounds, bitmap, bitmapW, bitmapH);
751
752 if (mCurrentQuadIndex != 0) {
753 issueDrawCommand();
754 mCurrentQuadIndex = 0;
755 }
756 }
757
measureText(const char * text,uint32_t len,Font::Rect * bounds)758 void FontState::measureText(const char *text, uint32_t len, Font::Rect *bounds) {
759 renderText(text, len, 0, 0, 0, -1, Font::MEASURE, bounds);
760 bounds->bottom = - bounds->bottom;
761 bounds->top = - bounds->top;
762 }
763
setFontColor(float r,float g,float b,float a)764 void FontState::setFontColor(float r, float g, float b, float a) {
765 mConstants.mFontColor[0] = r;
766 mConstants.mFontColor[1] = g;
767 mConstants.mFontColor[2] = b;
768 mConstants.mFontColor[3] = a;
769
770 mConstants.mGamma = 1.0f;
771 const float luminance = (r * 2.0f + g * 5.0f + b) / 8.0f;
772 if (luminance <= mBlackThreshold) {
773 mConstants.mGamma = mBlackGamma;
774 } else if (luminance >= mWhiteThreshold) {
775 mConstants.mGamma = mWhiteGamma;
776 }
777
778 mConstantsDirty = true;
779 }
780
getFontColor(float * r,float * g,float * b,float * a) const781 void FontState::getFontColor(float *r, float *g, float *b, float *a) const {
782 *r = mConstants.mFontColor[0];
783 *g = mConstants.mFontColor[1];
784 *b = mConstants.mFontColor[2];
785 *a = mConstants.mFontColor[3];
786 }
787
deinit(Context * rsc)788 void FontState::deinit(Context *rsc) {
789 mInitialized = false;
790
791 mFontShaderFConstant.clear();
792
793 mMesh.clear();
794
795 mFontShaderF.clear();
796 mFontSampler.clear();
797 mFontProgramStore.clear();
798
799 mTextTexture.clear();
800 for (uint32_t i = 0; i < mCacheLines.size(); i ++) {
801 delete mCacheLines[i];
802 }
803 mCacheLines.clear();
804
805 mDefault.clear();
806 #ifndef ANDROID_RS_SERIALIZE
807 if (mLibrary) {
808 FT_Done_FreeType( mLibrary );
809 mLibrary = NULL;
810 }
811 #endif //ANDROID_RS_SERIALIZE
812 }
813
814 #ifndef ANDROID_RS_SERIALIZE
fitBitmap(FT_Bitmap_ * bitmap,uint32_t * retOriginX,uint32_t * retOriginY)815 bool FontState::CacheTextureLine::fitBitmap(FT_Bitmap_ *bitmap, uint32_t *retOriginX, uint32_t *retOriginY) {
816 if ((uint32_t)bitmap->rows > mMaxHeight) {
817 return false;
818 }
819
820 if (mCurrentCol + (uint32_t)bitmap->width < mMaxWidth) {
821 *retOriginX = mCurrentCol;
822 *retOriginY = mCurrentRow;
823 mCurrentCol += bitmap->width;
824 mDirty = true;
825 return true;
826 }
827
828 return false;
829 }
830 #endif //ANDROID_RS_SERIALIZE
831
832 namespace android {
833 namespace renderscript {
834
rsi_FontCreateFromFile(Context * rsc,char const * name,size_t name_length,float fontSize,uint32_t dpi)835 RsFont rsi_FontCreateFromFile(Context *rsc,
836 char const *name, size_t name_length,
837 float fontSize, uint32_t dpi) {
838 Font *newFont = Font::create(rsc, name, fontSize, dpi);
839 if (newFont) {
840 newFont->incUserRef();
841 }
842 return newFont;
843 }
844
rsi_FontCreateFromMemory(Context * rsc,char const * name,size_t name_length,float fontSize,uint32_t dpi,const void * data,size_t data_length)845 RsFont rsi_FontCreateFromMemory(Context *rsc,
846 char const *name, size_t name_length,
847 float fontSize, uint32_t dpi,
848 const void *data, size_t data_length) {
849 Font *newFont = Font::create(rsc, name, fontSize, dpi, data, data_length);
850 if (newFont) {
851 newFont->incUserRef();
852 }
853 return newFont;
854 }
855
856 } // renderscript
857 } // android
858