rsFont.cpp revision 6d8eb266dd398abf0511685fdaf98abba3396174
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#ifndef ANDROID_RS_BUILD_FOR_HOST
19#include "rsContext.h"
20#else
21#include "rsContextHostStub.h"
22#endif
23
24#include "rsFont.h"
25#include "rsProgramFragment.h"
26#include <cutils/properties.h>
27#include FT_BITMAP_H
28
29#include <GLES/gl.h>
30#include <GLES/glext.h>
31#include <GLES2/gl2.h>
32#include <GLES2/gl2ext.h>
33
34using namespace android;
35using namespace android::renderscript;
36
37Font::Font(Context *rsc) : ObjectBase(rsc), mCachedGlyphs(NULL) {
38    mInitialized = false;
39    mHasKerning = false;
40    mFace = NULL;
41}
42
43bool Font::init(const char *name, uint32_t fontSize, uint32_t dpi) {
44    if (mInitialized) {
45        LOGE("Reinitialization of fonts not supported");
46        return false;
47    }
48
49    String8 fontsDir("/fonts/");
50    String8 fullPath(getenv("ANDROID_ROOT"));
51    fullPath += fontsDir;
52    fullPath += name;
53
54    FT_Error error = FT_New_Face(mRSC->mStateFont.getLib(), fullPath.string(), 0, &mFace);
55    if (error) {
56        LOGE("Unable to initialize font %s", fullPath.string());
57        return false;
58    }
59
60    mFontName = name;
61    mFontSize = fontSize;
62    mDpi = dpi;
63
64    error = FT_Set_Char_Size(mFace, fontSize * 64, 0, dpi, 0);
65    if (error) {
66        LOGE("Unable to set font size on %s", fullPath.string());
67        return false;
68    }
69
70    mHasKerning = FT_HAS_KERNING(mFace);
71
72    mInitialized = true;
73    return true;
74}
75
76void Font::invalidateTextureCache() {
77    for (uint32_t i = 0; i < mCachedGlyphs.size(); i ++) {
78        mCachedGlyphs.valueAt(i)->mIsValid = false;
79    }
80}
81
82void Font::drawCachedGlyph(CachedGlyphInfo *glyph, int32_t x, int32_t y) {
83    FontState *state = &mRSC->mStateFont;
84
85    int32_t nPenX = x + glyph->mBitmapLeft;
86    int32_t nPenY = y - glyph->mBitmapTop + glyph->mBitmapHeight;
87
88    float u1 = glyph->mBitmapMinU;
89    float u2 = glyph->mBitmapMaxU;
90    float v1 = glyph->mBitmapMinV;
91    float v2 = glyph->mBitmapMaxV;
92
93    int32_t width = (int32_t) glyph->mBitmapWidth;
94    int32_t height = (int32_t) glyph->mBitmapHeight;
95
96    state->appendMeshQuad(nPenX, nPenY, 0, u1, v2,
97                          nPenX + width, nPenY, 0, u2, v2,
98                          nPenX + width, nPenY - height, 0, u2, v1,
99                          nPenX, nPenY - height, 0, u1, v1);
100}
101
102void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int32_t x, int32_t y,
103                           uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) {
104    int32_t nPenX = x + glyph->mBitmapLeft;
105    int32_t nPenY = y + glyph->mBitmapTop;
106
107    uint32_t endX = glyph->mBitmapMinX + glyph->mBitmapWidth;
108    uint32_t endY = glyph->mBitmapMinY + glyph->mBitmapHeight;
109
110    FontState *state = &mRSC->mStateFont;
111    uint32_t cacheWidth = state->getCacheTextureType()->getDimX();
112    const uint8_t* cacheBuffer = state->getTextTextureData();
113
114    uint32_t cacheX = 0, cacheY = 0;
115    int32_t bX = 0, bY = 0;
116    for (cacheX = glyph->mBitmapMinX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
117        for (cacheY = glyph->mBitmapMinY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
118            if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
119                LOGE("Skipping invalid index");
120                continue;
121            }
122            uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
123            bitmap[bY * bitmapW + bX] = tempCol;
124        }
125    }
126}
127
128void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int32_t x, int32_t y, Rect *bounds) {
129    int32_t nPenX = x + glyph->mBitmapLeft;
130    int32_t nPenY = y - glyph->mBitmapTop + glyph->mBitmapHeight;
131
132    int32_t width = (int32_t) glyph->mBitmapWidth;
133    int32_t height = (int32_t) glyph->mBitmapHeight;
134
135    if (bounds->bottom > nPenY) {
136        bounds->bottom = nPenY;
137    }
138    if (bounds->left > nPenX) {
139        bounds->left = nPenX;
140    }
141    if (bounds->right < nPenX + width) {
142        bounds->right = nPenX + width;
143    }
144    if (bounds->top < nPenY + height) {
145        bounds->top = nPenY + height;
146    }
147}
148
149void Font::renderUTF(const char *text, uint32_t len, int32_t x, int32_t y,
150                     uint32_t start, int32_t numGlyphs,
151                     RenderMode mode, Rect *bounds,
152                     uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
153    if (!mInitialized || numGlyphs == 0 || text == NULL || len == 0) {
154        return;
155    }
156
157    if (mode == Font::MEASURE) {
158        if (bounds == NULL) {
159            LOGE("No return rectangle provided to measure text");
160            return;
161        }
162        // Reset min and max of the bounding box to something large
163        bounds->set(1e6, -1e6, -1e6, 1e6);
164    }
165
166    int32_t penX = x, penY = y;
167    int32_t glyphsLeft = 1;
168    if (numGlyphs > 0) {
169        glyphsLeft = numGlyphs;
170    }
171
172    size_t index = start;
173    size_t nextIndex = 0;
174
175    while (glyphsLeft > 0) {
176
177        int32_t utfChar = utf32_from_utf8_at(text, len, index, &nextIndex);
178
179        // Reached the end of the string or encountered
180        if (utfChar < 0) {
181            break;
182        }
183
184        // Move to the next character in the array
185        index = nextIndex;
186
187        CachedGlyphInfo *cachedGlyph = getCachedUTFChar(utfChar);
188
189        // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
190        if (cachedGlyph->mIsValid) {
191            switch (mode) {
192            case FRAMEBUFFER:
193                drawCachedGlyph(cachedGlyph, penX, penY);
194                break;
195            case BITMAP:
196                drawCachedGlyph(cachedGlyph, penX, penY, bitmap, bitmapW, bitmapH);
197                break;
198            case MEASURE:
199                measureCachedGlyph(cachedGlyph, penX, penY, bounds);
200                break;
201            }
202        }
203
204        penX += (cachedGlyph->mAdvance.x >> 6);
205
206        // If we were given a specific number of glyphs, decrement
207        if (numGlyphs > 0) {
208            glyphsLeft --;
209        }
210    }
211}
212
213Font::CachedGlyphInfo* Font::getCachedUTFChar(int32_t utfChar) {
214
215    CachedGlyphInfo *cachedGlyph = mCachedGlyphs.valueFor((uint32_t)utfChar);
216    if (cachedGlyph == NULL) {
217        cachedGlyph = cacheGlyph((uint32_t)utfChar);
218    }
219    // Is the glyph still in texture cache?
220    if (!cachedGlyph->mIsValid) {
221        updateGlyphCache(cachedGlyph);
222    }
223
224    return cachedGlyph;
225}
226
227void Font::updateGlyphCache(CachedGlyphInfo *glyph) {
228    FT_Error error = FT_Load_Glyph( mFace, glyph->mGlyphIndex, FT_LOAD_RENDER );
229    if (error) {
230        LOGE("Couldn't load glyph.");
231        return;
232    }
233
234    glyph->mAdvance = mFace->glyph->advance;
235    glyph->mBitmapLeft = mFace->glyph->bitmap_left;
236    glyph->mBitmapTop = mFace->glyph->bitmap_top;
237
238    FT_Bitmap *bitmap = &mFace->glyph->bitmap;
239
240    // Now copy the bitmap into the cache texture
241    uint32_t startX = 0;
242    uint32_t startY = 0;
243
244    // Let the font state figure out where to put the bitmap
245    FontState *state = &mRSC->mStateFont;
246    glyph->mIsValid = state->cacheBitmap(bitmap, &startX, &startY);
247
248    if (!glyph->mIsValid) {
249        return;
250    }
251
252    uint32_t endX = startX + bitmap->width;
253    uint32_t endY = startY + bitmap->rows;
254
255    glyph->mBitmapMinX = startX;
256    glyph->mBitmapMinY = startY;
257    glyph->mBitmapWidth = bitmap->width;
258    glyph->mBitmapHeight = bitmap->rows;
259
260    uint32_t cacheWidth = state->getCacheTextureType()->getDimX();
261    uint32_t cacheHeight = state->getCacheTextureType()->getDimY();
262
263    glyph->mBitmapMinU = (float)startX / (float)cacheWidth;
264    glyph->mBitmapMinV = (float)startY / (float)cacheHeight;
265    glyph->mBitmapMaxU = (float)endX / (float)cacheWidth;
266    glyph->mBitmapMaxV = (float)endY / (float)cacheHeight;
267}
268
269Font::CachedGlyphInfo *Font::cacheGlyph(uint32_t glyph) {
270    CachedGlyphInfo *newGlyph = new CachedGlyphInfo();
271    mCachedGlyphs.add(glyph, newGlyph);
272
273    newGlyph->mGlyphIndex = FT_Get_Char_Index(mFace, glyph);
274    newGlyph->mIsValid = false;
275
276    updateGlyphCache(newGlyph);
277
278    return newGlyph;
279}
280
281Font * Font::create(Context *rsc, const char *name, uint32_t fontSize, uint32_t dpi) {
282    rsc->mStateFont.checkInit();
283    Vector<Font*> &activeFonts = rsc->mStateFont.mActiveFonts;
284
285    for (uint32_t i = 0; i < activeFonts.size(); i ++) {
286        Font *ithFont = activeFonts[i];
287        if (ithFont->mFontName == name && ithFont->mFontSize == fontSize && ithFont->mDpi == dpi) {
288            return ithFont;
289        }
290    }
291
292    Font *newFont = new Font(rsc);
293    bool isInitialized = newFont->init(name, fontSize, dpi);
294    if (isInitialized) {
295        activeFonts.push(newFont);
296        rsc->mStateFont.precacheLatin(newFont);
297        return newFont;
298    }
299
300    ObjectBase::checkDelete(newFont);
301    return NULL;
302}
303
304Font::~Font() {
305    if (mFace) {
306        FT_Done_Face(mFace);
307    }
308
309    for (uint32_t ct = 0; ct < mRSC->mStateFont.mActiveFonts.size(); ct++) {
310        if (mRSC->mStateFont.mActiveFonts[ct] == this) {
311            mRSC->mStateFont.mActiveFonts.removeAt(ct);
312            break;
313        }
314    }
315
316    for (uint32_t i = 0; i < mCachedGlyphs.size(); i ++) {
317        CachedGlyphInfo *glyph = mCachedGlyphs.valueAt(i);
318        delete glyph;
319    }
320}
321
322FontState::FontState() {
323    mInitialized = false;
324    mMaxNumberOfQuads = 1024;
325    mCurrentQuadIndex = 0;
326    mRSC = NULL;
327    mLibrary = NULL;
328
329    // Get the renderer properties
330    char property[PROPERTY_VALUE_MAX];
331
332    // Get the gamma
333    float gamma = DEFAULT_TEXT_GAMMA;
334    if (property_get(PROPERTY_TEXT_GAMMA, property, NULL) > 0) {
335        LOGD("  Setting text gamma to %s", property);
336        gamma = atof(property);
337    } else {
338        LOGD("  Using default text gamma of %.2f", DEFAULT_TEXT_GAMMA);
339    }
340
341    // Get the black gamma threshold
342    int32_t blackThreshold = DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD;
343    if (property_get(PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD, property, NULL) > 0) {
344        LOGD("  Setting text black gamma threshold to %s", property);
345        blackThreshold = atoi(property);
346    } else {
347        LOGD("  Using default text black gamma threshold of %d",
348                DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD);
349    }
350    mBlackThreshold = (float)(blackThreshold) / 255.0f;
351
352    // Get the white gamma threshold
353    int32_t whiteThreshold = DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD;
354    if (property_get(PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD, property, NULL) > 0) {
355        LOGD("  Setting text white gamma threshold to %s", property);
356        whiteThreshold = atoi(property);
357    } else {
358        LOGD("  Using default white black gamma threshold of %d",
359                DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD);
360    }
361    mWhiteThreshold = (float)(whiteThreshold) / 255.0f;
362
363    // Compute the gamma tables
364    mBlackGamma = gamma;
365    mWhiteGamma = 1.0f / gamma;
366
367    setFontColor(0.1f, 0.1f, 0.1f, 1.0f);
368}
369
370FontState::~FontState() {
371    for (uint32_t i = 0; i < mCacheLines.size(); i ++) {
372        delete mCacheLines[i];
373    }
374
375    rsAssert(!mActiveFonts.size());
376}
377
378FT_Library FontState::getLib() {
379    if (!mLibrary) {
380        FT_Error error = FT_Init_FreeType(&mLibrary);
381        if (error) {
382            LOGE("Unable to initialize freetype");
383            return NULL;
384        }
385    }
386
387    return mLibrary;
388}
389
390void FontState::init(Context *rsc) {
391    mRSC = rsc;
392}
393
394void FontState::flushAllAndInvalidate() {
395    if (mCurrentQuadIndex != 0) {
396        issueDrawCommand();
397        mCurrentQuadIndex = 0;
398    }
399    for (uint32_t i = 0; i < mActiveFonts.size(); i ++) {
400        mActiveFonts[i]->invalidateTextureCache();
401    }
402    for (uint32_t i = 0; i < mCacheLines.size(); i ++) {
403        mCacheLines[i]->mCurrentCol = 0;
404    }
405}
406
407bool FontState::cacheBitmap(FT_Bitmap *bitmap, uint32_t *retOriginX, uint32_t *retOriginY) {
408    // If the glyph is too tall, don't cache it
409    if ((uint32_t)bitmap->rows > mCacheLines[mCacheLines.size()-1]->mMaxHeight) {
410        LOGE("Font size to large to fit in cache. width, height = %i, %i", (int)bitmap->width, (int)bitmap->rows);
411        return false;
412    }
413
414    // Now copy the bitmap into the cache texture
415    uint32_t startX = 0;
416    uint32_t startY = 0;
417
418    bool bitmapFit = false;
419    for (uint32_t i = 0; i < mCacheLines.size(); i ++) {
420        bitmapFit = mCacheLines[i]->fitBitmap(bitmap, &startX, &startY);
421        if (bitmapFit) {
422            break;
423        }
424    }
425
426    // If the new glyph didn't fit, flush the state so far and invalidate everything
427    if (!bitmapFit) {
428        flushAllAndInvalidate();
429
430        // Try to fit it again
431        for (uint32_t i = 0; i < mCacheLines.size(); i ++) {
432            bitmapFit = mCacheLines[i]->fitBitmap(bitmap, &startX, &startY);
433            if (bitmapFit) {
434                break;
435            }
436        }
437
438        // if we still don't fit, something is wrong and we shouldn't draw
439        if (!bitmapFit) {
440            LOGE("Bitmap doesn't fit in cache. width, height = %i, %i", (int)bitmap->width, (int)bitmap->rows);
441            return false;
442        }
443    }
444
445    *retOriginX = startX;
446    *retOriginY = startY;
447
448    uint32_t endX = startX + bitmap->width;
449    uint32_t endY = startY + bitmap->rows;
450
451    uint32_t cacheWidth = getCacheTextureType()->getDimX();
452
453    uint8_t *cacheBuffer = (uint8_t*)mTextTexture->getPtr();
454    uint8_t *bitmapBuffer = bitmap->buffer;
455
456    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
457    for (cacheX = startX, bX = 0; cacheX < endX; cacheX ++, bX ++) {
458        for (cacheY = startY, bY = 0; cacheY < endY; cacheY ++, bY ++) {
459            uint8_t tempCol = bitmapBuffer[bY * bitmap->width + bX];
460            cacheBuffer[cacheY*cacheWidth + cacheX] = tempCol;
461        }
462    }
463
464    // This will dirty the texture and the shader so next time
465    // we draw it will upload the data
466    mTextTexture->syncAll(mRSC, RS_ALLOCATION_USAGE_SCRIPT);
467    mFontShaderF->bindTexture(mRSC, 0, mTextTexture.get());
468
469    // Some debug code
470    /*for (uint32_t i = 0; i < mCacheLines.size(); i ++) {
471        LOGE("Cache Line: H: %u Empty Space: %f",
472             mCacheLines[i]->mMaxHeight,
473              (1.0f - (float)mCacheLines[i]->mCurrentCol/(float)mCacheLines[i]->mMaxWidth)*100.0f);
474
475    }*/
476
477    return true;
478}
479
480void FontState::initRenderState() {
481    String8 shaderString("varying vec2 varTex0;\n");
482    shaderString.append("void main() {\n");
483    shaderString.append("  lowp vec4 col = UNI_Color;\n");
484    shaderString.append("  col.a = texture2D(UNI_Tex0, varTex0.xy).a;\n");
485    shaderString.append("  col.a = pow(col.a, UNI_Gamma);\n");
486    shaderString.append("  gl_FragColor = col;\n");
487    shaderString.append("}\n");
488
489    const Element *colorElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 4);
490    const Element *gammaElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 1);
491    mRSC->mStateElement.elementBuilderBegin();
492    mRSC->mStateElement.elementBuilderAdd(colorElem, "Color", 1);
493    mRSC->mStateElement.elementBuilderAdd(gammaElem, "Gamma", 1);
494    const Element *constInput = mRSC->mStateElement.elementBuilderCreate(mRSC);
495
496    Type *inputType = Type::getType(mRSC, constInput, 1, 0, 0, false, false);
497
498    uint32_t tmp[4];
499    tmp[0] = RS_PROGRAM_PARAM_CONSTANT;
500    tmp[1] = (uint32_t)inputType;
501    tmp[2] = RS_PROGRAM_PARAM_TEXTURE_TYPE;
502    tmp[3] = RS_TEXTURE_2D;
503
504    mFontShaderFConstant.set(new Allocation(mRSC, inputType,
505                                            RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_GRAPHICS_CONSTANTS));
506    ProgramFragment *pf = new ProgramFragment(mRSC, shaderString.string(),
507                                              shaderString.length(), tmp, 4);
508    mFontShaderF.set(pf);
509    mFontShaderF->bindAllocation(mRSC, mFontShaderFConstant.get(), 0);
510
511    Sampler *sampler = new Sampler(mRSC, RS_SAMPLER_NEAREST, RS_SAMPLER_NEAREST,
512                                      RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP);
513    mFontSampler.set(sampler);
514    mFontShaderF->bindSampler(mRSC, 0, sampler);
515
516    ProgramStore *fontStore = new ProgramStore(mRSC);
517    mFontProgramStore.set(fontStore);
518    mFontProgramStore->setDepthFunc(RS_DEPTH_FUNC_ALWAYS);
519    mFontProgramStore->setBlendFunc(RS_BLEND_SRC_SRC_ALPHA, RS_BLEND_DST_ONE_MINUS_SRC_ALPHA);
520    mFontProgramStore->setDitherEnable(false);
521    mFontProgramStore->setDepthMask(false);
522}
523
524void FontState::initTextTexture() {
525    const Element *alphaElem = Element::create(mRSC, RS_TYPE_UNSIGNED_8, RS_KIND_PIXEL_A, true, 1);
526
527    // We will allocate a texture to initially hold 32 character bitmaps
528    Type *texType = Type::getType(mRSC, alphaElem, 1024, 256, 0, false, false);
529
530    Allocation *cacheAlloc = new Allocation(mRSC, texType, RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_GRAPHICS_TEXTURE);
531    mTextTexture.set(cacheAlloc);
532    mTextTexture->syncAll(mRSC, RS_ALLOCATION_USAGE_SCRIPT);
533
534    // Split up our cache texture into lines of certain widths
535    int32_t nextLine = 0;
536    mCacheLines.push(new CacheTextureLine(16, texType->getDimX(), nextLine, 0));
537    nextLine += mCacheLines.top()->mMaxHeight;
538    mCacheLines.push(new CacheTextureLine(24, texType->getDimX(), nextLine, 0));
539    nextLine += mCacheLines.top()->mMaxHeight;
540    mCacheLines.push(new CacheTextureLine(24, texType->getDimX(), nextLine, 0));
541    nextLine += mCacheLines.top()->mMaxHeight;
542    mCacheLines.push(new CacheTextureLine(32, texType->getDimX(), nextLine, 0));
543    nextLine += mCacheLines.top()->mMaxHeight;
544    mCacheLines.push(new CacheTextureLine(32, texType->getDimX(), nextLine, 0));
545    nextLine += mCacheLines.top()->mMaxHeight;
546    mCacheLines.push(new CacheTextureLine(40, texType->getDimX(), nextLine, 0));
547    nextLine += mCacheLines.top()->mMaxHeight;
548    mCacheLines.push(new CacheTextureLine(texType->getDimY() - nextLine, texType->getDimX(), nextLine, 0));
549}
550
551// Avoid having to reallocate memory and render quad by quad
552void FontState::initVertexArrayBuffers() {
553    // Now lets write index data
554    const Element *indexElem = Element::create(mRSC, RS_TYPE_UNSIGNED_16, RS_KIND_USER, false, 1);
555    uint32_t numIndicies = mMaxNumberOfQuads * 6;
556    Type *indexType = Type::getType(mRSC, indexElem, numIndicies, 0, 0, false, false);
557
558    Allocation *indexAlloc = new Allocation(mRSC, indexType, RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_GRAPHICS_VERTEX);
559    uint16_t *indexPtr = (uint16_t*)indexAlloc->getPtr();
560
561    // Four verts, two triangles , six indices per quad
562    for (uint32_t i = 0; i < mMaxNumberOfQuads; i ++) {
563        int32_t i6 = i * 6;
564        int32_t i4 = i * 4;
565
566        indexPtr[i6 + 0] = i4 + 0;
567        indexPtr[i6 + 1] = i4 + 1;
568        indexPtr[i6 + 2] = i4 + 2;
569
570        indexPtr[i6 + 3] = i4 + 0;
571        indexPtr[i6 + 4] = i4 + 2;
572        indexPtr[i6 + 5] = i4 + 3;
573    }
574
575    indexAlloc->deferedUploadToBufferObject(mRSC);
576    mIndexBuffer.set(indexAlloc);
577
578    const Element *posElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 3);
579    const Element *texElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 2);
580
581    mRSC->mStateElement.elementBuilderBegin();
582    mRSC->mStateElement.elementBuilderAdd(posElem, "position", 1);
583    mRSC->mStateElement.elementBuilderAdd(texElem, "texture0", 1);
584    const Element *vertexDataElem = mRSC->mStateElement.elementBuilderCreate(mRSC);
585
586    Type *vertexDataType = Type::getType(mRSC, vertexDataElem,
587                                         mMaxNumberOfQuads * 4,
588                                         0, 0, false, false);
589
590    Allocation *vertexAlloc = new Allocation(mRSC, vertexDataType, RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_GRAPHICS_VERTEX);
591    mTextMeshPtr = (float*)vertexAlloc->getPtr();
592
593    mVertexArray.set(vertexAlloc);
594}
595
596// We don't want to allocate anything unless we actually draw text
597void FontState::checkInit() {
598    if (mInitialized) {
599        return;
600    }
601
602    initTextTexture();
603    initRenderState();
604
605    initVertexArrayBuffers();
606
607    // We store a string with letters in a rough frequency of occurrence
608    mLatinPrecache = String8(" eisarntolcdugpmhbyfvkwzxjq");
609    mLatinPrecache += String8("EISARNTOLCDUGPMHBYFVKWZXJQ");
610    mLatinPrecache += String8(",.?!()-+@;:`'");
611    mLatinPrecache += String8("0123456789");
612
613    mInitialized = true;
614}
615
616void FontState::issueDrawCommand() {
617    Context::PushState ps(mRSC);
618
619    mRSC->setProgramVertex(mRSC->getDefaultProgramVertex());
620    mRSC->setProgramRaster(mRSC->getDefaultProgramRaster());
621    mRSC->setProgramFragment(mFontShaderF.get());
622    mRSC->setProgramStore(mFontProgramStore.get());
623
624    if (mConstantsDirty) {
625        mFontShaderFConstant->data(mRSC, &mConstants, sizeof(mConstants));
626        mConstantsDirty = false;
627    }
628
629    if (!mRSC->setupCheck()) {
630        return;
631    }
632
633    float *vtx = (float*)mVertexArray->getPtr();
634    float *tex = vtx + 3;
635
636    VertexArray::Attrib attribs[2];
637    attribs[0].set(GL_FLOAT, 3, 20, false, (uint32_t)vtx, "ATTRIB_position");
638    attribs[1].set(GL_FLOAT, 2, 20, false, (uint32_t)tex, "ATTRIB_texture0");
639    VertexArray va(attribs, 2);
640    va.setupGL2(mRSC, &mRSC->mStateVertexArray, &mRSC->mShaderCache);
641
642    mIndexBuffer->uploadCheck(mRSC);
643    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer->getBufferObjectID());
644    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, (uint16_t *)(0));
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            mDefault.set(Font::create(mRSC, "DroidSans.ttf", 16, 96));
739        }
740        currentFont = mDefault.get();
741    }
742    if (!currentFont) {
743        LOGE("Unable to initialize any fonts");
744        return;
745    }
746
747    currentFont->renderUTF(text, len, x, y, startIndex, numGlyphs,
748                           mode, bounds, bitmap, bitmapW, bitmapH);
749
750    if (mCurrentQuadIndex != 0) {
751        issueDrawCommand();
752        mCurrentQuadIndex = 0;
753    }
754}
755
756void FontState::measureText(const char *text, uint32_t len, Font::Rect *bounds) {
757    renderText(text, len, 0, 0, 0, -1, Font::MEASURE, bounds);
758}
759
760void FontState::setFontColor(float r, float g, float b, float a) {
761    mConstants.mFontColor[0] = r;
762    mConstants.mFontColor[1] = g;
763    mConstants.mFontColor[2] = b;
764    mConstants.mFontColor[3] = a;
765
766    mConstants.mGamma = 1.0f;
767    const float luminance = (r * 2.0f + g * 5.0f + b) / 8.0f;
768    if (luminance <= mBlackThreshold) {
769        mConstants.mGamma = mBlackGamma;
770    } else if (luminance >= mWhiteThreshold) {
771        mConstants.mGamma = mWhiteGamma;
772    }
773
774    mConstantsDirty = true;
775}
776
777void FontState::getFontColor(float *r, float *g, float *b, float *a) const {
778    *r = mConstants.mFontColor[0];
779    *g = mConstants.mFontColor[1];
780    *b = mConstants.mFontColor[2];
781    *a = mConstants.mFontColor[3];
782}
783
784void FontState::deinit(Context *rsc) {
785    mInitialized = false;
786
787    mFontShaderFConstant.clear();
788
789    mIndexBuffer.clear();
790    mVertexArray.clear();
791
792    mFontShaderF.clear();
793    mFontSampler.clear();
794    mFontProgramStore.clear();
795
796    mTextTexture.clear();
797    for (uint32_t i = 0; i < mCacheLines.size(); i ++) {
798        delete mCacheLines[i];
799    }
800    mCacheLines.clear();
801
802    mDefault.clear();
803
804    Vector<Font*> fontsToDereference = mActiveFonts;
805    for (uint32_t i = 0; i < fontsToDereference.size(); i ++) {
806        fontsToDereference[i]->zeroUserRef();
807    }
808
809    if (mLibrary) {
810        FT_Done_FreeType( mLibrary );
811        mLibrary = NULL;
812    }
813}
814
815namespace android {
816namespace renderscript {
817
818RsFont rsi_FontCreateFromFile(Context *rsc, char const *name, uint32_t fontSize, uint32_t dpi) {
819    Font *newFont = Font::create(rsc, name, fontSize, dpi);
820    if (newFont) {
821        newFont->incUserRef();
822    }
823    return newFont;
824}
825
826} // renderscript
827} // android
828