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