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#include "rsContext.h"
19#include "rs.h"
20#include "rsFont.h"
21#include "rsProgramFragment.h"
22#include "rsMesh.h"
23#ifdef HAVE_ANDROID_OS
24#include <cutils/properties.h>
25#endif
26
27#ifndef ANDROID_RS_SERIALIZE
28#include <ft2build.h>
29#include FT_FREETYPE_H
30#include FT_BITMAP_H
31#endif //ANDROID_RS_SERIALIZE
32
33using namespace android;
34using namespace android::renderscript;
35
36Font::Font(Context *rsc) : ObjectBase(rsc), mCachedGlyphs(NULL) {
37    mInitialized = false;
38    mHasKerning = false;
39    mFace = NULL;
40}
41
42bool Font::init(const char *name, float fontSize, uint32_t dpi, const void *data, uint32_t dataLen) {
43#ifndef ANDROID_RS_SERIALIZE
44    if (mInitialized) {
45        ALOGE("Reinitialization of fonts not supported");
46        return false;
47    }
48
49    FT_Error error = 0;
50    if (data != NULL && dataLen > 0) {
51        error = FT_New_Memory_Face(mRSC->mStateFont.getLib(), (const FT_Byte*)data, dataLen, 0, &mFace);
52    } else {
53        error = FT_New_Face(mRSC->mStateFont.getLib(), name, 0, &mFace);
54    }
55
56    if (error) {
57        ALOGE("Unable to initialize font %s", name);
58        return false;
59    }
60
61    mFontName = rsuCopyString(name);
62    mFontSize = fontSize;
63    mDpi = dpi;
64
65    error = FT_Set_Char_Size(mFace, (FT_F26Dot6)(fontSize * 64.0f), 0, dpi, 0);
66    if (error) {
67        ALOGE("Unable to set font size on %s", name);
68        return false;
69    }
70
71    mHasKerning = FT_HAS_KERNING(mFace);
72
73    mInitialized = true;
74#endif //ANDROID_RS_SERIALIZE
75    return true;
76}
77
78void Font::preDestroy() const {
79    for (uint32_t ct = 0; ct < mRSC->mStateFont.mActiveFonts.size(); ct++) {
80        if (mRSC->mStateFont.mActiveFonts[ct] == this) {
81            mRSC->mStateFont.mActiveFonts.removeAt(ct);
82            break;
83        }
84    }
85}
86
87void Font::invalidateTextureCache() {
88    for (uint32_t i = 0; i < mCachedGlyphs.size(); i ++) {
89        mCachedGlyphs.valueAt(i)->mIsValid = false;
90    }
91}
92
93void Font::drawCachedGlyph(CachedGlyphInfo *glyph, int32_t x, int32_t y) {
94    FontState *state = &mRSC->mStateFont;
95
96    int32_t nPenX = x + glyph->mBitmapLeft;
97    int32_t nPenY = y - glyph->mBitmapTop + glyph->mBitmapHeight;
98
99    float u1 = glyph->mBitmapMinU;
100    float u2 = glyph->mBitmapMaxU;
101    float v1 = glyph->mBitmapMinV;
102    float v2 = glyph->mBitmapMaxV;
103
104    int32_t width = (int32_t) glyph->mBitmapWidth;
105    int32_t height = (int32_t) glyph->mBitmapHeight;
106
107    state->appendMeshQuad(nPenX, nPenY, 0, u1, v2,
108                          nPenX + width, nPenY, 0, u2, v2,
109                          nPenX + width, nPenY - height, 0, u2, v1,
110                          nPenX, nPenY - height, 0, u1, v1);
111}
112
113void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int32_t x, int32_t y,
114                           uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) {
115    int32_t nPenX = x + glyph->mBitmapLeft;
116    int32_t nPenY = y + glyph->mBitmapTop;
117
118    uint32_t endX = glyph->mBitmapMinX + glyph->mBitmapWidth;
119    uint32_t endY = glyph->mBitmapMinY + glyph->mBitmapHeight;
120
121    FontState *state = &mRSC->mStateFont;
122    uint32_t cacheWidth = state->getCacheTextureType()->getDimX();
123    const uint8_t* cacheBuffer = state->mCacheBuffer;
124
125    uint32_t cacheX = 0, cacheY = 0;
126    int32_t bX = 0, bY = 0;
127    for (cacheX = glyph->mBitmapMinX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
128        for (cacheY = glyph->mBitmapMinY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
129            if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
130                ALOGE("Skipping invalid index");
131                continue;
132            }
133            uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
134            bitmap[bY * bitmapW + bX] = tempCol;
135        }
136    }
137}
138
139void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int32_t x, int32_t y, Rect *bounds) {
140    int32_t nPenX = x + glyph->mBitmapLeft;
141    int32_t nPenY = y - glyph->mBitmapTop + glyph->mBitmapHeight;
142
143    int32_t width = (int32_t) glyph->mBitmapWidth;
144    int32_t height = (int32_t) glyph->mBitmapHeight;
145
146    // 0, 0 is top left, so bottom is a positive number
147    if (bounds->bottom < nPenY) {
148        bounds->bottom = nPenY;
149    }
150    if (bounds->left > nPenX) {
151        bounds->left = nPenX;
152    }
153    if (bounds->right < nPenX + width) {
154        bounds->right = nPenX + width;
155    }
156    if (bounds->top > nPenY - height) {
157        bounds->top = nPenY - height;
158    }
159}
160
161void Font::renderUTF(const char *text, uint32_t len, int32_t x, int32_t y,
162                     uint32_t start, int32_t numGlyphs,
163                     RenderMode mode, Rect *bounds,
164                     uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
165    if (!mInitialized || numGlyphs == 0 || text == NULL || len == 0) {
166        return;
167    }
168
169    if (mode == Font::MEASURE) {
170        if (bounds == NULL) {
171            ALOGE("No return rectangle provided to measure text");
172            return;
173        }
174        // Reset min and max of the bounding box to something large
175        bounds->set(1e6, -1e6, 1e6, -1e6);
176    }
177
178    int32_t penX = x, penY = y;
179    int32_t glyphsLeft = 1;
180    if (numGlyphs > 0) {
181        glyphsLeft = numGlyphs;
182    }
183
184    size_t index = start;
185    size_t nextIndex = 0;
186
187    while (glyphsLeft > 0) {
188
189        int32_t utfChar = utf32_from_utf8_at(text, len, index, &nextIndex);
190
191        // Reached the end of the string or encountered
192        if (utfChar < 0) {
193            break;
194        }
195
196        // Move to the next character in the array
197        index = nextIndex;
198
199        CachedGlyphInfo *cachedGlyph = getCachedUTFChar(utfChar);
200
201        // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
202        if (cachedGlyph->mIsValid) {
203            switch (mode) {
204            case FRAMEBUFFER:
205                drawCachedGlyph(cachedGlyph, penX, penY);
206                break;
207            case BITMAP:
208                drawCachedGlyph(cachedGlyph, penX, penY, bitmap, bitmapW, bitmapH);
209                break;
210            case MEASURE:
211                measureCachedGlyph(cachedGlyph, penX, penY, bounds);
212                break;
213            }
214        }
215
216        penX += (cachedGlyph->mAdvanceX >> 6);
217
218        // If we were given a specific number of glyphs, decrement
219        if (numGlyphs > 0) {
220            glyphsLeft --;
221        }
222    }
223}
224
225Font::CachedGlyphInfo* Font::getCachedUTFChar(int32_t utfChar) {
226
227    CachedGlyphInfo *cachedGlyph = mCachedGlyphs.valueFor((uint32_t)utfChar);
228    if (cachedGlyph == NULL) {
229        cachedGlyph = cacheGlyph((uint32_t)utfChar);
230    }
231    // Is the glyph still in texture cache?
232    if (!cachedGlyph->mIsValid) {
233        updateGlyphCache(cachedGlyph);
234    }
235
236    return cachedGlyph;
237}
238
239void Font::updateGlyphCache(CachedGlyphInfo *glyph) {
240#ifndef ANDROID_RS_SERIALIZE
241    FT_Error error = FT_Load_Glyph( mFace, glyph->mGlyphIndex, FT_LOAD_RENDER );
242    if (error) {
243        ALOGE("Couldn't load glyph.");
244        return;
245    }
246
247    glyph->mAdvanceX = mFace->glyph->advance.x;
248    glyph->mAdvanceY = mFace->glyph->advance.y;
249    glyph->mBitmapLeft = mFace->glyph->bitmap_left;
250    glyph->mBitmapTop = mFace->glyph->bitmap_top;
251
252    FT_Bitmap *bitmap = &mFace->glyph->bitmap;
253
254    // Now copy the bitmap into the cache texture
255    uint32_t startX = 0;
256    uint32_t startY = 0;
257
258    // Let the font state figure out where to put the bitmap
259    FontState *state = &mRSC->mStateFont;
260    glyph->mIsValid = state->cacheBitmap(bitmap, &startX, &startY);
261
262    if (!glyph->mIsValid) {
263        return;
264    }
265
266    uint32_t endX = startX + bitmap->width;
267    uint32_t endY = startY + bitmap->rows;
268
269    glyph->mBitmapMinX = startX;
270    glyph->mBitmapMinY = startY;
271    glyph->mBitmapWidth = bitmap->width;
272    glyph->mBitmapHeight = bitmap->rows;
273
274    uint32_t cacheWidth = state->getCacheTextureType()->getDimX();
275    uint32_t cacheHeight = state->getCacheTextureType()->getDimY();
276
277    glyph->mBitmapMinU = (float)startX / (float)cacheWidth;
278    glyph->mBitmapMinV = (float)startY / (float)cacheHeight;
279    glyph->mBitmapMaxU = (float)endX / (float)cacheWidth;
280    glyph->mBitmapMaxV = (float)endY / (float)cacheHeight;
281#endif //ANDROID_RS_SERIALIZE
282}
283
284Font::CachedGlyphInfo *Font::cacheGlyph(uint32_t glyph) {
285    CachedGlyphInfo *newGlyph = new CachedGlyphInfo();
286    mCachedGlyphs.add(glyph, newGlyph);
287#ifndef ANDROID_RS_SERIALIZE
288    newGlyph->mGlyphIndex = FT_Get_Char_Index(mFace, glyph);
289    newGlyph->mIsValid = false;
290#endif //ANDROID_RS_SERIALIZE
291    updateGlyphCache(newGlyph);
292
293    return newGlyph;
294}
295
296Font * Font::create(Context *rsc, const char *name, float fontSize, uint32_t dpi,
297                    const void *data, uint32_t dataLen) {
298    rsc->mStateFont.checkInit();
299    Vector<Font*> &activeFonts = rsc->mStateFont.mActiveFonts;
300
301    for (uint32_t i = 0; i < activeFonts.size(); i ++) {
302        Font *ithFont = activeFonts[i];
303        if (ithFont->mFontName == name && ithFont->mFontSize == fontSize && ithFont->mDpi == dpi) {
304            return ithFont;
305        }
306    }
307
308    Font *newFont = new Font(rsc);
309    bool isInitialized = newFont->init(name, fontSize, dpi, data, dataLen);
310    if (isInitialized) {
311        activeFonts.push(newFont);
312        rsc->mStateFont.precacheLatin(newFont);
313        return newFont;
314    }
315
316    ObjectBase::checkDelete(newFont);
317    return NULL;
318}
319
320Font::~Font() {
321#ifndef ANDROID_RS_SERIALIZE
322    if (mFace) {
323        FT_Done_Face(mFace);
324    }
325#endif
326
327    for (uint32_t i = 0; i < mCachedGlyphs.size(); i ++) {
328        CachedGlyphInfo *glyph = mCachedGlyphs.valueAt(i);
329        delete glyph;
330    }
331}
332
333FontState::FontState() {
334    mInitialized = false;
335    mMaxNumberOfQuads = 1024;
336    mCurrentQuadIndex = 0;
337    mRSC = NULL;
338#ifndef ANDROID_RS_SERIALIZE
339    mLibrary = NULL;
340#endif //ANDROID_RS_SERIALIZE
341
342    float gamma = DEFAULT_TEXT_GAMMA;
343    int32_t blackThreshold = DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD;
344    int32_t whiteThreshold = DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD;
345
346#ifdef HAVE_ANDROID_OS
347    // Get the renderer properties
348    char property[PROPERTY_VALUE_MAX];
349
350    // Get the gamma
351    if (property_get(PROPERTY_TEXT_GAMMA, property, NULL) > 0) {
352        gamma = atof(property);
353    }
354
355    // Get the black gamma threshold
356    if (property_get(PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD, property, NULL) > 0) {
357        blackThreshold = atoi(property);
358    }
359
360    // Get the white gamma threshold
361    if (property_get(PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD, property, NULL) > 0) {
362        whiteThreshold = atoi(property);
363    }
364#endif
365
366    mBlackThreshold = (float)(blackThreshold) / 255.0f;
367    mWhiteThreshold = (float)(whiteThreshold) / 255.0f;
368
369    // Compute the gamma tables
370    mBlackGamma = gamma;
371    mWhiteGamma = 1.0f / gamma;
372
373    setFontColor(0.1f, 0.1f, 0.1f, 1.0f);
374}
375
376FontState::~FontState() {
377    for (uint32_t i = 0; i < mCacheLines.size(); i ++) {
378        delete mCacheLines[i];
379    }
380
381    rsAssert(!mActiveFonts.size());
382}
383#ifndef ANDROID_RS_SERIALIZE
384FT_Library FontState::getLib() {
385    if (!mLibrary) {
386        FT_Error error = FT_Init_FreeType(&mLibrary);
387        if (error) {
388            ALOGE("Unable to initialize freetype");
389            return NULL;
390        }
391    }
392
393    return mLibrary;
394}
395#endif //ANDROID_RS_SERIALIZE
396
397
398void FontState::init(Context *rsc) {
399    mRSC = rsc;
400}
401
402void FontState::flushAllAndInvalidate() {
403    if (mCurrentQuadIndex != 0) {
404        issueDrawCommand();
405        mCurrentQuadIndex = 0;
406    }
407    for (uint32_t i = 0; i < mActiveFonts.size(); i ++) {
408        mActiveFonts[i]->invalidateTextureCache();
409    }
410    for (uint32_t i = 0; i < mCacheLines.size(); i ++) {
411        mCacheLines[i]->mCurrentCol = 0;
412    }
413}
414
415#ifndef ANDROID_RS_SERIALIZE
416bool FontState::cacheBitmap(FT_Bitmap *bitmap, uint32_t *retOriginX, uint32_t *retOriginY) {
417    // If the glyph is too tall, don't cache it
418    if ((uint32_t)bitmap->rows > mCacheLines[mCacheLines.size()-1]->mMaxHeight) {
419        ALOGE("Font size to large to fit in cache. width, height = %i, %i", (int)bitmap->width, (int)bitmap->rows);
420        return false;
421    }
422
423    // Now copy the bitmap into the cache texture
424    uint32_t startX = 0;
425    uint32_t startY = 0;
426
427    bool bitmapFit = false;
428    for (uint32_t i = 0; i < mCacheLines.size(); i ++) {
429        bitmapFit = mCacheLines[i]->fitBitmap(bitmap, &startX, &startY);
430        if (bitmapFit) {
431            break;
432        }
433    }
434
435    // If the new glyph didn't fit, flush the state so far and invalidate everything
436    if (!bitmapFit) {
437        flushAllAndInvalidate();
438
439        // Try to fit it again
440        for (uint32_t i = 0; i < mCacheLines.size(); i ++) {
441            bitmapFit = mCacheLines[i]->fitBitmap(bitmap, &startX, &startY);
442            if (bitmapFit) {
443                break;
444            }
445        }
446
447        // if we still don't fit, something is wrong and we shouldn't draw
448        if (!bitmapFit) {
449            ALOGE("Bitmap doesn't fit in cache. width, height = %i, %i", (int)bitmap->width, (int)bitmap->rows);
450            return false;
451        }
452    }
453
454    *retOriginX = startX;
455    *retOriginY = startY;
456
457    uint32_t endX = startX + bitmap->width;
458    uint32_t endY = startY + bitmap->rows;
459
460    uint32_t cacheWidth = getCacheTextureType()->getDimX();
461
462    uint8_t *cacheBuffer = mCacheBuffer;
463    uint8_t *bitmapBuffer = bitmap->buffer;
464
465    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
466    for (cacheX = startX, bX = 0; cacheX < endX; cacheX ++, bX ++) {
467        for (cacheY = startY, bY = 0; cacheY < endY; cacheY ++, bY ++) {
468            uint8_t tempCol = bitmapBuffer[bY * bitmap->width + bX];
469            cacheBuffer[cacheY*cacheWidth + cacheX] = tempCol;
470        }
471    }
472
473    // This will dirty the texture and the shader so next time
474    // we draw it will upload the data
475
476    mRSC->mHal.funcs.allocation.data2D(mRSC, mTextTexture.get(), 0, 0, 0,
477        RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X, mCacheWidth, mCacheHeight,
478        mCacheBuffer, mCacheWidth*mCacheHeight, mCacheWidth);
479
480    mFontShaderF->bindTexture(mRSC, 0, mTextTexture.get());
481
482    // Some debug code
483    /*for (uint32_t i = 0; i < mCacheLines.size(); i ++) {
484        ALOGE("Cache Line: H: %u Empty Space: %f",
485             mCacheLines[i]->mMaxHeight,
486              (1.0f - (float)mCacheLines[i]->mCurrentCol/(float)mCacheLines[i]->mMaxWidth)*100.0f);
487
488    }*/
489
490    return true;
491}
492#endif //ANDROID_RS_SERIALIZE
493
494void FontState::initRenderState() {
495    const char *shaderString = "varying vec2 varTex0;\n"
496                               "void main() {\n"
497                               "  lowp vec4 col = UNI_Color;\n"
498                               "  col.a = texture2D(UNI_Tex0, varTex0.xy).a;\n"
499                               "  col.a = pow(col.a, UNI_Gamma);\n"
500                               "  gl_FragColor = col;\n"
501                               "}\n";
502
503    const char *textureNames[] = { "Tex0" };
504    const size_t textureNamesLengths[] = { 4 };
505    size_t numTextures = sizeof(textureNamesLengths)/sizeof(*textureNamesLengths);
506
507    ObjectBaseRef<const Element> colorElem = Element::createRef(mRSC, RS_TYPE_FLOAT_32,
508                                                                RS_KIND_USER, false, 4);
509    ObjectBaseRef<const Element> gammaElem = Element::createRef(mRSC, RS_TYPE_FLOAT_32,
510                                                                RS_KIND_USER, false, 1);
511
512    const char *ebn1[] = { "Color", "Gamma" };
513    const Element *ebe1[] = {colorElem.get(), gammaElem.get()};
514    ObjectBaseRef<const Element> constInput = Element::create(mRSC, 2, ebe1, ebn1);
515
516    ObjectBaseRef<Type> inputType = Type::getTypeRef(mRSC, constInput.get(), 1, 0, 0, false, false, 0);
517
518    uintptr_t tmp[4];
519    tmp[0] = RS_PROGRAM_PARAM_CONSTANT;
520    tmp[1] = (uintptr_t)inputType.get();
521    tmp[2] = RS_PROGRAM_PARAM_TEXTURE_TYPE;
522    tmp[3] = RS_TEXTURE_2D;
523
524    mFontShaderFConstant.set(Allocation::createAllocation(mRSC, inputType.get(),
525                                                          RS_ALLOCATION_USAGE_SCRIPT |
526                                                          RS_ALLOCATION_USAGE_GRAPHICS_CONSTANTS));
527    ProgramFragment *pf = new ProgramFragment(mRSC, shaderString, strlen(shaderString),
528                                              textureNames, numTextures, textureNamesLengths,
529                                              tmp, 4);
530    mFontShaderF.set(pf);
531    mFontShaderF->bindAllocation(mRSC, mFontShaderFConstant.get(), 0);
532
533    mFontSampler.set(Sampler::getSampler(mRSC, RS_SAMPLER_NEAREST, RS_SAMPLER_NEAREST,
534                                         RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP,
535                                         RS_SAMPLER_CLAMP).get());
536    mFontShaderF->bindSampler(mRSC, 0, mFontSampler.get());
537
538    mFontProgramStore.set(ProgramStore::getProgramStore(mRSC, true, true, true, true,
539                                                        false, false,
540                                                        RS_BLEND_SRC_SRC_ALPHA,
541                                                        RS_BLEND_DST_ONE_MINUS_SRC_ALPHA,
542                                                        RS_DEPTH_FUNC_ALWAYS).get());
543    mFontProgramStore->init();
544}
545
546void FontState::initTextTexture() {
547    ObjectBaseRef<const Element> alphaElem = Element::createRef(mRSC, RS_TYPE_UNSIGNED_8,
548                                                                RS_KIND_PIXEL_A, true, 1);
549
550    // We will allocate a texture to initially hold 32 character bitmaps
551    mCacheHeight = 256;
552    mCacheWidth = 1024;
553    ObjectBaseRef<Type> texType = Type::getTypeRef(mRSC, alphaElem.get(),
554                                                   mCacheWidth, mCacheHeight, 0, false, false, 0);
555    mCacheBuffer = new uint8_t[mCacheWidth * mCacheHeight];
556
557
558    Allocation *cacheAlloc = Allocation::createAllocation(mRSC, texType.get(),
559                                RS_ALLOCATION_USAGE_GRAPHICS_TEXTURE);
560    mTextTexture.set(cacheAlloc);
561
562    // Split up our cache texture into lines of certain widths
563    int32_t nextLine = 0;
564    mCacheLines.push(new CacheTextureLine(16, texType->getDimX(), nextLine, 0));
565    nextLine += mCacheLines.top()->mMaxHeight;
566    mCacheLines.push(new CacheTextureLine(24, texType->getDimX(), nextLine, 0));
567    nextLine += mCacheLines.top()->mMaxHeight;
568    mCacheLines.push(new CacheTextureLine(24, texType->getDimX(), nextLine, 0));
569    nextLine += mCacheLines.top()->mMaxHeight;
570    mCacheLines.push(new CacheTextureLine(32, texType->getDimX(), nextLine, 0));
571    nextLine += mCacheLines.top()->mMaxHeight;
572    mCacheLines.push(new CacheTextureLine(32, texType->getDimX(), nextLine, 0));
573    nextLine += mCacheLines.top()->mMaxHeight;
574    mCacheLines.push(new CacheTextureLine(40, texType->getDimX(), nextLine, 0));
575    nextLine += mCacheLines.top()->mMaxHeight;
576    mCacheLines.push(new CacheTextureLine(texType->getDimY() - nextLine, texType->getDimX(), nextLine, 0));
577}
578
579// Avoid having to reallocate memory and render quad by quad
580void FontState::initVertexArrayBuffers() {
581    // Now lets write index data
582    ObjectBaseRef<const Element> indexElem = Element::createRef(mRSC, RS_TYPE_UNSIGNED_16, RS_KIND_USER, false, 1);
583    uint32_t numIndicies = mMaxNumberOfQuads * 6;
584    ObjectBaseRef<Type> indexType = Type::getTypeRef(mRSC, indexElem.get(), numIndicies, 0, 0, false, false, 0);
585
586    Allocation *indexAlloc = Allocation::createAllocation(mRSC, indexType.get(),
587                                                          RS_ALLOCATION_USAGE_SCRIPT |
588                                                          RS_ALLOCATION_USAGE_GRAPHICS_VERTEX);
589    uint16_t *indexPtr = (uint16_t*)mRSC->mHal.funcs.allocation.lock1D(mRSC, indexAlloc);
590
591    // Four verts, two triangles , six indices per quad
592    for (uint32_t i = 0; i < mMaxNumberOfQuads; i ++) {
593        int32_t i6 = i * 6;
594        int32_t i4 = i * 4;
595
596        indexPtr[i6 + 0] = i4 + 0;
597        indexPtr[i6 + 1] = i4 + 1;
598        indexPtr[i6 + 2] = i4 + 2;
599
600        indexPtr[i6 + 3] = i4 + 0;
601        indexPtr[i6 + 4] = i4 + 2;
602        indexPtr[i6 + 5] = i4 + 3;
603    }
604
605    indexAlloc->sendDirty(mRSC);
606
607    ObjectBaseRef<const Element> posElem = Element::createRef(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 3);
608    ObjectBaseRef<const Element> texElem = Element::createRef(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 2);
609
610    const char *ebn1[] = { "position", "texture0" };
611    const Element *ebe1[] = {posElem.get(), texElem.get()};
612    ObjectBaseRef<const Element> vertexDataElem = Element::create(mRSC, 2, ebe1, ebn1);
613
614    ObjectBaseRef<Type> vertexDataType = Type::getTypeRef(mRSC, vertexDataElem.get(),
615                                                          mMaxNumberOfQuads * 4,
616                                                          0, 0, false, false, 0);
617
618    Allocation *vertexAlloc = Allocation::createAllocation(mRSC, vertexDataType.get(),
619                                                           RS_ALLOCATION_USAGE_SCRIPT);
620    mTextMeshPtr = (float*)mRSC->mHal.funcs.allocation.lock1D(mRSC, vertexAlloc);
621
622    mMesh.set(new Mesh(mRSC, 1, 1));
623    mMesh->setVertexBuffer(vertexAlloc, 0);
624    mMesh->setPrimitive(indexAlloc, RS_PRIMITIVE_TRIANGLE, 0);
625    mMesh->init();
626    mRSC->mHal.funcs.allocation.unlock1D(mRSC, indexAlloc);
627    mRSC->mHal.funcs.allocation.unlock1D(mRSC, vertexAlloc);
628}
629
630// We don't want to allocate anything unless we actually draw text
631void FontState::checkInit() {
632    if (mInitialized) {
633        return;
634    }
635
636    initTextTexture();
637    initRenderState();
638
639    initVertexArrayBuffers();
640
641    // We store a string with letters in a rough frequency of occurrence
642    mLatinPrecache = " eisarntolcdugpmhbyfvkwzxjq"
643                     "EISARNTOLCDUGPMHBYFVKWZXJQ"
644                     ",.?!()-+@;:`'0123456789";
645    mInitialized = true;
646}
647
648void FontState::issueDrawCommand() {
649    Context::PushState ps(mRSC);
650
651    mRSC->setProgramVertex(mRSC->getDefaultProgramVertex());
652    mRSC->setProgramRaster(mRSC->getDefaultProgramRaster());
653    mRSC->setProgramFragment(mFontShaderF.get());
654    mRSC->setProgramStore(mFontProgramStore.get());
655
656    if (mConstantsDirty) {
657        mFontShaderFConstant->data(mRSC, 0, 0, 1, &mConstants, sizeof(mConstants));
658        mConstantsDirty = false;
659    }
660
661    if (!mRSC->setupCheck()) {
662        return;
663    }
664
665    mMesh->renderPrimitiveRange(mRSC, 0, 0, mCurrentQuadIndex * 6);
666}
667
668void FontState::appendMeshQuad(float x1, float y1, float z1,
669                               float u1, float v1,
670                               float x2, float y2, float z2,
671                               float u2, float v2,
672                               float x3, float y3, float z3,
673                               float u3, float v3,
674                               float x4, float y4, float z4,
675                               float u4, float v4) {
676    const uint32_t vertsPerQuad = 4;
677    const uint32_t floatsPerVert = 6;
678    float *currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
679
680    if (x1 > mSurfaceWidth || y1 < 0.0f || x2 < 0 || y4 > mSurfaceHeight) {
681        return;
682    }
683
684    /*LOGE("V0 x: %f y: %f z: %f", x1, y1, z1);
685    ALOGE("V1 x: %f y: %f z: %f", x2, y2, z2);
686    ALOGE("V2 x: %f y: %f z: %f", x3, y3, z3);
687    ALOGE("V3 x: %f y: %f z: %f", x4, y4, z4);*/
688
689    (*currentPos++) = x1;
690    (*currentPos++) = y1;
691    (*currentPos++) = z1;
692    (*currentPos++) = 0;
693    (*currentPos++) = u1;
694    (*currentPos++) = v1;
695
696    (*currentPos++) = x2;
697    (*currentPos++) = y2;
698    (*currentPos++) = z2;
699    (*currentPos++) = 0;
700    (*currentPos++) = u2;
701    (*currentPos++) = v2;
702
703    (*currentPos++) = x3;
704    (*currentPos++) = y3;
705    (*currentPos++) = z3;
706    (*currentPos++) = 0;
707    (*currentPos++) = u3;
708    (*currentPos++) = v3;
709
710    (*currentPos++) = x4;
711    (*currentPos++) = y4;
712    (*currentPos++) = z4;
713    (*currentPos++) = 0;
714    (*currentPos++) = u4;
715    (*currentPos++) = v4;
716
717    mCurrentQuadIndex ++;
718
719    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
720        issueDrawCommand();
721        mCurrentQuadIndex = 0;
722    }
723}
724
725uint32_t FontState::getRemainingCacheCapacity() {
726    uint32_t remainingCapacity = 0;
727    uint32_t totalPixels = 0;
728    for (uint32_t i = 0; i < mCacheLines.size(); i ++) {
729         remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
730         totalPixels += mCacheLines[i]->mMaxWidth;
731    }
732    remainingCapacity = (remainingCapacity * 100) / totalPixels;
733    return remainingCapacity;
734}
735
736void FontState::precacheLatin(Font *font) {
737    // Remaining capacity is measured in %
738    uint32_t remainingCapacity = getRemainingCacheCapacity();
739    uint32_t precacheIdx = 0;
740    const size_t l = strlen(mLatinPrecache);
741    while ((remainingCapacity > 25) && (precacheIdx < l)) {
742        font->getCachedUTFChar((int32_t)mLatinPrecache[precacheIdx]);
743        remainingCapacity = getRemainingCacheCapacity();
744        precacheIdx ++;
745    }
746}
747
748
749void FontState::renderText(const char *text, uint32_t len, int32_t x, int32_t y,
750                           uint32_t startIndex, int32_t numGlyphs,
751                           Font::RenderMode mode,
752                           Font::Rect *bounds,
753                           uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
754    checkInit();
755
756    // Render code here
757    Font *currentFont = mRSC->getFont();
758    if (!currentFont) {
759        if (!mDefault.get()) {
760            char fullPath[1024];
761            const char * root = getenv("ANDROID_ROOT");
762            rsAssert(strlen(root) < 256);
763            strcpy(fullPath, root);
764            strcat(fullPath, "/fonts/Roboto-Regular.ttf");
765            mDefault.set(Font::create(mRSC, fullPath, 8, mRSC->getDPI()));
766        }
767        currentFont = mDefault.get();
768    }
769    if (!currentFont) {
770        ALOGE("Unable to initialize any fonts");
771        return;
772    }
773
774    // Cull things that are off the screen
775    mSurfaceWidth = (float)mRSC->getCurrentSurfaceWidth();
776    mSurfaceHeight = (float)mRSC->getCurrentSurfaceHeight();
777
778    currentFont->renderUTF(text, len, x, y, startIndex, numGlyphs,
779                           mode, bounds, bitmap, bitmapW, bitmapH);
780
781    if (mCurrentQuadIndex != 0) {
782        issueDrawCommand();
783        mCurrentQuadIndex = 0;
784    }
785}
786
787void FontState::measureText(const char *text, uint32_t len, Font::Rect *bounds) {
788    renderText(text, len, 0, 0, 0, -1, Font::MEASURE, bounds);
789    bounds->bottom = - bounds->bottom;
790    bounds->top = - bounds->top;
791}
792
793void FontState::setFontColor(float r, float g, float b, float a) {
794    mConstants.mFontColor[0] = r;
795    mConstants.mFontColor[1] = g;
796    mConstants.mFontColor[2] = b;
797    mConstants.mFontColor[3] = a;
798
799    mConstants.mGamma = 1.0f;
800    const float luminance = (r * 2.0f + g * 5.0f + b) / 8.0f;
801    if (luminance <= mBlackThreshold) {
802        mConstants.mGamma = mBlackGamma;
803    } else if (luminance >= mWhiteThreshold) {
804        mConstants.mGamma = mWhiteGamma;
805    }
806
807    mConstantsDirty = true;
808}
809
810void FontState::getFontColor(float *r, float *g, float *b, float *a) const {
811    *r = mConstants.mFontColor[0];
812    *g = mConstants.mFontColor[1];
813    *b = mConstants.mFontColor[2];
814    *a = mConstants.mFontColor[3];
815}
816
817void FontState::deinit(Context *rsc) {
818    mInitialized = false;
819
820    mFontShaderFConstant.clear();
821
822    mMesh.clear();
823
824    mFontShaderF.clear();
825    mFontSampler.clear();
826    mFontProgramStore.clear();
827
828    mTextTexture.clear();
829    for (uint32_t i = 0; i < mCacheLines.size(); i ++) {
830        delete mCacheLines[i];
831    }
832    mCacheLines.clear();
833
834    mDefault.clear();
835#ifndef ANDROID_RS_SERIALIZE
836    if (mLibrary) {
837        FT_Done_FreeType( mLibrary );
838        mLibrary = NULL;
839    }
840#endif //ANDROID_RS_SERIALIZE
841}
842
843#ifndef ANDROID_RS_SERIALIZE
844bool FontState::CacheTextureLine::fitBitmap(FT_Bitmap_ *bitmap, uint32_t *retOriginX, uint32_t *retOriginY) {
845    if ((uint32_t)bitmap->rows > mMaxHeight) {
846        return false;
847    }
848
849    if (mCurrentCol + (uint32_t)bitmap->width < mMaxWidth) {
850        *retOriginX = mCurrentCol;
851        *retOriginY = mCurrentRow;
852        mCurrentCol += bitmap->width;
853        mDirty = true;
854       return true;
855    }
856
857    return false;
858}
859#endif //ANDROID_RS_SERIALIZE
860
861namespace android {
862namespace renderscript {
863
864RsFont rsi_FontCreateFromFile(Context *rsc,
865                              char const *name, size_t name_length,
866                              float fontSize, uint32_t dpi) {
867    Font *newFont = Font::create(rsc, name, fontSize, dpi);
868    if (newFont) {
869        newFont->incUserRef();
870    }
871    return newFont;
872}
873
874RsFont rsi_FontCreateFromMemory(Context *rsc,
875                                char const *name, size_t name_length,
876                                float fontSize, uint32_t dpi,
877                                const void *data, size_t data_length) {
878    Font *newFont = Font::create(rsc, name, fontSize, dpi, data, data_length);
879    if (newFont) {
880        newFont->incUserRef();
881    }
882    return newFont;
883}
884
885} // renderscript
886} // android
887