rsFont.cpp revision 46e45548dc80e801139c9ccc2f2aa927e7f35027
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    uint32_t arraySizes[2] = {1, 1};
492
493    const Element *vertexDataElem = Element::create(mRSC, 2, elemArray, nameArray, lengths, arraySizes);
494
495    Type *vertexDataType = new Type(mRSC);
496    vertexDataType->setDimX(mMaxNumberOfQuads * 4);
497    vertexDataType->setElement(vertexDataElem);
498    vertexDataType->compute();
499
500    Allocation *vertexAlloc = new Allocation(mRSC, vertexDataType);
501    mTextMeshPtr = (float*)vertexAlloc->getPtr();
502
503    mVertexArray.set(vertexAlloc);
504}
505
506// We don't want to allocate anything unless we actually draw text
507void FontState::checkInit()
508{
509    if(mInitialized) {
510        return;
511    }
512
513    initTextTexture();
514    initRenderState();
515
516    initVertexArrayBuffers();
517
518    // We store a string with letters in a rough frequency of occurrence
519    mLatinPrecache = String8(" eisarntolcdugpmhbyfvkwzxjq");
520    mLatinPrecache += String8("EISARNTOLCDUGPMHBYFVKWZXJQ");
521    mLatinPrecache += String8(",.?!()-+@;:`'");
522    mLatinPrecache += String8("0123456789");
523
524    mInitialized = true;
525}
526
527void FontState::issueDrawCommand() {
528
529    ObjectBaseRef<const ProgramVertex> tmpV(mRSC->getVertex());
530    mRSC->setVertex(mRSC->getDefaultProgramVertex());
531
532    ObjectBaseRef<const ProgramRaster> tmpR(mRSC->getRaster());
533    mRSC->setRaster(mRSC->getDefaultProgramRaster());
534
535    ObjectBaseRef<const ProgramFragment> tmpF(mRSC->getFragment());
536    mRSC->setFragment(mFontShaderF.get());
537
538    ObjectBaseRef<const ProgramStore> tmpPS(mRSC->getFragmentStore());
539    mRSC->setFragmentStore(mFontProgramStore.get());
540
541    if(mFontColorDirty) {
542        mFontShaderF->setConstantColor(mFontColor[0], mFontColor[1], mFontColor[2], mFontColor[3]);
543        mFontColorDirty = false;
544    }
545
546    if (!mRSC->setupCheck()) {
547        mRSC->setVertex((ProgramVertex *)tmpV.get());
548        mRSC->setRaster((ProgramRaster *)tmpR.get());
549        mRSC->setFragment((ProgramFragment *)tmpF.get());
550        mRSC->setFragmentStore((ProgramStore *)tmpPS.get());
551        return;
552    }
553
554    float *vtx = (float*)mVertexArray->getPtr();
555    float *tex = vtx + 3;
556
557    VertexArray va;
558    va.add(GL_FLOAT, 3, 20, false, (uint32_t)vtx, "position");
559    va.add(GL_FLOAT, 2, 20, false, (uint32_t)tex, "texture0");
560    va.setupGL2(mRSC, &mRSC->mStateVertexArray, &mRSC->mShaderCache);
561
562    mIndexBuffer->uploadCheck(mRSC);
563    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer->getBufferObjectID());
564    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, (uint16_t *)(0));
565
566    // Reset the state
567    mRSC->setVertex((ProgramVertex *)tmpV.get());
568    mRSC->setRaster((ProgramRaster *)tmpR.get());
569    mRSC->setFragment((ProgramFragment *)tmpF.get());
570    mRSC->setFragmentStore((ProgramStore *)tmpPS.get());
571}
572
573void FontState::appendMeshQuad(float x1, float y1, float z1,
574                                  float u1, float v1,
575                                  float x2, float y2, float z2,
576                                  float u2, float v2,
577                                  float x3, float y3, float z3,
578                                  float u3, float v3,
579                                  float x4, float y4, float z4,
580                                  float u4, float v4)
581{
582    const uint32_t vertsPerQuad = 4;
583    const uint32_t floatsPerVert = 5;
584    float *currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
585
586    // Cull things that are off the screen
587    float width = (float)mRSC->getWidth();
588    float height = (float)mRSC->getHeight();
589
590    if(x1 > width || y1 < 0.0f || x2 < 0 || y4 > height) {
591        return;
592    }
593
594    /*LOGE("V0 x: %f y: %f z: %f", x1, y1, z1);
595    LOGE("V1 x: %f y: %f z: %f", x2, y2, z2);
596    LOGE("V2 x: %f y: %f z: %f", x3, y3, z3);
597    LOGE("V3 x: %f y: %f z: %f", x4, y4, z4);*/
598
599    (*currentPos++) = x1;
600    (*currentPos++) = y1;
601    (*currentPos++) = z1;
602    (*currentPos++) = u1;
603    (*currentPos++) = v1;
604
605    (*currentPos++) = x2;
606    (*currentPos++) = y2;
607    (*currentPos++) = z2;
608    (*currentPos++) = u2;
609    (*currentPos++) = v2;
610
611    (*currentPos++) = x3;
612    (*currentPos++) = y3;
613    (*currentPos++) = z3;
614    (*currentPos++) = u3;
615    (*currentPos++) = v3;
616
617    (*currentPos++) = x4;
618    (*currentPos++) = y4;
619    (*currentPos++) = z4;
620    (*currentPos++) = u4;
621    (*currentPos++) = v4;
622
623    mCurrentQuadIndex ++;
624
625    if(mCurrentQuadIndex == mMaxNumberOfQuads) {
626        issueDrawCommand();
627        mCurrentQuadIndex = 0;
628    }
629}
630
631uint32_t FontState::getRemainingCacheCapacity() {
632    uint32_t remainingCapacity = 0;
633    uint32_t totalPixels = 0;
634    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
635         remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
636         totalPixels += mCacheLines[i]->mMaxWidth;
637    }
638    remainingCapacity = (remainingCapacity * 100) / totalPixels;
639    return remainingCapacity;
640}
641
642void FontState::precacheLatin(Font *font) {
643    // Remaining capacity is measured in %
644    uint32_t remainingCapacity = getRemainingCacheCapacity();
645    uint32_t precacheIdx = 0;
646    while(remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
647        font->getCachedUTFChar((int32_t)mLatinPrecache[precacheIdx]);
648        remainingCapacity = getRemainingCacheCapacity();
649        precacheIdx ++;
650    }
651}
652
653
654void FontState::renderText(const char *text, uint32_t len, uint32_t startIndex, int numGlyphs, int x, int y)
655{
656    checkInit();
657
658    // Render code here
659    Font *currentFont = mRSC->getFont();
660    if(!currentFont) {
661        if(!mDefault.get()) {
662            mDefault.set(Font::create(mRSC, "DroidSans.ttf", 16, 96));
663        }
664        currentFont = mDefault.get();
665    }
666    if(!currentFont) {
667        LOGE("Unable to initialize any fonts");
668        return;
669    }
670
671    currentFont->renderUTF(text, len, startIndex, numGlyphs, x, y);
672
673    if(mCurrentQuadIndex != 0) {
674        issueDrawCommand();
675        mCurrentQuadIndex = 0;
676    }
677}
678
679void FontState::renderText(const char *text, int x, int y)
680{
681    size_t textLen = strlen(text);
682    renderText(text, textLen, 0, -1, x, y);
683}
684
685void FontState::renderText(Allocation *alloc, int x, int y)
686{
687    if(!alloc) {
688        return;
689    }
690
691    const char *text = (const char *)alloc->getPtr();
692    size_t allocSize = alloc->getType()->getSizeBytes();
693    renderText(text, allocSize, 0, -1, x, y);
694}
695
696void FontState::renderText(Allocation *alloc, uint32_t start, int len, int x, int y)
697{
698    if(!alloc) {
699        return;
700    }
701
702    const char *text = (const char *)alloc->getPtr();
703    size_t allocSize = alloc->getType()->getSizeBytes();
704    renderText(text, allocSize, start, len, x, y);
705}
706
707void FontState::setFontColor(float r, float g, float b, float a) {
708    mFontColor[0] = r;
709    mFontColor[1] = g;
710    mFontColor[2] = b;
711    mFontColor[3] = a;
712    mFontColorDirty = true;
713}
714
715void FontState::getFontColor(float *r, float *g, float *b, float *a) const {
716    *r = mFontColor[0];
717    *g = mFontColor[1];
718    *b = mFontColor[2];
719    *a = mFontColor[3];
720}
721
722void FontState::deinit(Context *rsc)
723{
724    mInitialized = false;
725
726    mIndexBuffer.clear();
727    mVertexArray.clear();
728
729    mFontShaderF.clear();
730    mFontSampler.clear();
731    mFontProgramStore.clear();
732
733    mTextTexture.clear();
734    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
735        delete mCacheLines[i];
736    }
737    mCacheLines.clear();
738
739    mDefault.clear();
740
741    Vector<Font*> fontsToDereference = mActiveFonts;
742    for(uint32_t i = 0; i < fontsToDereference.size(); i ++) {
743        fontsToDereference[i]->zeroUserRef();
744    }
745
746    if(mLibrary) {
747        FT_Done_FreeType( mLibrary );
748        mLibrary = NULL;
749    }
750}
751
752namespace android {
753namespace renderscript {
754
755RsFont rsi_FontCreateFromFile(Context *rsc, char const *name, uint32_t fontSize, uint32_t dpi)
756{
757    Font *newFont = Font::create(rsc, name, fontSize, dpi);
758    if(newFont) {
759        newFont->incUserRef();
760    }
761    return newFont;
762}
763
764} // renderscript
765} // android
766