diff --git a/gfx/2d/2D.h b/gfx/2d/2D.h index ee609a25bab4..f00ab6047baf 100644 --- a/gfx/2d/2D.h +++ b/gfx/2d/2D.h @@ -679,7 +679,14 @@ public: MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFont) virtual ~ScaledFont() {} - typedef void (*FontFileDataOutput)(const uint8_t* aData, uint32_t aLength, uint32_t aIndex, Float aGlyphSize, void* aBaton); + typedef struct { + uint32_t mTag; + Float mValue; + } VariationSetting; + + typedef void (*FontFileDataOutput)(const uint8_t *aData, uint32_t aLength, uint32_t aIndex, Float aGlyphSize, + uint32_t aVariationCount, const VariationSetting* aVariations, + void *aBaton); typedef void (*FontInstanceDataOutput)(const uint8_t* aData, uint32_t aLength, void* aBaton); typedef void (*FontDescriptorOutput)(const uint8_t* aData, uint32_t aLength, Float aFontSize, void* aBaton); @@ -1398,11 +1405,16 @@ public: * * @param aData Pointer to the data * @param aSize Size of the TrueType data + * @param aVariationCount Number of VariationSetting records provided. + * @param aVariations Pointer to VariationSetting records. * @param aType Type of NativeFontResource that should be created. * @return a NativeFontResource of nullptr if failed. */ static already_AddRefed - CreateNativeFontResource(uint8_t *aData, uint32_t aSize, FontType aType); + CreateNativeFontResource(uint8_t *aData, uint32_t aSize, + uint32_t aVariationCount, + const ScaledFont::VariationSetting* aVariations, + FontType aType); /** * This creates a scaled font of the given type based on font descriptor diff --git a/gfx/2d/Factory.cpp b/gfx/2d/Factory.cpp index 5f8f2c705c79..e2cf203bb32b 100644 --- a/gfx/2d/Factory.cpp +++ b/gfx/2d/Factory.cpp @@ -502,6 +502,8 @@ Factory::CreateScaledFontForNativeFont(const NativeFont &aNativeFont, Float aSiz already_AddRefed Factory::CreateNativeFontResource(uint8_t *aData, uint32_t aSize, + uint32_t aVariationCount, + const ScaledFont::VariationSetting* aVariations, FontType aType) { switch (aType) { @@ -526,7 +528,8 @@ Factory::CreateNativeFontResource(uint8_t *aData, uint32_t aSize, /* aNeedsCairo = */ true); } #elif defined(XP_DARWIN) - return NativeFontResourceMac::Create(aData, aSize); + return NativeFontResourceMac::Create(aData, aSize, + aVariationCount, aVariations); #elif defined(MOZ_WIDGET_GTK) return NativeFontResourceFontconfig::Create(aData, aSize); #else diff --git a/gfx/2d/NativeFontResourceMac.cpp b/gfx/2d/NativeFontResourceMac.cpp index aaf6db181309..ff50691695f3 100644 --- a/gfx/2d/NativeFontResourceMac.cpp +++ b/gfx/2d/NativeFontResourceMac.cpp @@ -13,12 +13,146 @@ #include #endif +// Simple helper class to automatically release a CFObject when it goes out +// of scope. +template +class AutoRelease +{ +public: + explicit AutoRelease(T aObject) + : mObject(aObject) + { + } + + ~AutoRelease() + { + if (mObject) { + CFRelease(mObject); + } + } + + operator T() + { + return mObject; + } + + T forget() + { + T obj = mObject; + mObject = nullptr; + return obj; + } + +private: + T mObject; +}; + +// This is essentially identical to the similarly-named helper function +// in gfx/thebes/gfxMacFont.cpp. Maybe we should put it somewhere that +// can be shared by both Moz2d and Thebes callers? +static CFDictionaryRef +CreateVariationDictionaryOrNull(CGFontRef aCGFont, uint32_t aVariationCount, + const mozilla::gfx::ScaledFont::VariationSetting* aVariations) +{ + AutoRelease + ctFont(CTFontCreateWithGraphicsFont(aCGFont, 0, nullptr, nullptr)); + AutoRelease axes(CTFontCopyVariationAxes(ctFont)); + if (!axes) { + return nullptr; + } + + CFIndex axisCount = CFArrayGetCount(axes); + AutoRelease + dict(CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks)); + + // Number of variation settings passed in the aVariations parameter. + // This will typically be a very low value, so we just linear-search them. + bool allDefaultValues = true; + + for (CFIndex i = 0; i < axisCount; ++i) { + // We sanity-check the axis info found in the CTFont, and bail out + // (returning null) if it doesn't have the expected types. + CFTypeRef axisInfo = CFArrayGetValueAtIndex(axes, i); + if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) { + return nullptr; + } + CFDictionaryRef axis = static_cast(axisInfo); + + CFTypeRef axisTag = + CFDictionaryGetValue(axis, kCTFontVariationAxisIdentifierKey); + if (!axisTag || CFGetTypeID(axisTag) != CFNumberGetTypeID()) { + return nullptr; + } + int64_t tagLong; + if (!CFNumberGetValue(static_cast(axisTag), + kCFNumberSInt64Type, &tagLong)) { + return nullptr; + } + + CFTypeRef axisName = + CFDictionaryGetValue(axis, kCTFontVariationAxisNameKey); + if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) { + return nullptr; + } + + // Clamp axis values to the supported range. + CFTypeRef min = CFDictionaryGetValue(axis, kCTFontVariationAxisMinimumValueKey); + CFTypeRef max = CFDictionaryGetValue(axis, kCTFontVariationAxisMaximumValueKey); + CFTypeRef def = CFDictionaryGetValue(axis, kCTFontVariationAxisDefaultValueKey); + if (!min || CFGetTypeID(min) != CFNumberGetTypeID() || + !max || CFGetTypeID(max) != CFNumberGetTypeID() || + !def || CFGetTypeID(def) != CFNumberGetTypeID()) { + return nullptr; + } + double minDouble; + double maxDouble; + double defDouble; + if (!CFNumberGetValue(static_cast(min), kCFNumberDoubleType, + &minDouble) || + !CFNumberGetValue(static_cast(max), kCFNumberDoubleType, + &maxDouble) || + !CFNumberGetValue(static_cast(def), kCFNumberDoubleType, + &defDouble)) { + return nullptr; + } + + double value = defDouble; + for (uint32_t j = 0; j < aVariationCount; ++j) { + if (aVariations[j].mTag == tagLong) { + value = std::min(std::max(aVariations[j].mValue, + minDouble), + maxDouble); + if (value != defDouble) { + allDefaultValues = false; + } + break; + } + } + AutoRelease valueNumber(CFNumberCreate(kCFAllocatorDefault, + kCFNumberDoubleType, + &value)); + CFDictionaryAddValue(dict, axisName, valueNumber); + } + + if (allDefaultValues) { + // We didn't actually set any non-default values, so throw away the + // variations dictionary and just use the default rendering. + return nullptr; + } + + return dict.forget(); +} + namespace mozilla { namespace gfx { /* static */ already_AddRefed -NativeFontResourceMac::Create(uint8_t *aFontData, uint32_t aDataLength) +NativeFontResourceMac::Create(uint8_t *aFontData, uint32_t aDataLength, + uint32_t aVariationCount, + const ScaledFont::VariationSetting* aVariations) { // copy font data CFDataRef data = CFDataCreate(kCFAllocatorDefault, aFontData, aDataLength); @@ -42,6 +176,20 @@ NativeFontResourceMac::Create(uint8_t *aFontData, uint32_t aDataLength) return nullptr; } + if (aVariationCount > 0) { + MOZ_ASSERT(aVariations); + AutoRelease + varDict(CreateVariationDictionaryOrNull(fontRef, aVariationCount, + aVariations)); + if (varDict) { + CGFontRef varFont = CGFontCreateCopyWithVariations(fontRef, varDict); + if (varFont) { + CFRelease(fontRef); + fontRef = varFont; + } + } + } + // passes ownership of fontRef to the NativeFontResourceMac instance RefPtr fontResource = new NativeFontResourceMac(fontRef); diff --git a/gfx/2d/NativeFontResourceMac.h b/gfx/2d/NativeFontResourceMac.h index 47ca92e68f50..73cd693a6a5a 100644 --- a/gfx/2d/NativeFontResourceMac.h +++ b/gfx/2d/NativeFontResourceMac.h @@ -20,7 +20,9 @@ public: MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(NativeFontResourceMac) static already_AddRefed - Create(uint8_t *aFontData, uint32_t aDataLength); + Create(uint8_t *aFontData, uint32_t aDataLength, + uint32_t aVariationCount, + const ScaledFont::VariationSetting* aVariations); already_AddRefed CreateScaledFont(uint32_t aIndex, Float aGlyphSize, diff --git a/gfx/2d/RecordedEvent.cpp b/gfx/2d/RecordedEvent.cpp index d298572df59c..6d3688ac05f3 100644 --- a/gfx/2d/RecordedEvent.cpp +++ b/gfx/2d/RecordedEvent.cpp @@ -1513,6 +1513,7 @@ RecordedSnapshot::OutputSimpleEventInfo(stringstream &aStringStream) const RecordedFontData::~RecordedFontData() { delete[] mData; + delete[] mVariations; } bool @@ -1520,6 +1521,7 @@ RecordedFontData::PlayEvent(Translator *aTranslator) const { RefPtr fontResource = Factory::CreateNativeFontResource(mData, mFontDetails.size, + mFontDetails.variationCount, mVariations, aTranslator->GetDesiredFontType()); if (!fontResource) { return false; @@ -1537,6 +1539,8 @@ RecordedFontData::RecordToStream(std::ostream &aStream) const WriteElement(aStream, mFontDetails.fontDataKey); WriteElement(aStream, mFontDetails.size); aStream.write((const char*)mData, mFontDetails.size); + WriteElement(aStream, mFontDetails.variationCount); + aStream.write((const char*)mVariations, mFontDetails.variationCount * sizeof(ScaledFont::VariationSetting)); } void @@ -1546,14 +1550,23 @@ RecordedFontData::OutputSimpleEventInfo(stringstream &aStringStream) const } void -RecordedFontData::SetFontData(const uint8_t *aData, uint32_t aSize, uint32_t aIndex, Float aGlyphSize) +RecordedFontData::SetFontData(const uint8_t *aData, uint32_t aSize, uint32_t aIndex, + Float aGlyphSize, uint32_t aVariationCount, + const ScaledFont::VariationSetting* aVariations) { mData = new uint8_t[aSize]; memcpy(mData, aData, aSize); - mFontDetails.fontDataKey = SFNTData::GetUniqueKey(aData, aSize); + uint32_t varDataSize = aVariationCount * sizeof(ScaledFont::VariationSetting); + mFontDetails.fontDataKey = + SFNTData::GetUniqueKey(aData, aSize, varDataSize, aVariations); mFontDetails.size = aSize; mFontDetails.index = aIndex; mFontDetails.glyphSize = aGlyphSize; + mFontDetails.variationCount = aVariationCount; + if (aVariationCount > 0) { + mVariations = new ScaledFont::VariationSetting[aVariationCount]; + memcpy(mVariations, aVariations, varDataSize); + } } bool @@ -1567,16 +1580,26 @@ RecordedFontData::GetFontDetails(RecordedFontDetails& fontDetails) fontDetails.size = mFontDetails.size; fontDetails.glyphSize = mFontDetails.glyphSize; fontDetails.index = mFontDetails.index; + fontDetails.variationCount = mFontDetails.variationCount; return true; } RecordedFontData::RecordedFontData(istream &aStream) : RecordedEvent(FONTDATA) + , mData(nullptr) + , mVariations(nullptr) { ReadElement(aStream, mFontDetails.fontDataKey); ReadElement(aStream, mFontDetails.size); mData = new uint8_t[mFontDetails.size]; aStream.read((char*)mData, mFontDetails.size); + ReadElement(aStream, mFontDetails.variationCount); + if (mFontDetails.variationCount > 0) { + mVariations = new ScaledFont::VariationSetting[mFontDetails.variationCount]; + aStream.read((char*)mVariations, mFontDetails.variationCount * sizeof(ScaledFont::VariationSetting)); + } else { + mVariations = nullptr; + } } RecordedFontDescriptor::~RecordedFontDescriptor() diff --git a/gfx/2d/RecordedEvent.h b/gfx/2d/RecordedEvent.h index dcf4d9e36e02..2773671f73ab 100644 --- a/gfx/2d/RecordedEvent.h +++ b/gfx/2d/RecordedEvent.h @@ -24,7 +24,7 @@ const uint32_t kMagicInt = 0xc001feed; // loss of backwards compatibility. Old streams will not work in a player // using a newer major revision. And new streams will not work in a player // using an older major revision. -const uint16_t kMajorRevision = 6; +const uint16_t kMajorRevision = 7; // A change in minor revision means additions of new events. New streams will // not play in older players. const uint16_t kMinorRevision = 0; @@ -67,6 +67,7 @@ struct RecordedFontDetails uint64_t fontDataKey; uint32_t size; uint32_t index; + uint32_t variationCount; Float glyphSize; }; @@ -1014,14 +1015,18 @@ class RecordedFontData : public RecordedEvent { public: static void FontDataProc(const uint8_t *aData, uint32_t aSize, - uint32_t aIndex, Float aGlyphSize, void* aBaton) + uint32_t aIndex, Float aGlyphSize, + uint32_t aVariationCount, + const ScaledFont::VariationSetting* aVariations, + void* aBaton) { auto recordedFontData = static_cast(aBaton); - recordedFontData->SetFontData(aData, aSize, aIndex, aGlyphSize); + recordedFontData->SetFontData(aData, aSize, aIndex, aGlyphSize, + aVariationCount, aVariations); } explicit RecordedFontData(ScaledFont *aScaledFont) - : RecordedEvent(FONTDATA), mData(nullptr) + : RecordedEvent(FONTDATA), mData(nullptr), mVariations(nullptr) { mGetFontFileDataSucceeded = aScaledFont->GetFontFileData(&FontDataProc, this); } @@ -1037,14 +1042,16 @@ public: virtual ReferencePtr GetObjectRef() const { return nullptr; }; void SetFontData(const uint8_t *aData, uint32_t aSize, uint32_t aIndex, - Float aGlyphSize); + Float aGlyphSize, uint32_t aVariationCount, + const ScaledFont::VariationSetting* aVariations); bool GetFontDetails(RecordedFontDetails& fontDetails); private: friend class RecordedEvent; - uint8_t *mData; + uint8_t* mData; + ScaledFont::VariationSetting* mVariations; RecordedFontDetails mFontDetails; bool mGetFontFileDataSucceeded; diff --git a/gfx/2d/SFNTData.cpp b/gfx/2d/SFNTData.cpp index fd29f6694d44..ae36f92caedd 100644 --- a/gfx/2d/SFNTData.cpp +++ b/gfx/2d/SFNTData.cpp @@ -152,7 +152,8 @@ SFNTData::Create(const uint8_t *aFontData, uint32_t aDataLength) /* static */ uint64_t -SFNTData::GetUniqueKey(const uint8_t *aFontData, uint32_t aDataLength) +SFNTData::GetUniqueKey(const uint8_t *aFontData, uint32_t aDataLength, + uint32_t aVarDataSize, const void* aVarData) { uint64_t hash; UniquePtr sfntData = SFNTData::Create(aFontData, aDataLength); @@ -164,6 +165,10 @@ SFNTData::GetUniqueKey(const uint8_t *aFontData, uint32_t aDataLength) hash = HashString(aFontData, aDataLength); } + if (aVarDataSize) { + hash = AddToHash(hash, HashBytes(aVarData, aVarDataSize)); + } + return hash << 32 | aDataLength;; } diff --git a/gfx/2d/SFNTData.h b/gfx/2d/SFNTData.h index e10d63caed6f..2f3c2a558067 100644 --- a/gfx/2d/SFNTData.h +++ b/gfx/2d/SFNTData.h @@ -31,13 +31,14 @@ public: uint32_t aDataLength); /** - * Creates a unique key for the given font data. + * Creates a unique key for the given font data and variation settings. * * @param aFontData the SFNT data * @param aDataLength length * @return unique key to be used for caching */ - static uint64_t GetUniqueKey(const uint8_t *aFontData, uint32_t aDataLength); + static uint64_t GetUniqueKey(const uint8_t *aFontData, uint32_t aDataLength, + uint32_t aVarDataSize, const void* aVarData); ~SFNTData(); diff --git a/gfx/2d/ScaledFontDWrite.cpp b/gfx/2d/ScaledFontDWrite.cpp index dc8e586f5618..a4b770705fd9 100644 --- a/gfx/2d/ScaledFontDWrite.cpp +++ b/gfx/2d/ScaledFontDWrite.cpp @@ -257,7 +257,8 @@ ScaledFontDWrite::GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton void *context; stream->ReadFileFragment(&fragmentStart, 0, fileSize, &context); - aDataCallback((uint8_t*)fragmentStart, fileSize, mFontFace->GetIndex(), mSize, aBaton); + aDataCallback((uint8_t*)fragmentStart, fileSize, mFontFace->GetIndex(), mSize, + 0, nullptr, aBaton); stream->ReleaseFileFragment(context); diff --git a/gfx/2d/ScaledFontMac.cpp b/gfx/2d/ScaledFontMac.cpp index bd9d5fbaded3..d1166384bbbf 100644 --- a/gfx/2d/ScaledFontMac.cpp +++ b/gfx/2d/ScaledFontMac.cpp @@ -186,6 +186,24 @@ struct writeBuf int offset; }; +static void CollectVariationSetting(const void *key, const void *value, void *context) +{ + auto keyPtr = static_cast(key); + auto valuePtr = static_cast(value); + auto vpp = static_cast(context); + if (CFGetTypeID(keyPtr) == CFNumberGetTypeID() && + CFGetTypeID(valuePtr) == CFNumberGetTypeID()) { + uint64_t t; + double v; + if (CFNumberGetValue(static_cast(keyPtr), kCFNumberSInt64Type, &t) && + CFNumberGetValue(static_cast(valuePtr), kCFNumberDoubleType, &v)) { + (*vpp)->mTag = t; + (*vpp)->mValue = v; + (*vpp)++; + } + } +} + bool ScaledFontMac::GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton) { @@ -254,8 +272,26 @@ ScaledFontMac::GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton) // set checkSumAdjust to the computed checksum memcpy(&buf.data[checkSumAdjustmentOffset], &fontChecksum, sizeof(fontChecksum)); + // Collect any variation settings that were incorporated into the CTFont. + uint32_t variationCount = 0; + VariationSetting* variations = nullptr; + if (mCTFont) { + CFDictionaryRef dict = CTFontCopyVariation(mCTFont); + if (dict) { + CFIndex count = CFDictionaryGetCount(dict); + if (count > 0) { + variations = new VariationSetting[count]; + VariationSetting* vPtr = variations; + CFDictionaryApplyFunction(dict, CollectVariationSetting, &vPtr); + variationCount = vPtr - variations; + } + CFRelease(dict); + } + } + // we always use an index of 0 - aDataCallback(buf.data, buf.offset, 0, mSize, aBaton); + aDataCallback(buf.data, buf.offset, 0, mSize, variationCount, variations, aBaton); + delete[] variations; return true; diff --git a/gfx/2d/ScaledFontWin.cpp b/gfx/2d/ScaledFontWin.cpp index d722c443273b..6d7662c53384 100644 --- a/gfx/2d/ScaledFontWin.cpp +++ b/gfx/2d/ScaledFontWin.cpp @@ -77,7 +77,7 @@ ScaledFontWin::GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton) } } - aDataCallback(fontData.get(), tableSize, index, mSize, aBaton); + aDataCallback(fontData.get(), tableSize, index, mSize, 0, nullptr, aBaton); return true; }