/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* * gfxFontCache - global cache of gfxFont instances. * Expires unused fonts after a short interval; * notifies fonts to age their cached shaped-word records; * observes memory-pressure notification and tells fonts to clear their * shaped-word caches to free up memory.
*/
nsresult gfxFontCache::Init() {
NS_ASSERTION(!gGlobalCache, "Where did this come from?");
gGlobalCache = new gfxFontCache(GetMainThreadSerialEventTarget()); if (!gGlobalCache) { return NS_ERROR_OUT_OF_MEMORY;
}
RegisterStrongMemoryReporter(new MemoryReporter()); return NS_OK;
}
// Create the timer used to expire shaped-word records from each font's // cache after a short period of non-use. We have a single timer in // gfxFontCache that loops over all fonts known to the cache, to avoid // the overhead of individual timers in each font instance. // The timer will be started any time shaped word records are cached // (and pauses itself when all caches become empty).
mWordCacheExpirationTimer = NS_NewTimer(target);
}
gfxFontCache::~gfxFontCache() { // Ensure the user font cache releases its references to font entries, // so they aren't kept alive after the font instances and font-list // have been shut down.
gfxUserFontSet::UserFontCache::Shutdown();
if (mWordCacheExpirationTimer) {
mWordCacheExpirationTimer->Cancel();
mWordCacheExpirationTimer = nullptr;
}
// Expire everything manually so we don't leak them.
Flush();
}
// If it is null, then we are inserting a new entry. Otherwise we are // attempting to replace an existing font, probably due to a thread race, in // which case stick with the original font. if (!entry->mFont) {
entry->mFont = aFont; // Assert that we can find the entry we just put in (this fails if the key // has a NaN float value in it, e.g. 'sizeAdjust').
MOZ_ASSERT(entry == mFonts.GetEntry(key));
} else {
MOZ_ASSERT(entry->mFont != aFont);
aFont->Destroy(); if (entry->mFont->GetExpirationState()->IsTracked()) {
RemoveObjectLocked(entry->mFont, lock);
}
}
// If the font has a non-zero refcount, then we must have lost the race with // gfxFontCache::Lookup and the same font was reacquired. if (aFont->GetRefCount() > 0) { returnfalse;
}
// If the font is being tracked, we must have then also lost another race with // gfxFontCache::MaybeDestroy which re-added it to the tracker. if (aFont->GetExpirationState()->IsTracked()) { returnfalse;
}
// Typically this won't fail, but it may during startup/shutdown if the timer // service is not available.
nsresult rv = AddObjectLocked(aFont, lock); if (NS_SUCCEEDED(rv)) { returnfalse;
}
void gfxFontCache::DestroyDiscard(nsTArray<gfxFont*>& aDiscard) { for (auto& font : aDiscard) {
NS_ASSERTION(font->GetRefCount() == 0, "Destroying with refs outside cache!");
font->ClearCachedWords();
font->Destroy();
}
aDiscard.Clear();
}
void gfxFontCache::Flush() {
nsTArray<gfxFont*> discard;
{
MutexAutoLock lock(mMutex);
discard.SetCapacity(mFonts.Count()); for (auto iter = mFonts.Iter(); !iter.Done(); iter.Next()) {
HashEntry* entry = static_cast<HashEntry*>(iter.Get()); if (!entry || !entry->mFont) {
MOZ_ASSERT_UNREACHABLE("Invalid font?"); continue;
}
if (entry->mFont->GetRefCount() == 0) { // If we are not tracked, then we must have won the race with // gfxFont::MaybeDestroy and it is waiting on the mutex. To avoid a // double free, we let gfxFont::MaybeDestroy handle the freeing when it // acquires the mutex and discovers there is no matching entry in the // hashtable. if (entry->mFont->GetExpirationState()->IsTracked()) {
RemoveObjectLocked(entry->mFont, lock);
discard.AppendElement(entry->mFont);
}
} else {
MOZ_ASSERT(!entry->mFont->GetExpirationState()->IsTracked());
}
}
MOZ_ASSERT(IsEmptyLocked(lock), "Cache tracker still has fonts after flush!");
mFonts.Clear();
}
DestroyDiscard(discard);
}
// Bail immediately if nothing to do, which is the common case. if (styleRuleFeatures.IsEmpty() && aFontFeatures.IsEmpty() &&
!aDisableLigatures &&
aStyle->variantCaps == NS_FONT_VARIANT_CAPS_NORMAL &&
aStyle->variantSubSuper == NS_FONT_VARIANT_POSITION_NORMAL &&
aStyle->variantAlternates.IsEmpty()) { return;
}
AutoTArray<gfxFontFeature, 32> mergedFeatures;
struct FeatureTagCmp { bool Equals(const gfxFontFeature& a, const gfxFontFeature& b) const { return a.mTag == b.mTag;
} bool LessThan(const gfxFontFeature& a, const gfxFontFeature& b) const { return a.mTag < b.mTag;
}
} cmp;
auto addOrReplace = [&](const gfxFontFeature& aFeature) { auto index = mergedFeatures.BinaryIndexOf(aFeature, cmp); if (index == nsTArray<gfxFontFeature>::NoIndex) {
mergedFeatures.InsertElementSorted(aFeature, cmp);
} else {
mergedFeatures[index].mValue = aFeature.mValue;
}
};
// add feature values from font for (const gfxFontFeature& feature : aFontFeatures) {
addOrReplace(feature);
}
// font-variant-caps - handled here due to the need for fallback handling // petite caps cases can fallback to appropriate smallcaps
uint32_t variantCaps = aStyle->variantCaps; switch (variantCaps) { case NS_FONT_VARIANT_CAPS_NORMAL: break;
case NS_FONT_VARIANT_CAPS_ALLSMALL:
addOrReplace(gfxFontFeature{HB_TAG('c', '2', 's', 'c'), 1}); // fall through to the small-caps case
[[fallthrough]];
case NS_FONT_VARIANT_CAPS_SMALLCAPS:
addOrReplace(gfxFontFeature{HB_TAG('s', 'm', 'c', 'p'), 1}); break;
case NS_FONT_VARIANT_CAPS_ALLPETITE:
addOrReplace(gfxFontFeature{aAddSmallCaps ? HB_TAG('c', '2', 's', 'c')
: HB_TAG('c', '2', 'p', 'c'),
1}); // fall through to the petite-caps case
[[fallthrough]];
// font-variant-position - handled here due to the need for fallback switch (aStyle->variantSubSuper) { case NS_FONT_VARIANT_POSITION_NORMAL: break; case NS_FONT_VARIANT_POSITION_SUPER:
addOrReplace(gfxFontFeature{HB_TAG('s', 'u', 'p', 's'), 1}); break; case NS_FONT_VARIANT_POSITION_SUB:
addOrReplace(gfxFontFeature{HB_TAG('s', 'u', 'b', 's'), 1}); break; default:
MOZ_ASSERT_UNREACHABLE("Unexpected variantSubSuper"); break;
}
// add font-specific feature values from style rules if (aStyle->featureValueLookup && !aStyle->variantAlternates.IsEmpty()) {
AutoTArray<gfxFontFeature, 4> featureList;
// insert list of alternate feature settings for (auto& alternate : aStyle->variantAlternates.AsSpan()) {
LookupAlternateValues(*aStyle->featureValueLookup, aFamilyName, alternate,
featureList);
}
// Add features that are already resolved to tags & values in the style. if (styleRuleFeatures.IsEmpty()) { // Disable optional ligatures if non-zero letter-spacing is in effect. if (aDisableLigatures) {
disableOptionalLigatures();
}
} else { for (const gfxFontFeature& feature : styleRuleFeatures) { // A dummy feature (0,0) is used as a sentinel to separate features // originating from font-variant-* or other high-level properties from // those directly specified as font-feature-settings. The high-level // features may be overridden by aDisableLigatures, while low-level // features specified directly as tags will come last and therefore // take precedence over everything else. if (feature.mTag) {
addOrReplace(gfxFontFeature{feature.mTag, feature.mValue});
} elseif (aDisableLigatures) { // Handle ligature-disabling setting at the boundary between high- // and low-level features.
disableOptionalLigatures();
}
}
}
for (constauto& f : mergedFeatures) {
aHandleFeature(f.mTag, f.mValue, aHandleFeatureData);
}
}
// GraphemeClusterBreakIteratorUtf16 won't be able to tell us if the string // _begins_ with a cluster-extender, so we handle that here
uint32_t ch = aString[0]; if (aLength > 1 && NS_IS_SURROGATE_PAIR(ch, aString[1])) {
ch = SURROGATE_TO_UCS4(ch, aString[1]);
} if (IsClusterExtender(ch)) {
glyphs[0] = extendCluster;
}
const char16_t kIdeographicSpace = 0x3000; // Special case for Bengali: although Virama normally clusters with the // preceding letter, we *also* want to cluster it with a following Ya // so that when the Virama+Ya form ya-phala, this is not separated from the // preceding letter by any letter-spacing or justification. const char16_t kBengaliVirama = 0x09CD; const char16_t kBengaliYa = 0x09AF; bool prevWasHyphen = false; while (pos < aLength) { const char16_t ch = aString[pos]; if (prevWasHyphen) { if (nsContentUtils::IsAlphanumeric(ch)) {
glyphs[pos].SetCanBreakBefore(
CompressedGlyph::FLAG_BREAK_TYPE_EMERGENCY_WRAP);
}
prevWasHyphen = false;
} if (ch == char16_t(' ') || ch == kIdeographicSpace) {
glyphs[pos].SetIsSpace();
} elseif (nsContentUtils::IsHyphen(ch) && pos &&
nsContentUtils::IsAlphanumeric(aString[pos - 1])) {
prevWasHyphen = true;
} elseif (ch == kBengaliYa) { // Unless we're at the start, check for a preceding virama. if (pos > 0 && aString[pos - 1] == kBengaliVirama) {
glyphs[pos] = extendCluster;
}
} // advance iter to the next cluster-start (or end of text) const uint32_t nextPos = *iter.Next(); // step past the first char of the cluster
++pos; // mark all the rest as cluster-continuations for (; pos < nextPos; ++pos) {
glyphs[pos] = extendCluster;
}
}
}
// Leaving advance as zero will prevent drawing the hexbox for ignorables.
int32_t advance = 0; if (!IsIgnorable(aChar)) {
gfxFloat width =
std::max(aFont->GetMetrics(nsFontMetrics::eHorizontal).aveCharWidth,
gfxFloat(gfxFontMissingGlyphs::GetDesiredMinWidth(
aChar, mAppUnitsPerDevUnit)));
advance = int32_t(width * mAppUnitsPerDevUnit);
}
DetailedGlyph detail = {aChar, advance, gfx::Point()};
SetDetailedGlyphs(aIndex, 1, &detail);
g.SetMissing();
}
bool gfxShapedText::FilterIfIgnorable(uint32_t aIndex, uint32_t aCh) { if (IsIgnorable(aCh)) { // There are a few default-ignorables of Letter category (currently, // just the Hangul filler characters) that we'd better not discard // if they're followed by additional characters in the same cluster. // Some fonts use them to carry the width of a whole cluster of // combining jamos; see bug 1238243. auto* charGlyphs = GetCharacterGlyphs(); if (GetGenCategory(aCh) == nsUGenCategory::kLetter &&
aIndex + 1 < GetLength() && !charGlyphs[aIndex + 1].IsClusterStart()) { returnfalse;
} // A compressedGlyph that is set to MISSING but has no DetailedGlyphs list // will be zero-width/invisible, which is what we want here.
CompressedGlyph& g = charGlyphs[aIndex];
g.SetComplex(g.IsClusterStart(), g.IsLigatureGroupStart()).SetMissing(); returntrue;
} returnfalse;
}
void gfxShapedText::ApplyTrackingToClusters(gfxFloat aTrackingAdjustment,
uint32_t aOffset,
uint32_t aLength) {
int32_t appUnitAdjustment =
NS_round(aTrackingAdjustment * gfxFloat(mAppUnitsPerDevUnit));
CompressedGlyph* charGlyphs = GetCharacterGlyphs(); for (uint32_t i = aOffset; i < aOffset + aLength; ++i) {
CompressedGlyph* glyphData = charGlyphs + i; if (glyphData->IsSimpleGlyph()) { // simple glyphs ==> just add the advance
int32_t advance = glyphData->GetSimpleAdvance(); if (advance > 0) {
advance = std::max(0, advance + appUnitAdjustment); if (CompressedGlyph::IsSimpleAdvance(advance)) {
glyphData->SetSimpleGlyph(advance, glyphData->GetSimpleGlyph());
} else { // rare case, tested by making this the default
uint32_t glyphIndex = glyphData->GetSimpleGlyph(); // convert the simple CompressedGlyph to an empty complex record
glyphData->SetComplex(true, true); // then set its details (glyph ID with its new advance)
DetailedGlyph detail = {glyphIndex, advance, gfx::Point()};
SetDetailedGlyphs(i, 1, &detail);
}
}
} else { // complex glyphs ==> add offset at cluster/ligature boundaries
uint32_t detailedLength = glyphData->GetGlyphCount(); if (detailedLength) {
DetailedGlyph* details = GetDetailedGlyphs(i); if (!details) { continue;
} auto& advance = IsRightToLeft() ? details[0].mAdvance
: details[detailedLength - 1].mAdvance; if (advance > 0) {
advance = std::max(0, advance + appUnitAdjustment);
}
}
}
}
}
size_t gfxShapedWord::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
size_t total = aMallocSizeOf(this); if (mDetailedGlyphs) {
total += mDetailedGlyphs->SizeOfIncludingThis(aMallocSizeOf);
} return total;
}
float gfxFont::AngleForSyntheticOblique() const { // First check conditions that mean no synthetic slant should be used: if (mStyle.style == FontSlantStyle::NORMAL) { return 0.0f; // Requested style is 'normal'.
} if (!mStyle.allowSyntheticStyle) { return 0.0f; // Synthetic obliquing is disabled.
} if (!mFontEntry->MayUseSyntheticSlant()) { return 0.0f; // The resource supports "real" slant, so don't synthesize.
}
// If style calls for italic, and face doesn't support it, use default // oblique angle as a simulation. if (mStyle.style.IsItalic()) { return mFontEntry->SupportsItalic()
? 0.0f
: FontSlantStyle::DEFAULT_OBLIQUE_DEGREES;
}
// OK, we're going to use synthetic oblique: return the requested angle. return mStyle.style.ObliqueAngle();
}
float gfxFont::SkewForSyntheticOblique() const { // Precomputed value of tan(kDefaultAngle), the default italic/oblique slant; // avoids calling tan() at runtime except for custom oblique values. staticconstfloat kTanDefaultAngle =
tan(FontSlantStyle::DEFAULT_OBLIQUE_DEGREES * (M_PI / 180.0));
if (MOZ_UNLIKELY(StaticPrefs::gfx_text_disable_aa_AtStartup())) {
mAntialiasOption = kAntialiasNone;
}
// Turn off AA for Ahem for testing purposes when requested. if (MOZ_UNLIKELY(StaticPrefs::gfx_font_rendering_ahem_antialias_none() &&
mFontEntry->FamilyName().EqualsLiteral("Ahem"))) {
mAntialiasOption = kAntialiasNone;
}
// Ensure the gfxFontEntry's unitsPerEm and extents fields are initialized, // so that GetFontExtents can use them without risk of races.
Unused << mFontEntry->UnitsPerEm();
}
// Delete objects owned through atomic pointers. (Some of these may be null, // but that's OK.) delete mVerticalMetrics.exchange(nullptr); delete mHarfBuzzShaper.exchange(nullptr); delete mGraphiteShaper.exchange(nullptr); delete mMathTable.exchange(nullptr); delete mNonAAFont.exchange(nullptr);
if (auto* scaledFont = mAzureScaledFont.exchange(nullptr)) {
scaledFont->Release();
}
if (mGlyphChangeObservers) { for (constauto& key : *mGlyphChangeObservers) {
key->ForgetFont();
}
}
}
// Work out whether cairo will snap inter-glyph spacing to pixels. // // Layout does not align text to pixel boundaries, so, with font drawing // backends that snap glyph positions to pixels, it is important that // inter-glyph spacing within words is always an integer number of pixels. // This ensures that the drawing backend snaps all of the word's glyphs in the // same direction and so inter-glyph spacing remains the same. //
gfxFont::RoundingFlags gfxFont::GetRoundOffsetsToPixels(
DrawTarget* aDrawTarget) { // Could do something fancy here for ScaleFactors of // AxisAlignedTransforms, but we leave things simple. // Not much point rounding if a matrix will mess things up anyway. // Also check if the font already knows hint metrics is off... if (aDrawTarget->GetTransform().HasNonTranslation() || !ShouldHintMetrics()) { return RoundingFlags(0);
}
gfxFloat gfxFont::GetGlyphAdvance(uint16_t aGID, bool aVertical) { if (!aVertical && ProvidesGlyphWidths()) { return GetGlyphWidth(aGID) / 65536.0;
} if (mFUnitsConvFactor < 0.0f) { // 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 (gfxHarfBuzzShaper* shaper = GetHarfBuzzShaper()) { if (aVertical) { // Note that GetGlyphVAdvance may return -1 to indicate it was unable // to retrieve vertical metrics; in that case we fall back to the // aveCharWidth value as a default advance.
int32_t advance = shaper->GetGlyphVAdvance(aGID); if (advance < 0) { return GetMetrics(nsFontMetrics::eVertical).aveCharWidth;
} return advance / 65536.0;
} return shaper->GetGlyphHAdvance(aGID) / 65536.0;
} return 0.0;
}
uint32_t featureIndexes[32];
uint32_t i, len, offset;
offset = 0; do {
len = std::size(featureIndexes);
hb_ot_layout_language_get_feature_indexes(aFace, aTableTag, aScriptIndex,
aLangIndex, offset, &len,
featureIndexes);
for (i = 0; i < len; i++) {
uint32_t featureIndex = featureIndexes[i];
// get the feature tag
hb_tag_t featureTag;
uint32_t tagLen = 1;
hb_ot_layout_language_get_feature_tags(aFace, aTableTag, aScriptIndex,
aLangIndex, offset + i, &tagLen,
&featureTag);
for (script = 0; script < numScripts; script++) { // default lang
CollectLookupsByLanguage(aFace, aTableTag, specificFeature, otherLookups,
specificFeatureLookups, script,
HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
// iterate over langs
numLangs = hb_ot_layout_script_get_language_tags(
aFace, HB_OT_TAG_GPOS, script, 0, nullptr, nullptr); for (lang = 0; lang < numLangs; lang++) {
CollectLookupsByLanguage(aFace, aTableTag, specificFeature, otherLookups,
specificFeatureLookups, script, lang);
}
}
// look for the glyph among non-specific feature lookups
hb_set_t* glyphs = hb_set_create();
hb_codepoint_t index = -1; while (hb_set_next(otherLookups, &index)) {
hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index, glyphs, glyphs,
glyphs, nullptr); if (hb_set_has(glyphs, aGlyph)) {
aHasGlyph = true; break;
}
}
// look for the glyph among specific feature lookups
hb_set_clear(glyphs);
index = -1; while (hb_set_next(specificFeatureLookups, &index)) {
hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index, glyphs, glyphs,
glyphs, nullptr); if (hb_set_has(glyphs, aGlyph)) {
aHasGlyphSpecific = true; break;
}
}
uint32_t spaceGlyph = GetSpaceGlyph(); if (!spaceGlyph) { return;
}
auto face(GetFontEntry()->GetHBFace());
// GSUB lookups - examine per script if (hb_ot_layout_has_substitution(face)) { // Get the script ==> code hashtable, creating it on first use.
nsTHashMap<nsUint32HashKey, Script>* tagToCode = sScriptTagToCode; if (!tagToCode) {
tagToCode = new nsTHashMap<nsUint32HashKey, Script>(
size_t(Script::NUM_SCRIPT_CODES));
tagToCode->InsertOrUpdate(HB_TAG('D', 'F', 'L', 'T'), Script::COMMON); // Ensure that we don't try to look at script codes beyond what the // current version of ICU (at runtime -- in case of system ICU) // knows about.
Script scriptCount = Script(
std::min<int>(intl::UnicodeProperties::GetMaxNumberOfScripts() + 1, int(Script::NUM_SCRIPT_CODES))); for (Script s = Script::ARABIC; s < scriptCount;
s = Script(static_cast<int>(s) + 1)) {
hb_script_t script = hb_script_t(GetScriptTagForCode(s)); unsignedint scriptCount = 4;
hb_tag_t scriptTags[4];
hb_ot_tags_from_script_and_language(script, HB_LANGUAGE_INVALID,
&scriptCount, scriptTags, nullptr,
nullptr); for (unsignedint i = 0; i < scriptCount; i++) {
tagToCode->InsertOrUpdate(scriptTags[i], s);
}
} if (!sScriptTagToCode.compareExchange(nullptr, tagToCode)) { // We lost a race! Discard our new table and use the winner. delete tagToCode;
tagToCode = sScriptTagToCode;
}
}
// Set up the default-features hashset on first use. if (!sDefaultFeatures) {
uint32_t numDefaultFeatures = std::size(defaultFeatures); auto* set = new nsTHashSet<uint32_t>(numDefaultFeatures); for (uint32_t i = 0; i < numDefaultFeatures; i++) {
set->Insert(defaultFeatures[i]);
} if (!sDefaultFeatures.compareExchange(nullptr, set)) { delete set;
}
}
// iterate over the scripts in the font
hb_tag_t scriptTags[8];
uint32_t len, offset = 0; do {
len = std::size(scriptTags);
hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GSUB, offset, &len,
scriptTags); for (uint32_t i = 0; i < len; i++) { bool isDefaultFeature = false;
Script s; if (!HasLookupRuleWithGlyphByScript(
face, HB_OT_TAG_GSUB, scriptTags[i], offset + i, spaceGlyph,
*sDefaultFeatures, isDefaultFeature) ||
!tagToCode->Get(scriptTags[i], &s)) { continue;
}
flags = flags | gfxFontEntry::SpaceFeatures::HasFeatures;
uint32_t index = static_cast<uint32_t>(s) >> 5;
uint32_t bit = static_cast<uint32_t>(s) & 0x1f; if (isDefaultFeature) {
mFontEntry->mDefaultSubSpaceFeatures[index] |= (1 << bit);
} else {
mFontEntry->mNonDefaultSubSpaceFeatures[index] |= (1 << bit);
}
}
offset += len;
} while (len == std::size(scriptTags));
}
// spaces in default features of default script? // ==> can't use word cache, skip GPOS analysis bool canUseWordCache = true; if (HasSubstitution(mFontEntry->mDefaultSubSpaceFeatures, Script::COMMON)) {
canUseWordCache = false;
}
// GPOS lookups - distinguish kerning from non-kerning features if (canUseWordCache && hb_ot_layout_has_positioning(face)) { bool hasKerning = false, hasNonKerning = false;
HasLookupRuleWithGlyph(face, HB_OT_TAG_GPOS, hasNonKerning,
HB_TAG('k', 'e', 'r', 'n'), hasKerning, spaceGlyph); if (hasKerning) {
flags |= gfxFontEntry::SpaceFeatures::HasFeatures |
gfxFontEntry::SpaceFeatures::Kerning;
} if (hasNonKerning) {
flags |= gfxFontEntry::SpaceFeatures::HasFeatures |
gfxFontEntry::SpaceFeatures::NonKerning;
}
}
// default features have space lookups ==> true if (HasSubstitution(mFontEntry->mDefaultSubSpaceFeatures, Script::COMMON) ||
HasSubstitution(mFontEntry->mDefaultSubSpaceFeatures, aRunScript)) { returntrue;
}
// non-default features have space lookups and some type of // font feature, in font or style is specified ==> true if ((HasSubstitution(mFontEntry->mNonDefaultSubSpaceFeatures,
Script::COMMON) ||
HasSubstitution(mFontEntry->mNonDefaultSubSpaceFeatures, aRunScript)) &&
(!mStyle.featureSettings.IsEmpty() ||
!mFontEntry->mFeatureSettings.IsEmpty())) { returntrue;
}
returnfalse;
}
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() &&
mFontEntry->mFeatureSettings.IsEmpty()) { returnfalse;
}
}
if (FontCanSupportGraphite()) { if (gfxPlatform::GetPlatform()->UseGraphiteShaping()) { return mFontEntry->HasGraphiteSpaceContextuals();
}
}
// We record the presence of space-dependent features in the font entry // so that subsequent instantiations for the same font face won't // require us to re-check the tables; however, the actual check is done // by gfxFont because not all font entry subclasses know how to create // a harfbuzz face for introspection.
gfxFontEntry::SpaceFeatures flags = mFontEntry->mHasSpaceFeatures; if (flags == gfxFontEntry::SpaceFeatures::Uninitialized) {
CheckForFeaturesInvolvingSpace();
flags = mFontEntry->mHasSpaceFeatures;
}
if (!(flags & gfxFontEntry::SpaceFeatures::HasFeatures)) { returnfalse;
}
// if font has substitution rules or non-kerning positioning rules // that involve spaces, bypass if (HasSubstitutionRulesWithSpaceLookups(aRunScript) ||
(flags & gfxFontEntry::SpaceFeatures::NonKerning)) { returntrue;
}
// if kerning explicitly enabled/disabled via font-feature-settings or // font-kerning and kerning rules use spaces, only bypass when enabled if (mKerningSet && (flags & gfxFontEntry::SpaceFeatures::Kerning)) { return mKerningEnabled;
}
if (!SupportsFeature(aRunScript, feature)) { returnfalse;
}
// xxx - for graphite, don't really know how to sniff lookups so bail if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) { returntrue;
}
gfxHarfBuzzShaper* shaper = GetHarfBuzzShaper(); if (!shaper) { returnfalse;
}
// get the hbset containing input glyphs for the feature const hb_set_t* inputGlyphs =
mFontEntry->InputsForOpenTypeFeature(aRunScript, feature);
// create an hbset containing default glyphs for the script run
hb_set_t* defaultGlyphsInRun = hb_set_create();
// for each character, get the glyph id for (uint32_t i = 0; i < aLength; i++) {
uint32_t ch = aString[i];
if (i + 1 < aLength && NS_IS_SURROGATE_PAIR(ch, aString[i + 1])) {
i++;
ch = SURROGATE_TO_UCS4(ch, aString[i]);
}
// intersect with input glyphs, if size is not the same ==> fallback
uint32_t origSize = hb_set_get_population(defaultGlyphsInRun);
hb_set_intersect(defaultGlyphsInRun, inputGlyphs);
uint32_t intersectionSize = hb_set_get_population(defaultGlyphsInRun);
hb_set_destroy(defaultGlyphsInRun);
// xxx - for graphite, don't really know how to sniff lookups so bail if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) { returntrue;
}
if (gfxHarfBuzzShaper* shaper = GetHarfBuzzShaper()) { // get the hbset containing input glyphs for the feature const hb_set_t* inputGlyphs =
mFontEntry->InputsForOpenTypeFeature(aRunScript, aFeature);
/** * A helper function in case we need to do any rounding or other * processing here.
*/ #define ToDeviceUnits(aAppUnits, aDevUnitsPerAppUnit) \
(double(aAppUnits) * double(aDevUnitsPerAppUnit))
static AntialiasMode Get2DAAMode(gfxFont::AntialiasOption aAAOption) { switch (aAAOption) { case gfxFont::kAntialiasSubpixel: return AntialiasMode::SUBPIXEL; case gfxFont::kAntialiasGrayscale: return AntialiasMode::GRAY; case gfxFont::kAntialiasNone: return AntialiasMode::NONE; default: return AntialiasMode::DEFAULT;
}
}
class GlyphBufferAzure { #define AUTO_BUFFER_SIZE (2048 / sizeof(Glyph))
~GlyphBufferAzure() { if (mNumGlyphs > 0) {
FlushGlyphs();
}
if (mBuffer != *mAutoBuffer.addr()) {
free(mBuffer);
}
}
// Ensure the buffer has enough space for aGlyphCount glyphs to be added, // considering the supplied strike multipler aStrikeCount. // This MUST be called before OutputGlyph is used to actually store glyph // records in the buffer. It may be called repeated to add further capacity // in case we don't know up-front exactly what will be needed. void AddCapacity(uint32_t aGlyphCount, uint32_t aStrikeCount) { // Calculate the new capacity and ensure it will fit within the maximum // allowed capacity. staticconst uint64_t kMaxCapacity = 64 * 1024;
mCapacity = uint32_t(std::min(
kMaxCapacity,
uint64_t(mCapacity) + uint64_t(aGlyphCount) * uint64_t(aStrikeCount))); // See if the required capacity fits within the already-allocated space if (mCapacity <= mBufSize) { return;
} // We need to grow the buffer: determine a new size, allocate, and // copy the existing data over if we didn't use realloc (which would // do it automatically).
mBufSize = std::max(mCapacity, mBufSize * 2); if (mBuffer == *mAutoBuffer.addr()) { // switching from autobuffer to malloc, so we need to copy
mBuffer = reinterpret_cast<Glyph*>(moz_xmalloc(mBufSize * sizeof(Glyph)));
std::memcpy(mBuffer, *mAutoBuffer.addr(), mNumGlyphs * sizeof(Glyph));
} else {
mBuffer = reinterpret_cast<Glyph*>(
moz_xrealloc(mBuffer, mBufSize * sizeof(Glyph)));
}
}
void OutputGlyph(uint32_t aGlyphID, const gfx::Point& aPt) { // If the buffer is full, flush to make room for the new glyph. if (mNumGlyphs >= mCapacity) { // Check that AddCapacity has been used appropriately!
MOZ_ASSERT(mCapacity > 0 && mNumGlyphs == mCapacity);
Flush();
}
Glyph* glyph = mBuffer + mNumGlyphs++;
glyph->mIndex = aGlyphID;
glyph->mPosition = aPt;
}
// Render the buffered glyphs to the draw target. void FlushGlyphs() {
gfx::GlyphBuffer buf;
buf.mGlyphs = mBuffer;
buf.mNumGlyphs = mNumGlyphs;
const gfxContext::AzureState& state = mRunParams.context->CurrentState();
// Draw stroke first if the UNDERNEATH flag is set in drawMode. if (mRunParams.strokeOpts &&
GetStrokeMode(mRunParams.drawMode) ==
(DrawMode::GLYPH_STROKE | DrawMode::GLYPH_STROKE_UNDERNEATH)) {
DrawStroke(state, buf);
}
if (mRunParams.drawMode & DrawMode::GLYPH_FILL) { if (state.pattern || mFontParams.contextPaint) {
Pattern* pat;
RefPtr<gfxPattern> fillPattern; if (mFontParams.contextPaint) {
imgDrawingParams imgParams;
fillPattern = mFontParams.contextPaint->GetFillPattern(
mRunParams.context->GetDrawTarget(),
mRunParams.context->CurrentMatrixDouble(), imgParams);
} if (!fillPattern) { if (state.pattern) {
--> --------------------
--> maximum size reached
--> --------------------
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.24Angebot
Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.