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
30using namespace android;
31using namespace android::renderscript;
32
33Font::Font(Context *rsc) : ObjectBase(rsc), mCachedGlyphs(NULL) {
34    mInitialized = false;
35    mHasKerning = false;
36    mFace = NULL;
37}
38
39bool 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
75void 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
84void Font::invalidateTextureCache() {
85    for (uint32_t i = 0; i < mCachedGlyphs.size(); i ++) {
86        mCachedGlyphs.valueAt(i)->mIsValid = false;
87    }
88}
89
90void 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
110void 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
136void 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
158void 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
222Font::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
236void 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
281Font::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
293Font * 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
317Font::~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
330FontState::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
369FontState::~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
377FT_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
391void FontState::init(Context *rsc) {
392    mRSC = rsc;
393}
394
395void 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
409bool 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
484void 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
527void 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
556void 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
606void 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
625void 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
645void 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
702uint32_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
713void 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
725void 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
758void 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
764void 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
781void 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
788void 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
815bool 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
832namespace android {
833namespace renderscript {
834
835RsFont 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
845RsFont 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