rsFont.cpp revision e23d239828a229eb7d4d33c9630070f0a87833e1
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 <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        ALOGE("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        ALOGE("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        ALOGE("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                ALOGE("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            ALOGE("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        ALOGE("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            ALOGE("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        ALOGE("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            ALOGE("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        ALOGE("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    const char *textureNames[] = { "Tex0" };
494    const size_t textureNamesLengths[] = { 4 };
495    size_t numTextures = sizeof(textureNamesLengths)/sizeof(*textureNamesLengths);
496
497    ObjectBaseRef<const Element> colorElem = Element::createRef(mRSC, RS_TYPE_FLOAT_32,
498                                                                RS_KIND_USER, false, 4);
499    ObjectBaseRef<const Element> gammaElem = Element::createRef(mRSC, RS_TYPE_FLOAT_32,
500                                                                RS_KIND_USER, false, 1);
501    Element::Builder builder;
502    builder.add(colorElem.get(), "Color", 1);
503    builder.add(gammaElem.get(), "Gamma", 1);
504    ObjectBaseRef<const Element> constInput = builder.create(mRSC);
505
506    ObjectBaseRef<Type> inputType = Type::getTypeRef(mRSC, constInput.get(), 1, 0, 0, false, false);
507
508    uint32_t tmp[4];
509    tmp[0] = RS_PROGRAM_PARAM_CONSTANT;
510    tmp[1] = (uint32_t)inputType.get();
511    tmp[2] = RS_PROGRAM_PARAM_TEXTURE_TYPE;
512    tmp[3] = RS_TEXTURE_2D;
513
514    mFontShaderFConstant.set(Allocation::createAllocation(mRSC, inputType.get(),
515                                                          RS_ALLOCATION_USAGE_SCRIPT |
516                                                          RS_ALLOCATION_USAGE_GRAPHICS_CONSTANTS));
517    ProgramFragment *pf = new ProgramFragment(mRSC, shaderString.string(), shaderString.length(),
518                                              textureNames, numTextures, textureNamesLengths,
519                                              tmp, 4);
520    mFontShaderF.set(pf);
521    mFontShaderF->bindAllocation(mRSC, mFontShaderFConstant.get(), 0);
522
523    mFontSampler.set(Sampler::getSampler(mRSC, RS_SAMPLER_NEAREST, RS_SAMPLER_NEAREST,
524                                         RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP,
525                                         RS_SAMPLER_CLAMP).get());
526    mFontShaderF->bindSampler(mRSC, 0, mFontSampler.get());
527
528    mFontProgramStore.set(ProgramStore::getProgramStore(mRSC, true, true, true, true,
529                                                        false, false,
530                                                        RS_BLEND_SRC_SRC_ALPHA,
531                                                        RS_BLEND_DST_ONE_MINUS_SRC_ALPHA,
532                                                        RS_DEPTH_FUNC_ALWAYS).get());
533    mFontProgramStore->init();
534}
535
536void FontState::initTextTexture() {
537    ObjectBaseRef<const Element> alphaElem = Element::createRef(mRSC, RS_TYPE_UNSIGNED_8,
538                                                                RS_KIND_PIXEL_A, true, 1);
539
540    // We will allocate a texture to initially hold 32 character bitmaps
541    ObjectBaseRef<Type> texType = Type::getTypeRef(mRSC, alphaElem.get(),
542                                                   1024, 256, 0, false, false);
543
544    Allocation *cacheAlloc = Allocation::createAllocation(mRSC, texType.get(),
545                                RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_GRAPHICS_TEXTURE);
546    mTextTexture.set(cacheAlloc);
547    mTextTexture->syncAll(mRSC, RS_ALLOCATION_USAGE_SCRIPT);
548
549    // Split up our cache texture into lines of certain widths
550    int32_t nextLine = 0;
551    mCacheLines.push(new CacheTextureLine(16, texType->getDimX(), nextLine, 0));
552    nextLine += mCacheLines.top()->mMaxHeight;
553    mCacheLines.push(new CacheTextureLine(24, texType->getDimX(), nextLine, 0));
554    nextLine += mCacheLines.top()->mMaxHeight;
555    mCacheLines.push(new CacheTextureLine(24, texType->getDimX(), nextLine, 0));
556    nextLine += mCacheLines.top()->mMaxHeight;
557    mCacheLines.push(new CacheTextureLine(32, texType->getDimX(), nextLine, 0));
558    nextLine += mCacheLines.top()->mMaxHeight;
559    mCacheLines.push(new CacheTextureLine(32, texType->getDimX(), nextLine, 0));
560    nextLine += mCacheLines.top()->mMaxHeight;
561    mCacheLines.push(new CacheTextureLine(40, texType->getDimX(), nextLine, 0));
562    nextLine += mCacheLines.top()->mMaxHeight;
563    mCacheLines.push(new CacheTextureLine(texType->getDimY() - nextLine, texType->getDimX(), nextLine, 0));
564}
565
566// Avoid having to reallocate memory and render quad by quad
567void FontState::initVertexArrayBuffers() {
568    // Now lets write index data
569    ObjectBaseRef<const Element> indexElem = Element::createRef(mRSC, RS_TYPE_UNSIGNED_16, RS_KIND_USER, false, 1);
570    uint32_t numIndicies = mMaxNumberOfQuads * 6;
571    ObjectBaseRef<Type> indexType = Type::getTypeRef(mRSC, indexElem.get(), numIndicies, 0, 0, false, false);
572
573    Allocation *indexAlloc = Allocation::createAllocation(mRSC, indexType.get(),
574                                                          RS_ALLOCATION_USAGE_SCRIPT |
575                                                          RS_ALLOCATION_USAGE_GRAPHICS_VERTEX);
576    uint16_t *indexPtr = (uint16_t*)indexAlloc->getPtr();
577
578    // Four verts, two triangles , six indices per quad
579    for (uint32_t i = 0; i < mMaxNumberOfQuads; i ++) {
580        int32_t i6 = i * 6;
581        int32_t i4 = i * 4;
582
583        indexPtr[i6 + 0] = i4 + 0;
584        indexPtr[i6 + 1] = i4 + 1;
585        indexPtr[i6 + 2] = i4 + 2;
586
587        indexPtr[i6 + 3] = i4 + 0;
588        indexPtr[i6 + 4] = i4 + 2;
589        indexPtr[i6 + 5] = i4 + 3;
590    }
591
592    indexAlloc->sendDirty(mRSC);
593
594    ObjectBaseRef<const Element> posElem = Element::createRef(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 3);
595    ObjectBaseRef<const Element> texElem = Element::createRef(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 2);
596
597    Element::Builder builder;
598    builder.add(posElem.get(), "position", 1);
599    builder.add(texElem.get(), "texture0", 1);
600    ObjectBaseRef<const Element> vertexDataElem = builder.create(mRSC);
601
602    ObjectBaseRef<Type> vertexDataType = Type::getTypeRef(mRSC, vertexDataElem.get(),
603                                                          mMaxNumberOfQuads * 4,
604                                                          0, 0, false, false);
605
606    Allocation *vertexAlloc = Allocation::createAllocation(mRSC, vertexDataType.get(),
607                                                           RS_ALLOCATION_USAGE_SCRIPT);
608    mTextMeshPtr = (float*)vertexAlloc->getPtr();
609
610    mMesh.set(new Mesh(mRSC, 1, 1));
611    mMesh->setVertexBuffer(vertexAlloc, 0);
612    mMesh->setPrimitive(indexAlloc, RS_PRIMITIVE_TRIANGLE, 0);
613    mMesh->init();
614}
615
616// We don't want to allocate anything unless we actually draw text
617void FontState::checkInit() {
618    if (mInitialized) {
619        return;
620    }
621
622    initTextTexture();
623    initRenderState();
624
625    initVertexArrayBuffers();
626
627    // We store a string with letters in a rough frequency of occurrence
628    mLatinPrecache = String8(" eisarntolcdugpmhbyfvkwzxjq");
629    mLatinPrecache += String8("EISARNTOLCDUGPMHBYFVKWZXJQ");
630    mLatinPrecache += String8(",.?!()-+@;:`'");
631    mLatinPrecache += String8("0123456789");
632
633    mInitialized = true;
634}
635
636void FontState::issueDrawCommand() {
637    Context::PushState ps(mRSC);
638
639    mRSC->setProgramVertex(mRSC->getDefaultProgramVertex());
640    mRSC->setProgramRaster(mRSC->getDefaultProgramRaster());
641    mRSC->setProgramFragment(mFontShaderF.get());
642    mRSC->setProgramStore(mFontProgramStore.get());
643
644    if (mConstantsDirty) {
645        mFontShaderFConstant->data(mRSC, 0, 0, 1, &mConstants, sizeof(mConstants));
646        mConstantsDirty = false;
647    }
648
649    if (!mRSC->setupCheck()) {
650        return;
651    }
652
653    mMesh->renderPrimitiveRange(mRSC, 0, 0, mCurrentQuadIndex * 6);
654}
655
656void FontState::appendMeshQuad(float x1, float y1, float z1,
657                               float u1, float v1,
658                               float x2, float y2, float z2,
659                               float u2, float v2,
660                               float x3, float y3, float z3,
661                               float u3, float v3,
662                               float x4, float y4, float z4,
663                               float u4, float v4) {
664    const uint32_t vertsPerQuad = 4;
665    const uint32_t floatsPerVert = 6;
666    float *currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
667
668    if (x1 > mSurfaceWidth || y1 < 0.0f || x2 < 0 || y4 > mSurfaceHeight) {
669        return;
670    }
671
672    /*LOGE("V0 x: %f y: %f z: %f", x1, y1, z1);
673    ALOGE("V1 x: %f y: %f z: %f", x2, y2, z2);
674    ALOGE("V2 x: %f y: %f z: %f", x3, y3, z3);
675    ALOGE("V3 x: %f y: %f z: %f", x4, y4, z4);*/
676
677    (*currentPos++) = x1;
678    (*currentPos++) = y1;
679    (*currentPos++) = z1;
680    (*currentPos++) = 0;
681    (*currentPos++) = u1;
682    (*currentPos++) = v1;
683
684    (*currentPos++) = x2;
685    (*currentPos++) = y2;
686    (*currentPos++) = z2;
687    (*currentPos++) = 0;
688    (*currentPos++) = u2;
689    (*currentPos++) = v2;
690
691    (*currentPos++) = x3;
692    (*currentPos++) = y3;
693    (*currentPos++) = z3;
694    (*currentPos++) = 0;
695    (*currentPos++) = u3;
696    (*currentPos++) = v3;
697
698    (*currentPos++) = x4;
699    (*currentPos++) = y4;
700    (*currentPos++) = z4;
701    (*currentPos++) = 0;
702    (*currentPos++) = u4;
703    (*currentPos++) = v4;
704
705    mCurrentQuadIndex ++;
706
707    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
708        issueDrawCommand();
709        mCurrentQuadIndex = 0;
710    }
711}
712
713uint32_t FontState::getRemainingCacheCapacity() {
714    uint32_t remainingCapacity = 0;
715    uint32_t totalPixels = 0;
716    for (uint32_t i = 0; i < mCacheLines.size(); i ++) {
717         remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
718         totalPixels += mCacheLines[i]->mMaxWidth;
719    }
720    remainingCapacity = (remainingCapacity * 100) / totalPixels;
721    return remainingCapacity;
722}
723
724void FontState::precacheLatin(Font *font) {
725    // Remaining capacity is measured in %
726    uint32_t remainingCapacity = getRemainingCacheCapacity();
727    uint32_t precacheIdx = 0;
728    while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
729        font->getCachedUTFChar((int32_t)mLatinPrecache[precacheIdx]);
730        remainingCapacity = getRemainingCacheCapacity();
731        precacheIdx ++;
732    }
733}
734
735
736void FontState::renderText(const char *text, uint32_t len, int32_t x, int32_t y,
737                           uint32_t startIndex, int32_t numGlyphs,
738                           Font::RenderMode mode,
739                           Font::Rect *bounds,
740                           uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
741    checkInit();
742
743    // Render code here
744    Font *currentFont = mRSC->getFont();
745    if (!currentFont) {
746        if (!mDefault.get()) {
747            String8 fontsDir("/fonts/Roboto-Regular.ttf");
748            String8 fullPath(getenv("ANDROID_ROOT"));
749            fullPath += fontsDir;
750
751            mDefault.set(Font::create(mRSC, fullPath.string(), 8, mRSC->getDPI()));
752        }
753        currentFont = mDefault.get();
754    }
755    if (!currentFont) {
756        ALOGE("Unable to initialize any fonts");
757        return;
758    }
759
760    // Cull things that are off the screen
761    mSurfaceWidth = (float)mRSC->getCurrentSurfaceWidth();
762    mSurfaceHeight = (float)mRSC->getCurrentSurfaceHeight();
763
764    currentFont->renderUTF(text, len, x, y, startIndex, numGlyphs,
765                           mode, bounds, bitmap, bitmapW, bitmapH);
766
767    if (mCurrentQuadIndex != 0) {
768        issueDrawCommand();
769        mCurrentQuadIndex = 0;
770    }
771}
772
773void FontState::measureText(const char *text, uint32_t len, Font::Rect *bounds) {
774    renderText(text, len, 0, 0, 0, -1, Font::MEASURE, bounds);
775    bounds->bottom = - bounds->bottom;
776    bounds->top = - bounds->top;
777}
778
779void FontState::setFontColor(float r, float g, float b, float a) {
780    mConstants.mFontColor[0] = r;
781    mConstants.mFontColor[1] = g;
782    mConstants.mFontColor[2] = b;
783    mConstants.mFontColor[3] = a;
784
785    mConstants.mGamma = 1.0f;
786    const float luminance = (r * 2.0f + g * 5.0f + b) / 8.0f;
787    if (luminance <= mBlackThreshold) {
788        mConstants.mGamma = mBlackGamma;
789    } else if (luminance >= mWhiteThreshold) {
790        mConstants.mGamma = mWhiteGamma;
791    }
792
793    mConstantsDirty = true;
794}
795
796void FontState::getFontColor(float *r, float *g, float *b, float *a) const {
797    *r = mConstants.mFontColor[0];
798    *g = mConstants.mFontColor[1];
799    *b = mConstants.mFontColor[2];
800    *a = mConstants.mFontColor[3];
801}
802
803void FontState::deinit(Context *rsc) {
804    mInitialized = false;
805
806    mFontShaderFConstant.clear();
807
808    mMesh.clear();
809
810    mFontShaderF.clear();
811    mFontSampler.clear();
812    mFontProgramStore.clear();
813
814    mTextTexture.clear();
815    for (uint32_t i = 0; i < mCacheLines.size(); i ++) {
816        delete mCacheLines[i];
817    }
818    mCacheLines.clear();
819
820    mDefault.clear();
821#ifndef ANDROID_RS_SERIALIZE
822    if (mLibrary) {
823        FT_Done_FreeType( mLibrary );
824        mLibrary = NULL;
825    }
826#endif //ANDROID_RS_SERIALIZE
827}
828
829#ifndef ANDROID_RS_SERIALIZE
830bool FontState::CacheTextureLine::fitBitmap(FT_Bitmap_ *bitmap, uint32_t *retOriginX, uint32_t *retOriginY) {
831    if ((uint32_t)bitmap->rows > mMaxHeight) {
832        return false;
833    }
834
835    if (mCurrentCol + (uint32_t)bitmap->width < mMaxWidth) {
836        *retOriginX = mCurrentCol;
837        *retOriginY = mCurrentRow;
838        mCurrentCol += bitmap->width;
839        mDirty = true;
840       return true;
841    }
842
843    return false;
844}
845#endif //ANDROID_RS_SERIALIZE
846
847namespace android {
848namespace renderscript {
849
850RsFont rsi_FontCreateFromFile(Context *rsc,
851                              char const *name, size_t name_length,
852                              float fontSize, uint32_t dpi) {
853    Font *newFont = Font::create(rsc, name, fontSize, dpi);
854    if (newFont) {
855        newFont->incUserRef();
856    }
857    return newFont;
858}
859
860RsFont rsi_FontCreateFromMemory(Context *rsc,
861                                char const *name, size_t name_length,
862                                float fontSize, uint32_t dpi,
863                                const void *data, size_t data_length) {
864    Font *newFont = Font::create(rsc, name, fontSize, dpi, data, data_length);
865    if (newFont) {
866        newFont->incUserRef();
867    }
868    return newFont;
869}
870
871} // renderscript
872} // android
873