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

This commit is contained in:
Vladimir Vukicevic 2009-09-10 13:52:40 -07:00
parent 141bc1831c
commit 718840c3e2
2 changed files with 156 additions and 68 deletions

View File

@ -127,7 +127,46 @@ public: // new functions
static already_AddRefed<gfxFT2Font> static already_AddRefed<gfxFT2Font>
GetOrMakeFont(FontEntry *aFontEntry, const gfxFontStyle *aStyle); 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; cairo_scaled_font_t *mScaledFont;
PRBool mHasSpaceGlyph; PRBool mHasSpaceGlyph;
@ -135,6 +174,12 @@ private:
PRBool mHasMetrics; PRBool mHasMetrics;
Metrics mMetrics; Metrics mMetrics;
gfxFloat mAdjustedSize; 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 { class THEBES_API gfxFT2FontGroup : public gfxFontGroup {

View File

@ -81,6 +81,14 @@ static const char *sCJKLangGroup[] = {
#define CJK_LANG_ZH_HK sCJKLangGroup[3] #define CJK_LANG_ZH_HK sCJKLangGroup[3]
#define CJK_LANG_ZH_TW sCJKLangGroup[4] #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 * FontEntry
*/ */
@ -107,15 +115,15 @@ FontEntry::~FontEntry()
} }
/* static */ /* static */
FontEntry* FontEntry*
FontEntry::CreateFontEntry(const gfxProxyFontEntry &aProxyEntry, FontEntry::CreateFontEntry(const gfxProxyFontEntry &aProxyEntry,
nsISupports *aLoader, const PRUint8 *aFontData, nsISupports *aLoader, const PRUint8 *aFontData,
PRUint32 aLength) { PRUint32 aLength) {
if (!gfxFontUtils::ValidateSFNTHeaders(aFontData, aLength)) if (!gfxFontUtils::ValidateSFNTHeaders(aFontData, aLength))
return nsnull; return nsnull;
FT_Face face; FT_Face face;
FT_Error error = FT_Error error =
FT_New_Memory_Face(gfxToolkitPlatform::GetPlatform()->GetFTLibrary(), FT_New_Memory_Face(gfxToolkitPlatform::GetPlatform()->GetFTLibrary(),
aFontData, aLength, 0, &face); aFontData, aLength, 0, &face);
if (error != FT_Err_Ok) if (error != FT_Err_Ok)
return nsnull; return nsnull;
@ -134,7 +142,7 @@ FTFontDestroyFunc(void *data)
FT_Done_Face(face); FT_Done_Face(face);
} }
/* static */ FontEntry* /* static */ FontEntry*
FontEntry::CreateFontEntryFromFace(FT_Face aFace) { FontEntry::CreateFontEntryFromFace(FT_Face aFace) {
static cairo_user_data_key_t key; static cairo_user_data_key_t key;
@ -322,7 +330,7 @@ gfxFT2FontGroup::gfxFT2FontGroup(const nsAString& families,
#elif defined(XP_WIN) #elif defined(XP_WIN)
HGDIOBJ hGDI = ::GetStockObject(SYSTEM_FONT); HGDIOBJ hGDI = ::GetStockObject(SYSTEM_FONT);
LOGFONTW logFont; LOGFONTW logFont;
if (hGDI && ::GetObjectW(hGDI, sizeof(logFont), &logFont)) if (hGDI && ::GetObjectW(hGDI, sizeof(logFont), &logFont))
familyArray.AppendElement(nsDependentString(logFont.lfFaceName)); familyArray.AppendElement(nsDependentString(logFont.lfFaceName));
#else #else
#error "Platform not supported" #error "Platform not supported"
@ -441,10 +449,10 @@ AddFontNameToArray(const nsAString& aName,
if (list->IndexOf(aName) == list->NoIndex) if (list->IndexOf(aName) == list->NoIndex)
list->AppendElement(aName); list->AppendElement(aName);
} }
return PR_TRUE; return PR_TRUE;
} }
void void
gfxFT2FontGroup::FamilyListToArrayList(const nsString& aFamilies, gfxFT2FontGroup::FamilyListToArrayList(const nsString& aFamilies,
const nsCString& aLangGroup, const nsCString& aLangGroup,
@ -598,7 +606,7 @@ gfxFT2FontGroup::WhichFontSupportsChar(const nsTArray<nsRefPtr<FontEntry> >& aFo
return nsnull; return nsnull;
} }
already_AddRefed<gfxFont> already_AddRefed<gfxFont>
gfxFT2FontGroup::WhichPrefFontSupportsChar(PRUint32 aCh) gfxFT2FontGroup::WhichPrefFontSupportsChar(PRUint32 aCh)
{ {
if (aCh > 0xFFFF) if (aCh > 0xFFFF)
@ -617,8 +625,9 @@ gfxFT2FontGroup::WhichPrefFontSupportsChar(PRUint32 aCh)
/* special case CJK */ /* special case CJK */
if (unicodeRange == kRangeSetCJK) { 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")); PR_LOG(gFontLog, PR_LOG_DEBUG, (" - Trying to find fonts for: CJK"));
}
nsAutoTArray<nsRefPtr<FontEntry>, 15> fonts; nsAutoTArray<nsRefPtr<FontEntry>, 15> fonts;
GetCJKPrefFonts(fonts); GetCJKPrefFonts(fonts);
@ -643,7 +652,7 @@ gfxFT2FontGroup::WhichPrefFontSupportsChar(PRUint32 aCh)
return nsnull; return nsnull;
} }
already_AddRefed<gfxFont> already_AddRefed<gfxFont>
gfxFT2FontGroup::WhichSystemFontSupportsChar(PRUint32 aCh) gfxFT2FontGroup::WhichSystemFontSupportsChar(PRUint32 aCh)
{ {
nsRefPtr<gfxFont> selectedFont; nsRefPtr<gfxFont> selectedFont;
@ -667,7 +676,7 @@ void gfxFT2FontGroup::CreateGlyphRunsFT(gfxTextRun *aTextRun)
AddRange(aTextRun, font, mString.get(), offset, rangeLength); AddRange(aTextRun, font, mString.get(), offset, rangeLength);
offset += rangeLength; offset += rangeLength;
} }
} }
void void
@ -675,10 +684,15 @@ gfxFT2FontGroup::AddRange(gfxTextRun *aTextRun, gfxFT2Font *font, const PRUnicha
{ {
const PRUint32 appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit(); const PRUint32 appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
// we'll pass this in/figure it out dynamically, but at this point there can be only one face. // 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; gfxTextRun::CompressedGlyph g;
const gfxFT2Font::CachedGlyphData *cgd = nsnull, *cgdNext = nsnull;
FT_UInt spaceGlyph = font->GetSpaceGlyph();
aTextRun->AddGlyphRun(font, offset); aTextRun->AddGlyphRun(font, offset);
for (PRUint32 i = 0; i < len; i++) { for (PRUint32 i = 0; i < len; i++) {
PRUint32 ch = str[offset + 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"); 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; PRInt32 advance = 0;
if (gid == font->GetSpaceGlyph()) { if (gid == 0) {
advance = (int)(font->GetMetrics().spaceWidth * appUnitsPerDevUnit);
} else if (gid == 0) {
advance = -1; // trigger the missing glyphs case below advance = -1; // trigger the missing glyphs case below
} else { } else {
// find next character and its glyph -- in case they exist // 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) { if (FT_HAS_KERNING(face) && i + 1 < len) {
chNext = str[offset + i + 1]; chNext = str[offset + i + 1];
if (chNext != 0) { if (chNext != 0) {
gidNext = FT_Get_Char_Index(face, chNext); cgdNext = font->GetGlyphDataForChar(chNext);
if (gidNext && gidNext != font->GetSpaceGlyph()) { gidNext = cgdNext->glyphIndex;
FT_Load_Glyph(face, gidNext, FT_LOAD_DEFAULT); if (gidNext && gidNext != spaceGlyph)
lsbDeltaNext = face->glyph->lsb_delta; lsbDeltaNext = cgdNext->lsbDelta;
}
} }
} }
// now load the current glyph advance = cgd->xAdvance;
FT_Load_Glyph(face, gid, FT_LOAD_DEFAULT); // load glyph into the slot
advance = face->glyph->advance.x;
// now add kerning to the current glyph's advance // now add kerning to the current glyph's advance
if (chNext && gidNext) { if (chNext && gidNext) {
FT_Vector kerning; kerning.x = 0; FT_Vector kerning; kerning.x = 0;
FT_Get_Kerning(face, gid, gidNext, FT_KERNING_DEFAULT, &kerning); FT_Get_Kerning(face, gid, gidNext, FT_KERNING_DEFAULT, &kerning);
advance += kerning.x; advance += kerning.x;
if (face->glyph->rsb_delta - lsbDeltaNext >= 32) { if (cgd->rsbDelta - lsbDeltaNext >= 32) {
advance -= 64; advance -= 64;
} else if (face->glyph->rsb_delta - lsbDeltaNext < -32) { } else if (cgd->rsbDelta - lsbDeltaNext < -32) {
advance += 64; advance += 64;
} }
} }
// now apply unit conversion and scaling // now apply unit conversion and scaling
advance = (advance >> 6) * appUnitsPerDevUnit; advance = MOZ_FT_TRUNC(advance) * appUnitsPerDevUnit;
} }
#ifdef DEBUG_thebes_2 #ifdef DEBUG_thebes_2
printf(" gid=%d, advance=%d (%s)\n", gid, advance, 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); 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);
} }
/** /**
@ -767,14 +796,16 @@ gfxFT2FontGroup::AddRange(gfxTextRun *aTextRun, gfxFT2Font *font, const PRUnicha
gfxFT2Font::gfxFT2Font(FontEntry *aFontEntry, gfxFT2Font::gfxFT2Font(FontEntry *aFontEntry,
const gfxFontStyle *aFontStyle) const gfxFontStyle *aFontStyle)
: gfxFont(aFontEntry, aFontStyle), : gfxFont(aFontEntry, aFontStyle),
mScaledFont(nsnull), mScaledFont(nsnull),
mHasSpaceGlyph(PR_FALSE), mHasSpaceGlyph(PR_FALSE),
mSpaceGlyph(0), mSpaceGlyph(0),
mHasMetrics(PR_FALSE), mHasMetrics(PR_FALSE),
mAdjustedSize(0) mAdjustedSize(0)
{ {
mFontEntry = aFontEntry; mFontEntry = aFontEntry;
NS_ASSERTION(mFontEntry, "Unable to find font entry for font. Something is whack."); NS_ASSERTION(mFontEntry, "Unable to find font entry for font. Something is whack.");
mCharGlyphCache.Init(64);
} }
gfxFT2Font::~gfxFT2Font() 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& const gfxFont::Metrics&
gfxFT2Font::GetMetrics() gfxFT2Font::GetMetrics()
{ {
@ -801,7 +824,8 @@ gfxFT2Font::GetMetrics()
mMetrics.emHeight = GetStyle()->size; 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) { if (!face) {
// Abort here already, otherwise we crash in the following // Abort here already, otherwise we crash in the following
@ -818,18 +842,9 @@ gfxFT2Font::GetMetrics()
const double xScale = face->size->metrics.x_ppem / emUnit; const double xScale = face->size->metrics.x_ppem / emUnit;
const double yScale = face->size->metrics.y_ppem / emUnit; const double yScale = face->size->metrics.y_ppem / emUnit;
// properties of space // cache properties of space
gid = FT_Get_Char_Index(face, ' '); GetSpaceGlyph();
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");
}
// properties of 'x', also use its width as average width // properties of 'x', also use its width as average width
gid = FT_Get_Char_Index(face, 'x'); // select the glyph gid = FT_Get_Char_Index(face, 'x'); // select the glyph
if (gid) { if (gid) {
@ -916,7 +931,6 @@ gfxFT2Font::GetMetrics()
*/ */
// XXX mMetrics.height needs to be set. // XXX mMetrics.height needs to be set.
cairo_ft_scaled_font_unlock_face(CairoScaledFont());
mHasMetrics = PR_TRUE; mHasMetrics = PR_TRUE;
return mMetrics; return mMetrics;
@ -932,19 +946,18 @@ gfxFT2Font::GetUniqueName()
PRUint32 PRUint32
gfxFT2Font::GetSpaceGlyph() gfxFT2Font::GetSpaceGlyph()
{ {
NS_ASSERTION (GetStyle ()->size != 0, NS_ASSERTION (GetStyle()->size != 0, "forgot to short-circuit a text run with zero-sized font?");
"forgot to short-circuit a text run with zero-sized font?");
if(!mHasSpaceGlyph) if (!mHasSpaceGlyph) {
{ const CachedGlyphData *gdata = GetGlyphDataForChar(' ');
FT_UInt gid = 0; // glyph ID
FT_Face face = cairo_ft_scaled_font_lock_face(CairoScaledFont()); mSpaceGlyph = gdata->glyphIndex;
gid = FT_Get_Char_Index(face, ' '); NS_ASSERTION(mSpaceGlyph != 0, "Font has no space glyph!");
FT_Load_Glyph(face, gid, FT_LOAD_DEFAULT);
mSpaceGlyph = gid; mMetrics.spaceWidth = MOZ_FT_TRUNC(gdata->xAdvance);
mHasSpaceGlyph = PR_TRUE; mHasSpaceGlyph = PR_TRUE;
cairo_ft_scaled_font_unlock_face(CairoScaledFont());
} }
return mSpaceGlyph; return mSpaceGlyph;
} }
@ -1023,7 +1036,7 @@ gfxFT2Font::GetOrMakeFont(const nsAString& aName, const gfxFontStyle *aStyle)
{ {
FontEntry *fe = gfxToolkitPlatform::GetPlatform()->FindFontEntry(aName, *aStyle); FontEntry *fe = gfxToolkitPlatform::GetPlatform()->FindFontEntry(aName, *aStyle);
if (!fe) { 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; return nsnull;
} }
@ -1046,3 +1059,33 @@ gfxFT2Font::GetOrMakeFont(FontEntry *aFontEntry, const gfxFontStyle *aStyle)
return static_cast<gfxFT2Font *>(f); 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;
}