rsFont.cpp revision ca5a454e022caec6c6d3cbb404cc09ea095ba97a
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.1f, 0.1f, 0.1f, 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[] = {
383        RS_TEX_ENV_MODE_REPLACE, 1,
384        RS_TEX_ENV_MODE_NONE, 0,
385        0, 0
386    };
387    ProgramFragment *pf = new ProgramFragment(mRSC, tmp, 6);
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(mFontColorDirty) {
525        mFontShaderF->setConstantColor(mFontColor[0], mFontColor[1], mFontColor[2], mFontColor[3]);
526        mFontColorDirty = false;
527    }
528
529    if (!mRSC->setupCheck()) {
530        mRSC->setVertex((ProgramVertex *)tmpV.get());
531        mRSC->setRaster((ProgramRaster *)tmpR.get());
532        mRSC->setFragment((ProgramFragment *)tmpF.get());
533        mRSC->setFragmentStore((ProgramStore *)tmpPS.get());
534        return;
535    }
536
537    float *vtx = (float*)mVertexArray->getPtr();
538    float *tex = vtx + 3;
539
540    VertexArray va;
541    va.add(GL_FLOAT, 3, 20, false, (uint32_t)vtx, "position");
542    va.add(GL_FLOAT, 2, 20, false, (uint32_t)tex, "texture0");
543    va.setupGL2(mRSC, &mRSC->mStateVertexArray, &mRSC->mShaderCache);
544
545    mIndexBuffer->uploadCheck(mRSC);
546    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer->getBufferObjectID());
547    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, (uint16_t *)(0));
548
549    // Reset the state
550    mRSC->setVertex((ProgramVertex *)tmpV.get());
551    mRSC->setRaster((ProgramRaster *)tmpR.get());
552    mRSC->setFragment((ProgramFragment *)tmpF.get());
553    mRSC->setFragmentStore((ProgramStore *)tmpPS.get());
554}
555
556void FontState::appendMeshQuad(float x1, float y1, float z1,
557                                  float u1, float v1,
558                                  float x2, float y2, float z2,
559                                  float u2, float v2,
560                                  float x3, float y3, float z3,
561                                  float u3, float v3,
562                                  float x4, float y4, float z4,
563                                  float u4, float v4)
564{
565    const uint32_t vertsPerQuad = 4;
566    const uint32_t floatsPerVert = 5;
567    float *currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
568
569    // Cull things that are off the screen
570    float width = (float)mRSC->getWidth();
571    float height = (float)mRSC->getHeight();
572
573    if(x1 > width || y1 < 0.0f || x2 < 0 || y4 > height) {
574        return;
575    }
576
577    /*LOGE("V0 x: %f y: %f z: %f", x1, y1, z1);
578    LOGE("V1 x: %f y: %f z: %f", x2, y2, z2);
579    LOGE("V2 x: %f y: %f z: %f", x3, y3, z3);
580    LOGE("V3 x: %f y: %f z: %f", x4, y4, z4);*/
581
582    (*currentPos++) = x1;
583    (*currentPos++) = y1;
584    (*currentPos++) = z1;
585    (*currentPos++) = u1;
586    (*currentPos++) = v1;
587
588    (*currentPos++) = x2;
589    (*currentPos++) = y2;
590    (*currentPos++) = z2;
591    (*currentPos++) = u2;
592    (*currentPos++) = v2;
593
594    (*currentPos++) = x3;
595    (*currentPos++) = y3;
596    (*currentPos++) = z3;
597    (*currentPos++) = u3;
598    (*currentPos++) = v3;
599
600    (*currentPos++) = x4;
601    (*currentPos++) = y4;
602    (*currentPos++) = z4;
603    (*currentPos++) = u4;
604    (*currentPos++) = v4;
605
606    mCurrentQuadIndex ++;
607
608    if(mCurrentQuadIndex == mMaxNumberOfQuads) {
609        issueDrawCommand();
610        mCurrentQuadIndex = 0;
611    }
612}
613
614void FontState::renderText(const char *text, uint32_t len, uint32_t startIndex, int numGlyphs, int x, int y)
615{
616    checkInit();
617
618    //String8 text8(text);
619
620    // Render code here
621    Font *currentFont = mRSC->getFont();
622    if(!currentFont) {
623        if(!mDefault.get()) {
624            mDefault.set(Font::create(mRSC, "DroidSans.ttf", 16, 96));
625        }
626        currentFont = mDefault.get();
627    }
628    if(!currentFont) {
629        LOGE("Unable to initialize any fonts");
630        return;
631    }
632
633    currentFont->renderUTF(text, len, startIndex, numGlyphs, x, y);
634
635    if(mCurrentQuadIndex != 0) {
636        issueDrawCommand();
637        mCurrentQuadIndex = 0;
638    }
639}
640
641void FontState::renderText(const char *text, int x, int y)
642{
643    size_t textLen = strlen(text);
644    renderText(text, textLen, 0, -1, x, y);
645}
646
647void FontState::renderText(Allocation *alloc, int x, int y)
648{
649    if(!alloc) {
650        return;
651    }
652
653    const char *text = (const char *)alloc->getPtr();
654    size_t allocSize = alloc->getType()->getSizeBytes();
655    renderText(text, allocSize, 0, -1, x, y);
656}
657
658void FontState::renderText(Allocation *alloc, uint32_t start, int len, int x, int y)
659{
660    if(!alloc) {
661        return;
662    }
663
664    const char *text = (const char *)alloc->getPtr();
665    size_t allocSize = alloc->getType()->getSizeBytes();
666    renderText(text, allocSize, start, len, x, y);
667}
668
669void FontState::setFontColor(float r, float g, float b, float a) {
670    mFontColor[0] = r;
671    mFontColor[1] = g;
672    mFontColor[2] = b;
673    mFontColor[3] = a;
674    mFontColorDirty = true;
675}
676
677void FontState::getFontColor(float *r, float *g, float *b, float *a) const {
678    *r = mFontColor[0];
679    *g = mFontColor[1];
680    *b = mFontColor[2];
681    *a = mFontColor[3];
682}
683
684void FontState::deinit(Context *rsc)
685{
686    mInitialized = false;
687
688    mIndexBuffer.clear();
689    mVertexArray.clear();
690
691    mFontShaderF.clear();
692    mFontSampler.clear();
693    mFontProgramStore.clear();
694
695    mTextTexture.clear();
696    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
697        delete mCacheLines[i];
698    }
699    mCacheLines.clear();
700
701    mDefault.clear();
702
703    Vector<Font*> fontsToDereference = mActiveFonts;
704    for(uint32_t i = 0; i < fontsToDereference.size(); i ++) {
705        fontsToDereference[i]->zeroUserRef();
706    }
707
708    if(mLibrary) {
709        FT_Done_FreeType( mLibrary );
710        mLibrary = NULL;
711    }
712}
713
714namespace android {
715namespace renderscript {
716
717RsFont rsi_FontCreateFromFile(Context *rsc, char const *name, uint32_t fontSize, uint32_t dpi)
718{
719    Font *newFont = Font::create(rsc, name, fontSize, dpi);
720    if(newFont) {
721        newFont->incUserRef();
722    }
723    return newFont;
724}
725
726} // renderscript
727} // android
728