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