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