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