Font.cpp revision f088c349dfea985e561d7e838ecd41be5168cd4a
11ae29591efbb29492ce05378909ccf4028d7c1eeBehdad Esfahbod/*
230e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbod * Copyright (C) 2012 The Android Open Source Project
330e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbod *
42b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod * Licensed under the Apache License, Version 2.0 (the "License");
57842e56b97ce677b83bdab09cda48bc2d89ac75aJust * you may not use this file except in compliance with the License.
630e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbod * You may obtain a copy of the License at
77842e56b97ce677b83bdab09cda48bc2d89ac75aJust *
87842e56b97ce677b83bdab09cda48bc2d89ac75aJust *      http://www.apache.org/licenses/LICENSE-2.0
97842e56b97ce677b83bdab09cda48bc2d89ac75aJust *
107842e56b97ce677b83bdab09cda48bc2d89ac75aJust * Unless required by applicable law or agreed to in writing, software
117842e56b97ce677b83bdab09cda48bc2d89ac75aJust * distributed under the License is distributed on an "AS IS" BASIS,
127842e56b97ce677b83bdab09cda48bc2d89ac75aJust * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137842e56b97ce677b83bdab09cda48bc2d89ac75aJust * See the License for the specific language governing permissions and
147842e56b97ce677b83bdab09cda48bc2d89ac75aJust * limitations under the License.
157842e56b97ce677b83bdab09cda48bc2d89ac75aJust */
167842e56b97ce677b83bdab09cda48bc2d89ac75aJust
17e23942f0e6c9020d2a677b23ed58e04ad1173bbdJust#define LOG_TAG "OpenGLRenderer"
18dc66e7e1180e77999ba8c02683956fb31f0e1003fcoiffie#define ATRACE_TAG ATRACE_TAG_VIEW
197842e56b97ce677b83bdab09cda48bc2d89ac75aJust
207842e56b97ce677b83bdab09cda48bc2d89ac75aJust#include <cutils/compiler.h>
217842e56b97ce677b83bdab09cda48bc2d89ac75aJust
227842e56b97ce677b83bdab09cda48bc2d89ac75aJust#include <utils/JenkinsHash.h>
237842e56b97ce677b83bdab09cda48bc2d89ac75aJust#include <utils/Trace.h>
247842e56b97ce677b83bdab09cda48bc2d89ac75aJust
257842e56b97ce677b83bdab09cda48bc2d89ac75aJust#include <SkGlyph.h>
267842e56b97ce677b83bdab09cda48bc2d89ac75aJust#include <SkGlyphCache.h>
277842e56b97ce677b83bdab09cda48bc2d89ac75aJust#include <SkUtils.h>
287842e56b97ce677b83bdab09cda48bc2d89ac75aJust
29c2297cd41d6c00b95f857b65bc9fd4b57559ac5eBehdad Esfahbod#include "FontUtil.h"
307842e56b97ce677b83bdab09cda48bc2d89ac75aJust#include "Font.h"
317842e56b97ce677b83bdab09cda48bc2d89ac75aJust#include "../Debug.h"
327842e56b97ce677b83bdab09cda48bc2d89ac75aJust#include "../FontRenderer.h"
337842e56b97ce677b83bdab09cda48bc2d89ac75aJust#include "../PixelBuffer.h"
347842e56b97ce677b83bdab09cda48bc2d89ac75aJust#include "../Properties.h"
357842e56b97ce677b83bdab09cda48bc2d89ac75aJust
367842e56b97ce677b83bdab09cda48bc2d89ac75aJustnamespace android {
377842e56b97ce677b83bdab09cda48bc2d89ac75aJustnamespace uirenderer {
387842e56b97ce677b83bdab09cda48bc2d89ac75aJust
397842e56b97ce677b83bdab09cda48bc2d89ac75aJust///////////////////////////////////////////////////////////////////////////////
40ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod// Font
417842e56b97ce677b83bdab09cda48bc2d89ac75aJust///////////////////////////////////////////////////////////////////////////////
427842e56b97ce677b83bdab09cda48bc2d89ac75aJust
437842e56b97ce677b83bdab09cda48bc2d89ac75aJustFont::Font(FontRenderer* state, const Font::FontDescription& desc) :
447842e56b97ce677b83bdab09cda48bc2d89ac75aJust        mState(state), mDescription(desc) {
453a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod    mDeviceProperties = SkDeviceProperties::Make(SkDeviceProperties::Geometry::MakeDefault(), 1.0f);
467842e56b97ce677b83bdab09cda48bc2d89ac75aJust}
477842e56b97ce677b83bdab09cda48bc2d89ac75aJust
48180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad EsfahbodFont::FontDescription::FontDescription(const SkPaint* paint, const SkMatrix& rasterMatrix)
497842e56b97ce677b83bdab09cda48bc2d89ac75aJust        : mLookupTransform(rasterMatrix) {
507842e56b97ce677b83bdab09cda48bc2d89ac75aJust    mFontId = SkTypeface::UniqueID(paint->getTypeface());
517842e56b97ce677b83bdab09cda48bc2d89ac75aJust    mFontSize = paint->getTextSize();
52    mFlags = 0;
53    if (paint->isFakeBoldText()) {
54        mFlags |= Font::kFakeBold;
55    }
56    mItalicStyle = paint->getTextSkewX();
57    mScaleX = paint->getTextScaleX();
58    mStyle = paint->getStyle();
59    mStrokeWidth = paint->getStrokeWidth();
60    mAntiAliasing = paint->isAntiAlias();
61    mHinting = paint->getHinting();
62    if (!mLookupTransform.invert(&mInverseLookupTransform)) {
63        ALOGW("Could not query the inverse lookup transform for this font");
64    }
65}
66
67Font::~Font() {
68    mState->removeFont(this);
69
70    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
71        delete mCachedGlyphs.valueAt(i);
72    }
73}
74
75hash_t Font::FontDescription::hash() const {
76    uint32_t hash = JenkinsHashMix(0, mFontId);
77    hash = JenkinsHashMix(hash, android::hash_type(mFontSize));
78    hash = JenkinsHashMix(hash, android::hash_type(mFlags));
79    hash = JenkinsHashMix(hash, android::hash_type(mItalicStyle));
80    hash = JenkinsHashMix(hash, android::hash_type(mScaleX));
81    hash = JenkinsHashMix(hash, android::hash_type(mStyle));
82    hash = JenkinsHashMix(hash, android::hash_type(mStrokeWidth));
83    hash = JenkinsHashMix(hash, int(mAntiAliasing));
84    hash = JenkinsHashMix(hash, android::hash_type(mHinting));
85    hash = JenkinsHashMix(hash, android::hash_type(mLookupTransform[SkMatrix::kMScaleX]));
86    hash = JenkinsHashMix(hash, android::hash_type(mLookupTransform[SkMatrix::kMScaleY]));
87    return JenkinsHashWhiten(hash);
88}
89
90int Font::FontDescription::compare(const Font::FontDescription& lhs,
91        const Font::FontDescription& rhs) {
92    int deltaInt = int(lhs.mFontId) - int(rhs.mFontId);
93    if (deltaInt != 0) return deltaInt;
94
95    if (lhs.mFontSize < rhs.mFontSize) return -1;
96    if (lhs.mFontSize > rhs.mFontSize) return +1;
97
98    if (lhs.mItalicStyle < rhs.mItalicStyle) return -1;
99    if (lhs.mItalicStyle > rhs.mItalicStyle) return +1;
100
101    deltaInt = int(lhs.mFlags) - int(rhs.mFlags);
102    if (deltaInt != 0) return deltaInt;
103
104    if (lhs.mScaleX < rhs.mScaleX) return -1;
105    if (lhs.mScaleX > rhs.mScaleX) return +1;
106
107    deltaInt = int(lhs.mStyle) - int(rhs.mStyle);
108    if (deltaInt != 0) return deltaInt;
109
110    if (lhs.mStrokeWidth < rhs.mStrokeWidth) return -1;
111    if (lhs.mStrokeWidth > rhs.mStrokeWidth) return +1;
112
113    deltaInt = int(lhs.mAntiAliasing) - int(rhs.mAntiAliasing);
114    if (deltaInt != 0) return deltaInt;
115
116    deltaInt = int(lhs.mHinting) - int(rhs.mHinting);
117    if (deltaInt != 0) return deltaInt;
118
119    if (lhs.mLookupTransform[SkMatrix::kMScaleX] <
120            rhs.mLookupTransform[SkMatrix::kMScaleX]) return -1;
121    if (lhs.mLookupTransform[SkMatrix::kMScaleX] >
122            rhs.mLookupTransform[SkMatrix::kMScaleX]) return +1;
123
124    if (lhs.mLookupTransform[SkMatrix::kMScaleY] <
125            rhs.mLookupTransform[SkMatrix::kMScaleY]) return -1;
126    if (lhs.mLookupTransform[SkMatrix::kMScaleY] >
127            rhs.mLookupTransform[SkMatrix::kMScaleY]) return +1;
128
129    return 0;
130}
131
132void Font::invalidateTextureCache(CacheTexture* cacheTexture) {
133    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
134        CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i);
135        if (!cacheTexture || cachedGlyph->mCacheTexture == cacheTexture) {
136            cachedGlyph->mIsValid = false;
137        }
138    }
139}
140
141void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
142        uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds,
143        const float* pos) {
144    int width = (int) glyph->mBitmapWidth;
145    int height = (int) glyph->mBitmapHeight;
146
147    int nPenX = x + glyph->mBitmapLeft;
148    int nPenY = y + glyph->mBitmapTop;
149
150    if (bounds->bottom > nPenY) {
151        bounds->bottom = nPenY;
152    }
153    if (bounds->left > nPenX) {
154        bounds->left = nPenX;
155    }
156    if (bounds->right < nPenX + width) {
157        bounds->right = nPenX + width;
158    }
159    if (bounds->top < nPenY + height) {
160        bounds->top = nPenY + height;
161    }
162}
163
164void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
165        uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds,
166        const float* pos) {
167    float width = (float) glyph->mBitmapWidth;
168    float height = (float) glyph->mBitmapHeight;
169
170    float nPenX = x + glyph->mBitmapLeft;
171    float nPenY = y + glyph->mBitmapTop + height;
172
173    float u1 = glyph->mBitmapMinU;
174    float u2 = glyph->mBitmapMaxU;
175    float v1 = glyph->mBitmapMinV;
176    float v2 = glyph->mBitmapMaxV;
177
178    mState->appendMeshQuad(nPenX, nPenY, u1, v2,
179            nPenX + width, nPenY, u2, v2,
180            nPenX + width, nPenY - height, u2, v1,
181            nPenX, nPenY - height, u1, v1, glyph->mCacheTexture);
182}
183
184void Font::drawCachedGlyphTransformed(CachedGlyphInfo* glyph, int x, int y,
185        uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds,
186        const float* pos) {
187    float width = (float) glyph->mBitmapWidth;
188    float height = (float) glyph->mBitmapHeight;
189
190    SkPoint p[4];
191    p[0].iset(glyph->mBitmapLeft, glyph->mBitmapTop + height);
192    p[1].iset(glyph->mBitmapLeft + width, glyph->mBitmapTop + height);
193    p[2].iset(glyph->mBitmapLeft + width, glyph->mBitmapTop);
194    p[3].iset(glyph->mBitmapLeft, glyph->mBitmapTop);
195
196    mDescription.mInverseLookupTransform.mapPoints(p, 4);
197
198    p[0].offset(x, y);
199    p[1].offset(x, y);
200    p[2].offset(x, y);
201    p[3].offset(x, y);
202
203    float u1 = glyph->mBitmapMinU;
204    float u2 = glyph->mBitmapMaxU;
205    float v1 = glyph->mBitmapMinV;
206    float v2 = glyph->mBitmapMaxV;
207
208    mState->appendRotatedMeshQuad(
209            p[0].x(), p[0].y(), u1, v2,
210            p[1].x(), p[1].y(), u2, v2,
211            p[2].x(), p[2].y(), u2, v1,
212            p[3].x(), p[3].y(), u1, v1, glyph->mCacheTexture);
213}
214
215void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y, uint8_t* bitmap,
216        uint32_t bitmapWidth, uint32_t bitmapHeight, Rect* bounds,
217        const float* pos) {
218    int dstX = x + glyph->mBitmapLeft;
219    int dstY = y + glyph->mBitmapTop;
220
221    CacheTexture* cacheTexture = glyph->mCacheTexture;
222    PixelBuffer* pixelBuffer = cacheTexture->getPixelBuffer();
223
224    uint32_t formatSize = PixelBuffer::formatSize(pixelBuffer->getFormat());
225    uint32_t alpha_channel_offset = PixelBuffer::formatAlphaOffset(pixelBuffer->getFormat());
226    uint32_t cacheWidth = cacheTexture->getWidth();
227    uint32_t srcStride = formatSize * cacheWidth;
228    uint32_t startY = glyph->mStartY * srcStride;
229    uint32_t endY = startY + (glyph->mBitmapHeight * srcStride);
230
231    const uint8_t* cacheBuffer = pixelBuffer->map();
232
233    for (uint32_t cacheY = startY, bitmapY = dstY * bitmapWidth; cacheY < endY;
234            cacheY += srcStride, bitmapY += bitmapWidth) {
235
236        if (formatSize == 1) {
237            memcpy(&bitmap[bitmapY + dstX], &cacheBuffer[cacheY + glyph->mStartX], glyph->mBitmapWidth);
238        } else {
239            for (uint32_t i = 0; i < glyph->mBitmapWidth; ++i) {
240                bitmap[bitmapY + dstX + i] = cacheBuffer[cacheY + (glyph->mStartX + i)*formatSize + alpha_channel_offset];
241            }
242        }
243    }
244
245}
246
247void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
248        SkPathMeasure& measure, SkPoint* position, SkVector* tangent) {
249    const float halfWidth = glyph->mBitmapWidth * 0.5f;
250    const float height = glyph->mBitmapHeight;
251
252    vOffset += glyph->mBitmapTop + height;
253
254    SkPoint destination[4];
255    bool ok = measure.getPosTan(x + hOffset + glyph->mBitmapLeft + halfWidth, position, tangent);
256    if (!ok) {
257        ALOGW("The path for drawTextOnPath is empty or null");
258    }
259
260    // Move along the tangent and offset by the normal
261    destination[0].set(-tangent->fX * halfWidth - tangent->fY * vOffset,
262            -tangent->fY * halfWidth + tangent->fX * vOffset);
263    destination[1].set(tangent->fX * halfWidth - tangent->fY * vOffset,
264            tangent->fY * halfWidth + tangent->fX * vOffset);
265    destination[2].set(destination[1].fX + tangent->fY * height,
266            destination[1].fY - tangent->fX * height);
267    destination[3].set(destination[0].fX + tangent->fY * height,
268            destination[0].fY - tangent->fX * height);
269
270    const float u1 = glyph->mBitmapMinU;
271    const float u2 = glyph->mBitmapMaxU;
272    const float v1 = glyph->mBitmapMinV;
273    const float v2 = glyph->mBitmapMaxV;
274
275    mState->appendRotatedMeshQuad(
276            position->x() + destination[0].x(),
277            position->y() + destination[0].y(), u1, v2,
278            position->x() + destination[1].x(),
279            position->y() + destination[1].y(), u2, v2,
280            position->x() + destination[2].x(),
281            position->y() + destination[2].y(), u2, v1,
282            position->x() + destination[3].x(),
283            position->y() + destination[3].y(), u1, v1,
284            glyph->mCacheTexture);
285}
286
287CachedGlyphInfo* Font::getCachedGlyph(const SkPaint* paint, glyph_t textUnit, bool precaching) {
288    CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueFor(textUnit);
289    if (cachedGlyph) {
290        // Is the glyph still in texture cache?
291        if (!cachedGlyph->mIsValid) {
292            SkAutoGlyphCache autoCache(*paint, &mDeviceProperties, &mDescription.mLookupTransform);
293            const SkGlyph& skiaGlyph = GET_METRICS(autoCache.getCache(), textUnit);
294            updateGlyphCache(paint, skiaGlyph, autoCache.getCache(), cachedGlyph, precaching);
295        }
296    } else {
297        cachedGlyph = cacheGlyph(paint, textUnit, precaching);
298    }
299
300    return cachedGlyph;
301}
302
303void Font::render(const SkPaint* paint, const char *text, uint32_t start, uint32_t len,
304            int numGlyphs, int x, int y, const float* positions) {
305    render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
306            0, 0, NULL, positions);
307}
308
309void Font::render(const SkPaint* paint, const char *text, uint32_t start, uint32_t len,
310        int numGlyphs, const SkPath* path, float hOffset, float vOffset) {
311    if (numGlyphs == 0 || text == NULL || len == 0) {
312        return;
313    }
314
315    text += start;
316
317    int glyphsCount = 0;
318    SkFixed prevRsbDelta = 0;
319
320    float penX = 0.0f;
321
322    SkPoint position;
323    SkVector tangent;
324
325    SkPathMeasure measure(*path, false);
326    float pathLength = SkScalarToFloat(measure.getLength());
327
328    if (paint->getTextAlign() != SkPaint::kLeft_Align) {
329        float textWidth = SkScalarToFloat(paint->measureText(text, len));
330        float pathOffset = pathLength;
331        if (paint->getTextAlign() == SkPaint::kCenter_Align) {
332            textWidth *= 0.5f;
333            pathOffset *= 0.5f;
334        }
335        penX += pathOffset - textWidth;
336    }
337
338    while (glyphsCount < numGlyphs && penX < pathLength) {
339        glyph_t glyph = GET_GLYPH(text);
340
341        if (IS_END_OF_STRING(glyph)) {
342            break;
343        }
344
345        CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
346        penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
347        prevRsbDelta = cachedGlyph->mRsbDelta;
348
349        if (cachedGlyph->mIsValid && cachedGlyph->mCacheTexture) {
350            drawCachedGlyph(cachedGlyph, penX, hOffset, vOffset, measure, &position, &tangent);
351        }
352
353        penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
354
355        glyphsCount++;
356    }
357}
358
359void Font::measure(const SkPaint* paint, const char* text, uint32_t start, uint32_t len,
360        int numGlyphs, Rect *bounds, const float* positions) {
361    if (bounds == NULL) {
362        ALOGE("No return rectangle provided to measure text");
363        return;
364    }
365    bounds->set(1e6, -1e6, -1e6, 1e6);
366    render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, positions);
367}
368
369void Font::precache(const SkPaint* paint, const char* text, int numGlyphs) {
370    ATRACE_NAME("Precache Glyphs");
371
372    if (numGlyphs == 0 || text == NULL) {
373        return;
374    }
375
376    int glyphsCount = 0;
377    while (glyphsCount < numGlyphs) {
378        glyph_t glyph = GET_GLYPH(text);
379
380        // Reached the end of the string
381        if (IS_END_OF_STRING(glyph)) {
382            break;
383        }
384
385        getCachedGlyph(paint, glyph, true);
386        glyphsCount++;
387    }
388}
389
390void Font::render(const SkPaint* paint, const char* text, uint32_t start, uint32_t len,
391        int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
392        uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) {
393    if (numGlyphs == 0 || text == NULL || len == 0) {
394        return;
395    }
396
397    static RenderGlyph gRenderGlyph[] = {
398            &android::uirenderer::Font::drawCachedGlyph,
399            &android::uirenderer::Font::drawCachedGlyphTransformed,
400            &android::uirenderer::Font::drawCachedGlyphBitmap,
401            &android::uirenderer::Font::drawCachedGlyphBitmap,
402            &android::uirenderer::Font::measureCachedGlyph,
403            &android::uirenderer::Font::measureCachedGlyph
404    };
405    RenderGlyph render = gRenderGlyph[(mode << 1) + !mIdentityTransform];
406
407    text += start;
408    int glyphsCount = 0;
409
410    while (glyphsCount < numGlyphs) {
411        glyph_t glyph = GET_GLYPH(text);
412
413        // Reached the end of the string
414        if (IS_END_OF_STRING(glyph)) {
415            break;
416        }
417
418        CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
419
420        // If it's still not valid, we couldn't cache it, so we shouldn't
421        // draw garbage; also skip empty glyphs (spaces)
422        if (cachedGlyph->mIsValid && cachedGlyph->mCacheTexture) {
423            int penX = x + (int) roundf(positions[(glyphsCount << 1)]);
424            int penY = y + (int) roundf(positions[(glyphsCount << 1) + 1]);
425
426            (*this.*render)(cachedGlyph, penX, penY,
427                    bitmap, bitmapW, bitmapH, bounds, positions);
428        }
429
430        glyphsCount++;
431    }
432}
433
434void Font::updateGlyphCache(const SkPaint* paint, const SkGlyph& skiaGlyph,
435        SkGlyphCache* skiaGlyphCache, CachedGlyphInfo* glyph, bool precaching) {
436    glyph->mAdvanceX = skiaGlyph.fAdvanceX;
437    glyph->mAdvanceY = skiaGlyph.fAdvanceY;
438    glyph->mBitmapLeft = skiaGlyph.fLeft;
439    glyph->mBitmapTop = skiaGlyph.fTop;
440    glyph->mLsbDelta = skiaGlyph.fLsbDelta;
441    glyph->mRsbDelta = skiaGlyph.fRsbDelta;
442
443    uint32_t startX = 0;
444    uint32_t startY = 0;
445
446    // Get the bitmap for the glyph
447    if (!skiaGlyph.fImage) {
448        skiaGlyphCache->findImage(skiaGlyph);
449    }
450    mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY, precaching);
451
452    if (!glyph->mIsValid) {
453        return;
454    }
455
456    uint32_t endX = startX + skiaGlyph.fWidth;
457    uint32_t endY = startY + skiaGlyph.fHeight;
458
459    glyph->mStartX = startX;
460    glyph->mStartY = startY;
461    glyph->mBitmapWidth = skiaGlyph.fWidth;
462    glyph->mBitmapHeight = skiaGlyph.fHeight;
463
464    bool empty = skiaGlyph.fWidth == 0 || skiaGlyph.fHeight == 0;
465    if (!empty) {
466        uint32_t cacheWidth = glyph->mCacheTexture->getWidth();
467        uint32_t cacheHeight = glyph->mCacheTexture->getHeight();
468
469        glyph->mBitmapMinU = startX / (float) cacheWidth;
470        glyph->mBitmapMinV = startY / (float) cacheHeight;
471        glyph->mBitmapMaxU = endX / (float) cacheWidth;
472        glyph->mBitmapMaxV = endY / (float) cacheHeight;
473
474        mState->setTextureDirty();
475    }
476}
477
478CachedGlyphInfo* Font::cacheGlyph(const SkPaint* paint, glyph_t glyph, bool precaching) {
479    CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
480    mCachedGlyphs.add(glyph, newGlyph);
481
482    SkAutoGlyphCache autoCache(*paint, &mDeviceProperties, &mDescription.mLookupTransform);
483    const SkGlyph& skiaGlyph = GET_METRICS(autoCache.getCache(), glyph);
484    newGlyph->mIsValid = false;
485    newGlyph->mGlyphIndex = skiaGlyph.fID;
486
487    updateGlyphCache(paint, skiaGlyph, autoCache.getCache(), newGlyph, precaching);
488
489    return newGlyph;
490}
491
492Font* Font::create(FontRenderer* state, const SkPaint* paint, const SkMatrix& matrix) {
493    FontDescription description(paint, matrix);
494    Font* font = state->mActiveFonts.get(description);
495
496    if (!font) {
497        font = new Font(state, description);
498        state->mActiveFonts.put(description, font);
499    }
500    font->mIdentityTransform = matrix.isIdentity();
501
502    return font;
503}
504
505}; // namespace uirenderer
506}; // namespace android
507