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