Commit 718840c3 authored by Vladimir Vukicevic's avatar Vladimir Vukicevic
Browse files

b=514968; optimize FT2 textrun creation with glyph cache; r=jfkthame

parent 141bc183
Loading
Loading
Loading
Loading
+46 −1
Original line number Diff line number Diff line
@@ -127,7 +127,46 @@ public: // new functions
    static already_AddRefed<gfxFT2Font>
    GetOrMakeFont(FontEntry *aFontEntry, const gfxFontStyle *aStyle);

private:
    struct CachedGlyphData {
        CachedGlyphData()
            : glyphIndex(0xffffffffU) { }

        CachedGlyphData(PRUint32 gid)
            : glyphIndex(gid) { }

        PRUint32 glyphIndex;
        PRInt32 lsbDelta;
        PRInt32 rsbDelta;
        PRInt32 xAdvance;
    };

    const CachedGlyphData* GetGlyphDataForChar(PRUint32 ch) {
        CharGlyphMapEntryType *entry = mCharGlyphCache.PutEntry(ch);

        if (!entry)
            return nsnull;

        if (entry->mData.glyphIndex == 0xffffffffU) {
            // this is a new entry, fill it
            FillGlyphDataForChar(ch, &entry->mData);
        }

        return &entry->mData;
    }

    class FaceLock {
    public:
        FaceLock(gfxFT2Font *font);
        ~FaceLock();

        FT_Face Face() { return mFace; }

    protected:
        cairo_scaled_font_t *mScaledFont;
        FT_Face mFace;
    };

protected:
    cairo_scaled_font_t *mScaledFont;

    PRBool mHasSpaceGlyph;
@@ -135,6 +174,12 @@ private:
    PRBool mHasMetrics;
    Metrics mMetrics;
    gfxFloat mAdjustedSize;

    void FillGlyphDataForChar(PRUint32 ch, CachedGlyphData *gd);

    typedef nsBaseHashtableET<nsUint32HashKey, CachedGlyphData> CharGlyphMapEntryType;
    typedef nsTHashtable<CharGlyphMapEntryType> CharGlyphMap;
    CharGlyphMap mCharGlyphCache;
};

class THEBES_API gfxFT2FontGroup : public gfxFontGroup {
+111 −68
Original line number Diff line number Diff line
@@ -81,6 +81,14 @@ static const char *sCJKLangGroup[] = {
#define CJK_LANG_ZH_HK sCJKLangGroup[3]
#define CJK_LANG_ZH_TW sCJKLangGroup[4]

// rounding and truncation functions for a Freetype floating point number
// (FT26Dot6) stored in a 32bit integer with high 26 bits for the integer
// part and low 6 bits for the fractional part.
#define MOZ_FT_ROUND(x) (((x) + 32) & ~63) // 63 = 2^6 - 1
#define MOZ_FT_TRUNC(x) ((x) >> 6)
#define CONVERT_DESIGN_UNITS_TO_PIXELS(v, s) \
        MOZ_FT_TRUNC(MOZ_FT_ROUND(FT_MulFix((v) , (s))))

/**
 * FontEntry
 */
@@ -617,8 +625,9 @@ gfxFT2FontGroup::WhichPrefFontSupportsChar(PRUint32 aCh)

        /* special case CJK */
        if (unicodeRange == kRangeSetCJK) {
            if (PR_LOG_TEST(gFontLog, PR_LOG_DEBUG))
            if (PR_LOG_TEST(gFontLog, PR_LOG_DEBUG)) {
                PR_LOG(gFontLog, PR_LOG_DEBUG, (" - Trying to find fonts for: CJK"));
            }

            nsAutoTArray<nsRefPtr<FontEntry>, 15> fonts;
            GetCJKPrefFonts(fonts);
@@ -675,10 +684,15 @@ gfxFT2FontGroup::AddRange(gfxTextRun *aTextRun, gfxFT2Font *font, const PRUnicha
{
    const PRUint32 appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
    // we'll pass this in/figure it out dynamically, but at this point there can be only one face.
    FT_Face face = cairo_ft_scaled_font_lock_face(font->CairoScaledFont());
    gfxFT2Font::FaceLock faceLock(font);
    FT_Face face = faceLock.Face();

    gfxTextRun::CompressedGlyph g;

    const gfxFT2Font::CachedGlyphData *cgd = nsnull, *cgdNext = nsnull;

    FT_UInt spaceGlyph = font->GetSpaceGlyph();

    aTextRun->AddGlyphRun(font, offset);
    for (PRUint32 i = 0; i < len; i++) {
        PRUint32 ch = str[offset + i];
@@ -690,12 +704,18 @@ gfxFT2FontGroup::AddRange(gfxTextRun *aTextRun, gfxFT2Font *font, const PRUnicha
        }

        NS_ASSERTION(!IsInvalidChar(ch), "Invalid char detected");
        FT_UInt gid = FT_Get_Char_Index(face, ch); // find the glyph id

        if (cgdNext) {
            cgd = cgdNext;
            cgdNext = nsnull;
        } else {
            cgd = font->GetGlyphDataForChar(ch);
        }

        FT_UInt gid = cgd->glyphIndex;
        PRInt32 advance = 0;

        if (gid == font->GetSpaceGlyph()) {
            advance = (int)(font->GetMetrics().spaceWidth * appUnitsPerDevUnit);
        } else if (gid == 0) {
        if (gid == 0) {
            advance = -1; // trigger the missing glyphs case below
        } else {
            // find next character and its glyph -- in case they exist
@@ -707,32 +727,29 @@ gfxFT2FontGroup::AddRange(gfxTextRun *aTextRun, gfxFT2Font *font, const PRUnicha
            if (FT_HAS_KERNING(face) && i + 1 < len) {
                chNext = str[offset + i + 1];
                if (chNext != 0) {
                    gidNext = FT_Get_Char_Index(face, chNext);
                    if (gidNext && gidNext != font->GetSpaceGlyph()) {
                        FT_Load_Glyph(face, gidNext, FT_LOAD_DEFAULT);
                        lsbDeltaNext = face->glyph->lsb_delta;
                    }
                    cgdNext = font->GetGlyphDataForChar(chNext);
                    gidNext = cgdNext->glyphIndex;
                    if (gidNext && gidNext != spaceGlyph)
                        lsbDeltaNext = cgdNext->lsbDelta;
                }
            }

            // now load the current glyph
            FT_Load_Glyph(face, gid, FT_LOAD_DEFAULT); // load glyph into the slot
            advance = face->glyph->advance.x;
            advance = cgd->xAdvance;

            // now add kerning to the current glyph's advance
            if (chNext && gidNext) {
                FT_Vector kerning; kerning.x = 0;
                FT_Get_Kerning(face, gid, gidNext, FT_KERNING_DEFAULT, &kerning);
                advance += kerning.x;
                if (face->glyph->rsb_delta - lsbDeltaNext >= 32) {
                if (cgd->rsbDelta - lsbDeltaNext >= 32) {
                    advance -= 64;
                } else if (face->glyph->rsb_delta - lsbDeltaNext < -32) {
                } else if (cgd->rsbDelta - lsbDeltaNext < -32) {
                    advance += 64;
                }
            }

            // now apply unit conversion and scaling
            advance = (advance >> 6) * appUnitsPerDevUnit;
            advance = MOZ_FT_TRUNC(advance) * appUnitsPerDevUnit;
        }
#ifdef DEBUG_thebes_2
        printf(" gid=%d, advance=%d (%s)\n", gid, advance,
@@ -757,8 +774,20 @@ gfxFT2FontGroup::AddRange(gfxTextRun *aTextRun, gfxFT2Font *font, const PRUnicha
            aTextRun->SetGlyphs(offset + i, g, &details);
        }
    }
}

    cairo_ft_scaled_font_unlock_face(font->CairoScaledFont());
/*
 * Stack-based face lock helper
 */
gfxFT2Font::FaceLock::FaceLock(gfxFT2Font *font)
{
    mScaledFont = font->CairoScaledFont();
    mFace = cairo_ft_scaled_font_lock_face(mScaledFont);
}

gfxFT2Font::FaceLock::~FaceLock()
{
    cairo_ft_scaled_font_unlock_face(mScaledFont);
}

/**
@@ -775,6 +804,8 @@ gfxFT2Font::gfxFT2Font(FontEntry *aFontEntry,
{
    mFontEntry = aFontEntry;
    NS_ASSERTION(mFontEntry, "Unable to find font entry for font.  Something is whack.");

    mCharGlyphCache.Init(64);
}

gfxFT2Font::~gfxFT2Font()
@@ -785,14 +816,6 @@ gfxFT2Font::~gfxFT2Font()
    }
}

// rounding and truncation functions for a Freetype floating point number 
// (FT26Dot6) stored in a 32bit integer with high 26 bits for the integer
// part and low 6 bits for the fractional part. 
#define MOZ_FT_ROUND(x) (((x) + 32) & ~63) // 63 = 2^6 - 1
#define MOZ_FT_TRUNC(x) ((x) >> 6)
#define CONVERT_DESIGN_UNITS_TO_PIXELS(v, s) \
        MOZ_FT_TRUNC(MOZ_FT_ROUND(FT_MulFix((v) , (s))))

const gfxFont::Metrics&
gfxFT2Font::GetMetrics()
{
@@ -801,7 +824,8 @@ gfxFT2Font::GetMetrics()

    mMetrics.emHeight = GetStyle()->size;

    FT_Face face = cairo_ft_scaled_font_lock_face(CairoScaledFont());
    gfxFT2Font::FaceLock faceLock(this);
    FT_Face face = faceLock.Face();

    if (!face) {
        // Abort here already, otherwise we crash in the following
@@ -818,17 +842,8 @@ gfxFT2Font::GetMetrics()
    const double xScale = face->size->metrics.x_ppem / emUnit;
    const double yScale = face->size->metrics.y_ppem / emUnit;

    // properties of space
    gid = FT_Get_Char_Index(face, ' ');
    if (gid) {
        FT_Load_Glyph(face, gid, FT_LOAD_DEFAULT);
        // face->glyph->metrics.width doesn't work for spaces, use advance.x instead
        mMetrics.spaceWidth = face->glyph->advance.x >> 6;
        // save the space glyph
        mSpaceGlyph = gid;
    } else {
        NS_ERROR("blah");
    }
    // cache properties of space
    GetSpaceGlyph();

    // properties of 'x', also use its width as average width
    gid = FT_Get_Char_Index(face, 'x'); // select the glyph
@@ -916,7 +931,6 @@ gfxFT2Font::GetMetrics()
    */

    // XXX mMetrics.height needs to be set.
    cairo_ft_scaled_font_unlock_face(CairoScaledFont());

    mHasMetrics = PR_TRUE;
    return mMetrics;
@@ -932,19 +946,18 @@ gfxFT2Font::GetUniqueName()
PRUint32
gfxFT2Font::GetSpaceGlyph()
{
    NS_ASSERTION (GetStyle ()->size != 0,
    "forgot to short-circuit a text run with zero-sized font?");
    NS_ASSERTION (GetStyle()->size != 0, "forgot to short-circuit a text run with zero-sized font?");

    if(!mHasSpaceGlyph)
    {
        FT_UInt gid = 0; // glyph ID
        FT_Face face = cairo_ft_scaled_font_lock_face(CairoScaledFont());
        gid = FT_Get_Char_Index(face, ' ');
        FT_Load_Glyph(face, gid, FT_LOAD_DEFAULT);
        mSpaceGlyph = gid;
    if (!mHasSpaceGlyph) {
        const CachedGlyphData *gdata = GetGlyphDataForChar(' ');

        mSpaceGlyph = gdata->glyphIndex;
        NS_ASSERTION(mSpaceGlyph != 0, "Font has no space glyph!");

        mMetrics.spaceWidth = MOZ_FT_TRUNC(gdata->xAdvance);
        mHasSpaceGlyph = PR_TRUE;
        cairo_ft_scaled_font_unlock_face(CairoScaledFont());
    }

    return mSpaceGlyph;
}

@@ -1023,7 +1036,7 @@ gfxFT2Font::GetOrMakeFont(const nsAString& aName, const gfxFontStyle *aStyle)
{
    FontEntry *fe = gfxToolkitPlatform::GetPlatform()->FindFontEntry(aName, *aStyle);
    if (!fe) {
        printf("Failed to find font entry for %s\n", NS_ConvertUTF16toUTF8(aName).get());
        NS_WARNING("Failed to find font entry for font!");
        return nsnull;
    }

@@ -1046,3 +1059,33 @@ gfxFT2Font::GetOrMakeFont(FontEntry *aFontEntry, const gfxFontStyle *aStyle)
    return static_cast<gfxFT2Font *>(f);
}

void
gfxFT2Font::FillGlyphDataForChar(PRUint32 ch, CachedGlyphData *gd)
{
    gfxFT2Font::FaceLock faceLock(this);
    FT_Face face = faceLock.Face();

    FT_UInt gid = FT_Get_Char_Index(face, ch);

    if (gid == 0) {
        // this font doesn't support this char!
        NS_ASSERTION(gid != 0, "We don't have a glyph, but font indicated that it supported this char in tables?");
        gd->glyphIndex = 0;
        return;
    }

    FT_Error err = FT_Load_Glyph(face, gid, FT_LOAD_DEFAULT);

    if (err) {
        // hmm, this is weird, we failed to load a glyph that we had?
        NS_WARNING("Failed to load glyph that we got from Get_Char_index");

        gd->glyphIndex = 0;
        return;
    }

    gd->glyphIndex = gid;
    gd->lsbDelta = face->glyph->lsb_delta;
    gd->rsbDelta = face->glyph->rsb_delta;
    gd->xAdvance = face->glyph->advance.x;
}