diff options
author | bungeman <bungeman@google.com> | 2016-01-21 14:17:47 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-01-21 14:17:47 -0800 |
commit | f6c7107d0385cc2b556802354b93b7dcff61570d (patch) | |
tree | 6acc6e0ae4621e2bbc6deb43c3198a73c5d56433 | |
parent | 27a6e86fb17fce7ce962b9080eae36926e87d568 (diff) |
Expose API for gx font variation axes.
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1590223003
Review URL: https://codereview.chromium.org/1590223003
-rw-r--r-- | gm/fontscalerdistortable.cpp | 9 | ||||
-rw-r--r-- | include/ports/SkFontMgr.h | 51 | ||||
-rw-r--r-- | src/core/SkFontMgr.cpp | 12 | ||||
-rw-r--r-- | src/ports/SkFontHost_FreeType.cpp | 54 | ||||
-rw-r--r-- | src/ports/SkFontHost_FreeType_common.h | 8 | ||||
-rw-r--r-- | src/ports/SkFontHost_mac.cpp | 139 | ||||
-rw-r--r-- | src/ports/SkFontMgr_android.cpp | 63 | ||||
-rw-r--r-- | src/ports/SkFontMgr_fontconfig.cpp | 23 |
8 files changed, 334 insertions, 25 deletions
diff --git a/gm/fontscalerdistortable.cpp b/gm/fontscalerdistortable.cpp index a207f967fd..f83365bb3e 100644 --- a/gm/fontscalerdistortable.cpp +++ b/gm/fontscalerdistortable.cpp @@ -41,6 +41,7 @@ protected: SkPaint paint; paint.setAntiAlias(true); paint.setLCDRenderText(true); + SkAutoTUnref<SkFontMgr> fontMgr(SkFontMgr::RefDefault()); SkAutoTDelete<SkStreamAsset> distortable(GetResourceAsStream("/fonts/Distortable.ttf")); if (!distortable) { @@ -54,9 +55,11 @@ protected: SkScalar x = SkIntToScalar(10); SkScalar y = SkIntToScalar(20); - SkFixed axis = SkDoubleToFixed(0.5 + (5*j + i) * ((2.0 - 0.5) / (2 * 5))); - SkAutoTUnref<SkTypeface> typeface(SkTypeface::CreateFromFontData( - new SkFontData(distortable->duplicate(), 0, &axis, 1))); + SkFourByteTag tag = SkSetFourByteTag('w','g','h','t'); + SkScalar styleValue = SkDoubleToScalar(0.5 + (5*j + i) * ((2.0 - 0.5) / (2 * 5))); + SkFontMgr::FontParameters::Axis axes[] = { { tag, styleValue } }; + SkAutoTUnref<SkTypeface> typeface(fontMgr->createFromStream( + distortable->duplicate(), SkFontMgr::FontParameters().setAxes(axes, 1))); paint.setTypeface(typeface); SkAutoCanvasRestore acr(canvas, true); diff --git a/include/ports/SkFontMgr.h b/include/ports/SkFontMgr.h index 96a8501c48..3e6c785be9 100644 --- a/include/ports/SkFontMgr.h +++ b/include/ports/SkFontMgr.h @@ -8,8 +8,10 @@ #ifndef SkFontMgr_DEFINED #define SkFontMgr_DEFINED -#include "SkRefCnt.h" #include "SkFontStyle.h" +#include "SkRefCnt.h" +#include "SkScalar.h" +#include "SkTypes.h" class SkData; class SkFontData; @@ -100,6 +102,52 @@ public: */ SkTypeface* createFromStream(SkStreamAsset*, int ttcIndex = 0) const; + struct FontParameters { + struct Axis { + SkFourByteTag fTag; + SkScalar fStyleValue; + }; + + FontParameters() : fCollectionIndex(0), fAxisCount(0), fAxes(nullptr) {} + + /** Specify the index of the desired font. + * + * Font formats like ttc, dfont, cff, cid, pfr, t42, t1, and fon may actually be indexed + * collections of fonts. + */ + FontParameters& setCollectionIndex(int collectionIndex) { + fCollectionIndex = collectionIndex; + return *this; + } + + /** Specify the GX variation axis values. + * + * Any axes not specified will use the default value. Specified axes not present in the + * font will be ignored. + * + * @param axes not copied. This pointer must remain valid for life of FontParameters. + */ + FontParameters& setAxes(const Axis* axes, int axisCount) { + fAxisCount = axisCount; + fAxes = axes; + return *this; + } + + int getCollectionIndex() const { + return fCollectionIndex; + } + const Axis* getAxes(int* axisCount) const { + *axisCount = fAxisCount; + return fAxes; + } + private: + int fCollectionIndex; + int fAxisCount; + const Axis* fAxes; + }; + /* Experimental, API subject to change. */ + SkTypeface* createFromStream(SkStreamAsset*, const FontParameters&) const; + /** * Create a typeface from the specified font data. * Takes ownership of the font data, so the caller should not reference it again. @@ -144,6 +192,7 @@ protected: virtual SkTypeface* onCreateFromData(SkData*, int ttcIndex) const = 0; virtual SkTypeface* onCreateFromStream(SkStreamAsset*, int ttcIndex) const = 0; // TODO: make pure virtual. + virtual SkTypeface* onCreateFromStream(SkStreamAsset*, const FontParameters&) const; virtual SkTypeface* onCreateFromFontData(SkFontData*) const; virtual SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const = 0; diff --git a/src/core/SkFontMgr.cpp b/src/core/SkFontMgr.cpp index 5246916463..ab04250c03 100644 --- a/src/core/SkFontMgr.cpp +++ b/src/core/SkFontMgr.cpp @@ -132,6 +132,13 @@ SkTypeface* SkFontMgr::createFromStream(SkStreamAsset* stream, int ttcIndex) con return this->onCreateFromStream(stream, ttcIndex); } +SkTypeface* SkFontMgr::createFromStream(SkStreamAsset* stream, const FontParameters& params) const { + if (nullptr == stream) { + return nullptr; + } + return this->onCreateFromStream(stream, params); +} + SkTypeface* SkFontMgr::createFromFontData(SkFontData* data) const { if (nullptr == data) { return nullptr; @@ -140,6 +147,11 @@ SkTypeface* SkFontMgr::createFromFontData(SkFontData* data) const { } // This implementation is temporary until it can be made pure virtual. +SkTypeface* SkFontMgr::onCreateFromStream(SkStreamAsset* stream, const FontParameters& p) const { + return this->createFromStream(stream, p.getCollectionIndex()); +} + +// This implementation is temporary until it can be made pure virtual. SkTypeface* SkFontMgr::onCreateFromFontData(SkFontData* data) const { SkTypeface* ret = this->createFromStream(data->detachStream(), data->getIndex()); delete data; diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp index e8b72b5e09..ffac0ca974 100644 --- a/src/ports/SkFontHost_FreeType.cpp +++ b/src/ports/SkFontHost_FreeType.cpp @@ -1741,3 +1741,57 @@ bool SkTypeface_FreeType::Scanner::scanFont( FT_Done_Face(face); return true; } + +/*static*/ void SkTypeface_FreeType::Scanner::computeAxisValues( + AxisDefinitions axisDefinitions, + const SkFontMgr::FontParameters::Axis* requestedAxes, int requestedAxisCount, + SkFixed* axisValues, + const SkString& name) +{ + for (int i = 0; i < axisDefinitions.count(); ++i) { + const Scanner::AxisDefinition& axisDefinition = axisDefinitions[i]; + axisValues[i] = axisDefinition.fDefault; + for (int j = 0; j < requestedAxisCount; ++j) { + const SkFontMgr::FontParameters::Axis& axisSpecified = requestedAxes[j]; + if (axisDefinition.fTag == axisSpecified.fTag) { + SkFixed axisValue = SkScalarToFixed(axisSpecified.fStyleValue); + axisValues[i] = SkTPin(axisValue, axisDefinition.fMinimum, axisDefinition.fMaximum); + if (axisValues[i] != axisValue) { + SkDEBUGF(("Requested font axis value out of range: " + "%s '%c%c%c%c' %f; pinned to %f.\n", + name.c_str(), + (axisDefinition.fTag >> 24) & 0xFF, + (axisDefinition.fTag >> 16) & 0xFF, + (axisDefinition.fTag >> 8) & 0xFF, + (axisDefinition.fTag ) & 0xFF, + SkScalarToDouble(axisSpecified.fStyleValue), + SkFixedToDouble(axisValues[i]))); + } + break; + } + } + // TODO: warn on defaulted axis? + } + + SkDEBUGCODE( + // Check for axis specified, but not matched in font. + for (int i = 0; i < requestedAxisCount; ++i) { + SkFourByteTag skTag = requestedAxes[i].fTag; + bool found = false; + for (int j = 0; j < axisDefinitions.count(); ++j) { + if (skTag == axisDefinitions[j].fTag) { + found = true; + break; + } + } + if (!found) { + SkDEBUGF(("Requested font axis not found: %s '%c%c%c%c'\n", + name.c_str(), + (skTag >> 24) & 0xFF, + (skTag >> 16) & 0xFF, + (skTag >> 8) & 0xFF, + (skTag) & 0xFF)); + } + } + ) +} diff --git a/src/ports/SkFontHost_FreeType_common.h b/src/ports/SkFontHost_FreeType_common.h index a9ca42b35f..99b43dccca 100644 --- a/src/ports/SkFontHost_FreeType_common.h +++ b/src/ports/SkFontHost_FreeType_common.h @@ -15,6 +15,8 @@ #include "SkTypeface.h" #include "SkTypes.h" +#include "SkFontMgr.h" + #include <ft2build.h> #include FT_FREETYPE_H @@ -55,6 +57,12 @@ public: bool scanFont(SkStream* stream, int ttcIndex, SkString* name, SkFontStyle* style, bool* isFixedPitch, AxisDefinitions* axes) const; + static void computeAxisValues( + AxisDefinitions axisDefinitions, + const SkFontMgr::FontParameters::Axis* requestedAxis, int requestedAxisCount, + SkFixed* axisValues, + const SkString& name); + private: FT_Face openFace(SkStream* stream, int ttcIndex, FT_Stream ftStream) const; FT_Library fLibrary; diff --git a/src/ports/SkFontHost_mac.cpp b/src/ports/SkFontHost_mac.cpp index 1d68c433bf..c4e80b67c0 100644 --- a/src/ports/SkFontHost_mac.cpp +++ b/src/ports/SkFontHost_mac.cpp @@ -2371,6 +2371,145 @@ protected: return create_from_dataProvider(pr); } + static CFNumberRef get_tag_for_name(CFStringRef name, CFArrayRef ctAxes) { + CFIndex ctAxisCount = CFArrayGetCount(ctAxes); + for (int i = 0; i < ctAxisCount; ++i) { + CFTypeRef ctAxisInfo = CFArrayGetValueAtIndex(ctAxes, i); + if (CFDictionaryGetTypeID() != CFGetTypeID(ctAxisInfo)) { + return nullptr; + } + CFDictionaryRef ctAxisInfoDict = static_cast<CFDictionaryRef>(ctAxisInfo); + + CFTypeRef ctAxisName = CFDictionaryGetValue(ctAxisInfoDict, + kCTFontVariationAxisNameKey); + if (!ctAxisName || CFGetTypeID(ctAxisName) != CFStringGetTypeID()) { + return nullptr; + } + + if (CFEqual(name, ctAxisName)) { + CFTypeRef tag = CFDictionaryGetValue(ctAxisInfoDict, + kCTFontVariationAxisIdentifierKey); + if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) { + return nullptr; + } + return static_cast<CFNumberRef>(tag); + } + } + return nullptr; + } + static CFDictionaryRef get_axes(CGFontRef cg, const FontParameters& params) { + AutoCFRelease<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cg)); + if (!cgAxes) { + return nullptr; + } + CFIndex axisCount = CFArrayGetCount(cgAxes); + + // The CGFont variation data is keyed by name, and lacks the tag. + // The CTFont variation data is keyed by tag, and also has the name. + // We would like to work with CTFont variaitons, but creating a CTFont font with + // CTFont variation dictionary runs into bugs. So use the CTFont variation data + // to match names to tags to create the appropriate CGFont. + AutoCFRelease<CTFontRef> ct(CTFontCreateWithGraphicsFont(cg, 0, nullptr, nullptr)); + AutoCFRelease<CFArrayRef> ctAxes(CTFontCopyVariationAxes(ct)); + if (!ctAxes || CFArrayGetCount(ctAxes) != axisCount) { + return nullptr; + } + + int paramAxisCount; + const FontParameters::Axis* paramAxes = params.getAxes(¶mAxisCount); + + CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + for (int i = 0; i < axisCount; ++i) { + CFTypeRef axisInfo = CFArrayGetValueAtIndex(cgAxes, i); + if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) { + return nullptr; + } + CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo); + + CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisName); + if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) { + return nullptr; + } + + CFNumberRef tagNumber = get_tag_for_name(static_cast<CFStringRef>(axisName), ctAxes); + if (!tagNumber) { + // Could not find a tag to go with the name of this index. + // This would be a bug in CG/CT. + continue; + } + int64_t tagLong; + if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) { + return nullptr; + } + + // The variation axes can be set to any value, but cg will effectively pin them. + // Pin them here to normalize. + CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMinValue); + CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMaxValue); + CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisDefaultValue); + if (!min || CFGetTypeID(min) != CFNumberGetTypeID() || + !max || CFGetTypeID(max) != CFNumberGetTypeID() || + !def || CFGetTypeID(def) != CFNumberGetTypeID()) + { + return nullptr; + } + CFNumberRef minNumber = static_cast<CFNumberRef>(min); + CFNumberRef maxNumber = static_cast<CFNumberRef>(max); + CFNumberRef defNumber = static_cast<CFNumberRef>(def); + double minDouble; + double maxDouble; + double defDouble; + if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) || + !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble) || + !CFNumberGetValue(defNumber, kCFNumberDoubleType, &defDouble)) + { + return nullptr; + } + + double value = defDouble; + for (int j = 0; j < paramAxisCount; ++j) { + if (paramAxes[j].fTag == tagLong) { + value = SkTPin(SkScalarToDouble(paramAxes[j].fStyleValue),minDouble,maxDouble); + break; + } + } + CFNumberRef valueNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, + &value); + CFDictionaryAddValue(dict, axisName, valueNumber); + CFRelease(valueNumber); + } + return dict; + } + SkTypeface* onCreateFromStream(SkStreamAsset* s, const FontParameters& params) const override { + AutoCFRelease<CGDataProviderRef> provider(SkCreateDataProviderFromStream(s)); + if (nullptr == provider) { + return nullptr; + } + AutoCFRelease<CGFontRef> cg(CGFontCreateWithDataProvider(provider)); + if (nullptr == cg) { + return nullptr; + } + + AutoCFRelease<CFDictionaryRef> cgVariations(get_axes(cg, params)); + // The CGFontRef returned by CGFontCreateCopyWithVariations when the passed CGFontRef was + // created from a data provider does not appear to have any ownership of the underlying + // data. The original CGFontRef must be kept alive until the copy will no longer be used. + AutoCFRelease<CGFontRef> cgVariant; + if (cgVariations) { + cgVariant.reset(CGFontCreateCopyWithVariations(cg, cgVariations)); + } else { + cgVariant.reset(cg.detach()); + } + + CTFontRef ct = CTFontCreateWithGraphicsFont(cgVariant, 0, nullptr, nullptr); + if (!ct) { + return nullptr; + } + return NewFromFontRef(ct, cg.detach(), nullptr, true); + } + static CFDictionaryRef get_axes(CGFontRef cg, SkFontData* fontData) { AutoCFRelease<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cg)); if (!cgAxes) { diff --git a/src/ports/SkFontMgr_android.cpp b/src/ports/SkFontMgr_android.cpp index 11244c55ee..afa7873454 100644 --- a/src/ports/SkFontMgr_android.cpp +++ b/src/ports/SkFontMgr_android.cpp @@ -6,7 +6,6 @@ */ #include "SkTypes.h" -#if defined(SK_BUILD_FOR_ANDROID) #include "SkFixed.h" #include "SkFontDescriptor.h" @@ -208,26 +207,27 @@ public: } SkDEBUGCODE( - // Check for axis specified, but not matched in font. - for (int i = 0; i < fontFile.fAxes.count(); ++i) { - SkFourByteTag skTag = fontFile.fAxes[i].fTag; - bool found = false; - for (int j = 0; j < axisDefinitions.count(); ++j) { - if (skTag == axisDefinitions[j].fTag) { - found = true; - break; - } + // Check for axis specified, but not matched in font. + for (int i = 0; i < fontFile.fAxes.count(); ++i) { + SkFourByteTag skTag = fontFile.fAxes[i].fTag; + bool found = false; + for (int j = 0; j < axisDefinitions.count(); ++j) { + if (skTag == axisDefinitions[j].fTag) { + found = true; + break; } - if (!found) { - SkDEBUGF(("Requested font axis not found: %s '%c%c%c%c'\n", - familyName.c_str(), (skTag >> 24) & 0xFF, - (skTag >> 16) & 0xFF, (skTag >> 8) & 0xFF, (skTag)&0xFF)); - } - }) + } + if (!found) { + SkDEBUGF(("Requested font axis not found: %s '%c%c%c%c'\n", + familyName.c_str(), (skTag >> 24) & 0xFF, + (skTag >> 16) & 0xFF, (skTag >> 8) & 0xFF, (skTag)&0xFF)); + } + } + ) - fStyles.push_back().reset(new SkTypeface_AndroidSystem( - pathName, ttcIndex, axisValues.get(), axisDefinitions.count(), style, - isFixedWidth, familyName, lang, variant)); + fStyles.push_back().reset(new SkTypeface_AndroidSystem( + pathName, ttcIndex, axisValues.get(), axisDefinitions.count(), style, + isFixedWidth, familyName, lang, variant)); } } @@ -476,6 +476,29 @@ protected: return new SkTypeface_AndroidStream(data, style, isFixedPitch, name); } + SkTypeface* onCreateFromStream(SkStreamAsset* s, const FontParameters& params) const override { + using Scanner = SkTypeface_FreeType::Scanner; + SkAutoTDelete<SkStreamAsset> stream(s); + bool isFixedPitch; + SkFontStyle style; + SkString name; + Scanner::AxisDefinitions axisDefinitions; + if (!fScanner.scanFont(stream, params.getCollectionIndex(), &name, &style, &isFixedPitch, + &axisDefinitions)) + { + return nullptr; + } + + int paramAxisCount; + const FontParameters::Axis* paramAxes = params.getAxes(¶mAxisCount); + SkAutoSTMalloc<4, SkFixed> axisValues(axisDefinitions.count()); + Scanner::computeAxisValues(axisDefinitions, paramAxes, paramAxisCount, axisValues, name); + + SkFontData* data(new SkFontData(stream.detach(), params.getCollectionIndex(), + axisValues.get(), axisDefinitions.count())); + return new SkTypeface_AndroidStream(data, style, isFixedPitch, name); + } + SkTypeface* onCreateFromFontData(SkFontData* data) const override { SkStreamAsset* stream(data->getStream()); bool isFixedPitch; @@ -589,5 +612,3 @@ SkFontMgr* SkFontMgr_New_Android(const SkFontMgr_Android_CustomFonts* custom) { return new SkFontMgr_Android(custom); } - -#endif//defined(SK_BUILD_FOR_ANDROID) diff --git a/src/ports/SkFontMgr_fontconfig.cpp b/src/ports/SkFontMgr_fontconfig.cpp index 88d4de5929..6a40e5080d 100644 --- a/src/ports/SkFontMgr_fontconfig.cpp +++ b/src/ports/SkFontMgr_fontconfig.cpp @@ -834,6 +834,29 @@ protected: isFixedWidth); } + SkTypeface* onCreateFromStream(SkStreamAsset* s, const FontParameters& params) const override { + using Scanner = SkTypeface_FreeType::Scanner; + SkAutoTDelete<SkStreamAsset> stream(s); + bool isFixedPitch; + SkFontStyle style; + SkString name; + Scanner::AxisDefinitions axisDefinitions; + if (!fScanner.scanFont(stream, params.getCollectionIndex(), &name, &style, &isFixedPitch, + &axisDefinitions)) + { + return nullptr; + } + + int paramAxisCount; + const FontParameters::Axis* paramAxes = params.getAxes(¶mAxisCount); + SkAutoSTMalloc<4, SkFixed> axisValues(axisDefinitions.count()); + Scanner::computeAxisValues(axisDefinitions, paramAxes, paramAxisCount, axisValues, name); + + SkFontData* data(new SkFontData(stream.detach(), params.getCollectionIndex(), + axisValues.get(), axisDefinitions.count())); + return new SkTypeface_stream(data, style, isFixedPitch); + } + SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const override { return this->createFromStream(new SkMemoryStream(data), ttcIndex); } |