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