Font.cpp revision a4adcf0239039eb8f005be252409901c41b28839
1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "OpenGLRenderer"
18
19#include <cutils/compiler.h>
20
21#include <utils/JenkinsHash.h>
22
23#include <SkGlyph.h>
24#include <SkUtils.h>
25
26#include "Debug.h"
27#include "FontUtil.h"
28#include "Font.h"
29#include "FontRenderer.h"
30#include "Properties.h"
31
32namespace android {
33namespace uirenderer {
34
35///////////////////////////////////////////////////////////////////////////////
36// Font
37///////////////////////////////////////////////////////////////////////////////
38
39Font::Font(FontRenderer* state, const Font::FontDescription& desc) :
40        mState(state), mDescription(desc) {
41}
42
43Font::FontDescription::FontDescription(const SkPaint* paint, const mat4& matrix) {
44    mFontId = SkTypeface::UniqueID(paint->getTypeface());
45    mFontSize = paint->getTextSize();
46    mFlags = 0;
47    if (paint->isFakeBoldText()) {
48        mFlags |= Font::kFakeBold;
49    }
50    mItalicStyle = paint->getTextSkewX();
51    mScaleX = paint->getTextScaleX();
52    mStyle = paint->getStyle();
53    mStrokeWidth = paint->getStrokeWidth();
54    mAntiAliasing = paint->isAntiAlias();
55    mLookupTransform.reset();
56    mLookupTransform[SkMatrix::kMScaleX] = matrix.data[mat4::kScaleX];
57    mLookupTransform[SkMatrix::kMScaleY] = matrix.data[mat4::kScaleY];
58    mLookupTransform[SkMatrix::kMSkewX] = matrix.data[mat4::kSkewX];
59    mLookupTransform[SkMatrix::kMSkewY] = matrix.data[mat4::kSkewY];
60}
61
62Font::~Font() {
63    mState->removeFont(this);
64
65    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
66        delete mCachedGlyphs.valueAt(i);
67    }
68}
69
70hash_t Font::FontDescription::hash() const {
71    uint32_t hash = JenkinsHashMix(0, mFontId);
72    hash = JenkinsHashMix(hash, android::hash_type(mFontSize));
73    hash = JenkinsHashMix(hash, android::hash_type(mFlags));
74    hash = JenkinsHashMix(hash, android::hash_type(mItalicStyle));
75    hash = JenkinsHashMix(hash, android::hash_type(mScaleX));
76    hash = JenkinsHashMix(hash, android::hash_type(mStyle));
77    hash = JenkinsHashMix(hash, android::hash_type(mStrokeWidth));
78    hash = JenkinsHashMix(hash, int(mAntiAliasing));
79    hash = JenkinsHashMix(hash, android::hash_type(mLookupTransform[SkMatrix::kMScaleX]));
80    hash = JenkinsHashMix(hash, android::hash_type(mLookupTransform[SkMatrix::kMScaleY]));
81    hash = JenkinsHashMix(hash, android::hash_type(mLookupTransform[SkMatrix::kMSkewX]));
82    hash = JenkinsHashMix(hash, android::hash_type(mLookupTransform[SkMatrix::kMSkewY]));
83    return JenkinsHashWhiten(hash);
84}
85
86int Font::FontDescription::compare(const Font::FontDescription& lhs,
87        const Font::FontDescription& rhs) {
88    int deltaInt = int(lhs.mFontId) - int(rhs.mFontId);
89    if (deltaInt != 0) return deltaInt;
90
91    if (lhs.mFontSize < rhs.mFontSize) return -1;
92    if (lhs.mFontSize > rhs.mFontSize) return +1;
93
94    if (lhs.mItalicStyle < rhs.mItalicStyle) return -1;
95    if (lhs.mItalicStyle > rhs.mItalicStyle) return +1;
96
97    deltaInt = int(lhs.mFlags) - int(rhs.mFlags);
98    if (deltaInt != 0) return deltaInt;
99
100    if (lhs.mScaleX < rhs.mScaleX) return -1;
101    if (lhs.mScaleX > rhs.mScaleX) return +1;
102
103    deltaInt = int(lhs.mStyle) - int(rhs.mStyle);
104    if (deltaInt != 0) return deltaInt;
105
106    if (lhs.mStrokeWidth < rhs.mStrokeWidth) return -1;
107    if (lhs.mStrokeWidth > rhs.mStrokeWidth) return +1;
108
109    deltaInt = int(lhs.mAntiAliasing) - int(rhs.mAntiAliasing);
110    if (deltaInt != 0) return deltaInt;
111
112    if (lhs.mLookupTransform[SkMatrix::kMScaleX] <
113            rhs.mLookupTransform[SkMatrix::kMScaleX]) return -1;
114    if (lhs.mLookupTransform[SkMatrix::kMScaleX] >
115            rhs.mLookupTransform[SkMatrix::kMScaleX]) return +1;
116
117    if (lhs.mLookupTransform[SkMatrix::kMScaleY] <
118            rhs.mLookupTransform[SkMatrix::kMScaleY]) return -1;
119    if (lhs.mLookupTransform[SkMatrix::kMScaleY] >
120            rhs.mLookupTransform[SkMatrix::kMScaleY]) return +1;
121
122    if (lhs.mLookupTransform[SkMatrix::kMSkewX] <
123            rhs.mLookupTransform[SkMatrix::kMSkewX]) return -1;
124    if (lhs.mLookupTransform[SkMatrix::kMSkewX] >
125            rhs.mLookupTransform[SkMatrix::kMSkewX]) return +1;
126
127    if (lhs.mLookupTransform[SkMatrix::kMSkewY] <
128            rhs.mLookupTransform[SkMatrix::kMSkewY]) return -1;
129    if (lhs.mLookupTransform[SkMatrix::kMSkewY] >
130            rhs.mLookupTransform[SkMatrix::kMSkewY]) return +1;
131
132    return 0;
133}
134
135void Font::invalidateTextureCache(CacheTexture* cacheTexture) {
136    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
137        CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i);
138        if (!cacheTexture || cachedGlyph->mCacheTexture == cacheTexture) {
139            cachedGlyph->mIsValid = false;
140        }
141    }
142}
143
144void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
145        uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
146    int nPenX = x + glyph->mBitmapLeft;
147    int nPenY = y + glyph->mBitmapTop;
148
149    int width = (int) glyph->mBitmapWidth;
150    int height = (int) glyph->mBitmapHeight;
151
152    if (bounds->bottom > nPenY) {
153        bounds->bottom = nPenY;
154    }
155    if (bounds->left > nPenX) {
156        bounds->left = nPenX;
157    }
158    if (bounds->right < nPenX + width) {
159        bounds->right = nPenX + width;
160    }
161    if (bounds->top < nPenY + height) {
162        bounds->top = nPenY + height;
163    }
164}
165
166void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
167        uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
168    float nPenX = x + glyph->mBitmapLeft;
169    float nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
170
171    float width = (float) glyph->mBitmapWidth;
172    float height = (float) glyph->mBitmapHeight;
173
174    float u1 = glyph->mBitmapMinU;
175    float u2 = glyph->mBitmapMaxU;
176    float v1 = glyph->mBitmapMinV;
177    float v2 = glyph->mBitmapMaxV;
178
179    mState->appendMeshQuad(nPenX, nPenY, u1, v2,
180            nPenX + width, nPenY, u2, v2,
181            nPenX + width, nPenY - height, u2, v1,
182            nPenX, nPenY - height, u1, v1, glyph->mCacheTexture);
183}
184
185void Font::drawCachedGlyphPerspective(CachedGlyphInfo* glyph, int x, int y,
186        uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
187    SkMatrix i;
188    if (!mDescription.mLookupTransform.invert(&i)) {
189        return;
190    }
191
192    SkPoint p[4];
193    p[0].set(glyph->mBitmapLeft, glyph->mBitmapTop + glyph->mBitmapHeight);
194    p[1].set(glyph->mBitmapLeft + glyph->mBitmapWidth, glyph->mBitmapTop + glyph->mBitmapHeight);
195    p[2].set(glyph->mBitmapLeft + glyph->mBitmapWidth, glyph->mBitmapTop);
196    p[3].set(glyph->mBitmapLeft, glyph->mBitmapTop);
197
198    i.mapPoints(p, 4);
199
200    p[0].offset(x, y);
201    p[1].offset(x, y);
202    p[2].offset(x, y);
203    p[3].offset(x, y);
204
205    float u1 = glyph->mBitmapMinU;
206    float u2 = glyph->mBitmapMaxU;
207    float v1 = glyph->mBitmapMinV;
208    float v2 = glyph->mBitmapMaxV;
209
210    mState->appendRotatedMeshQuad(
211            p[0].fX, p[0].fY, u1, v2,
212            p[1].fX, p[1].fY, u2, v2,
213            p[2].fX, p[2].fY, u2, v1,
214            p[3].fX, p[3].fY, u1, v1, glyph->mCacheTexture);
215}
216
217void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
218        uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
219    int nPenX = x + glyph->mBitmapLeft;
220    int nPenY = y + glyph->mBitmapTop;
221
222    uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
223    uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
224
225    CacheTexture* cacheTexture = glyph->mCacheTexture;
226    uint32_t cacheWidth = cacheTexture->getWidth();
227    const uint8_t* cacheBuffer = cacheTexture->getTexture();
228
229    uint32_t cacheX = 0, cacheY = 0;
230    int32_t bX = 0, bY = 0;
231    for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
232        for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
233            uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
234            bitmap[bY * bitmapW + bX] = tempCol;
235        }
236    }
237}
238
239void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
240        SkPathMeasure& measure, SkPoint* position, SkVector* tangent) {
241    const float halfWidth = glyph->mBitmapWidth * 0.5f;
242    const float height = glyph->mBitmapHeight;
243
244    vOffset += glyph->mBitmapTop + height;
245
246    SkPoint destination[4];
247    bool ok = measure.getPosTan(x + hOffset + glyph->mBitmapLeft + halfWidth, position, tangent);
248    if (!ok) {
249        ALOGW("The path for drawTextOnPath is empty or null");
250    }
251
252    // Move along the tangent and offset by the normal
253    destination[0].set(-tangent->fX * halfWidth - tangent->fY * vOffset,
254            -tangent->fY * halfWidth + tangent->fX * vOffset);
255    destination[1].set(tangent->fX * halfWidth - tangent->fY * vOffset,
256            tangent->fY * halfWidth + tangent->fX * vOffset);
257    destination[2].set(destination[1].fX + tangent->fY * height,
258            destination[1].fY - tangent->fX * height);
259    destination[3].set(destination[0].fX + tangent->fY * height,
260            destination[0].fY - tangent->fX * height);
261
262    const float u1 = glyph->mBitmapMinU;
263    const float u2 = glyph->mBitmapMaxU;
264    const float v1 = glyph->mBitmapMinV;
265    const float v2 = glyph->mBitmapMaxV;
266
267    mState->appendRotatedMeshQuad(
268            position->fX + destination[0].fX,
269            position->fY + destination[0].fY, u1, v2,
270            position->fX + destination[1].fX,
271            position->fY + destination[1].fY, u2, v2,
272            position->fX + destination[2].fX,
273            position->fY + destination[2].fY, u2, v1,
274            position->fX + destination[3].fX,
275            position->fY + destination[3].fY, u1, v1,
276            glyph->mCacheTexture);
277}
278
279CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching) {
280    CachedGlyphInfo* cachedGlyph = NULL;
281    ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
282    if (index >= 0) {
283        cachedGlyph = mCachedGlyphs.valueAt(index);
284
285        // Is the glyph still in texture cache?
286        if (!cachedGlyph->mIsValid) {
287            const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit, &mDescription.mLookupTransform);
288            updateGlyphCache(paint, skiaGlyph, cachedGlyph, precaching);
289        }
290    } else {
291        cachedGlyph = cacheGlyph(paint, textUnit, precaching);
292    }
293
294    return cachedGlyph;
295}
296
297void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
298            int numGlyphs, int x, int y, const float* positions) {
299    render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
300            0, 0, NULL, positions);
301}
302
303void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
304        int numGlyphs, SkPath* path, float hOffset, float vOffset) {
305    if (numGlyphs == 0 || text == NULL || len == 0) {
306        return;
307    }
308
309    text += start;
310
311    int glyphsCount = 0;
312    SkFixed prevRsbDelta = 0;
313
314    float penX = 0.0f;
315
316    SkPoint position;
317    SkVector tangent;
318
319    SkPathMeasure measure(*path, false);
320    float pathLength = SkScalarToFloat(measure.getLength());
321
322    if (paint->getTextAlign() != SkPaint::kLeft_Align) {
323        float textWidth = SkScalarToFloat(paint->measureText(text, len));
324        float pathOffset = pathLength;
325        if (paint->getTextAlign() == SkPaint::kCenter_Align) {
326            textWidth *= 0.5f;
327            pathOffset *= 0.5f;
328        }
329        penX += pathOffset - textWidth;
330    }
331
332    while (glyphsCount < numGlyphs && penX < pathLength) {
333        glyph_t glyph = GET_GLYPH(text);
334
335        if (IS_END_OF_STRING(glyph)) {
336            break;
337        }
338
339        CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
340        penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
341        prevRsbDelta = cachedGlyph->mRsbDelta;
342
343        if (cachedGlyph->mIsValid && cachedGlyph->mCacheTexture) {
344            drawCachedGlyph(cachedGlyph, penX, hOffset, vOffset, measure, &position, &tangent);
345        }
346
347        penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
348
349        glyphsCount++;
350    }
351}
352
353void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
354        int numGlyphs, Rect *bounds, const float* positions) {
355    if (bounds == NULL) {
356        ALOGE("No return rectangle provided to measure text");
357        return;
358    }
359    bounds->set(1e6, -1e6, -1e6, 1e6);
360    render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, positions);
361}
362
363void Font::precache(SkPaint* paint, const char* text, int numGlyphs) {
364    if (numGlyphs == 0 || text == NULL) {
365        return;
366    }
367    int glyphsCount = 0;
368
369    while (glyphsCount < numGlyphs) {
370        glyph_t glyph = GET_GLYPH(text);
371
372        // Reached the end of the string
373        if (IS_END_OF_STRING(glyph)) {
374            break;
375        }
376
377        CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph, true);
378
379        glyphsCount++;
380    }
381}
382
383void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
384        int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
385        uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) {
386    if (numGlyphs == 0 || text == NULL || len == 0) {
387        return;
388    }
389
390    static RenderGlyph gRenderGlyph[] = {
391            &android::uirenderer::Font::drawCachedGlyph,
392            &android::uirenderer::Font::drawCachedGlyphPerspective,
393            &android::uirenderer::Font::drawCachedGlyphBitmap,
394            &android::uirenderer::Font::drawCachedGlyphBitmap,
395            &android::uirenderer::Font::measureCachedGlyph,
396            &android::uirenderer::Font::measureCachedGlyph
397    };
398    RenderGlyph render = gRenderGlyph[(mode << 1) + mTransform.isPerspective()];
399
400    text += start;
401    int glyphsCount = 0;
402
403    const bool applyTransform = !mTransform.isIdentity() && !mTransform.isPerspective();
404    const SkPaint::Align align = paint->getTextAlign();
405
406    while (glyphsCount < numGlyphs) {
407        glyph_t glyph = GET_GLYPH(text);
408
409        // Reached the end of the string
410        if (IS_END_OF_STRING(glyph)) {
411            break;
412        }
413
414        CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
415
416        // If it's still not valid, we couldn't cache it, so we shouldn't
417        // draw garbage; also skip empty glyphs (spaces)
418        if (cachedGlyph->mIsValid && cachedGlyph->mCacheTexture) {
419            float penX = x + positions[(glyphsCount << 1)];
420            float penY = y + positions[(glyphsCount << 1) + 1];
421
422            if (applyTransform) {
423                mTransform.mapPoint(penX, penY);
424            }
425
426            (*this.*render)(cachedGlyph, roundf(penX), roundf(penY),
427                    bitmap, bitmapW, bitmapH, bounds, positions);
428        }
429
430        glyphsCount++;
431    }
432}
433
434void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph,
435        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        paint->findImage(skiaGlyph, &mDescription.mLookupTransform);
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(SkPaint* paint, glyph_t glyph, bool precaching) {
479    CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
480    mCachedGlyphs.add(glyph, newGlyph);
481
482    const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph, &mDescription.mLookupTransform);
483    newGlyph->mIsValid = false;
484    newGlyph->mGlyphIndex = skiaGlyph.fID;
485
486    updateGlyphCache(paint, skiaGlyph, newGlyph, precaching);
487
488    return newGlyph;
489}
490
491Font* Font::create(FontRenderer* state, const SkPaint* paint, const mat4& matrix) {
492    FontDescription description(paint, matrix);
493    Font* font = state->mActiveFonts.get(description);
494
495    if (!font) {
496        font = new Font(state, description);
497        state->mActiveFonts.put(description, font);
498    }
499    font->mTransform.load(matrix);
500
501    return font;
502}
503
504}; // namespace uirenderer
505}; // namespace android
506