Bug 1845169 - Implement harfbuzz callbacks for batch char-to-glyph mappings and glyph widths. r=gfx-reviewers,lsalzman

No behavior change, but a small performance boost thanks to fewer
individual callbacks and less lock-twiddling.

Differential Revision: https://phabricator.services.mozilla.com/D184473
This commit is contained in:
Jonathan Kew 2023-07-25 17:30:21 +00:00
parent 5f01f95435
commit b0daebd2fc
2 changed files with 172 additions and 63 deletions

View File

@ -77,18 +77,12 @@ gfxHarfBuzzShaper::~gfxHarfBuzzShaper() {
#define UNICODE_BMP_LIMIT 0x10000
hb_codepoint_t gfxHarfBuzzShaper::GetNominalGlyph(
hb_codepoint_t gfxHarfBuzzShaper::GetGlyphUncached(
hb_codepoint_t unicode) const {
{
MutexAutoLock lock(mCacheLock);
if (auto cached = mCmapCache->Lookup(unicode)) {
return cached.Data().mGlyphId;
}
}
hb_codepoint_t gid = 0;
if (mUseFontGetGlyph) {
MutexAutoUnlock unlock(mCacheLock);
gid = mFont->GetGlyph(unicode, 0);
} else {
// we only instantiate a harfbuzz shaper if there's a cmap available
@ -126,33 +120,84 @@ hb_codepoint_t gfxHarfBuzzShaper::GetNominalGlyph(
// For legacy MS Symbol fonts, we try mapping the given character code
// to the PUA range used by these fonts' cmaps.
if (auto pua = gfxFontUtils::MapLegacySymbolFontCharToPUA(unicode)) {
gid = GetNominalGlyph(pua);
gid = GetGlyphUncached(pua);
}
if (gid) {
goto done;
return gid;
}
}
switch (unicode) {
case 0xA0:
case 0xA0: {
// if there's no glyph for  , just use the space glyph instead.
gid = mFont->GetSpaceGlyph();
break;
}
case 0x2010:
case 0x2011:
case 0x2011: {
// For Unicode HYPHEN and NON-BREAKING HYPHEN, fall back to the ASCII
// HYPHEN-MINUS as a substitute.
gid = GetNominalGlyph('-');
gid = GetGlyphUncached('-');
break;
}
}
}
done:
return gid;
}
hb_codepoint_t gfxHarfBuzzShaper::GetNominalGlyph(
hb_codepoint_t unicode) const {
MutexAutoLock lock(mCacheLock);
mCmapCache->Put(unicode, CmapCacheData{unicode, gid});
auto cached = mCmapCache->Lookup(unicode);
if (cached) {
return cached.Data().mGlyphId;
}
// This call can temporarily unlock the cache if mUseFontGetGlyph is true.
hb_codepoint_t gid = GetGlyphUncached(unicode);
if (mUseFontGetGlyph) {
// GetGlyphUncached may have invalidated our earlier cache lookup!
mCmapCache->Put(unicode, CmapCacheData{unicode, gid});
} else {
cached.Set(CmapCacheData{unicode, gid});
}
return gid;
}
unsigned int gfxHarfBuzzShaper::GetNominalGlyphs(
unsigned int count, const hb_codepoint_t* first_unicode,
unsigned int unicode_stride, hb_codepoint_t* first_glyph,
unsigned int glyph_stride) {
MutexAutoLock lock(mCacheLock);
unsigned int result = 0;
while (result < count) {
hb_codepoint_t usv = *first_unicode;
auto cached = mCmapCache->Lookup(usv);
if (cached) {
// Cache hit :)
*first_glyph = cached.Data().mGlyphId;
} else {
// Cache miss: call GetGlyphUncached (which handles things like symbol-
// encoding fallback) and fill in the cache entry with the result.
hb_codepoint_t gid = GetGlyphUncached(usv);
if (mUseFontGetGlyph) {
mCmapCache->Put(usv, CmapCacheData{usv, gid});
} else {
cached.Set(CmapCacheData{usv, gid});
}
*first_glyph = gid;
}
first_unicode = reinterpret_cast<const hb_codepoint_t*>(
reinterpret_cast<const char*>(first_unicode) + unicode_stride);
first_glyph = reinterpret_cast<hb_codepoint_t*>(
reinterpret_cast<char*>(first_glyph) + glyph_stride);
result++;
}
return result;
}
hb_codepoint_t gfxHarfBuzzShaper::GetVariationGlyph(
hb_codepoint_t unicode, hb_codepoint_t variation_selector) const {
if (mUseFontGetGlyph) {
@ -270,6 +315,20 @@ static hb_bool_t HBGetNominalGlyph(hb_font_t* font, void* font_data,
return *glyph != 0;
}
static unsigned int HBGetNominalGlyphs(
hb_font_t* font, void* font_data, unsigned int count,
const hb_codepoint_t* first_unicode, unsigned int unicode_stride,
hb_codepoint_t* first_glyph, unsigned int glyph_stride, void* user_data) {
const gfxHarfBuzzShaper::FontCallbackData* fcd =
static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
if (fcd->mShaper->UseVerticalPresentationForms()) {
return 0;
}
return fcd->mShaper->GetNominalGlyphs(count, first_unicode, unicode_stride,
first_glyph, glyph_stride);
}
static hb_bool_t HBGetVariationGlyph(hb_font_t* font, void* font_data,
hb_codepoint_t unicode,
hb_codepoint_t variation_selector,
@ -307,27 +366,13 @@ struct GlyphMetrics {
// AutoSwap_PRUint16 leftSideBearing[];
};
hb_position_t gfxHarfBuzzShaper::GetGlyphHAdvance(hb_codepoint_t glyph) const {
hb_position_t gfxHarfBuzzShaper::GetGlyphHAdvanceUncached(
hb_codepoint_t glyph) const {
if (mUseFontGlyphWidths) {
{
MutexAutoLock lock(mCacheLock);
if (auto cached = mWidthCache->Lookup(glyph)) {
return cached.Data().mAdvance;
}
}
hb_position_t advance = GetFont()->GetGlyphWidth(glyph);
MutexAutoLock lock(mCacheLock);
mWidthCache->Put(glyph, WidthCacheData{glyph, advance});
return advance;
return GetFont()->GetGlyphWidth(glyph);
}
// Font did not implement GetGlyphWidth, so get an unhinted value
// directly from the font tables. This lookup is cheap enough that we
// don't create an MruCache for it.
// Get an unhinted value directly from the font tables.
NS_ASSERTION((mNumLongHMetrics > 0) && mHmtxTable != nullptr,
"font is lacking metrics, we shouldn't be here");
@ -344,6 +389,60 @@ hb_position_t gfxHarfBuzzShaper::GetGlyphHAdvance(hb_codepoint_t glyph) const {
uint16_t(metrics->metrics[glyph].advanceWidth));
}
hb_position_t gfxHarfBuzzShaper::GetGlyphHAdvance(hb_codepoint_t glyph) const {
if (mUseFontGlyphWidths) {
MutexAutoLock lock(mCacheLock);
if (auto cached = mWidthCache->Lookup(glyph)) {
return cached.Data().mAdvance;
}
mCacheLock.Unlock();
hb_position_t advance = GetFont()->GetGlyphWidth(glyph);
mCacheLock.Lock();
mWidthCache->Put(glyph, WidthCacheData{glyph, advance});
return advance;
}
return GetGlyphHAdvanceUncached(glyph);
}
void gfxHarfBuzzShaper::GetGlyphHAdvances(unsigned int count,
const hb_codepoint_t* first_glyph,
unsigned int glyph_stride,
hb_position_t* first_advance,
unsigned int advance_stride) const {
if (mUseFontGlyphWidths) {
// Take the cache lock here, hoping we'll be able to retrieve a bunch of
// widths from the cache for the cost of a single locking operation.
MutexAutoLock lock(mCacheLock);
for (unsigned int i = 0; i < count; ++i) {
hb_codepoint_t gid = *first_glyph;
if (auto cached = mWidthCache->Lookup(gid)) {
*first_advance = cached.Data().mAdvance;
} else {
// Unlock to avoid deadlock if the font needs internal locking.
mCacheLock.Unlock();
hb_position_t advance = GetFont()->GetGlyphWidth(gid);
mCacheLock.Lock();
mWidthCache->Put(gid, WidthCacheData{gid, advance});
*first_advance = advance;
}
first_glyph = reinterpret_cast<const hb_codepoint_t*>(
reinterpret_cast<const char*>(first_glyph) + glyph_stride);
first_advance = reinterpret_cast<hb_position_t*>(
reinterpret_cast<char*>(first_advance) + advance_stride);
}
return;
}
for (unsigned int i = 0; i < count; ++i) {
*first_advance = GetGlyphHAdvanceUncached(*first_glyph);
first_glyph = reinterpret_cast<const hb_codepoint_t*>(
reinterpret_cast<const char*>(first_glyph) + glyph_stride);
first_advance = reinterpret_cast<hb_position_t*>(
reinterpret_cast<char*>(first_advance) + advance_stride);
}
}
hb_position_t gfxHarfBuzzShaper::GetGlyphVAdvance(hb_codepoint_t glyph) {
InitializeVertical();
@ -370,21 +469,27 @@ hb_position_t gfxHarfBuzzShaper::GetGlyphVAdvance(hb_codepoint_t glyph) {
uint16_t(metrics->metrics[glyph].advanceWidth));
}
/* static */
hb_position_t gfxHarfBuzzShaper::HBGetGlyphHAdvance(hb_font_t* font,
void* font_data,
hb_codepoint_t glyph,
void* user_data) {
static hb_position_t HBGetGlyphHAdvance(hb_font_t* font, void* font_data,
hb_codepoint_t glyph, void* user_data) {
const gfxHarfBuzzShaper::FontCallbackData* fcd =
static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
return fcd->mShaper->GetGlyphHAdvance(glyph);
}
/* static */
hb_position_t gfxHarfBuzzShaper::HBGetGlyphVAdvance(hb_font_t* font,
void* font_data,
hb_codepoint_t glyph,
void* user_data) {
static void HBGetGlyphHAdvances(hb_font_t* font, void* font_data,
unsigned int count,
const hb_codepoint_t* first_glyph,
unsigned int glyph_stride,
hb_position_t* first_advance,
unsigned int advance_stride, void* user_data) {
const gfxHarfBuzzShaper::FontCallbackData* fcd =
static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
fcd->mShaper->GetGlyphHAdvances(count, first_glyph, glyph_stride,
first_advance, advance_stride);
}
static hb_position_t HBGetGlyphVAdvance(hb_font_t* font, void* font_data,
hb_codepoint_t glyph, void* user_data) {
const gfxHarfBuzzShaper::FontCallbackData* fcd =
static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
// Currently, we don't offer gfxFont subclasses a method to override this
@ -416,12 +521,9 @@ struct VORGrec {
AutoSwap_PRInt16 vertOriginY;
};
/* static */
hb_bool_t gfxHarfBuzzShaper::HBGetGlyphVOrigin(hb_font_t* font, void* font_data,
hb_codepoint_t glyph,
hb_position_t* x,
hb_position_t* y,
void* user_data) {
static hb_bool_t HBGetGlyphVOrigin(hb_font_t* font, void* font_data,
hb_codepoint_t glyph, hb_position_t* x,
hb_position_t* y, void* user_data) {
const gfxHarfBuzzShaper::FontCallbackData* fcd =
static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
fcd->mShaper->GetGlyphVOrigin(glyph, x, y);
@ -1114,10 +1216,14 @@ bool gfxHarfBuzzShaper::Initialize() {
sHBFontFuncs = hb_font_funcs_create();
hb_font_funcs_set_nominal_glyph_func(sHBFontFuncs, HBGetNominalGlyph,
nullptr, nullptr);
hb_font_funcs_set_nominal_glyphs_func(sHBFontFuncs, HBGetNominalGlyphs,
nullptr, nullptr);
hb_font_funcs_set_variation_glyph_func(sHBFontFuncs, HBGetVariationGlyph,
nullptr, nullptr);
hb_font_funcs_set_glyph_h_advance_func(sHBFontFuncs, HBGetGlyphHAdvance,
nullptr, nullptr);
hb_font_funcs_set_glyph_h_advances_func(sHBFontFuncs, HBGetGlyphHAdvances,
nullptr, nullptr);
hb_font_funcs_set_glyph_v_advance_func(sHBFontFuncs, HBGetGlyphVAdvance,
nullptr, nullptr);
hb_font_funcs_set_glyph_v_origin_func(sHBFontFuncs, HBGetGlyphVOrigin,

View File

@ -43,11 +43,20 @@ class gfxHarfBuzzShaper : public gfxFontShaper {
// map unicode character to glyph ID
hb_codepoint_t GetNominalGlyph(hb_codepoint_t unicode) const;
unsigned int GetNominalGlyphs(unsigned int count,
const hb_codepoint_t* first_unicode,
unsigned int unicode_stride,
hb_codepoint_t* first_glyph,
unsigned int glyph_stride);
hb_codepoint_t GetVariationGlyph(hb_codepoint_t unicode,
hb_codepoint_t variation_selector) const;
// get harfbuzz glyph advance, in font design units
hb_position_t GetGlyphHAdvance(hb_codepoint_t glyph) const;
void GetGlyphHAdvances(unsigned int count, const hb_codepoint_t* first_glyph,
unsigned int glyph_stride,
hb_position_t* first_advance,
unsigned int advance_stride) const;
// Get vertical glyph advance, or -1 if not available; caller should check
// for a negative result and provide a fallback or fail, as appropriate.
@ -56,20 +65,6 @@ class gfxHarfBuzzShaper : public gfxFontShaper {
void GetGlyphVOrigin(hb_codepoint_t aGlyph, hb_position_t* aX,
hb_position_t* aY) const;
// get harfbuzz horizontal advance in 16.16 fixed point format.
static hb_position_t HBGetGlyphHAdvance(hb_font_t* font, void* font_data,
hb_codepoint_t glyph,
void* user_data);
// get harfbuzz vertical advance in 16.16 fixed point format.
static hb_position_t HBGetGlyphVAdvance(hb_font_t* font, void* font_data,
hb_codepoint_t glyph,
void* user_data);
static hb_bool_t HBGetGlyphVOrigin(hb_font_t* font, void* font_data,
hb_codepoint_t glyph, hb_position_t* x,
hb_position_t* y, void* user_data);
hb_position_t GetHKerning(uint16_t aFirstGlyph, uint16_t aSecondGlyph) const;
hb_bool_t GetGlyphExtents(hb_codepoint_t aGlyph,
@ -106,6 +101,14 @@ class gfxHarfBuzzShaper : public gfxFontShaper {
hb_face_t* GetHBFace() const { return hb_font_get_face(mHBFont); }
protected:
// This is called with the cache locked, but if mUseFontGetGlyph is true, it
// may unlock it temporarily. So in this case, it may invalidate an earlier
// cache entry reference.
hb_codepoint_t GetGlyphUncached(hb_codepoint_t unicode) const
MOZ_REQUIRES(mCacheLock);
hb_position_t GetGlyphHAdvanceUncached(hb_codepoint_t gid) const;
nsresult SetGlyphsFromRun(gfxShapedText* aShapedText, uint32_t aOffset,
uint32_t aLength, const char16_t* aText,
bool aVertical, RoundingFlags aRounding);