Bug 1756468 - Add locking to gfxFont, to allow worker threads to do text shaping. r=lsalzman

We protect the shaped-word cache in each font with a RWLock, so that multiple threads can
shape in parallel using cached data; only when a new entry needs to be cached will we need
to take a write lock.

(To improve clarity, this patch also constifys a bunch of methods that do not mutate the
font instance.)

Differential Revision: https://phabricator.services.mozilla.com/D141473
This commit is contained in:
Jonathan Kew 2022-03-24 18:58:00 +00:00
parent 68dd7e07df
commit 6bbbf58763
17 changed files with 421 additions and 262 deletions

View File

@ -980,7 +980,7 @@ class SharedFTFace : public external::AtomicRefCounted<SharedFTFace> {
* If no owner is given, then the user should avoid modifying any state on
* the face so as not to invalidate the prior owner's modification.
*/
bool Lock(void* aOwner = nullptr) CAPABILITY_ACQUIRE(mLock) {
bool Lock(const void* aOwner = nullptr) CAPABILITY_ACQUIRE(mLock) {
mLock.Lock();
return !aOwner || mLastLockOwner.exchange(aOwner) == aOwner;
}
@ -989,7 +989,7 @@ class SharedFTFace : public external::AtomicRefCounted<SharedFTFace> {
/** Should be called when a lock owner is destroyed so that we don't have
* a dangling pointer to a destroyed owner.
*/
void ForgetLockOwner(void* aOwner) {
void ForgetLockOwner(const void* aOwner) {
if (aOwner) {
mLastLockOwner.compareExchange(aOwner, nullptr);
}
@ -1002,7 +1002,7 @@ class SharedFTFace : public external::AtomicRefCounted<SharedFTFace> {
// Remember the last owner of the lock, even after unlocking, to allow users
// to avoid reinitializing state on the FT face if the last owner hasn't
// changed by the next time it is locked with the same owner.
Atomic<void*> mLastLockOwner;
Atomic<const void*> mLastLockOwner;
};
#endif

View File

@ -107,7 +107,12 @@ gfxDWriteFont::gfxDWriteFont(const RefPtr<UnscaledFontDWrite>& aUnscaledFont,
ComputeMetrics(anAAOption);
}
gfxDWriteFont::~gfxDWriteFont() { delete mMetrics; }
gfxDWriteFont::~gfxDWriteFont() {
if (auto* scaledFont = mAzureScaledFontGDI.exchange(nullptr)) {
scaledFont->Release();
}
delete mMetrics;
}
/* static */
bool gfxDWriteFont::InitDWriteSupport() {
@ -272,17 +277,12 @@ void gfxDWriteFont::UpdateClearTypeVars() {
pixelGeometry, renderingModePref);
}
UniquePtr<gfxFont> gfxDWriteFont::CopyWithAntialiasOption(
AntialiasOption anAAOption) {
gfxFont* gfxDWriteFont::CopyWithAntialiasOption(
AntialiasOption anAAOption) const {
auto entry = static_cast<gfxDWriteFontEntry*>(mFontEntry.get());
RefPtr<UnscaledFontDWrite> unscaledFont =
static_cast<UnscaledFontDWrite*>(mUnscaledFont.get());
return MakeUnique<gfxDWriteFont>(unscaledFont, entry, &mStyle, mFontFace,
anAAOption);
}
const gfxFont::Metrics& gfxDWriteFont::GetHorizontalMetrics() {
return *mMetrics;
return new gfxDWriteFont(unscaledFont, entry, &mStyle, mFontFace, anAAOption);
}
bool gfxDWriteFont::GetFakeMetricsForArialBlack(
@ -743,7 +743,7 @@ gfxFloat gfxDWriteFont::MeasureGlyphWidth(uint16_t aGlyph) {
}
bool gfxDWriteFont::GetGlyphBounds(uint16_t aGID, gfxRect* aBounds,
bool aTight) {
bool aTight) const {
DWRITE_GLYPH_METRICS m;
HRESULT hr = mFontFace->GetDesignGlyphMetrics(&aGID, 1, &m, FALSE);
if (FAILED(hr)) {
@ -780,32 +780,50 @@ void gfxDWriteFont::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
already_AddRefed<ScaledFont> gfxDWriteFont::GetScaledFont(
const TextRunDrawParams& aRunParams) {
if (mAzureScaledFontUsedClearType != UsingClearType()) {
mAzureScaledFont = nullptr;
mAzureScaledFontGDI = nullptr;
bool useClearType = UsingClearType();
if (mAzureScaledFontUsedClearType != useClearType) {
if (auto* oldScaledFont = mAzureScaledFont.exchange(nullptr)) {
oldScaledFont->Release();
}
if (auto* oldScaledFont = mAzureScaledFontGDI.exchange(nullptr)) {
oldScaledFont->Release();
}
}
bool forceGDI = aRunParams.allowGDI && GetForceGDIClassic();
RefPtr<ScaledFont>& azureScaledFont =
forceGDI ? mAzureScaledFontGDI : mAzureScaledFont;
if (!azureScaledFont) {
gfxDWriteFontEntry* fe = static_cast<gfxDWriteFontEntry*>(mFontEntry.get());
bool useEmbeddedBitmap =
(gfxVars::SystemTextRenderingMode() == DWRITE_RENDERING_MODE_DEFAULT ||
forceGDI) &&
fe->IsCJKFont() && HasBitmapStrikeForSize(NS_lround(mAdjustedSize));
const gfxFontStyle* fontStyle = GetStyle();
azureScaledFont = Factory::CreateScaledFontForDWriteFont(
mFontFace, fontStyle, GetUnscaledFont(), GetAdjustedSize(),
useEmbeddedBitmap, ApplySyntheticBold(), forceGDI);
if (!azureScaledFont) {
return nullptr;
}
InitializeScaledFont(azureScaledFont);
mAzureScaledFontUsedClearType = UsingClearType();
ScaledFont* scaledFont = forceGDI ? mAzureScaledFontGDI : mAzureScaledFont;
if (scaledFont) {
return do_AddRef(scaledFont);
}
return do_AddRef(azureScaledFont);
gfxDWriteFontEntry* fe = static_cast<gfxDWriteFontEntry*>(mFontEntry.get());
bool useEmbeddedBitmap =
(gfxVars::SystemTextRenderingMode() == DWRITE_RENDERING_MODE_DEFAULT ||
forceGDI) &&
fe->IsCJKFont() && HasBitmapStrikeForSize(NS_lround(mAdjustedSize));
const gfxFontStyle* fontStyle = GetStyle();
RefPtr<ScaledFont> newScaledFont = Factory::CreateScaledFontForDWriteFont(
mFontFace, fontStyle, GetUnscaledFont(), GetAdjustedSize(),
useEmbeddedBitmap, ApplySyntheticBold(), forceGDI);
if (!newScaledFont) {
return nullptr;
}
InitializeScaledFont(newScaledFont);
if (forceGDI) {
if (mAzureScaledFontGDI.compareExchange(nullptr, newScaledFont.get())) {
Unused << newScaledFont.forget();
mAzureScaledFontUsedClearType = useClearType;
}
scaledFont = mAzureScaledFontGDI;
} else {
if (mAzureScaledFont.compareExchange(nullptr, newScaledFont.get())) {
Unused << newScaledFont.forget();
mAzureScaledFontUsedClearType = useClearType;
}
scaledFont = mAzureScaledFont;
}
return do_AddRef(scaledFont);
}
bool gfxDWriteFont::ShouldRoundXOffset(cairo_t* aCairo) const {

View File

@ -39,10 +39,9 @@ class gfxDWriteFont final : public gfxFont {
static void SystemTextQualityChanged();
mozilla::UniquePtr<gfxFont> CopyWithAntialiasOption(
AntialiasOption anAAOption) override;
gfxFont* CopyWithAntialiasOption(AntialiasOption anAAOption) const override;
bool AllowSubpixelAA() override { return mAllowManualShowGlyphs; }
bool AllowSubpixelAA() const override { return mAllowManualShowGlyphs; }
bool IsValid() const;
@ -59,7 +58,8 @@ class gfxDWriteFont final : public gfxFont {
int32_t GetGlyphWidth(uint16_t aGID) override;
bool GetGlyphBounds(uint16_t aGID, gfxRect* aBounds, bool aTight) override;
bool GetGlyphBounds(uint16_t aGID, gfxRect* aBounds,
bool aTight) const override;
void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontCacheSizes* aSizes) const override;
@ -74,7 +74,7 @@ class gfxDWriteFont final : public gfxFont {
bool ShouldRoundXOffset(cairo_t* aCairo) const override;
protected:
const Metrics& GetHorizontalMetrics() override;
const Metrics& GetHorizontalMetrics() const override { return *mMetrics; }
bool GetFakeMetricsForArialBlack(DWRITE_FONT_METRICS* aFontMetrics);
@ -102,12 +102,12 @@ class gfxDWriteFont final : public gfxFont {
// Used to record the sUseClearType setting at the time mAzureScaledFont
// was set up, so we can tell if it's stale and needs to be re-created.
bool mAzureScaledFontUsedClearType;
mozilla::Atomic<bool> mAzureScaledFontUsedClearType;
// Cache the GDI version of the ScaledFont so that font keys and other
// meta-data can remain stable even if there is thrashing between GDI and
// non-GDI usage.
RefPtr<mozilla::gfx::ScaledFont> mAzureScaledFontGDI;
mozilla::Atomic<mozilla::gfx::ScaledFont*> mAzureScaledFontGDI;
bool UsingClearType() {
return mozilla::gfx::gfxVars::SystemTextQuality() == CLEARTYPE_QUALITY;

View File

@ -41,7 +41,7 @@ gfxFT2FontBase::gfxFT2FontBase(
gfxFT2FontBase::~gfxFT2FontBase() { mFTFace->ForgetLockOwner(this); }
FT_Face gfxFT2FontBase::LockFTFace()
FT_Face gfxFT2FontBase::LockFTFace() const
CAPABILITY_ACQUIRE(mFTFace) NO_THREAD_SAFETY_ANALYSIS {
if (!mFTFace->Lock(this)) {
FT_Set_Transform(mFTFace->GetFace(), nullptr, nullptr);
@ -52,7 +52,7 @@ FT_Face gfxFT2FontBase::LockFTFace()
return mFTFace->GetFace();
}
void gfxFT2FontBase::UnlockFTFace()
void gfxFT2FontBase::UnlockFTFace() const
CAPABILITY_RELEASE(mFTFace) NO_THREAD_SAFETY_ANALYSIS {
mFTFace->Unlock();
}
@ -553,10 +553,6 @@ void gfxFT2FontBase::InitMetrics() {
#endif
}
const gfxFont::Metrics& gfxFT2FontBase::GetHorizontalMetrics() {
return mMetrics;
}
uint32_t gfxFT2FontBase::GetGlyph(uint32_t unicode,
uint32_t variation_selector) {
if (variation_selector) {
@ -597,7 +593,7 @@ bool gfxFT2FontBase::ShouldRoundXOffset(cairo_t* aCairo) const {
gfx_text_subpixel_position_force_enabled_AtStartup()));
}
FT_Vector gfxFT2FontBase::GetEmboldenStrength(FT_Face aFace) {
FT_Vector gfxFT2FontBase::GetEmboldenStrength(FT_Face aFace) const {
FT_Vector strength = {0, 0};
if (!mEmbolden) {
return strength;
@ -628,7 +624,7 @@ FT_Vector gfxFT2FontBase::GetEmboldenStrength(FT_Face aFace) {
}
bool gfxFT2FontBase::GetFTGlyphExtents(uint16_t aGID, int32_t* aAdvance,
IntRect* aBounds) {
IntRect* aBounds) const {
gfxFT2LockedFace face(this);
MOZ_ASSERT(face.get());
if (!face.get()) {
@ -715,7 +711,7 @@ bool gfxFT2FontBase::GetFTGlyphExtents(uint16_t aGID, int32_t* aAdvance,
* FreeType for the glyph extents and initialize the glyph metrics.
*/
const gfxFT2FontBase::GlyphMetrics& gfxFT2FontBase::GetCachedGlyphMetrics(
uint16_t aGID, IntRect* aBounds) {
uint16_t aGID, IntRect* aBounds) const {
if (!mGlyphMetrics) {
mGlyphMetrics =
mozilla::MakeUnique<nsTHashMap<nsUint32HashKey, GlyphMetrics>>(128);
@ -739,7 +735,7 @@ int32_t gfxFT2FontBase::GetGlyphWidth(uint16_t aGID) {
}
bool gfxFT2FontBase::GetGlyphBounds(uint16_t aGID, gfxRect* aBounds,
bool aTight) {
bool aTight) const {
IntRect bounds;
const GlyphMetrics& metrics = GetCachedGlyphMetrics(aGID, &bounds);
if (!metrics.HasValidBounds()) {

View File

@ -51,7 +51,8 @@ class gfxFT2FontBase : public gfxFont {
uint32_t variation_selector) override;
bool ProvidesGlyphWidths() const override { return true; }
int32_t GetGlyphWidth(uint16_t aGID) override;
bool GetGlyphBounds(uint16_t aGID, gfxRect* aBounds, bool aTight) override;
bool GetGlyphBounds(uint16_t aGID, gfxRect* aBounds,
bool aTight) const override;
FontType GetType() const override { return FONT_TYPE_FT2; }
@ -61,8 +62,8 @@ class gfxFT2FontBase : public gfxFont {
const nsTArray<gfxFontVariation>& aVariations,
FT_Face aFTFace);
FT_Face LockFTFace();
void UnlockFTFace();
FT_Face LockFTFace() const;
void UnlockFTFace() const;
private:
uint32_t GetCharExtents(uint32_t aChar, gfxFloat* aWidth,
@ -71,12 +72,12 @@ class gfxFT2FontBase : public gfxFont {
// Get advance (and optionally bounds) of a single glyph from FreeType,
// and return true, or return false if we failed.
bool GetFTGlyphExtents(uint16_t aGID, int32_t* aWidth,
mozilla::gfx::IntRect* aBounds = nullptr);
mozilla::gfx::IntRect* aBounds = nullptr) const;
protected:
void InitMetrics();
const Metrics& GetHorizontalMetrics() override;
FT_Vector GetEmboldenStrength(FT_Face aFace);
const Metrics& GetHorizontalMetrics() const override { return mMetrics; }
FT_Vector GetEmboldenStrength(FT_Face aFace) const;
RefPtr<mozilla::gfx::SharedFTFace> mFTFace;
@ -133,9 +134,10 @@ class gfxFT2FontBase : public gfxFont {
};
const GlyphMetrics& GetCachedGlyphMetrics(
uint16_t aGID, mozilla::gfx::IntRect* aBounds = nullptr);
uint16_t aGID, mozilla::gfx::IntRect* aBounds = nullptr) const;
mozilla::UniquePtr<nsTHashMap<nsUint32HashKey, GlyphMetrics>> mGlyphMetrics;
mutable mozilla::UniquePtr<nsTHashMap<nsUint32HashKey, GlyphMetrics>>
mGlyphMetrics;
};
// Helper classes used for clearing out user font data when FT font

View File

@ -166,15 +166,24 @@ gfxFT2Font::~gfxFT2Font() {}
already_AddRefed<ScaledFont> gfxFT2Font::GetScaledFont(
const TextRunDrawParams& aRunParams) {
if (!mAzureScaledFont) {
mAzureScaledFont = Factory::CreateScaledFontForFreeTypeFont(
GetUnscaledFont(), GetAdjustedSize(), mFTFace,
GetStyle()->NeedsSyntheticBold(GetFontEntry()));
InitializeScaledFont();
if (ScaledFont* scaledFont = mAzureScaledFont) {
return do_AddRef(scaledFont);
}
RefPtr<ScaledFont> scaledFont(mAzureScaledFont);
return scaledFont.forget();
RefPtr<ScaledFont> newScaledFont = Factory::CreateScaledFontForFreeTypeFont(
GetUnscaledFont(), GetAdjustedSize(), mFTFace,
GetStyle()->NeedsSyntheticBold(GetFontEntry()));
if (!newScaledFont) {
return nullptr;
}
InitializeScaledFont(newScaledFont);
if (mAzureScaledFont.compareExchange(nullptr, newScaledFont.get())) {
Unused << newScaledFont.forget();
}
ScaledFont* scaledFont = mAzureScaledFont;
return do_AddRef(scaledFont);
}
bool gfxFT2Font::ShouldHintMetrics() const {

View File

@ -28,7 +28,7 @@ typedef struct FT_FaceRec_* FT_Face;
*/
class MOZ_STACK_CLASS gfxFT2LockedFace {
public:
explicit gfxFT2LockedFace(gfxFT2FontBase* aFont)
explicit gfxFT2LockedFace(const gfxFT2FontBase* aFont)
: mGfxFont(aFont), mFace(aFont->LockFTFace()) {}
~gfxFT2LockedFace() {
if (mFace) {
@ -54,7 +54,7 @@ class MOZ_STACK_CLASS gfxFT2LockedFace {
FT_ULong variantSelector);
CharVariantFunction FindCharVariantFunction();
gfxFT2FontBase* MOZ_NON_OWNING_REF mGfxFont; // owned by caller
const gfxFT2FontBase* MOZ_NON_OWNING_REF mGfxFont; // owned by caller
FT_Face mFace;
};

View File

@ -1272,14 +1272,23 @@ gfxFontconfigFont::~gfxFontconfigFont() = default;
already_AddRefed<ScaledFont> gfxFontconfigFont::GetScaledFont(
const TextRunDrawParams& aRunParams) {
if (!mAzureScaledFont) {
mAzureScaledFont = Factory::CreateScaledFontForFontconfigFont(
GetUnscaledFont(), GetAdjustedSize(), mFTFace, GetPattern());
InitializeScaledFont();
if (ScaledFont* scaledFont = mAzureScaledFont) {
return do_AddRef(scaledFont);
}
RefPtr<ScaledFont> scaledFont(mAzureScaledFont);
return scaledFont.forget();
RefPtr<ScaledFont> newScaledFont = Factory::CreateScaledFontForFontconfigFont(
GetUnscaledFont(), GetAdjustedSize(), mFTFace, GetPattern());
if (!newScaledFont) {
return nullptr;
}
InitializeScaledFont(newScaledFont);
if (mAzureScaledFont.compareExchange(nullptr, newScaledFont.get())) {
Unused << newScaledFont.forget();
}
ScaledFont* scaledFont = mAzureScaledFont;
return do_AddRef(scaledFont);
}
bool gfxFontconfigFont::ShouldHintMetrics() const {

View File

@ -13,6 +13,7 @@
#include "mozilla/intl/Segmenter.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/StaticPrefs_gfx.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/SVGContextPaint.h"
#include "mozilla/Logging.h"
@ -844,6 +845,7 @@ gfxFont::gfxFont(const RefPtr<UnscaledFont>& aUnscaledFont,
gfxFontEntry* aFontEntry, const gfxFontStyle* aFontStyle,
AntialiasOption anAAOption)
: mFontEntry(aFontEntry),
mLock("gfxFont lock"),
mUnscaledFont(aUnscaledFont),
mStyle(*aFontStyle),
mAdjustedSize(-1.0), // negative to indicate "not yet initialized"
@ -873,6 +875,23 @@ gfxFont::gfxFont(const RefPtr<UnscaledFont>& aUnscaledFont,
gfxFont::~gfxFont() {
mFontEntry->NotifyFontDestroyed(this);
// Delete objects owned through atomic pointers. (Some of these may be null,
// but that's OK.)
auto* verticalMetrics = mVerticalMetrics.exchange(nullptr);
delete verticalMetrics;
auto* hbShaper = mHarfBuzzShaper.exchange(nullptr);
delete hbShaper;
auto* grShaper = mGraphiteShaper.exchange(nullptr);
delete grShaper;
auto* mathTable = mMathTable.exchange(nullptr);
delete mathTable;
auto* nonAAfont = mNonAAFont.exchange(nullptr);
delete nonAAfont;
if (auto* scaledFont = mAzureScaledFont.exchange(nullptr)) {
scaledFont->Release();
}
if (mGlyphChangeObservers) {
for (const auto& key : *mGlyphChangeObservers) {
key->ForgetFont();
@ -927,26 +946,37 @@ gfxFont::RoundingFlags gfxFont::GetRoundOffsetsToPixels(
}
}
gfxHarfBuzzShaper* gfxFont::GetHarfBuzzShaper() {
if (!mHarfBuzzShaper) {
auto* shaper = new gfxHarfBuzzShaper(this);
shaper->Initialize();
if (!mHarfBuzzShaper.compareExchange(nullptr, shaper)) {
delete shaper;
}
}
gfxHarfBuzzShaper* shaper = mHarfBuzzShaper;
return shaper->IsInitialized() ? shaper : nullptr;
}
gfxFloat gfxFont::GetGlyphAdvance(uint16_t aGID, bool aVertical) {
if (!aVertical && ProvidesGlyphWidths()) {
return GetGlyphWidth(aGID) / 65536.0;
}
if (mFUnitsConvFactor < 0.0f) {
GetMetrics(nsFontMetrics::eHorizontal);
// Metrics haven't been initialized; lock while we do that.
AutoWriteLock lock(mLock);
if (mFUnitsConvFactor < 0.0f) {
GetMetrics(nsFontMetrics::eHorizontal);
}
}
NS_ASSERTION(mFUnitsConvFactor >= 0.0f,
"missing font unit conversion factor");
if (!mHarfBuzzShaper) {
mHarfBuzzShaper = MakeUnique<gfxHarfBuzzShaper>(this);
if (gfxHarfBuzzShaper* shaper = GetHarfBuzzShaper()) {
return (aVertical ? shaper->GetGlyphVAdvance(aGID)
: shaper->GetGlyphHAdvance(aGID)) /
65536.0;
}
gfxHarfBuzzShaper* shaper =
static_cast<gfxHarfBuzzShaper*>(mHarfBuzzShaper.get());
if (!shaper->Initialize()) {
return 0;
}
return (aVertical ? shaper->GetGlyphVAdvance(aGID)
: shaper->GetGlyphHAdvance(aGID)) /
65536.0;
return 0.0;
}
gfxFloat gfxFont::GetCharAdvance(uint32_t aUnicode, bool aVertical) {
@ -954,15 +984,9 @@ gfxFloat gfxFont::GetCharAdvance(uint32_t aUnicode, bool aVertical) {
if (ProvidesGetGlyph()) {
gid = GetGlyph(aUnicode, 0);
} else {
if (!mHarfBuzzShaper) {
mHarfBuzzShaper = MakeUnique<gfxHarfBuzzShaper>(this);
if (gfxHarfBuzzShaper* shaper = GetHarfBuzzShaper()) {
gid = shaper->GetNominalGlyph(aUnicode);
}
gfxHarfBuzzShaper* shaper =
static_cast<gfxHarfBuzzShaper*>(mHarfBuzzShaper.get());
if (!shaper->Initialize()) {
return -1.0;
}
gid = shaper->GetNominalGlyph(aUnicode);
}
if (!gid) {
return -1.0;
@ -1177,8 +1201,9 @@ static const hb_tag_t defaultFeatures[] = {
HB_TAG('t', 'j', 'm', 'o'), HB_TAG('v', 'a', 't', 'u'),
HB_TAG('v', 'e', 'r', 't'), HB_TAG('v', 'j', 'm', 'o')};
void gfxFont::CheckForFeaturesInvolvingSpace() {
mFontEntry->mHasSpaceFeaturesInitialized = true;
void gfxFont::CheckForFeaturesInvolvingSpace() const {
auto setInitializedFlag =
MakeScopeExit([&]() { mFontEntry->mHasSpaceFeaturesInitialized = true; });
bool log = LOG_FONTINIT_ENABLED();
TimeStamp start;
@ -1305,7 +1330,7 @@ void gfxFont::CheckForFeaturesInvolvingSpace() {
}
}
bool gfxFont::HasSubstitutionRulesWithSpaceLookups(Script aRunScript) {
bool gfxFont::HasSubstitutionRulesWithSpaceLookups(Script aRunScript) const {
NS_ASSERTION(GetFontEntry()->mHasSpaceFeaturesInitialized,
"need to initialize space lookup flags");
NS_ASSERTION(aRunScript < Script::NUM_SCRIPT_CODES, "weird script code");
@ -1332,7 +1357,8 @@ bool gfxFont::HasSubstitutionRulesWithSpaceLookups(Script aRunScript) {
return false;
}
tainted_boolean_hint gfxFont::SpaceMayParticipateInShaping(Script aRunScript) {
tainted_boolean_hint gfxFont::SpaceMayParticipateInShaping(
Script aRunScript) const {
// avoid checking fonts known not to include default space-dependent features
if (MOZ_UNLIKELY(mFontEntry->mSkipDefaultFeatureSpaceCheck)) {
if (!mKerningSet && mStyle.featureSettings.IsEmpty() &&
@ -1468,16 +1494,14 @@ bool gfxFont::SupportsSubSuperscript(uint32_t aSubSuperscript,
}
// xxx - for graphite, don't really know how to sniff lookups so bail
if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
return true;
{
if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
return true;
}
}
if (!mHarfBuzzShaper) {
mHarfBuzzShaper = MakeUnique<gfxHarfBuzzShaper>(this);
}
gfxHarfBuzzShaper* shaper =
static_cast<gfxHarfBuzzShaper*>(mHarfBuzzShaper.get());
if (!shaper->Initialize()) {
gfxHarfBuzzShaper* shaper = GetHarfBuzzShaper();
if (!shaper) {
return false;
}
@ -1521,21 +1545,16 @@ bool gfxFont::FeatureWillHandleChar(Script aRunScript, uint32_t aFeature,
return true;
}
if (!mHarfBuzzShaper) {
mHarfBuzzShaper = MakeUnique<gfxHarfBuzzShaper>(this);
}
gfxHarfBuzzShaper* shaper =
static_cast<gfxHarfBuzzShaper*>(mHarfBuzzShaper.get());
if (!shaper->Initialize()) {
return false;
if (gfxHarfBuzzShaper* shaper = GetHarfBuzzShaper()) {
// get the hbset containing input glyphs for the feature
const hb_set_t* inputGlyphs =
mFontEntry->InputsForOpenTypeFeature(aRunScript, aFeature);
hb_codepoint_t gid = shaper->GetNominalGlyph(aUnicode);
return hb_set_has(inputGlyphs, gid);
}
// get the hbset containing input glyphs for the feature
const hb_set_t* inputGlyphs =
mFontEntry->InputsForOpenTypeFeature(aRunScript, aFeature);
hb_codepoint_t gid = shaper->GetNominalGlyph(aUnicode);
return hb_set_has(inputGlyphs, gid);
return false;
}
bool gfxFont::HasFeatureSet(uint32_t aFeature, bool& aFeatureOn) {
@ -2490,11 +2509,8 @@ bool gfxFont::HasColorGlyphFor(uint32_t aCh, uint32_t aNextCh) {
return true;
}
// Use harfbuzz shaper to look up the default glyph ID for the character.
if (!mHarfBuzzShaper) {
mHarfBuzzShaper = MakeUnique<gfxHarfBuzzShaper>(this);
}
auto* shaper = static_cast<gfxHarfBuzzShaper*>(mHarfBuzzShaper.get());
if (!shaper->Initialize()) {
auto* shaper = GetHarfBuzzShaper();
if (!shaper) {
return false;
}
uint32_t gid = 0;
@ -2559,15 +2575,22 @@ gfxFont::RunMetrics gfxFont::Measure(const gfxTextRun* aTextRun,
// This is only used by MathML layout at present.
if (aBoundingBoxType == TIGHT_HINTED_OUTLINE_EXTENTS &&
mAntialiasOption != kAntialiasNone) {
if (!mNonAAFont) {
mNonAAFont = CopyWithAntialiasOption(kAntialiasNone);
gfxFont* nonAA = mNonAAFont;
if (!nonAA) {
nonAA = CopyWithAntialiasOption(kAntialiasNone);
if (nonAA) {
if (!mNonAAFont.compareExchange(nullptr, nonAA)) {
delete nonAA;
nonAA = mNonAAFont;
}
}
}
// if font subclass doesn't implement CopyWithAntialiasOption(),
// it will return null and we'll proceed to use the existing font
if (mNonAAFont) {
return mNonAAFont->Measure(aTextRun, aStart, aEnd,
TIGHT_HINTED_OUTLINE_EXTENTS, aRefDrawTarget,
aSpacing, aOrientation);
if (nonAA) {
return nonAA->Measure(aTextRun, aStart, aEnd,
TIGHT_HINTED_OUTLINE_EXTENTS, aRefDrawTarget,
aSpacing, aOrientation);
}
}
@ -2761,6 +2784,7 @@ gfxFont::RunMetrics gfxFont::Measure(const gfxTextRun* aTextRun,
}
bool gfxFont::AgeCachedWords() {
mozilla::AutoWriteLock lock(mLock);
if (mWordCache) {
for (auto it = mWordCache->Iter(); !it.Done(); it.Next()) {
CacheHashEntry* entry = it.Get();
@ -2776,7 +2800,8 @@ bool gfxFont::AgeCachedWords() {
return true;
}
void gfxFont::NotifyGlyphsChanged() {
void gfxFont::NotifyGlyphsChanged() const {
AutoReadLock lock(const_cast<gfxFont*>(this)->mLock);
uint32_t i, count = mGlyphExtentsArray.Length();
for (i = 0; i < count; ++i) {
// Flush cached extents array
@ -2814,51 +2839,84 @@ gfxShapedWord* gfxFont::GetShapedWord(
// if the cache is getting too big, flush it and start over
uint32_t wordCacheMaxEntries =
gfxPlatform::GetPlatform()->WordCacheMaxEntries();
mLock.ReadLock();
if (mWordCache->Count() > wordCacheMaxEntries) {
// Flush the cache if it is getting overly big.
// There's a chance another thread could race with the lock-twiddling here,
// but that's OK; it's harmless if we end up calling ClearCacheWords twice
// from separate threads. Internally, it will hold the lock exclusively
// while doing its thing.
mLock.ReadUnlock();
NS_WARNING("flushing shaped-word cache");
ClearCachedWords();
mLock.ReadLock();
// Once we've reclaimed a read lock, we can proceed knowing the cache
// isn't growing uncontrollably.
}
// if there's a cached entry for this word, just return it
CacheHashKey key(aText, aLength, aHash, aRunScript, aLanguage,
aAppUnitsPerDevUnit, aFlags, aRounding);
CacheHashEntry* entry = mWordCache->PutEntry(key, fallible);
if (!entry) {
NS_WARNING("failed to create word cache entry - expect missing text");
return nullptr;
}
gfxShapedWord* sw = entry->mShapedWord.get();
if (sw) {
CacheHashEntry* entry = mWordCache->GetEntry(key);
if (entry) {
gfxShapedWord* sw = entry->mShapedWord.get();
sw->ResetAge();
#ifndef RELEASE_OR_BETA
if (aTextPerf) {
// XXX we should make sure this is atomic
aTextPerf->current.wordCacheHit++;
}
#endif
mLock.ReadUnlock();
return sw;
}
mLock.ReadUnlock();
#ifndef RELEASE_OR_BETA
if (aTextPerf) {
aTextPerf->current.wordCacheMiss++;
}
#endif
sw = gfxShapedWord::Create(aText, aLength, aRunScript, aLanguage,
aAppUnitsPerDevUnit, aFlags, aRounding);
entry->mShapedWord.reset(sw);
gfxShapedWord* sw =
gfxShapedWord::Create(aText, aLength, aRunScript, aLanguage,
aAppUnitsPerDevUnit, aFlags, aRounding);
if (!sw) {
NS_WARNING("failed to create gfxShapedWord - expect missing text");
return nullptr;
}
DebugOnly<bool> ok = ShapeText(aDrawTarget, aText, 0, aLength, aRunScript,
aLanguage, aVertical, aRounding, sw);
NS_WARNING_ASSERTION(ok, "failed to shape word - expect garbled text");
{
// We're going to cache the new shaped word, so lock for writing.
AutoWriteLock lock(mLock);
entry = mWordCache->PutEntry(key, fallible);
if (!entry) {
NS_WARNING("failed to create word cache entry - expect missing text");
delete sw;
return nullptr;
}
// It's unlikely, but maybe another thread got there before us...
if (entry->mShapedWord) {
// Just discard the newly-created word, and use the existing one.
delete sw;
sw = entry->mShapedWord.get();
sw->ResetAge();
#ifndef RELEASE_OR_BETA
if (aTextPerf) {
aTextPerf->current.wordCacheHit++;
}
#endif
return sw;
}
entry->mShapedWord.reset(sw);
#ifndef RELEASE_OR_BETA
if (aTextPerf) {
aTextPerf->current.wordCacheMiss++;
}
#endif
}
gfxFontCache::GetCache()->RunWordCacheExpirationTimer();
return sw;
@ -2929,13 +2987,18 @@ bool gfxFont::ShapeText(DrawTarget* aDrawTarget, const char16_t* aText,
// Vertical graphite support may be wanted as a future enhancement.
if (FontCanSupportGraphite() && !aVertical) {
if (gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
if (!mGraphiteShaper) {
mGraphiteShaper = MakeUnique<gfxGraphiteShaper>(this);
Telemetry::ScalarAdd(Telemetry::ScalarID::BROWSER_USAGE_GRAPHITE, 1);
gfxGraphiteShaper* shaper = mGraphiteShaper;
if (!shaper) {
shaper = new gfxGraphiteShaper(this);
if (mGraphiteShaper.compareExchange(nullptr, shaper)) {
Telemetry::ScalarAdd(Telemetry::ScalarID::BROWSER_USAGE_GRAPHITE, 1);
} else {
delete shaper;
shaper = mGraphiteShaper;
}
}
if (mGraphiteShaper->ShapeText(aDrawTarget, aText, aOffset, aLength,
aScript, aLanguage, aVertical, aRounding,
aShapedText)) {
if (shaper->ShapeText(aDrawTarget, aText, aOffset, aLength, aScript,
aLanguage, aVertical, aRounding, aShapedText)) {
PostShapingFixup(aDrawTarget, aText, aOffset, aLength, aVertical,
aShapedText);
return true;
@ -2943,12 +3006,10 @@ bool gfxFont::ShapeText(DrawTarget* aDrawTarget, const char16_t* aText,
}
}
if (!mHarfBuzzShaper) {
mHarfBuzzShaper = MakeUnique<gfxHarfBuzzShaper>(this);
}
if (mHarfBuzzShaper->ShapeText(aDrawTarget, aText, aOffset, aLength, aScript,
aLanguage, aVertical, aRounding,
aShapedText)) {
gfxHarfBuzzShaper* shaper = GetHarfBuzzShaper();
if (shaper &&
shaper->ShapeText(aDrawTarget, aText, aOffset, aLength, aScript,
aLanguage, aVertical, aRounding, aShapedText)) {
PostShapingFixup(aDrawTarget, aText, aOffset, aLength, aVertical,
aShapedText);
if (GetFontEntry()->HasTrackingTable()) {
@ -3502,7 +3563,7 @@ bool gfxFont::InitFakeSmallCapsRun(
aSyntheticUpper);
}
gfxFont* gfxFont::GetSmallCapsFont() {
gfxFont* gfxFont::GetSmallCapsFont() const {
gfxFontStyle style(*GetStyle());
style.size *= SMALL_CAPS_SCALE_FACTOR;
style.variantCaps = NS_FONT_VARIANT_CAPS_NORMAL;
@ -3510,7 +3571,7 @@ gfxFont* gfxFont::GetSmallCapsFont() {
return fe->FindOrMakeFont(&style, mUnicodeRangeMap);
}
gfxFont* gfxFont::GetSubSuperscriptFont(int32_t aAppUnitsPerDevPixel) {
gfxFont* gfxFont::GetSubSuperscriptFont(int32_t aAppUnitsPerDevPixel) const {
gfxFontStyle style(*GetStyle());
style.AdjustForSubSuperscript(aAppUnitsPerDevPixel);
gfxFontEntry* fe = GetFontEntry();
@ -3518,6 +3579,16 @@ gfxFont* gfxFont::GetSubSuperscriptFont(int32_t aAppUnitsPerDevPixel) {
}
gfxGlyphExtents* gfxFont::GetOrCreateGlyphExtents(int32_t aAppUnitsPerDevUnit) {
{
AutoReadLock lock(mLock);
uint32_t i, count = mGlyphExtentsArray.Length();
for (i = 0; i < count; ++i) {
if (mGlyphExtentsArray[i]->GetAppUnitsPerDevUnit() == aAppUnitsPerDevUnit)
return mGlyphExtentsArray[i].get();
}
}
AutoWriteLock lock(mLock);
// Re-check in case of race.
uint32_t i, count = mGlyphExtentsArray.Length();
for (i = 0; i < count; ++i) {
if (mGlyphExtentsArray[i]->GetAppUnitsPerDevUnit() == aAppUnitsPerDevUnit)
@ -3849,8 +3920,7 @@ void gfxFont::CreateVerticalMetrics() {
const uint32_t kOS_2TableTag = TRUETYPE_TAG('O', 'S', '/', '2');
uint32_t len;
mVerticalMetrics = MakeUnique<Metrics>();
auto* metrics = mVerticalMetrics.get();
auto* metrics = new Metrics();
::memset(metrics, 0, sizeof(Metrics));
// Some basic defaults, in case the font lacks any real metrics tables.
@ -3997,6 +4067,10 @@ void gfxFont::CreateVerticalMetrics() {
metrics->maxHeight = metrics->maxAscent + metrics->maxDescent;
metrics->xHeight = metrics->emHeight / 2;
metrics->capHeight = metrics->maxAscent;
if (!mVerticalMetrics.compareExchange(nullptr, metrics)) {
delete metrics;
}
}
gfxFloat gfxFont::SynthesizeSpaceWidth(uint32_t aCh) {
@ -4037,6 +4111,7 @@ gfxFloat gfxFont::SynthesizeSpaceWidth(uint32_t aCh) {
void gfxFont::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
FontCacheSizes* aSizes) const {
AutoReadLock lock(const_cast<gfxFont*>(this)->mLock);
for (uint32_t i = 0; i < mGlyphExtentsArray.Length(); ++i) {
aSizes->mFontInstances +=
mGlyphExtentsArray[i]->SizeOfIncludingThis(aMallocSizeOf);
@ -4053,6 +4128,7 @@ void gfxFont::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
}
void gfxFont::AddGlyphChangeObserver(GlyphChangeObserver* aObserver) {
AutoWriteLock lock(mLock);
if (!mGlyphChangeObservers) {
mGlyphChangeObservers = MakeUnique<nsTHashSet<GlyphChangeObserver*>>();
}
@ -4060,6 +4136,7 @@ void gfxFont::AddGlyphChangeObserver(GlyphChangeObserver* aObserver) {
}
void gfxFont::RemoveGlyphChangeObserver(GlyphChangeObserver* aObserver) {
AutoWriteLock lock(mLock);
NS_ASSERTION(mGlyphChangeObservers, "No observers registered");
NS_ASSERTION(mGlyphChangeObservers->Contains(aObserver),
"Observer not registered");
@ -4199,17 +4276,21 @@ void gfxFontStyle::AdjustForSubSuperscript(int32_t aAppUnitsPerDevPixel) {
}
bool gfxFont::TryGetMathTable() {
if (!mMathInitialized) {
mMathInitialized = true;
hb_face_t* face = GetFontEntry()->GetHBFace();
if (face) {
if (hb_ot_math_has_data(face)) {
mMathTable = MakeUnique<gfxMathTable>(face, GetAdjustedSize());
}
hb_face_destroy(face);
}
if (mMathInitialized) {
return !!mMathTable;
}
hb_face_t* face = GetFontEntry()->GetHBFace();
if (face) {
if (hb_ot_math_has_data(face)) {
auto* mathTable = new gfxMathTable(face, GetAdjustedSize());
if (!mMathTable.compareExchange(nullptr, mathTable)) {
delete mathTable;
}
}
hb_face_destroy(face);
}
mMathInitialized = true;
return !!mMathTable;
}

View File

@ -60,6 +60,8 @@
#include "nsMathUtils.h"
class gfxContext;
class gfxGraphiteShaper;
class gfxHarfBuzzShaper;
class gfxGlyphExtents;
class gfxMathTable;
class gfxPattern;
@ -1417,7 +1419,8 @@ class gfxShapedWord final : public gfxShapedText {
gfxFontShaper::RoundingFlags mRounding;
uint32_t mAgeCounter;
// With multithreaded shaping, this may be updated by any thread.
std::atomic<uint32_t> mAgeCounter;
// The mCharGlyphsStorage array is actually a variable-size member;
// when the ShapedWord is created, its size will be increased as necessary
@ -1449,7 +1452,12 @@ class gfxFont {
nsrefcnt AddRef(void) {
MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt");
if (mExpirationState.IsTracked()) {
nsExpirationState state;
{
mozilla::AutoReadLock lock(mLock);
state = mExpirationState;
}
if (state.IsTracked()) {
gfxFontCache::GetCache()->RemoveObject(this);
}
++mRefCnt;
@ -1460,15 +1468,15 @@ class gfxFont {
MOZ_ASSERT(0 != mRefCnt, "dup release");
--mRefCnt;
NS_LOG_RELEASE(this, mRefCnt, "gfxFont");
if (mRefCnt == 0) {
nsrefcnt rval = mRefCnt;
if (!rval) {
NotifyReleased();
// |this| may have been deleted.
return 0;
}
return mRefCnt;
return rval;
}
int32_t GetRefCount() { return mRefCnt; }
int32_t GetRefCount() { return int32_t(mRefCnt); }
// options to specify the kind of AA to be used when creating a font
typedef enum : uint8_t {
@ -1479,7 +1487,7 @@ class gfxFont {
} AntialiasOption;
protected:
nsAutoRefCnt mRefCnt;
mozilla::ThreadSafeAutoRefCnt mRefCnt;
void NotifyReleased() {
gfxFontCache* cache = gfxFontCache::GetCache();
@ -1532,8 +1540,7 @@ class gfxFont {
const nsCString& GetName() const { return mFontEntry->Name(); }
const gfxFontStyle* GetStyle() const { return &mStyle; }
virtual mozilla::UniquePtr<gfxFont> CopyWithAntialiasOption(
AntialiasOption anAAOption) {
virtual gfxFont* CopyWithAntialiasOption(AntialiasOption anAAOption) const {
// platforms where this actually matters should override
return nullptr;
}
@ -1557,15 +1564,17 @@ class gfxFont {
}
// check whether this is an sfnt we can potentially use with harfbuzz
bool FontCanSupportHarfBuzz() { return mFontEntry->HasCmapTable(); }
bool FontCanSupportHarfBuzz() const { return mFontEntry->HasCmapTable(); }
// check whether this is an sfnt we can potentially use with Graphite
bool FontCanSupportGraphite() { return mFontEntry->HasGraphiteTables(); }
bool FontCanSupportGraphite() const {
return mFontEntry->HasGraphiteTables();
}
// Whether this is a font that may be doing full-color rendering,
// and therefore needs us to use a mask for text-shadow even when
// we're not actually blurring.
bool AlwaysNeedsMaskForShadow() {
bool AlwaysNeedsMaskForShadow() const {
return mFontEntry->TryGetColorGlyphs() || mFontEntry->TryGetSVGData(this) ||
mFontEntry->HasFontTable(TRUETYPE_TAG('C', 'B', 'D', 'T')) ||
mFontEntry->HasFontTable(TRUETYPE_TAG('s', 'b', 'i', 'x'));
@ -1621,6 +1630,10 @@ class gfxFont {
virtual bool ShouldHintMetrics() const { return true; }
virtual bool ShouldRoundXOffset(cairo_t* aCairo) const { return true; }
// Return the font's owned harfbuzz shaper, creating and initializing it if
// necessary; returns null if shaper initialization has failed.
gfxHarfBuzzShaper* GetHarfBuzzShaper();
// Font metrics
struct Metrics {
gfxFloat capHeight;
@ -1786,14 +1799,14 @@ class gfxFont {
nsExpirationState* GetExpirationState() { return &mExpirationState; }
// Get the glyphID of a space
uint16_t GetSpaceGlyph() { return mSpaceGlyph; }
uint16_t GetSpaceGlyph() const { return mSpaceGlyph; }
gfxGlyphExtents* GetOrCreateGlyphExtents(int32_t aAppUnitsPerDevUnit);
void SetupGlyphExtents(DrawTarget* aDrawTarget, uint32_t aGlyphID,
bool aNeedTight, gfxGlyphExtents* aExtents);
virtual bool AllowSubpixelAA() { return true; }
virtual bool AllowSubpixelAA() const { return true; }
bool ApplySyntheticBold() const { return mApplySyntheticBold; }
@ -1804,7 +1817,7 @@ class gfxFont {
// For size S up to a threshold size T, we use (0.25 + 3S / 4T),
// so that the result ranges from 0.25 to 1.0; thereafter,
// simply use (S / T).
gfxFloat GetSyntheticBoldOffset() {
gfxFloat GetSyntheticBoldOffset() const {
gfxFloat size = GetAdjustedSize();
const gfxFloat threshold = 48.0;
return size < threshold ? (0.25 + 0.75 * size / threshold)
@ -1812,7 +1825,7 @@ class gfxFont {
}
gfxFontEntry* GetFontEntry() const { return mFontEntry.get(); }
bool HasCharacter(uint32_t ch) {
bool HasCharacter(uint32_t ch) const {
if (!mIsValid || (mUnicodeRangeMap && !mUnicodeRangeMap->test(ch))) {
return false;
}
@ -1827,7 +1840,7 @@ class gfxFont {
mUnicodeRangeMap = aUnicodeRangeMap;
}
uint16_t GetUVSGlyph(uint32_t aCh, uint32_t aVS) {
uint16_t GetUVSGlyph(uint32_t aCh, uint32_t aVS) const {
if (!mIsValid) {
return 0;
}
@ -1867,8 +1880,15 @@ class gfxFont {
// Ensure the ShapedWord cache is initialized. This MUST be called before
// any attempt to use GetShapedWord().
void InitWordCache() {
mLock.ReadLock();
if (!mWordCache) {
mWordCache = mozilla::MakeUnique<nsTHashtable<CacheHashEntry>>();
mLock.ReadUnlock();
mozilla::AutoWriteLock lock(mLock);
if (!mWordCache) {
mWordCache = mozilla::MakeUnique<nsTHashtable<CacheHashEntry>>();
}
} else {
mLock.ReadUnlock();
}
}
@ -1879,13 +1899,14 @@ class gfxFont {
// Discard all cached word records; called on memory-pressure notification.
void ClearCachedWords() {
mozilla::AutoWriteLock lock(mLock);
if (mWordCache) {
mWordCache->Clear();
}
}
// Glyph rendering/geometry has changed, so invalidate data as necessary.
void NotifyGlyphsChanged();
void NotifyGlyphsChanged() const;
virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontCacheSizes* aSizes) const;
@ -1915,12 +1936,11 @@ class gfxFont {
// gfxFont implementations may cache ScaledFont versions other than the
// default, so InitializeScaledFont must support explicitly specifying
// other ScaledFonts than the default to initialize.
// which ScaledFonts to initialize.
void InitializeScaledFont(
const RefPtr<mozilla::gfx::ScaledFont>& aScaledFont);
void InitializeScaledFont() { InitializeScaledFont(mAzureScaledFont); }
bool KerningDisabled() { return mKerningSet && !mKerningEnabled; }
bool KerningDisabled() const { return mKerningSet && !mKerningEnabled; }
/**
* Subclass this object to be notified of glyph changes. Delete the object
@ -1947,7 +1967,7 @@ class gfxFont {
};
friend class GlyphChangeObserver;
bool GlyphsMayChange() {
bool GlyphsMayChange() const {
// Currently only fonts with SVG glyphs can have animated glyphs
return mFontEntry->TryGetSVGData(this);
}
@ -1961,21 +1981,21 @@ class gfxFont {
// If (and ONLY if) TryGetMathTable() has returned true, the MathTable()
// method may be called to access the gfxMathTable data.
bool TryGetMathTable();
gfxMathTable* MathTable() {
gfxMathTable* MathTable() const {
MOZ_RELEASE_ASSERT(mMathTable,
"A successful call to TryGetMathTable() must be "
"performed before calling this function");
return mMathTable.get();
return mMathTable;
}
// Return a cloned font resized and offset to simulate sub/superscript
// glyphs. This does not add a reference to the returned font.
gfxFont* GetSubSuperscriptFont(int32_t aAppUnitsPerDevPixel);
gfxFont* GetSubSuperscriptFont(int32_t aAppUnitsPerDevPixel) const;
bool HasColorGlyphFor(uint32_t aCh, uint32_t aNextCh);
protected:
virtual const Metrics& GetHorizontalMetrics() = 0;
virtual const Metrics& GetHorizontalMetrics() const = 0;
void CreateVerticalMetrics();
@ -2021,7 +2041,7 @@ class gfxFont {
// Return a font that is a "clone" of this one, but reduced to 80% size
// (and with variantCaps set to normal). This does not add a reference to
// the returned font.
gfxFont* GetSmallCapsFont();
gfxFont* GetSmallCapsFont() const;
// subclasses may provide (possibly hinted) glyph widths (in font units);
// if they do not override this, harfbuzz will use unhinted widths
@ -2033,7 +2053,7 @@ class gfxFont {
virtual int32_t GetGlyphWidth(uint16_t aGID) { return -1; }
virtual bool GetGlyphBounds(uint16_t aGID, gfxRect* aBounds,
bool aTight = false) {
bool aTight = false) const {
return false;
}
@ -2044,13 +2064,13 @@ class gfxFont {
void RemoveGlyphChangeObserver(GlyphChangeObserver* aObserver);
// whether font contains substitution lookups containing spaces
bool HasSubstitutionRulesWithSpaceLookups(Script aRunScript);
bool HasSubstitutionRulesWithSpaceLookups(Script aRunScript) const;
// do spaces participate in shaping rules? if so, can't used word cache
// Note that this function uses HasGraphiteSpaceContextuals, so it can only
// return a "hint" to the correct answer. The calling code must ensure it
// performs safe actions independent of the value returned.
tainted_boolean_hint SpaceMayParticipateInShaping(Script aRunScript);
tainted_boolean_hint SpaceMayParticipateInShaping(Script aRunScript) const;
// For 8-bit text, expand to 16-bit and then call the following method.
bool ShapeText(DrawTarget* aContext, const uint8_t* aText,
@ -2100,7 +2120,7 @@ class gfxFont {
bool aVertical, RoundingFlags aRounding,
gfxTextRun* aTextRun);
void CheckForFeaturesInvolvingSpace();
void CheckForFeaturesInvolvingSpace() const;
// whether a given feature is included in feature settings from both the
// font and the style. aFeatureOn set if resolved feature value is non-zero
@ -2111,6 +2131,7 @@ class gfxFont {
static nsTHashSet<uint32_t>* sDefaultFeatures;
RefPtr<gfxFontEntry> mFontEntry;
mozilla::RWLock mLock;
struct CacheHashKey {
union {
@ -2197,35 +2218,40 @@ class gfxFont {
mozilla::UniquePtr<gfxShapedWord> mShapedWord;
};
mozilla::UniquePtr<nsTHashtable<CacheHashEntry>> mWordCache;
mozilla::UniquePtr<nsTHashtable<CacheHashEntry>> mWordCache GUARDED_BY(mLock);
static const uint32_t kShapedWordCacheMaxAge = 3;
nsTArray<mozilla::UniquePtr<gfxGlyphExtents>> mGlyphExtentsArray;
mozilla::UniquePtr<nsTHashSet<GlyphChangeObserver*>> mGlyphChangeObservers;
nsTArray<mozilla::UniquePtr<gfxGlyphExtents>> mGlyphExtentsArray
GUARDED_BY(mLock);
mozilla::UniquePtr<nsTHashSet<GlyphChangeObserver*>> mGlyphChangeObservers
GUARDED_BY(mLock);
// a copy of the font without antialiasing, if needed for separate
// measurement by mathml code
mozilla::UniquePtr<gfxFont> mNonAAFont;
mozilla::Atomic<gfxFont*> mNonAAFont;
// we create either or both of these shapers when needed, depending
// whether the font has graphite tables, and whether graphite shaping
// is actually enabled
mozilla::UniquePtr<gfxFontShaper> mHarfBuzzShaper;
mozilla::UniquePtr<gfxFontShaper> mGraphiteShaper;
mozilla::Atomic<gfxHarfBuzzShaper*> mHarfBuzzShaper;
mozilla::Atomic<gfxGraphiteShaper*> mGraphiteShaper;
// if a userfont with unicode-range specified, contains map of *possible*
// ranges supported by font
// If a userfont with unicode-range specified, contains map of *possible*
// ranges supported by font. This is set during user-font initialization,
// before the font is available to other threads, and thereafter is inert
// so no guard is needed.
RefPtr<gfxCharacterMap> mUnicodeRangeMap;
RefPtr<mozilla::gfx::UnscaledFont> mUnscaledFont;
RefPtr<mozilla::gfx::ScaledFont> mAzureScaledFont;
RefPtr<mozilla::gfx::UnscaledFont> mUnscaledFont GUARDED_BY(mLock);
mozilla::Atomic<mozilla::gfx::ScaledFont*> mAzureScaledFont;
// For vertical metrics, created on demand.
mozilla::UniquePtr<Metrics> mVerticalMetrics;
mozilla::Atomic<Metrics*> mVerticalMetrics;
// Table used for MathML layout.
mozilla::UniquePtr<gfxMathTable> mMathTable;
mozilla::Atomic<gfxMathTable*> mMathTable;
gfxFontStyle mStyle;
mutable gfxFloat mAdjustedSize;
@ -2235,7 +2261,7 @@ class gfxFont {
// This is OK because we only multiply by this factor, never divide.
float mFUnitsConvFactor;
nsExpirationState mExpirationState;
nsExpirationState mExpirationState GUARDED_BY(mLock);
// Glyph ID of the font's <space> glyph, zero if missing
uint16_t mSpaceGlyph = 0;
@ -2252,7 +2278,7 @@ class gfxFont {
bool mKerningSet; // kerning explicitly set?
bool mKerningEnabled; // if set, on or off?
bool mMathInitialized; // TryGetMathTable() called?
mozilla::Atomic<bool> mMathInitialized; // TryGetMathTable() called?
// Helper for subclasses that want to initialize standard metrics from the
// tables of sfnt (TrueType/OpenType) fonts.

View File

@ -339,7 +339,7 @@ void gfxFontEntry::RenderSVGGlyph(gfxContext* aContext, uint32_t aGlyphId,
mSVGGlyphs->RenderGlyph(aContext, aGlyphId, aContextPaint);
}
bool gfxFontEntry::TryGetSVGData(gfxFont* aFont) {
bool gfxFontEntry::TryGetSVGData(const gfxFont* aFont) {
if (!gfxPlatform::GetPlatform()->OpenTypeSVGEnabled()) {
return false;
}
@ -377,7 +377,7 @@ void gfxFontEntry::NotifyFontDestroyed(gfxFont* aFont) {
void gfxFontEntry::NotifyGlyphsChanged() {
for (uint32_t i = 0, count = mFontsUsingSVGGlyphs.Length(); i < count; ++i) {
gfxFont* font = mFontsUsingSVGGlyphs[i];
const gfxFont* font = mFontsUsingSVGGlyphs[i];
font->NotifyGlyphsChanged();
}
}

View File

@ -251,7 +251,7 @@ class gfxFontEntry {
// can be safely dereferenced.
virtual nsresult ReadCMAP(FontInfoData* aFontInfoData = nullptr);
bool TryGetSVGData(gfxFont* aFont);
bool TryGetSVGData(const gfxFont* aFont);
bool HasSVGGlyph(uint32_t aGlyphId);
bool GetSVGGlyphExtents(DrawTarget* aDrawTarget, uint32_t aGlyphId,
gfxFloat aSize, gfxRect* aResult);
@ -472,7 +472,7 @@ class gfxFontEntry {
mozilla::UniquePtr<gfxUserFontData> mUserFontData;
mozilla::UniquePtr<gfxSVGGlyphs> mSVGGlyphs;
// list of gfxFonts that are using SVG glyphs
nsTArray<gfxFont*> mFontsUsingSVGGlyphs;
nsTArray<const gfxFont*> mFontsUsingSVGGlyphs;
nsTArray<gfxFontFeature> mFeatureSettings;
nsTArray<gfxFontVariation> mVariationSettings;
mozilla::UniquePtr<nsTHashMap<nsUint32HashKey, bool>> mSupportedFeatures;

View File

@ -51,10 +51,9 @@ gfxGDIFont::~gfxGDIFont() {
delete mMetrics;
}
UniquePtr<gfxFont> gfxGDIFont::CopyWithAntialiasOption(
AntialiasOption anAAOption) {
gfxFont* gfxGDIFont::CopyWithAntialiasOption(AntialiasOption anAAOption) const {
auto entry = static_cast<GDIFontEntry*>(mFontEntry.get());
return MakeUnique<gfxGDIFont>(entry, &mStyle, anAAOption);
return new gfxGDIFont(entry, &mStyle, anAAOption);
}
bool gfxGDIFont::ShapeText(DrawTarget* aDrawTarget, const char16_t* aText,
@ -71,21 +70,28 @@ bool gfxGDIFont::ShapeText(DrawTarget* aDrawTarget, const char16_t* aText,
aLanguage, aVertical, aRounding, aShapedText);
}
const gfxFont::Metrics& gfxGDIFont::GetHorizontalMetrics() { return *mMetrics; }
already_AddRefed<ScaledFont> gfxGDIFont::GetScaledFont(
const TextRunDrawParams& aRunParams) {
if (!mAzureScaledFont) {
LOGFONT lf;
GetObject(GetHFONT(), sizeof(LOGFONT), &lf);
mAzureScaledFont = Factory::CreateScaledFontForGDIFont(
&lf, GetUnscaledFont(), GetAdjustedSize());
InitializeScaledFont();
if (ScaledFont* scaledFont = mAzureScaledFont) {
return do_AddRef(scaledFont);
}
RefPtr<ScaledFont> scaledFont(mAzureScaledFont);
return scaledFont.forget();
LOGFONT lf;
GetObject(GetHFONT(), sizeof(LOGFONT), &lf);
RefPtr<ScaledFont> newScaledFont = Factory::CreateScaledFontForGDIFont(
&lf, GetUnscaledFont(), GetAdjustedSize());
if (!newScaledFont) {
return nullptr;
}
InitializeScaledFont(newScaledFont);
if (mAzureScaledFont.compareExchange(nullptr, newScaledFont.get())) {
Unused << newScaledFont.forget();
}
ScaledFont* scaledFont = mAzureScaledFont;
return do_AddRef(scaledFont);
}
gfxFont::RunMetrics gfxGDIFont::Measure(const gfxTextRun* aTextRun,
@ -485,7 +491,8 @@ int32_t gfxGDIFont::GetGlyphWidth(uint16_t aGID) {
});
}
bool gfxGDIFont::GetGlyphBounds(uint16_t aGID, gfxRect* aBounds, bool aTight) {
bool gfxGDIFont::GetGlyphBounds(uint16_t aGID, gfxRect* aBounds,
bool aTight) const {
DCForMetrics dc;
AutoSelectFont fs(dc, GetHFONT());

View File

@ -22,7 +22,7 @@ class gfxGDIFont : public gfxFont {
virtual ~gfxGDIFont();
HFONT GetHFONT() { return mFont; }
HFONT GetHFONT() const { return mFont; }
already_AddRefed<mozilla::gfx::ScaledFont> GetScaledFont(
const TextRunDrawParams& aRunParams) override;
@ -35,8 +35,7 @@ class gfxGDIFont : public gfxFont {
mozilla::gfx::ShapedTextFlags aOrientation) override;
/* required for MathML to suppress effects of ClearType "padding" */
mozilla::UniquePtr<gfxFont> CopyWithAntialiasOption(
AntialiasOption anAAOption) override;
gfxFont* CopyWithAntialiasOption(AntialiasOption anAAOption) const override;
// If the font has a cmap table, we handle it purely with harfbuzz;
// but if not (e.g. .fon fonts), we'll use a GDI callback to get glyphs.
@ -49,7 +48,8 @@ class gfxGDIFont : public gfxFont {
// get hinted glyph width in pixels as 16.16 fixed-point value
int32_t GetGlyphWidth(uint16_t aGID) override;
bool GetGlyphBounds(uint16_t aGID, gfxRect* aBounds, bool aTight) override;
bool GetGlyphBounds(uint16_t aGID, gfxRect* aBounds,
bool aTight) const override;
void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontCacheSizes* aSizes) const;
@ -59,7 +59,7 @@ class gfxGDIFont : public gfxFont {
FontType GetType() const override { return FONT_TYPE_GDI; }
protected:
const Metrics& GetHorizontalMetrics() override;
const Metrics& GetHorizontalMetrics() const override { return *mMetrics; }
bool ShapeText(DrawTarget* aDrawTarget, const char16_t* aText,
uint32_t aOffset, uint32_t aLength, Script aScript,

View File

@ -25,8 +25,12 @@ class gfxHarfBuzzShaper : public gfxFontShaper {
gfxHarfBuzzShaper* mShaper;
};
// Initializes the shaper and returns whether this was successful.
bool Initialize();
// Returns whether the shaper has been successfully initialized.
bool IsInitialized() const { return mHBFont != nullptr; }
bool ShapeText(DrawTarget* aDrawTarget, const char16_t* aText,
uint32_t aOffset, uint32_t aLength, Script aScript,
nsAtom* aLanguage, bool aVertical, RoundingFlags aRounding,

View File

@ -518,7 +518,8 @@ int32_t gfxMacFont::GetGlyphWidth(uint16_t aGID) {
return advance.width * 0x10000;
}
bool gfxMacFont::GetGlyphBounds(uint16_t aGID, gfxRect* aBounds, bool aTight) {
bool gfxMacFont::GetGlyphBounds(uint16_t aGID, gfxRect* aBounds,
bool aTight) const {
CGRect bb;
if (!::CGFontGetGlyphBBoxes(mCGFont, &aGID, 1, &bb)) {
return false;
@ -581,21 +582,27 @@ void gfxMacFont::InitMetricsFromPlatform() {
already_AddRefed<ScaledFont> gfxMacFont::GetScaledFont(
const TextRunDrawParams& aRunParams) {
if (!mAzureScaledFont) {
gfxFontEntry* fe = GetFontEntry();
bool hasColorGlyphs = fe->HasColorBitmapTable() || fe->TryGetColorGlyphs();
mAzureScaledFont = Factory::CreateScaledFontForMacFont(
GetCGFontRef(), GetUnscaledFont(), GetAdjustedSize(),
ToDeviceColor(mFontSmoothingBackgroundColor),
!mStyle.useGrayscaleAntialiasing, ApplySyntheticBold(), hasColorGlyphs);
if (!mAzureScaledFont) {
return nullptr;
}
InitializeScaledFont();
if (ScaledFont* scaledFont = mAzureScaledFont) {
return do_AddRef(scaledFont);
}
RefPtr<ScaledFont> scaledFont(mAzureScaledFont);
return scaledFont.forget();
gfxFontEntry* fe = GetFontEntry();
bool hasColorGlyphs = fe->HasColorBitmapTable() || fe->TryGetColorGlyphs();
RefPtr<ScaledFont> newScaledFont = Factory::CreateScaledFontForMacFont(
GetCGFontRef(), GetUnscaledFont(), GetAdjustedSize(),
ToDeviceColor(mFontSmoothingBackgroundColor),
!mStyle.useGrayscaleAntialiasing, ApplySyntheticBold(), hasColorGlyphs);
if (!newScaledFont) {
return nullptr;
}
InitializeScaledFont(newScaledFont);
if (mAzureScaledFont.compareExchange(nullptr, newScaledFont.get())) {
Unused << newScaledFont.forget();
}
ScaledFont* scaledFont = mAzureScaledFont;
return do_AddRef(scaledFont);
}
bool gfxMacFont::ShouldRoundXOffset(cairo_t* aCairo) const {

View File

@ -37,7 +37,7 @@ class gfxMacFont : public gfxFont {
int32_t GetGlyphWidth(uint16_t aGID) override;
bool GetGlyphBounds(uint16_t aGID, gfxRect* aBounds, bool aTight) override;
bool GetGlyphBounds(uint16_t aGID, gfxRect* aBounds, bool aTight) const override;
already_AddRefed<mozilla::gfx::ScaledFont> GetScaledFont(
const TextRunDrawParams& aRunParams) override;
@ -59,7 +59,7 @@ class gfxMacFont : public gfxFont {
CTFontDescriptorRef aFontDesc = nullptr);
protected:
const Metrics& GetHorizontalMetrics() override { return mMetrics; }
const Metrics& GetHorizontalMetrics() const override { return mMetrics; }
// override to prefer CoreText shaping with fonts that depend on AAT
bool ShapeText(DrawTarget* aDrawTarget, const char16_t* aText, uint32_t aOffset, uint32_t aLength,