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