Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0af10b54 authored by Fabrice Di Meglio's avatar Fabrice Di Meglio
Browse files

Improve TextLayoutCache performances

- introduce TextLayoutEngine
- reduce calls to HB_NewFace as they are opening the font files under the cover
- refactor code for removing FontData structure
- fix logging

Change-Id: Id9658fcd454b74c34ecf4e9dfd1bd2201e04b988
parent a4f5aa87
Loading
Loading
Loading
Loading
+21 −45
Original line number Diff line number Diff line
@@ -47,25 +47,14 @@ extern "C" {

namespace android {

static void setupPaintWithFontData(SkPaint* paint, FontData* data) {
    paint->setTypeface(data->typeFace);
    paint->setTextSize(data->textSize);
    paint->setTextSkewX(data->textSkewX);
    paint->setTextScaleX(data->textScaleX);
    paint->setFlags(data->flags);
    paint->setHinting(data->hinting);
}

static HB_Bool stringToGlyphs(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length,
        HB_Glyph* glyphs, hb_uint32* glyphsSize, HB_Bool isRTL)
{
    FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
    SkPaint paint;
    setupPaintWithFontData(&paint, data);
    SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
    paint->setTextEncoding(SkPaint::kUTF16_TextEncoding);

    paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
    uint16_t* skiaGlyphs = reinterpret_cast<uint16_t*>(glyphs);
    int numGlyphs = paint.textToGlyphs(characters, length * sizeof(uint16_t), skiaGlyphs);
    int numGlyphs = paint->textToGlyphs(characters, length * sizeof(uint16_t), skiaGlyphs);

    // HB_Glyph is 32-bit, but Skia outputs only 16-bit numbers. So our
    // |glyphs| array needs to be converted.
@@ -80,11 +69,8 @@ static HB_Bool stringToGlyphs(HB_Font hbFont, const HB_UChar16* characters, hb_u
static void glyphsToAdvances(HB_Font hbFont, const HB_Glyph* glyphs, hb_uint32 numGlyphs,
        HB_Fixed* advances, int flags)
{
    FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
    SkPaint paint;
    setupPaintWithFontData(&paint, data);

    paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
    SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
    paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);

    uint16_t* glyphs16 = new uint16_t[numGlyphs];
    if (!glyphs16)
@@ -92,7 +78,7 @@ static void glyphsToAdvances(HB_Font hbFont, const HB_Glyph* glyphs, hb_uint32 n
    for (unsigned i = 0; i < numGlyphs; ++i)
        glyphs16[i] = glyphs[i];
    SkScalar* scalarAdvances = reinterpret_cast<SkScalar*>(advances);
    paint.getTextWidths(glyphs16, numGlyphs * sizeof(uint16_t), scalarAdvances);
    paint->getTextWidths(glyphs16, numGlyphs * sizeof(uint16_t), scalarAdvances);

    // The |advances| values which Skia outputs are SkScalars, which are floats
    // in Chromium. However, Harfbuzz wants them in 26.6 fixed point format.
@@ -108,14 +94,11 @@ static void glyphsToAdvances(HB_Font hbFont, const HB_Glyph* glyphs, hb_uint32 n

static HB_Bool canRender(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length)
{
    FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
    SkPaint paint;
    setupPaintWithFontData(&paint, data);

    paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
    SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
    paint->setTextEncoding(SkPaint::kUTF16_TextEncoding);

    uint16_t* glyphs16 = new uint16_t[length];
    int numGlyphs = paint.textToGlyphs(characters, length * sizeof(uint16_t), glyphs16);
    int numGlyphs = paint->textToGlyphs(characters, length * sizeof(uint16_t), glyphs16);

    bool result = true;
    for (int i = 0; i < numGlyphs; ++i) {
@@ -131,22 +114,20 @@ static HB_Bool canRender(HB_Font hbFont, const HB_UChar16* characters, hb_uint32
static HB_Error getOutlinePoint(HB_Font hbFont, HB_Glyph glyph, int flags, hb_uint32 point,
        HB_Fixed* xPos, HB_Fixed* yPos, hb_uint32* resultingNumPoints)
{
    FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
    SkPaint paint;
    setupPaintWithFontData(&paint, data);

    if (flags & HB_ShaperFlag_UseDesignMetrics)
        // This is requesting pre-hinted positions. We can't support this.
        return HB_Err_Invalid_Argument;

    paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
    SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
    paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);

    uint16_t glyph16 = glyph;
    SkPath path;
    paint.getTextPath(&glyph16, sizeof(glyph16), 0, 0, &path);
    paint->getTextPath(&glyph16, sizeof(glyph16), 0, 0, &path);
    uint32_t numPoints = path.getPoints(0, 0);
    if (point >= numPoints)
        return HB_Err_Invalid_SubTable;
    SkPoint* points = reinterpret_cast<SkPoint*>(malloc(sizeof(SkPoint) * (point + 1)));
    SkPoint* points = static_cast<SkPoint*>(malloc(sizeof(SkPoint) * (point + 1)));
    if (!points)
        return HB_Err_Invalid_SubTable;
    // Skia does let us get a single point from the path.
@@ -161,15 +142,13 @@ static HB_Error getOutlinePoint(HB_Font hbFont, HB_Glyph glyph, int flags, hb_ui

static void getGlyphMetrics(HB_Font hbFont, HB_Glyph glyph, HB_GlyphMetrics* metrics)
{
    FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
    SkPaint paint;
    setupPaintWithFontData(&paint, data);
    SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
    paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);

    paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
    uint16_t glyph16 = glyph;
    SkScalar width;
    SkRect bounds;
    paint.getTextWidths(&glyph16, sizeof(glyph16), &width, &bounds);
    paint->getTextWidths(&glyph16, sizeof(glyph16), &width, &bounds);

    metrics->x = SkScalarToHBFixed(bounds.fLeft);
    metrics->y = SkScalarToHBFixed(bounds.fTop);
@@ -185,12 +164,10 @@ static void getGlyphMetrics(HB_Font hbFont, HB_Glyph glyph, HB_GlyphMetrics* met

static HB_Fixed getFontMetric(HB_Font hbFont, HB_FontMetric metric)
{
    FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
    SkPaint paint;
    setupPaintWithFontData(&paint, data);
    SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);

    SkPaint::FontMetrics skiaMetrics;
    paint.getFontMetrics(&skiaMetrics);
    paint->getFontMetrics(&skiaMetrics);

    switch (metric) {
    case HB_FontAscent:
@@ -211,10 +188,9 @@ const HB_FontClass harfbuzzSkiaClass = {
    getFontMetric,
};

HB_Error harfbuzzSkiaGetTable(void* voidface, const HB_Tag tag, HB_Byte* buffer, HB_UInt* len)
HB_Error harfbuzzSkiaGetTable(void* font, const HB_Tag tag, HB_Byte* buffer, HB_UInt* len)
{
    FontData* data = reinterpret_cast<FontData*>(voidface);
    SkTypeface* typeface = data->typeFace;
    SkTypeface* typeface = static_cast<SkTypeface*>(font);

    if (!typeface) {
        LOGD("Typeface cannot be null");
+0 −9
Original line number Diff line number Diff line
@@ -47,15 +47,6 @@ static inline HB_Fixed SkScalarToHBFixed(SkScalar value) {
    return SkScalarToFloat(value) * 64.0f;
}

typedef struct {
    SkTypeface* typeFace;
    SkScalar textSize;
    SkScalar textSkewX;
    SkScalar textScaleX;
    uint32_t flags;
    SkPaint::Hinting hinting;
} FontData;

HB_Error harfbuzzSkiaGetTable(void* voidface, const HB_Tag, HB_Byte* buffer, HB_UInt* len);
extern const HB_FontClass harfbuzzSkiaClass;

+2 −2
Original line number Diff line number Diff line
@@ -484,8 +484,8 @@ public:

        jchar* glyphsArray = env->GetCharArrayElements(glyphs, NULL);

        TextLayoutCacheValue value;
        value.computeValues(paint, text, start, count, contextCount, flags);
        TextLayoutCacheValue value(contextCount);
        TextLayoutEngine::getInstance().computeValues(&value, paint, text, start, count, contextCount, flags);
        const jchar* shapedGlyphs = value.getGlyphs();
        size_t glyphsCount = value.getGlyphsCount();
        memcpy(glyphsArray, shapedGlyphs, sizeof(jchar) * glyphsCount);
+244 −225

File changed.

Preview size limit exceeded, changes collapsed.

+74 −34
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@
#include <utils/threads.h>
#include <utils/String16.h>
#include <utils/GenerationCache.h>
#include <utils/KeyedVector.h>
#include <utils/Compare.h>
#include <utils/RefBase.h>
#include <utils/Singleton.h>
@@ -116,26 +117,17 @@ inline int compare_type(const TextLayoutCacheKey& lhs, const TextLayoutCacheKey&
 */
class TextLayoutCacheValue : public RefBase {
public:
    TextLayoutCacheValue();
    TextLayoutCacheValue(size_t contextCount);

    void setElapsedTime(uint32_t time);
    uint32_t getElapsedTime();

    void computeValues(SkPaint* paint, const UChar* chars, size_t start, size_t count,
            size_t contextCount, int dirFlags);

    inline const jfloat* getAdvances() const { return mAdvances.array(); }
    inline size_t getAdvancesCount() const { return mAdvances.size(); }
    inline jfloat getTotalAdvance() const { return mTotalAdvance; }
    inline const jchar* getGlyphs() const { return mGlyphs.array(); }
    inline size_t getGlyphsCount() const { return mGlyphs.size(); }

    /**
     * Get the size of the Cache entry
     */
    size_t getSize() const;

private:
    /**
     * Advances vector
     */
@@ -151,35 +143,17 @@ private:
     */
    Vector<jchar> mGlyphs;

    /**
     * Get the size of the Cache entry
     */
    size_t getSize() const;

private:
    /**
     * Time for computing the values (in milliseconds)
     */
    uint32_t mElapsedTime;

    static void computeValuesWithHarfbuzz(SkPaint* paint, const UChar* chars,
            size_t start, size_t count, size_t contextCount, int dirFlags,
            Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
            Vector<jchar>* const outGlyphs);

    static void computeRunValuesWithHarfbuzz(SkPaint* paint, const UChar* chars,
            size_t count, bool isRTL,
            Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
            Vector<jchar>* const outGlyphs);

    static void initShaperItem(HB_ShaperItem& shaperItem, HB_FontRec* font, FontData* fontData,
            SkPaint* paint, const UChar* chars, size_t count);

    static void freeShaperItem(HB_ShaperItem& shaperItem);

    static unsigned shapeFontRun(HB_ShaperItem& shaperItem, SkPaint* paint,
            size_t count, bool isRTL);

    static SkTypeface* getCachedTypeface(SkTypeface** typeface, const char path[]);

    static void deleteGlyphArrays(HB_ShaperItem& shaperItem);

    static void createGlyphArrays(HB_ShaperItem& shaperItem, int size);

}; // TextLayoutCacheValue

/**
@@ -240,6 +214,72 @@ private:

}; // TextLayoutCache

/**
 * The TextLayoutEngine is responsible for shaping with Harfbuzz library
 */
class TextLayoutEngine : public Singleton<TextLayoutEngine> {
public:
    TextLayoutEngine();
    virtual ~TextLayoutEngine();

    void computeValues(TextLayoutCacheValue* value, SkPaint* paint, const UChar* chars,
            size_t start, size_t count, size_t contextCount, int dirFlags);

private:
    /**
     * Harfbuzz shaper item
     */
    HB_ShaperItem mShaperItem;

    /**
     * Harfbuzz font
     */
    HB_FontRec mFontRec;

    /**
     * Skia Paint used for shaping
     */
    SkPaint mShapingPaint;

    /**
     * Skia typefaces cached for shaping
     */
    SkTypeface* mDefaultTypeface;
    SkTypeface* mArabicTypeface;
    SkTypeface* mHebrewRegularTypeface;
    SkTypeface* mHebrewBoldTypeface;

    KeyedVector<SkFontID, HB_Face> mCachedHBFaces;

    size_t mShaperItemGlyphArraySize;
    size_t mShaperItemLogClustersArraySize;

    size_t shapeFontRun(SkPaint* paint, bool isRTL);

    void computeValuesWithHarfbuzz(SkPaint* paint, const UChar* chars,
            size_t start, size_t count, size_t contextCount, int dirFlags,
            Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
            Vector<jchar>* const outGlyphs);

    void computeRunValuesWithHarfbuzz(SkPaint* paint, const UChar* chars,
            size_t count, bool isRTL,
            Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
            Vector<jchar>* const outGlyphs);

    SkTypeface* getCachedTypeface(SkTypeface** typeface, const char path[]);
    HB_Face getCachedHBFace(SkTypeface* typeface);

    void ensureShaperItemGlyphArrays(size_t size);
    void createShaperItemGlyphArrays(size_t size);
    void deleteShaperItemGlyphArrays();

    void ensureShaperItemLogClustersArray(size_t size);
    void createShaperItemLogClustersArray(size_t size);
    void deleteShaperItemLogClustersArray();

}; // TextLayoutEngine


} // namespace android
#endif /* ANDROID_TEXT_LAYOUT_CACHE_H */