rsFont.cpp revision 9fc9f0375a92fe22fecb3782b18a5c6060a07290
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 FT_BITMAP_H
27
28#include <GLES/gl.h>
29#include <GLES/glext.h>
30#include <GLES2/gl2.h>
31#include <GLES2/gl2ext.h>
32
33using namespace android;
34using namespace android::renderscript;
35
36Font::Font(Context *rsc) : ObjectBase(rsc), mCachedGlyphs(NULL)
37{
38    mAllocFile = __FILE__;
39    mAllocLine = __LINE__;
40    mInitialized = false;
41    mHasKerning = false;
42    mFace = NULL;
43}
44
45bool Font::init(const char *name, uint32_t fontSize, uint32_t dpi)
46{
47    if(mInitialized) {
48        LOGE("Reinitialization of fonts not supported");
49        return false;
50    }
51
52    String8 fontsDir("/fonts/");
53    String8 fullPath(getenv("ANDROID_ROOT"));
54    fullPath += fontsDir;
55    fullPath += name;
56
57    FT_Error error = FT_New_Face(mRSC->mStateFont.getLib(), fullPath.string(), 0, &mFace);
58    if(error) {
59        LOGE("Unable to initialize font %s", fullPath.string());
60        return false;
61    }
62
63    mFontName = name;
64    mFontSize = fontSize;
65    mDpi = dpi;
66
67    error = FT_Set_Char_Size(mFace, fontSize * 64, 0, dpi, 0);
68    if(error) {
69        LOGE("Unable to set font size on %s", fullPath.string());
70        return false;
71    }
72
73    mHasKerning = FT_HAS_KERNING(mFace);
74
75    mInitialized = true;
76    return true;
77}
78
79void Font::invalidateTextureCache()
80{
81    for(uint32_t i = 0; i < mCachedGlyphs.size(); i ++) {
82        mCachedGlyphs.valueAt(i)->mIsValid = false;
83    }
84}
85
86void Font::drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y)
87{
88    FontState *state = &mRSC->mStateFont;
89
90    int nPenX = x + glyph->mBitmapLeft;
91    int nPenY = y - glyph->mBitmapTop + glyph->mBitmapHeight;
92
93    state->appendMeshQuad(nPenX, nPenY, 0,
94                            glyph->mBitmapMinU, glyph->mBitmapMaxV,
95
96                            nPenX + (int)glyph->mBitmapWidth, nPenY, 0,
97                            glyph->mBitmapMaxU, glyph->mBitmapMaxV,
98
99                            nPenX + (int)glyph->mBitmapWidth, nPenY - (int)glyph->mBitmapHeight, 0,
100                            glyph->mBitmapMaxU, glyph->mBitmapMinV,
101
102                            nPenX, nPenY - (int)glyph->mBitmapHeight, 0,
103                            glyph->mBitmapMinU, glyph->mBitmapMinV);
104}
105
106void Font::renderUTF(const char *text, uint32_t len, uint32_t start, int numGlyphs, int x, int y)
107{
108    if(!mInitialized || numGlyphs == 0 || text == NULL || len == 0) {
109        return;
110    }
111
112    int penX = x, penY = y;
113    int glyphsLeft = 1;
114    if(numGlyphs > 0) {
115        glyphsLeft = numGlyphs;
116    }
117
118    size_t index = start;
119    size_t nextIndex = 0;
120
121    while (glyphsLeft > 0) {
122
123        int32_t utfChar = utf32_at(text, len, index, &nextIndex);
124
125        // Reached the end of the string or encountered
126        if(utfChar < 0) {
127            break;
128        }
129
130        // Move to the next character in the array
131        index = nextIndex;
132
133        CachedGlyphInfo *cachedGlyph = mCachedGlyphs.valueFor((uint32_t)utfChar);
134
135        if(cachedGlyph == NULL) {
136            cachedGlyph = cacheGlyph((uint32_t)utfChar);
137        }
138        // Is the glyph still in texture cache?
139        if(!cachedGlyph->mIsValid) {
140            updateGlyphCache(cachedGlyph);
141        }
142
143        // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
144        if(cachedGlyph->mIsValid) {
145            drawCachedGlyph(cachedGlyph, penX, penY);
146        }
147
148        penX += (cachedGlyph->mAdvance.x >> 6);
149
150        // If we were given a specific number of glyphs, decrement
151        if(numGlyphs > 0) {
152            glyphsLeft --;
153        }
154    }
155}
156
157void Font::updateGlyphCache(CachedGlyphInfo *glyph)
158{
159    FT_Error error = FT_Load_Glyph( mFace, glyph->mGlyphIndex, FT_LOAD_RENDER );
160    if(error) {
161        LOGE("Couldn't load glyph.");
162        return;
163    }
164
165    glyph->mAdvance = mFace->glyph->advance;
166    glyph->mBitmapLeft = mFace->glyph->bitmap_left;
167    glyph->mBitmapTop = mFace->glyph->bitmap_top;
168
169    FT_Bitmap *bitmap = &mFace->glyph->bitmap;
170
171    // Now copy the bitmap into the cache texture
172    uint32_t startX = 0;
173    uint32_t startY = 0;
174
175    // Let the font state figure out where to put the bitmap
176    FontState *state = &mRSC->mStateFont;
177    glyph->mIsValid = state->cacheBitmap(bitmap, &startX, &startY);
178
179    if(!glyph->mIsValid) {
180        return;
181    }
182
183    uint32_t endX = startX + bitmap->width;
184    uint32_t endY = startY + bitmap->rows;
185
186    glyph->mBitmapMinX = startX;
187    glyph->mBitmapMinY = startY;
188    glyph->mBitmapWidth = bitmap->width;
189    glyph->mBitmapHeight = bitmap->rows;
190
191    uint32_t cacheWidth = state->getCacheTextureType()->getDimX();
192    uint32_t cacheHeight = state->getCacheTextureType()->getDimY();
193
194    glyph->mBitmapMinU = (float)startX / (float)cacheWidth;
195    glyph->mBitmapMinV = (float)startY / (float)cacheHeight;
196    glyph->mBitmapMaxU = (float)endX / (float)cacheWidth;
197    glyph->mBitmapMaxV = (float)endY / (float)cacheHeight;
198}
199
200Font::CachedGlyphInfo *Font::cacheGlyph(uint32_t glyph)
201{
202    CachedGlyphInfo *newGlyph = new CachedGlyphInfo();
203    mCachedGlyphs.add(glyph, newGlyph);
204
205    newGlyph->mGlyphIndex = FT_Get_Char_Index(mFace, glyph);
206    newGlyph->mIsValid = false;
207
208    updateGlyphCache(newGlyph);
209
210    return newGlyph;
211}
212
213Font * Font::create(Context *rsc, const char *name, uint32_t fontSize, uint32_t dpi)
214{
215    Vector<Font*> &activeFonts = rsc->mStateFont.mActiveFonts;
216
217    for(uint32_t i = 0; i < activeFonts.size(); i ++) {
218        Font *ithFont = activeFonts[i];
219        if(ithFont->mFontName == name && ithFont->mFontSize == fontSize && ithFont->mDpi == dpi) {
220            return ithFont;
221        }
222    }
223
224    Font *newFont = new Font(rsc);
225    bool isInitialized = newFont->init(name, fontSize, dpi);
226    if(isInitialized) {
227        activeFonts.push(newFont);
228        return newFont;
229    }
230
231    delete newFont;
232    return NULL;
233
234}
235
236Font::~Font()
237{
238    if(mFace) {
239        FT_Done_Face(mFace);
240    }
241
242    for (uint32_t ct = 0; ct < mRSC->mStateFont.mActiveFonts.size(); ct++) {
243        if (mRSC->mStateFont.mActiveFonts[ct] == this) {
244            mRSC->mStateFont.mActiveFonts.removeAt(ct);
245            break;
246        }
247    }
248
249    for(uint32_t i = 0; i < mCachedGlyphs.size(); i ++) {
250        CachedGlyphInfo *glyph = mCachedGlyphs.valueAt(i);
251        delete glyph;
252    }
253}
254
255FontState::FontState()
256{
257    mInitialized = false;
258    mMaxNumberOfQuads = 1024;
259    mCurrentQuadIndex = 0;
260    mRSC = NULL;
261    mLibrary = NULL;
262    setFontColor(0.0f, 0.0f, 0.0f, 1.0f);
263}
264
265FontState::~FontState()
266{
267    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
268        delete mCacheLines[i];
269    }
270
271    rsAssert(!mActiveFonts.size());
272}
273
274FT_Library FontState::getLib()
275{
276    if(!mLibrary) {
277        FT_Error error = FT_Init_FreeType(&mLibrary);
278        if(error) {
279            LOGE("Unable to initialize freetype");
280            return NULL;
281        }
282    }
283
284    return mLibrary;
285}
286
287void FontState::init(Context *rsc)
288{
289    mRSC = rsc;
290}
291
292void FontState::flushAllAndInvalidate()
293{
294    if(mCurrentQuadIndex != 0) {
295        issueDrawCommand();
296        mCurrentQuadIndex = 0;
297    }
298    for(uint32_t i = 0; i < mActiveFonts.size(); i ++) {
299        mActiveFonts[i]->invalidateTextureCache();
300    }
301    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
302        mCacheLines[i]->mCurrentCol = 0;
303    }
304}
305
306bool FontState::cacheBitmap(FT_Bitmap *bitmap, uint32_t *retOriginX, uint32_t *retOriginY)
307{
308    // If the glyph is too tall, don't cache it
309    if((uint32_t)bitmap->rows > mCacheLines[mCacheLines.size()-1]->mMaxHeight) {
310        LOGE("Font size to large to fit in cache. width, height = %i, %i", (int)bitmap->width, (int)bitmap->rows);
311        return false;
312    }
313
314    // Now copy the bitmap into the cache texture
315    uint32_t startX = 0;
316    uint32_t startY = 0;
317
318    bool bitmapFit = false;
319    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
320        bitmapFit = mCacheLines[i]->fitBitmap(bitmap, &startX, &startY);
321        if(bitmapFit) {
322            break;
323        }
324    }
325
326    // If the new glyph didn't fit, flush the state so far and invalidate everything
327    if(!bitmapFit) {
328        flushAllAndInvalidate();
329
330        // Try to fit it again
331        for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
332            bitmapFit = mCacheLines[i]->fitBitmap(bitmap, &startX, &startY);
333            if(bitmapFit) {
334                break;
335            }
336        }
337
338        // if we still don't fit, something is wrong and we shouldn't draw
339        if(!bitmapFit) {
340            LOGE("Bitmap doesn't fit in cache. width, height = %i, %i", (int)bitmap->width, (int)bitmap->rows);
341            return false;
342        }
343    }
344
345    *retOriginX = startX;
346    *retOriginY = startY;
347
348    uint32_t endX = startX + bitmap->width;
349    uint32_t endY = startY + bitmap->rows;
350
351    uint32_t cacheWidth = getCacheTextureType()->getDimX();
352
353    unsigned char *cacheBuffer = (unsigned char*)mTextTexture->getPtr();
354    unsigned char *bitmapBuffer = bitmap->buffer;
355
356    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
357    for(cacheX = startX, bX = 0; cacheX < endX; cacheX ++, bX ++) {
358        for(cacheY = startY, bY = 0; cacheY < endY; cacheY ++, bY ++) {
359            unsigned char tempCol = bitmapBuffer[bY * bitmap->width + bX];
360            cacheBuffer[cacheY*cacheWidth + cacheX] = tempCol;
361        }
362    }
363
364    // This will dirty the texture and the shader so next time
365    // we draw it will upload the data
366    mTextTexture->deferedUploadToTexture(mRSC, false, 0);
367    mFontShaderF->bindTexture(0, mTextTexture.get());
368
369    // Some debug code
370    /*for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
371        LOGE("Cache Line: H: %u Empty Space: %f",
372             mCacheLines[i]->mMaxHeight,
373              (1.0f - (float)mCacheLines[i]->mCurrentCol/(float)mCacheLines[i]->mMaxWidth)*100.0f);
374
375    }*/
376
377    return true;
378}
379
380void FontState::initRenderState()
381{
382    uint32_t tmp[5] = {
383        RS_TEX_ENV_MODE_REPLACE, 1,
384        RS_TEX_ENV_MODE_NONE, 0,
385        0
386    };
387    ProgramFragment *pf = new ProgramFragment(mRSC, tmp, 5);
388    mFontShaderF.set(pf);
389    mFontShaderF->init(mRSC);
390
391    Sampler *sampler = new Sampler(mRSC, RS_SAMPLER_NEAREST, RS_SAMPLER_NEAREST,
392                                      RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP);
393    mFontSampler.set(sampler);
394    mFontShaderF->bindSampler(0, sampler);
395
396    ProgramStore *fontStore = new ProgramStore(mRSC);
397    mFontProgramStore.set(fontStore);
398    mFontProgramStore->setDepthFunc(RS_DEPTH_FUNC_ALWAYS);
399    mFontProgramStore->setBlendFunc(RS_BLEND_SRC_SRC_ALPHA, RS_BLEND_DST_ONE_MINUS_SRC_ALPHA);
400    mFontProgramStore->setDitherEnable(false);
401    mFontProgramStore->setDepthMask(false);
402}
403
404void FontState::initTextTexture()
405{
406    const Element *alphaElem = Element::create(mRSC, RS_TYPE_UNSIGNED_8, RS_KIND_PIXEL_A, true, 1);
407
408    // We will allocate a texture to initially hold 32 character bitmaps
409    Type *texType = new Type(mRSC);
410    texType->setElement(alphaElem);
411    texType->setDimX(1024);
412    texType->setDimY(256);
413    texType->compute();
414
415    Allocation *cacheAlloc = new Allocation(mRSC, texType);
416    mTextTexture.set(cacheAlloc);
417    mTextTexture->deferedUploadToTexture(mRSC, false, 0);
418
419    // Split up our cache texture into lines of certain widths
420    int nextLine = 0;
421    mCacheLines.push(new CacheTextureLine(16, texType->getDimX(), nextLine, 0));
422    nextLine += mCacheLines.top()->mMaxHeight;
423    mCacheLines.push(new CacheTextureLine(24, texType->getDimX(), nextLine, 0));
424    nextLine += mCacheLines.top()->mMaxHeight;
425    mCacheLines.push(new CacheTextureLine(32, texType->getDimX(), nextLine, 0));
426    nextLine += mCacheLines.top()->mMaxHeight;
427    mCacheLines.push(new CacheTextureLine(32, texType->getDimX(), nextLine, 0));
428    nextLine += mCacheLines.top()->mMaxHeight;
429    mCacheLines.push(new CacheTextureLine(40, texType->getDimX(), nextLine, 0));
430    nextLine += mCacheLines.top()->mMaxHeight;
431    mCacheLines.push(new CacheTextureLine(texType->getDimY() - nextLine, texType->getDimX(), nextLine, 0));
432}
433
434// Avoid having to reallocate memory and render quad by quad
435void FontState::initVertexArrayBuffers()
436{
437    // Now lets write index data
438    const Element *indexElem = Element::create(mRSC, RS_TYPE_UNSIGNED_16, RS_KIND_USER, false, 1);
439    Type *indexType = new Type(mRSC);
440    uint32_t numIndicies = mMaxNumberOfQuads * 6;
441    indexType->setDimX(numIndicies);
442    indexType->setElement(indexElem);
443    indexType->compute();
444
445    Allocation *indexAlloc = new Allocation(mRSC, indexType);
446    uint16_t *indexPtr = (uint16_t*)indexAlloc->getPtr();
447
448    // Four verts, two triangles , six indices per quad
449    for(uint32_t i = 0; i < mMaxNumberOfQuads; i ++) {
450        int i6 = i * 6;
451        int i4 = i * 4;
452
453        indexPtr[i6 + 0] = i4 + 0;
454        indexPtr[i6 + 1] = i4 + 1;
455        indexPtr[i6 + 2] = i4 + 2;
456
457        indexPtr[i6 + 3] = i4 + 0;
458        indexPtr[i6 + 4] = i4 + 2;
459        indexPtr[i6 + 5] = i4 + 3;
460    }
461
462    indexAlloc->deferedUploadToBufferObject(mRSC);
463    mIndexBuffer.set(indexAlloc);
464
465    const Element *posElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 3);
466    const Element *texElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 2);
467
468    const Element *elemArray[2];
469    elemArray[0] = posElem;
470    elemArray[1] = texElem;
471
472    String8 posName("position");
473    String8 texName("texture0");
474
475    const char *nameArray[2];
476    nameArray[0] = posName.string();
477    nameArray[1] = texName.string();
478    size_t lengths[2];
479    lengths[0] = posName.size();
480    lengths[1] = texName.size();
481
482    const Element *vertexDataElem = Element::create(mRSC, 2, elemArray, nameArray, lengths);
483
484    Type *vertexDataType = new Type(mRSC);
485    vertexDataType->setDimX(mMaxNumberOfQuads * 4);
486    vertexDataType->setElement(vertexDataElem);
487    vertexDataType->compute();
488
489    Allocation *vertexAlloc = new Allocation(mRSC, vertexDataType);
490    mTextMeshPtr = (float*)vertexAlloc->getPtr();
491
492    mVertexArray.set(vertexAlloc);
493}
494
495// We don't want to allocate anything unless we actually draw text
496void FontState::checkInit()
497{
498    if(mInitialized) {
499        return;
500    }
501
502    initTextTexture();
503    initRenderState();
504
505    initVertexArrayBuffers();
506
507    mInitialized = true;
508}
509
510void FontState::issueDrawCommand() {
511
512    ObjectBaseRef<const ProgramVertex> tmpV(mRSC->getVertex());
513    mRSC->setVertex(mRSC->getDefaultProgramVertex());
514
515    ObjectBaseRef<const ProgramRaster> tmpR(mRSC->getRaster());
516    mRSC->setRaster(mRSC->getDefaultProgramRaster());
517
518    ObjectBaseRef<const ProgramFragment> tmpF(mRSC->getFragment());
519    mRSC->setFragment(mFontShaderF.get());
520
521    ObjectBaseRef<const ProgramStore> tmpPS(mRSC->getFragmentStore());
522    mRSC->setFragmentStore(mFontProgramStore.get());
523
524    if (!mRSC->setupCheck()) {
525        mRSC->setVertex((ProgramVertex *)tmpV.get());
526        mRSC->setRaster((ProgramRaster *)tmpR.get());
527        mRSC->setFragment((ProgramFragment *)tmpF.get());
528        mRSC->setFragmentStore((ProgramStore *)tmpPS.get());
529        return;
530    }
531
532    float *vtx = (float*)mVertexArray->getPtr();
533    float *tex = vtx + 3;
534
535    VertexArray va;
536    va.add(GL_FLOAT, 3, 20, false, (uint32_t)vtx, "position");
537    va.add(GL_FLOAT, 2, 20, false, (uint32_t)tex, "texture0");
538    va.setupGL2(mRSC, &mRSC->mStateVertexArray, &mRSC->mShaderCache);
539
540    mIndexBuffer->uploadCheck(mRSC);
541    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer->getBufferObjectID());
542    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, (uint16_t *)(0));
543
544    // Reset the state
545    mRSC->setVertex((ProgramVertex *)tmpV.get());
546    mRSC->setRaster((ProgramRaster *)tmpR.get());
547    mRSC->setFragment((ProgramFragment *)tmpF.get());
548    mRSC->setFragmentStore((ProgramStore *)tmpPS.get());
549}
550
551void FontState::appendMeshQuad(float x1, float y1, float z1,
552                                  float u1, float v1,
553                                  float x2, float y2, float z2,
554                                  float u2, float v2,
555                                  float x3, float y3, float z3,
556                                  float u3, float v3,
557                                  float x4, float y4, float z4,
558                                  float u4, float v4)
559{
560    const uint32_t vertsPerQuad = 4;
561    const uint32_t floatsPerVert = 5;
562    float *currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
563
564    // Cull things that are off the screen
565    float width = (float)mRSC->getWidth();
566    float height = (float)mRSC->getHeight();
567
568    if(x1 > width || y1 < 0.0f || x2 < 0 || y4 > height) {
569        return;
570    }
571
572    /*LOGE("V0 x: %f y: %f z: %f", x1, y1, z1);
573    LOGE("V1 x: %f y: %f z: %f", x2, y2, z2);
574    LOGE("V2 x: %f y: %f z: %f", x3, y3, z3);
575    LOGE("V3 x: %f y: %f z: %f", x4, y4, z4);*/
576
577    (*currentPos++) = x1;
578    (*currentPos++) = y1;
579    (*currentPos++) = z1;
580    (*currentPos++) = u1;
581    (*currentPos++) = v1;
582
583    (*currentPos++) = x2;
584    (*currentPos++) = y2;
585    (*currentPos++) = z2;
586    (*currentPos++) = u2;
587    (*currentPos++) = v2;
588
589    (*currentPos++) = x3;
590    (*currentPos++) = y3;
591    (*currentPos++) = z3;
592    (*currentPos++) = u3;
593    (*currentPos++) = v3;
594
595    (*currentPos++) = x4;
596    (*currentPos++) = y4;
597    (*currentPos++) = z4;
598    (*currentPos++) = u4;
599    (*currentPos++) = v4;
600
601    mCurrentQuadIndex ++;
602
603    if(mCurrentQuadIndex == mMaxNumberOfQuads) {
604        issueDrawCommand();
605        mCurrentQuadIndex = 0;
606    }
607}
608
609void FontState::renderText(const char *text, uint32_t len, uint32_t startIndex, int numGlyphs, int x, int y)
610{
611    checkInit();
612
613    //String8 text8(text);
614
615    // Render code here
616    Font *currentFont = mRSC->getFont();
617    if(!currentFont) {
618        if(!mDefault.get()) {
619            mDefault.set(Font::create(mRSC, "DroidSans.ttf", 16, 96));
620        }
621        currentFont = mDefault.get();
622    }
623    if(!currentFont) {
624        LOGE("Unable to initialize any fonts");
625        return;
626    }
627
628    currentFont->renderUTF(text, len, startIndex, numGlyphs, x, y);
629
630    if(mCurrentQuadIndex != 0) {
631        issueDrawCommand();
632        mCurrentQuadIndex = 0;
633    }
634}
635
636void FontState::renderText(const char *text, int x, int y)
637{
638    size_t textLen = strlen(text);
639    renderText(text, textLen, 0, -1, x, y);
640}
641
642void FontState::renderText(Allocation *alloc, int x, int y)
643{
644    if(!alloc) {
645        return;
646    }
647
648    const char *text = (const char *)alloc->getPtr();
649    size_t allocSize = alloc->getType()->getSizeBytes();
650    renderText(text, allocSize, 0, -1, x, y);
651}
652
653void FontState::renderText(Allocation *alloc, uint32_t start, int len, int x, int y)
654{
655    if(!alloc) {
656        return;
657    }
658
659    const char *text = (const char *)alloc->getPtr();
660    size_t allocSize = alloc->getType()->getSizeBytes();
661    renderText(text, allocSize, start, len, x, y);
662}
663
664void FontState::setFontColor(float r, float g, float b, float a) {
665    mFontColor[0] = r;
666    mFontColor[1] = g;
667    mFontColor[2] = b;
668    mFontColor[3] = a;
669    mFontColorDirty = true;
670}
671
672void FontState::deinit(Context *rsc)
673{
674    mInitialized = false;
675
676    mIndexBuffer.clear();
677    mVertexArray.clear();
678
679    mFontShaderF.clear();
680    mFontSampler.clear();
681    mFontProgramStore.clear();
682
683    mTextTexture.clear();
684    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
685        delete mCacheLines[i];
686    }
687    mCacheLines.clear();
688
689    mDefault.clear();
690
691    Vector<Font*> fontsToDereference = mActiveFonts;
692    for(uint32_t i = 0; i < fontsToDereference.size(); i ++) {
693        fontsToDereference[i]->zeroUserRef();
694    }
695
696    if(mLibrary) {
697        FT_Done_FreeType( mLibrary );
698        mLibrary = NULL;
699    }
700}
701
702namespace android {
703namespace renderscript {
704
705RsFont rsi_FontCreateFromFile(Context *rsc, char const *name, uint32_t fontSize, uint32_t dpi)
706{
707    Font *newFont = Font::create(rsc, name, fontSize, dpi);
708    if(newFont) {
709        newFont->incUserRef();
710    }
711    return newFont;
712}
713
714} // renderscript
715} // android
716