rsFont.cpp revision d18c744a37441311c9b65254a35db456835adad3
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}
263
264FontState::~FontState()
265{
266    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
267        delete mCacheLines[i];
268    }
269
270    rsAssert(!mActiveFonts.size());
271}
272
273FT_Library FontState::getLib()
274{
275    if(!mLibrary) {
276        FT_Error error = FT_Init_FreeType(&mLibrary);
277        if(error) {
278            LOGE("Unable to initialize freetype");
279            return NULL;
280        }
281    }
282
283    return mLibrary;
284}
285
286void FontState::init(Context *rsc)
287{
288    mRSC = rsc;
289}
290
291void FontState::flushAllAndInvalidate()
292{
293    if(mCurrentQuadIndex != 0) {
294        issueDrawCommand();
295        mCurrentQuadIndex = 0;
296    }
297    for(uint32_t i = 0; i < mActiveFonts.size(); i ++) {
298        mActiveFonts[i]->invalidateTextureCache();
299    }
300    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
301        mCacheLines[i]->mCurrentCol = 0;
302    }
303}
304
305bool FontState::cacheBitmap(FT_Bitmap *bitmap, uint32_t *retOriginX, uint32_t *retOriginY)
306{
307    // If the glyph is too tall, don't cache it
308    if((uint32_t)bitmap->rows > mCacheLines[mCacheLines.size()-1]->mMaxHeight) {
309        LOGE("Font size to large to fit in cache. width, height = %i, %i", (int)bitmap->width, (int)bitmap->rows);
310        return false;
311    }
312
313    // Now copy the bitmap into the cache texture
314    uint32_t startX = 0;
315    uint32_t startY = 0;
316
317    bool bitmapFit = false;
318    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
319        bitmapFit = mCacheLines[i]->fitBitmap(bitmap, &startX, &startY);
320        if(bitmapFit) {
321            break;
322        }
323    }
324
325    // If the new glyph didn't fit, flush the state so far and invalidate everything
326    if(!bitmapFit) {
327        flushAllAndInvalidate();
328
329        // Try to fit it again
330        for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
331            bitmapFit = mCacheLines[i]->fitBitmap(bitmap, &startX, &startY);
332            if(bitmapFit) {
333                break;
334            }
335        }
336
337        // if we still don't fit, something is wrong and we shouldn't draw
338        if(!bitmapFit) {
339            LOGE("Bitmap doesn't fit in cache. width, height = %i, %i", (int)bitmap->width, (int)bitmap->rows);
340            return false;
341        }
342    }
343
344    *retOriginX = startX;
345    *retOriginY = startY;
346
347    uint32_t endX = startX + bitmap->width;
348    uint32_t endY = startY + bitmap->rows;
349
350    uint32_t cacheWidth = getCacheTextureType()->getDimX();
351
352    unsigned char *cacheBuffer = (unsigned char*)mTextTexture->getPtr();
353    unsigned char *bitmapBuffer = bitmap->buffer;
354
355    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
356    for(cacheX = startX, bX = 0; cacheX < endX; cacheX ++, bX ++) {
357        for(cacheY = startY, bY = 0; cacheY < endY; cacheY ++, bY ++) {
358            unsigned char tempCol = bitmapBuffer[bY * bitmap->width + bX];
359            cacheBuffer[cacheY*cacheWidth + cacheX] = tempCol;
360        }
361    }
362
363    // This will dirty the texture and the shader so next time
364    // we draw it will upload the data
365    mTextTexture->deferedUploadToTexture(mRSC, false, 0);
366    mFontShaderF->bindTexture(0, mTextTexture.get());
367
368    // Some debug code
369    /*for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
370        LOGE("Cache Line: H: %u Empty Space: %f",
371             mCacheLines[i]->mMaxHeight,
372              (1.0f - (float)mCacheLines[i]->mCurrentCol/(float)mCacheLines[i]->mMaxWidth)*100.0f);
373
374    }*/
375
376    return true;
377}
378
379void FontState::initRenderState()
380{
381    uint32_t tmp[5] = {
382        RS_TEX_ENV_MODE_REPLACE, 1,
383        RS_TEX_ENV_MODE_NONE, 0,
384        0
385    };
386    ProgramFragment *pf = new ProgramFragment(mRSC, tmp, 5);
387    mFontShaderF.set(pf);
388    mFontShaderF->init(mRSC);
389
390    Sampler *sampler = new Sampler(mRSC, RS_SAMPLER_NEAREST, RS_SAMPLER_NEAREST,
391                                      RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP);
392    mFontSampler.set(sampler);
393    mFontShaderF->bindSampler(0, sampler);
394
395    ProgramStore *fontStore = new ProgramStore(mRSC);
396    mFontProgramStore.set(fontStore);
397    mFontProgramStore->setDepthFunc(RS_DEPTH_FUNC_ALWAYS);
398    mFontProgramStore->setBlendFunc(RS_BLEND_SRC_SRC_ALPHA, RS_BLEND_DST_ONE_MINUS_SRC_ALPHA);
399    mFontProgramStore->setDitherEnable(false);
400    mFontProgramStore->setDepthMask(false);
401}
402
403void FontState::initTextTexture()
404{
405    const Element *alphaElem = Element::create(mRSC, RS_TYPE_UNSIGNED_8, RS_KIND_PIXEL_A, true, 1);
406
407    // We will allocate a texture to initially hold 32 character bitmaps
408    Type *texType = new Type(mRSC);
409    texType->setElement(alphaElem);
410    texType->setDimX(1024);
411    texType->setDimY(256);
412    texType->compute();
413
414    Allocation *cacheAlloc = new Allocation(mRSC, texType);
415    mTextTexture.set(cacheAlloc);
416    mTextTexture->deferedUploadToTexture(mRSC, false, 0);
417
418    // Split up our cache texture into lines of certain widths
419    int nextLine = 0;
420    mCacheLines.push(new CacheTextureLine(16, texType->getDimX(), nextLine, 0));
421    nextLine += mCacheLines.top()->mMaxHeight;
422    mCacheLines.push(new CacheTextureLine(24, texType->getDimX(), nextLine, 0));
423    nextLine += mCacheLines.top()->mMaxHeight;
424    mCacheLines.push(new CacheTextureLine(32, texType->getDimX(), nextLine, 0));
425    nextLine += mCacheLines.top()->mMaxHeight;
426    mCacheLines.push(new CacheTextureLine(32, texType->getDimX(), nextLine, 0));
427    nextLine += mCacheLines.top()->mMaxHeight;
428    mCacheLines.push(new CacheTextureLine(40, texType->getDimX(), nextLine, 0));
429    nextLine += mCacheLines.top()->mMaxHeight;
430    mCacheLines.push(new CacheTextureLine(texType->getDimY() - nextLine, texType->getDimX(), nextLine, 0));
431}
432
433// Avoid having to reallocate memory and render quad by quad
434void FontState::initVertexArrayBuffers()
435{
436    // Now lets write index data
437    const Element *indexElem = Element::create(mRSC, RS_TYPE_UNSIGNED_16, RS_KIND_USER, false, 1);
438    Type *indexType = new Type(mRSC);
439    uint32_t numIndicies = mMaxNumberOfQuads * 6;
440    indexType->setDimX(numIndicies);
441    indexType->setElement(indexElem);
442    indexType->compute();
443
444    Allocation *indexAlloc = new Allocation(mRSC, indexType);
445    uint16_t *indexPtr = (uint16_t*)indexAlloc->getPtr();
446
447    // Four verts, two triangles , six indices per quad
448    for(uint32_t i = 0; i < mMaxNumberOfQuads; i ++) {
449        int i6 = i * 6;
450        int i4 = i * 4;
451
452        indexPtr[i6 + 0] = i4 + 0;
453        indexPtr[i6 + 1] = i4 + 1;
454        indexPtr[i6 + 2] = i4 + 2;
455
456        indexPtr[i6 + 3] = i4 + 0;
457        indexPtr[i6 + 4] = i4 + 2;
458        indexPtr[i6 + 5] = i4 + 3;
459    }
460
461    indexAlloc->deferedUploadToBufferObject(mRSC);
462    mIndexBuffer.set(indexAlloc);
463
464    const Element *posElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 3);
465    const Element *texElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 2);
466
467    const Element *elemArray[2];
468    elemArray[0] = posElem;
469    elemArray[1] = texElem;
470
471    String8 posName("position");
472    String8 texName("texture0");
473
474    const char *nameArray[2];
475    nameArray[0] = posName.string();
476    nameArray[1] = texName.string();
477    size_t lengths[2];
478    lengths[0] = posName.size();
479    lengths[1] = texName.size();
480
481    const Element *vertexDataElem = Element::create(mRSC, 2, elemArray, nameArray, lengths);
482
483    Type *vertexDataType = new Type(mRSC);
484    vertexDataType->setDimX(mMaxNumberOfQuads * 4);
485    vertexDataType->setElement(vertexDataElem);
486    vertexDataType->compute();
487
488    Allocation *vertexAlloc = new Allocation(mRSC, vertexDataType);
489    mTextMeshPtr = (float*)vertexAlloc->getPtr();
490
491    mVertexArray.set(vertexAlloc);
492}
493
494// We don't want to allocate anything unless we actually draw text
495void FontState::checkInit()
496{
497    if(mInitialized) {
498        return;
499    }
500
501    initTextTexture();
502    initRenderState();
503
504    initVertexArrayBuffers();
505
506    mInitialized = true;
507}
508
509void FontState::issueDrawCommand() {
510
511    ObjectBaseRef<const ProgramVertex> tmpV(mRSC->getVertex());
512    mRSC->setVertex(mRSC->getDefaultProgramVertex());
513
514    ObjectBaseRef<const ProgramRaster> tmpR(mRSC->getRaster());
515    mRSC->setRaster(mRSC->getDefaultProgramRaster());
516
517    ObjectBaseRef<const ProgramFragment> tmpF(mRSC->getFragment());
518    mRSC->setFragment(mFontShaderF.get());
519
520    ObjectBaseRef<const ProgramStore> tmpPS(mRSC->getFragmentStore());
521    mRSC->setFragmentStore(mFontProgramStore.get());
522
523    if (!mRSC->setupCheck()) {
524        mRSC->setVertex((ProgramVertex *)tmpV.get());
525        mRSC->setRaster((ProgramRaster *)tmpR.get());
526        mRSC->setFragment((ProgramFragment *)tmpF.get());
527        mRSC->setFragmentStore((ProgramStore *)tmpPS.get());
528        return;
529    }
530
531    float *vtx = (float*)mVertexArray->getPtr();
532    float *tex = vtx + 3;
533
534    VertexArray va;
535    va.add(GL_FLOAT, 3, 20, false, (uint32_t)vtx, "position");
536    va.add(GL_FLOAT, 2, 20, false, (uint32_t)tex, "texture0");
537    va.setupGL2(mRSC, &mRSC->mStateVertexArray, &mRSC->mShaderCache);
538
539    mIndexBuffer->uploadCheck(mRSC);
540    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer->getBufferObjectID());
541    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, (uint16_t *)(0));
542
543    // Reset the state
544    mRSC->setVertex((ProgramVertex *)tmpV.get());
545    mRSC->setRaster((ProgramRaster *)tmpR.get());
546    mRSC->setFragment((ProgramFragment *)tmpF.get());
547    mRSC->setFragmentStore((ProgramStore *)tmpPS.get());
548}
549
550void FontState::appendMeshQuad(float x1, float y1, float z1,
551                                  float u1, float v1,
552                                  float x2, float y2, float z2,
553                                  float u2, float v2,
554                                  float x3, float y3, float z3,
555                                  float u3, float v3,
556                                  float x4, float y4, float z4,
557                                  float u4, float v4)
558{
559    const uint32_t vertsPerQuad = 4;
560    const uint32_t floatsPerVert = 5;
561    float *currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
562
563    // Cull things that are off the screen
564    float width = (float)mRSC->getWidth();
565    float height = (float)mRSC->getHeight();
566
567    if(x1 > width || y1 < 0.0f || x2 < 0 || y4 > height) {
568        return;
569    }
570
571    /*LOGE("V0 x: %f y: %f z: %f", x1, y1, z1);
572    LOGE("V1 x: %f y: %f z: %f", x2, y2, z2);
573    LOGE("V2 x: %f y: %f z: %f", x3, y3, z3);
574    LOGE("V3 x: %f y: %f z: %f", x4, y4, z4);*/
575
576    (*currentPos++) = x1;
577    (*currentPos++) = y1;
578    (*currentPos++) = z1;
579    (*currentPos++) = u1;
580    (*currentPos++) = v1;
581
582    (*currentPos++) = x2;
583    (*currentPos++) = y2;
584    (*currentPos++) = z2;
585    (*currentPos++) = u2;
586    (*currentPos++) = v2;
587
588    (*currentPos++) = x3;
589    (*currentPos++) = y3;
590    (*currentPos++) = z3;
591    (*currentPos++) = u3;
592    (*currentPos++) = v3;
593
594    (*currentPos++) = x4;
595    (*currentPos++) = y4;
596    (*currentPos++) = z4;
597    (*currentPos++) = u4;
598    (*currentPos++) = v4;
599
600    mCurrentQuadIndex ++;
601
602    if(mCurrentQuadIndex == mMaxNumberOfQuads) {
603        issueDrawCommand();
604        mCurrentQuadIndex = 0;
605    }
606}
607
608void FontState::renderText(const char *text, uint32_t len, uint32_t startIndex, int numGlyphs, int x, int y)
609{
610    checkInit();
611
612    //String8 text8(text);
613
614    // Render code here
615    Font *currentFont = mRSC->getFont();
616    if(!currentFont) {
617        if(!mDefault.get()) {
618            mDefault.set(Font::create(mRSC, "DroidSans.ttf", 16, 96));
619        }
620        currentFont = mDefault.get();
621    }
622    if(!currentFont) {
623        LOGE("Unable to initialize any fonts");
624        return;
625    }
626
627    currentFont->renderUTF(text, len, startIndex, numGlyphs, x, y);
628
629    if(mCurrentQuadIndex != 0) {
630        issueDrawCommand();
631        mCurrentQuadIndex = 0;
632    }
633}
634
635void FontState::renderText(const char *text, int x, int y)
636{
637    size_t textLen = strlen(text);
638    renderText(text, textLen, 0, -1, x, y);
639}
640
641void FontState::renderText(Allocation *alloc, int x, int y)
642{
643    if(!alloc) {
644        return;
645    }
646
647    const char *text = (const char *)alloc->getPtr();
648    size_t allocSize = alloc->getType()->getSizeBytes();
649    renderText(text, allocSize, 0, -1, x, y);
650}
651
652void FontState::renderText(Allocation *alloc, uint32_t start, int len, int x, int y)
653{
654    if(!alloc) {
655        return;
656    }
657
658    const char *text = (const char *)alloc->getPtr();
659    size_t allocSize = alloc->getType()->getSizeBytes();
660    renderText(text, allocSize, start, len, x, y);
661}
662
663void FontState::deinit(Context *rsc)
664{
665    mInitialized = false;
666
667    mIndexBuffer.clear();
668    mVertexArray.clear();
669
670    mFontShaderF.clear();
671    mFontSampler.clear();
672    mFontProgramStore.clear();
673
674    mTextTexture.clear();
675    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
676        delete mCacheLines[i];
677    }
678    mCacheLines.clear();
679
680    mDefault.clear();
681
682    Vector<Font*> fontsToDereference = mActiveFonts;
683    for(uint32_t i = 0; i < fontsToDereference.size(); i ++) {
684        fontsToDereference[i]->zeroUserRef();
685    }
686
687    if(mLibrary) {
688        FT_Done_FreeType( mLibrary );
689        mLibrary = NULL;
690    }
691}
692
693namespace android {
694namespace renderscript {
695
696RsFont rsi_FontCreateFromFile(Context *rsc, char const *name, uint32_t fontSize, uint32_t dpi)
697{
698    Font *newFont = Font::create(rsc, name, fontSize, dpi);
699    if(newFont) {
700        newFont->incUserRef();
701    }
702    return newFont;
703}
704
705} // renderscript
706} // android
707