From fc497343cbcbd526f77da913ae2feca0e1b1b866 Mon Sep 17 00:00:00 2001 From: Ben Wagner Date: Fri, 24 Feb 2017 11:15:26 -0500 Subject: [PATCH] Add SkTypeface::getVariationDesignPosition. Allow users to query a typeface's position in variation design space. Change-Id: Id7cae439e795b8c9586394f11359fb7fe55e1c0b Reviewed-on: https://skia-review.googlesource.com/8861 Reviewed-by: Mike Reed Commit-Queue: Ben Wagner --- BUILD.gn | 9 +- gm/fontscalerdistortable.cpp | 7 +- gn/core.gni | 1 + include/core/SkFontArguments.h | 79 +++ include/core/SkTypeface.h | 34 +- include/ports/SkFontMgr.h | 51 +- src/core/SkFontMgr.cpp | 8 +- src/core/SkTypeface.cpp | 18 + src/fonts/SkGScalerContext.cpp | 6 + src/fonts/SkGScalerContext.h | 2 + src/fonts/SkRandomScalerContext.cpp | 6 + src/fonts/SkRandomScalerContext.h | 2 + src/fonts/SkTestScalerContext.h | 6 + src/ports/SkFontHost_FreeType.cpp | 584 ++++++++++++-------- src/ports/SkFontHost_FreeType_common.h | 11 +- src/ports/SkFontHost_mac.cpp | 232 +++++--- src/ports/SkFontHost_win.cpp | 19 +- src/ports/SkFontMgr_FontConfigInterface.cpp | 11 +- src/ports/SkFontMgr_android.cpp | 18 +- src/ports/SkFontMgr_android_parser.cpp | 10 +- src/ports/SkFontMgr_android_parser.h | 2 +- src/ports/SkFontMgr_custom.cpp | 14 +- src/ports/SkFontMgr_custom.h | 2 +- src/ports/SkFontMgr_fontconfig.cpp | 11 +- src/ports/SkTypeface_win_dw.h | 12 +- tests/FontMgrAndroidParserTest.cpp | 12 +- tests/FontMgrTest.cpp | 6 + tests/TypefaceTest.cpp | 79 +++ third_party/freetype2/BUILD.gn | 1 + 29 files changed, 843 insertions(+), 410 deletions(-) create mode 100644 include/core/SkFontArguments.h diff --git a/BUILD.gn b/BUILD.gn index af188f8acc..e8d24db8b3 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -335,8 +335,8 @@ optional("fontmgr_android") { enabled = fontmgr_android_enabled deps = [ + ":typeface_freetype", "//third_party/expat", - "//third_party/freetype2", ] sources = [ "src/ports/SkFontMgr_android.cpp", @@ -349,7 +349,7 @@ optional("fontmgr_custom") { enabled = is_linux && skia_use_freetype && !skia_use_fontconfig deps = [ - "//third_party/freetype2", + ":typeface_freetype", ] sources = [ "src/ports/SkFontMgr_custom.cpp", @@ -365,8 +365,8 @@ optional("fontmgr_fontconfig") { enabled = skia_use_freetype && skia_use_fontconfig deps = [ + ":typeface_freetype", "//third_party:fontconfig", - "//third_party/freetype2", ] sources = [ "src/ports/SkFontConfigInterface.cpp", @@ -382,7 +382,7 @@ optional("fontmgr_fuchsia") { enabled = is_fuchsia && skia_use_freetype deps = [ - "//third_party/freetype2", + ":typeface_freetype", ] sources = [ "src/ports/SkFontMgr_custom.cpp", @@ -581,7 +581,6 @@ component("skia") { ":sse41", ":sse42", ":ssse3", - ":typeface_freetype", ":webp", ":xml", ] diff --git a/gm/fontscalerdistortable.cpp b/gm/fontscalerdistortable.cpp index c557ec573d..1823b50efa 100644 --- a/gm/fontscalerdistortable.cpp +++ b/gm/fontscalerdistortable.cpp @@ -49,9 +49,12 @@ protected: 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 } }; + SkFontArguments::VariationPosition::Coordinate coordinates[] = {{tag, styleValue}}; + SkFontArguments::VariationPosition position = + { coordinates, SK_ARRAY_COUNT(coordinates) }; paint.setTypeface(sk_sp(fontMgr->createFromStream( - distortable->duplicate(), SkFontMgr::FontParameters().setAxes(axes, 1)))); + distortable->duplicate(), + SkFontArguments().setVariationDesignPosition(position)))); SkAutoCanvasRestore acr(canvas, true); canvas->translate(SkIntToScalar(30 + i * 100), SkIntToScalar(20)); diff --git a/gn/core.gni b/gn/core.gni index 7dd997ed05..3b71c45f49 100644 --- a/gn/core.gni +++ b/gn/core.gni @@ -398,6 +398,7 @@ skia_core_sources = [ "$_include/core/SkDrawLooper.h", "$_include/core/SkFlattenable.h", "$_include/core/SkFlattenableSerialization.h", + "$_include/core/SkFontArguments.h", "$_include/core/SkFontLCDConfig.h", "$_include/core/SkFontStyle.h", "$_include/core/SkGraphics.h", diff --git a/include/core/SkFontArguments.h b/include/core/SkFontArguments.h new file mode 100644 index 0000000000..473798fb84 --- /dev/null +++ b/include/core/SkFontArguments.h @@ -0,0 +1,79 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkFontAgruments_DEFINED +#define SkFontAgruments_DEFINED + +#include "SkScalar.h" +#include "SkTypes.h" + +/** Represents a set of actual arguments for a font. */ +struct SkFontArguments { + struct VariationPosition { + struct Coordinate { + SkFourByteTag axis; + SkScalar value; + }; + const Coordinate* coordinates; + int coordinateCount; + }; + // deprecated, use VariationCoordinate instead + struct Axis { + SkFourByteTag fTag; + SkScalar fStyleValue; + }; + + SkFontArguments() : fCollectionIndex(0), fVariationDesignPosition{nullptr, 0} {} + + /** 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. + */ + SkFontArguments& setCollectionIndex(int collectionIndex) { + fCollectionIndex = collectionIndex; + return *this; + } + + // deprecated, use setVariationDesignPosition instead. + SkFontArguments& setAxes(const Axis* axes, int axisCount) { + fVariationDesignPosition.coordinates = + reinterpret_cast(axes); + fVariationDesignPosition.coordinateCount = axisCount; + return *this; + } + + /** Specify a position in the variation design space. + * + * Any axis not specified will use the default value. + * Any specified axis not actually present in the font will be ignored. + * + * @param position not copied. The value must remain valid for life of SkFontArguments. + */ + SkFontArguments& setVariationDesignPosition(VariationPosition position) { + fVariationDesignPosition.coordinates = position.coordinates; + fVariationDesignPosition.coordinateCount = position.coordinateCount; + return *this; + } + + int getCollectionIndex() const { + return fCollectionIndex; + } + // deprecated, use getVariationDesignPosition instead. + const Axis* getAxes(int* axisCount) const { + *axisCount = fVariationDesignPosition.coordinateCount; + return reinterpret_cast(fVariationDesignPosition.coordinates); + } + VariationPosition getVariationDesignPosition() const { + return fVariationDesignPosition; + } +private: + int fCollectionIndex; + VariationPosition fVariationDesignPosition; +}; + +#endif diff --git a/include/core/SkTypeface.h b/include/core/SkTypeface.h index 51d5a0b11c..24693a9af1 100644 --- a/include/core/SkTypeface.h +++ b/include/core/SkTypeface.h @@ -11,6 +11,7 @@ #include "../private/SkBitmaskEnum.h" #include "../private/SkOnce.h" #include "../private/SkWeakRefCnt.h" +#include "SkFontArguments.h" #include "SkFontStyle.h" #include "SkRect.h" #include "SkString.h" @@ -77,6 +78,20 @@ public: */ bool isFixedPitch() const { return fIsFixedPitch; } + /** Copy into 'coordinates' (allocated by the caller) the design variation coordinates. + * + * @param coordinates the buffer into which to write the design variation coordinates. + * @param coordinateCount the number of entries available through 'coordinates'. + * + * @return The number of axes, or -1 if there is an error. + * If 'coordinates != nullptr' and 'coordinateCount >= numAxes' then 'coordinates' will be + * filled with the variation coordinates describing the position of this typeface in design + * variation space. It is possible the number of axes can be retrieved but actual position + * cannot. + */ + int getVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], + int coordinateCount) const; + /** Return a 32bit value for this typeface, unique for the underlying font data. Will never return 0. */ @@ -96,16 +111,16 @@ public: /** Returns the default typeface, which is never nullptr. */ static sk_sp MakeDefault(Style style = SkTypeface::kNormal); - /** Creates a new reference to the typeface that most closely matches the - requested familyName and fontStyle. This method allows extended font - face specifiers as in the SkFontStyle type. Will never return null. + /** Creates a new reference to the typeface that most closely matches the + requested familyName and fontStyle. This method allows extended font + face specifiers as in the SkFontStyle type. Will never return null. - @param familyName May be NULL. The name of the font family. - @param fontStyle The style of the typeface. - @return reference to the closest-matching typeface. Call must call + @param familyName May be NULL. The name of the font family. + @param fontStyle The style of the typeface. + @return reference to the closest-matching typeface. Call must call unref() when they are done. */ - static sk_sp MakeFromName(const char familyName[], SkFontStyle fontStyle); + static sk_sp MakeFromName(const char familyName[], SkFontStyle fontStyle); /** Return the typeface that most closely matches the requested typeface and style. Use this to pick a new style from the same family of the existing typeface. @@ -341,6 +356,11 @@ protected: // TODO: make pure virtual. virtual std::unique_ptr onMakeFontData() const; + // TODO: make pure virtual. + virtual int onGetVariationDesignPosition( + SkFontArguments::VariationPosition::Coordinate coordinates[], + int coordinateCount) const; + virtual void onGetFontDescriptor(SkFontDescriptor*, bool* isLocal) const = 0; virtual int onCharsToGlyphs(const void* chars, Encoding, SkGlyphID glyphs[], diff --git a/include/ports/SkFontMgr.h b/include/ports/SkFontMgr.h index 4103e8747e..b5879d35b8 100644 --- a/include/ports/SkFontMgr.h +++ b/include/ports/SkFontMgr.h @@ -8,9 +8,9 @@ #ifndef SkFontMgr_DEFINED #define SkFontMgr_DEFINED +#include "SkFontArguments.h" #include "SkFontStyle.h" #include "SkRefCnt.h" -#include "SkScalar.h" #include "SkTypes.h" class SkData; @@ -102,51 +102,10 @@ 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; - }; + // deprecated, use SkFontArguments instead. + using FontParameters = SkFontArguments; /* Experimental, API subject to change. */ - SkTypeface* createFromStream(SkStreamAsset*, const FontParameters&) const; + SkTypeface* createFromStream(SkStreamAsset*, const SkFontArguments&) const; /** * Create a typeface from the specified font data. @@ -187,7 +146,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* onCreateFromStream(SkStreamAsset*, const SkFontArguments&) const; virtual SkTypeface* onCreateFromFontData(std::unique_ptr) const; virtual SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const = 0; diff --git a/src/core/SkFontMgr.cpp b/src/core/SkFontMgr.cpp index 03ac2eba83..8ce0b39682 100644 --- a/src/core/SkFontMgr.cpp +++ b/src/core/SkFontMgr.cpp @@ -132,11 +132,11 @@ SkTypeface* SkFontMgr::createFromStream(SkStreamAsset* stream, int ttcIndex) con return this->onCreateFromStream(stream, ttcIndex); } -SkTypeface* SkFontMgr::createFromStream(SkStreamAsset* stream, const FontParameters& params) const { +SkTypeface* SkFontMgr::createFromStream(SkStreamAsset* stream, const SkFontArguments& args) const { if (nullptr == stream) { return nullptr; } - return this->onCreateFromStream(stream, params); + return this->onCreateFromStream(stream, args); } SkTypeface* SkFontMgr::createFromFontData(std::unique_ptr data) const { @@ -147,8 +147,8 @@ SkTypeface* SkFontMgr::createFromFontData(std::unique_ptr data) cons } // 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()); +SkTypeface* SkFontMgr::onCreateFromStream(SkStreamAsset* stream, const SkFontArguments& args) const{ + return this->createFromStream(stream, args.getCollectionIndex()); } // This implementation is temporary until it can be made pure virtual. diff --git a/src/core/SkTypeface.cpp b/src/core/SkTypeface.cpp index 70c0c76d06..ff473c9665 100644 --- a/src/core/SkTypeface.cpp +++ b/src/core/SkTypeface.cpp @@ -73,6 +73,11 @@ protected: SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override { return new EmptyLocalizedStrings; } + int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], + int coordinateCount) const override + { + return 0; + } int onGetTableTags(SkFontTableTag tags[]) const override { return 0; } size_t onGetTableData(SkFontTableTag, size_t, size_t, void*) const override { return 0; @@ -202,6 +207,12 @@ sk_sp SkTypeface::MakeDeserialize(SkStream* stream) { /////////////////////////////////////////////////////////////////////////////// +int SkTypeface::getVariationDesignPosition( + SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) const +{ + return this->onGetVariationDesignPosition(coordinates, coordinateCount); +} + int SkTypeface::countTables() const { return this->onGetTableTags(nullptr); } @@ -239,6 +250,13 @@ std::unique_ptr SkTypeface::onMakeFontData() const { return skstd::make_unique(std::move(stream), index, nullptr, 0); }; +// This implementation is temporary until this method can be made pure virtual. +int SkTypeface::onGetVariationDesignPosition( + SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) const +{ + return -1; +} + int SkTypeface::charsToGlyphs(const void* chars, Encoding encoding, uint16_t glyphs[], int glyphCount) const { if (glyphCount <= 0) { diff --git a/src/fonts/SkGScalerContext.cpp b/src/fonts/SkGScalerContext.cpp index 8a0ac4cd64..3b9c660636 100644 --- a/src/fonts/SkGScalerContext.cpp +++ b/src/fonts/SkGScalerContext.cpp @@ -195,6 +195,12 @@ SkTypeface::LocalizedStrings* SkGTypeface::onCreateFamilyNameIterator() const { return fProxy->createFamilyNameIterator(); } +int SkGTypeface::onGetVariationDesignPosition( + SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) const +{ + return fProxy->onGetVariationDesignPosition(coordinates, coordinateCount); +} + int SkGTypeface::onGetTableTags(SkFontTableTag tags[]) const { return fProxy->getTableTags(tags); } diff --git a/src/fonts/SkGScalerContext.h b/src/fonts/SkGScalerContext.h index 3eb25a81da..562513c7bc 100644 --- a/src/fonts/SkGScalerContext.h +++ b/src/fonts/SkGScalerContext.h @@ -37,6 +37,8 @@ protected: void onGetFamilyName(SkString* familyName) const override; SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override; + int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], + int coordinateCount) const override; int onGetTableTags(SkFontTableTag tags[]) const override; size_t onGetTableData(SkFontTableTag, size_t offset, size_t length, void* data) const override; diff --git a/src/fonts/SkRandomScalerContext.cpp b/src/fonts/SkRandomScalerContext.cpp index 55c7fb30d4..3a292dcd79 100644 --- a/src/fonts/SkRandomScalerContext.cpp +++ b/src/fonts/SkRandomScalerContext.cpp @@ -242,6 +242,12 @@ SkTypeface::LocalizedStrings* SkRandomTypeface::onCreateFamilyNameIterator() con return fProxy->createFamilyNameIterator(); } +int SkRandomTypeface::onGetVariationDesignPosition( + SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) const +{ + return fProxy->onGetVariationDesignPosition(coordinates, coordinateCount); +} + int SkRandomTypeface::onGetTableTags(SkFontTableTag tags[]) const { return fProxy->getTableTags(tags); } diff --git a/src/fonts/SkRandomScalerContext.h b/src/fonts/SkRandomScalerContext.h index 076689d93a..c84b76470e 100644 --- a/src/fonts/SkRandomScalerContext.h +++ b/src/fonts/SkRandomScalerContext.h @@ -42,6 +42,8 @@ protected: void onGetFamilyName(SkString* familyName) const override; SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override; + int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], + int coordinateCount) const override; int onGetTableTags(SkFontTableTag tags[]) const override; size_t onGetTableData(SkFontTableTag, size_t offset, size_t length, void* data) const override; diff --git a/src/fonts/SkTestScalerContext.h b/src/fonts/SkTestScalerContext.h index 217e57c2a4..20cce91ab9 100644 --- a/src/fonts/SkTestScalerContext.h +++ b/src/fonts/SkTestScalerContext.h @@ -91,6 +91,12 @@ protected: void onGetFamilyName(SkString* familyName) const override; SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override; + int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], + int coordinateCount) const override + { + return 0; + } + int onGetTableTags(SkFontTableTag tags[]) const override { return 0; } diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp index 47790f3644..1feea4d198 100644 --- a/src/ports/SkFontHost_FreeType.cpp +++ b/src/ports/SkFontHost_FreeType.cpp @@ -27,9 +27,6 @@ #include "SkTemplates.h" #include -#if defined(SK_CAN_USE_DLOPEN) -#include -#endif #include #include FT_ADVANCES_H #include FT_BITMAP_H @@ -44,6 +41,20 @@ #include FT_TYPE1_TABLES_H #include FT_XFREE86_H +// SK_FREETYPE_MINIMUM_RUNTIME_VERSION 0x +// Flag SK_FREETYPE_DLOPEN: also try dlopen to get newer features. +#define SK_FREETYPE_DLOPEN (0x1) +#ifndef SK_FREETYPE_MINIMUM_RUNTIME_VERSION +# if defined(SK_BUILD_FOR_ANDROID_FRAMEWORK) || defined (GOOGLE3) +# define SK_FREETYPE_MINIMUM_RUNTIME_VERSION (((FREETYPE_MAJOR) << 24) | ((FREETYPE_MINOR) << 16) | ((FREETYPE_PATCH) << 8)) +# else +# define SK_FREETYPE_MINIMUM_RUNTIME_VERSION ((2 << 24) | (3 << 16) | (11 << 8) | (SK_FREETYPE_DLOPEN)) +# endif +#endif +#if SK_FREETYPE_MINIMUM_RUNTIME_VERSION & SK_FREETYPE_DLOPEN +# include +#endif + // FT_LOAD_COLOR and the corresponding FT_Pixel_Mode::FT_PIXEL_MODE_BGRA // were introduced in FreeType 2.5.0. // The following may be removed once FreeType 2.5.0 is required to build. @@ -85,12 +96,37 @@ FT_MemoryRec_ gFTMemory = { nullptr, sk_ft_alloc, sk_ft_free, sk_ft_realloc }; class FreeTypeLibrary : SkNoncopyable { public: - FreeTypeLibrary() : fLibrary(nullptr), fIsLCDSupported(false), fLCDExtra(0) { + FreeTypeLibrary() + : fGetVarDesignCoordinates(nullptr) + , fLibrary(nullptr) + , fIsLCDSupported(false) + , fLCDExtra(0) + { if (FT_New_Library(&gFTMemory, &fLibrary)) { return; } FT_Add_Default_Modules(fLibrary); +#if SK_FREETYPE_MINIMUM_RUNTIME_VERSION >= 0x02070100 + fGetVarDesignCoordinates = FT_Get_Var_Design_Coordinates; +#elif SK_FREETYPE_MINIMUM_RUNTIME_VERSION & SK_FREETYPE_DLOPEN + FT_Int major, minor, patch; + FT_Library_Version(fLibrary, &major, &minor, &patch); + if (major > 2 || ((major == 2 && minor > 7) || (major == 2 && minor == 7 && patch >= 1))) { + //The FreeType library is already loaded, so symbols are available in process. + void* self = dlopen(nullptr, RTLD_LAZY); + if (self) { + // The following cast is non-standard, but safe for POSIX. + // Cannot write this with reinterpret_cast, because clang has not implemented DR573. + // See http://clang.llvm.org/cxx_dr_status.html . + //*reinterpret_cast(&fGetVarDesignCoordinates) = + // dlsym(self, "FT_Get_Var_Design_Coordinates"); + *(void**)(&fGetVarDesignCoordinates) = dlsym(self, "FT_Get_Var_Design_Coordinates"); + dlclose(self); + } + } +#endif + // Setup LCD filtering. This reduces color fringes for LCD smoothed glyphs. // Default { 0x10, 0x40, 0x70, 0x40, 0x10 } adds up to 0x110, simulating ink spread. // SetLcdFilter must be called before SetLcdFilterWeights. @@ -102,23 +138,26 @@ public: // Adds to 0x110 simulating ink spread, but provides better results than default. static unsigned char gGaussianLikeHeavyWeights[] = { 0x1A, 0x43, 0x56, 0x43, 0x1A, }; -# if SK_FONTHOST_FREETYPE_RUNTIME_VERSION > 0x020400 +# if SK_FREETYPE_MINIMUM_RUNTIME_VERSION >= 0x02040000 FT_Library_SetLcdFilterWeights(fLibrary, gGaussianLikeHeavyWeights); -# elif SK_CAN_USE_DLOPEN == 1 +# elif SK_FREETYPE_MINIMUM_RUNTIME_VERSION & SK_FREETYPE_DLOPEN //The FreeType library is already loaded, so symbols are available in process. void* self = dlopen(nullptr, RTLD_LAZY); if (self) { FT_Library_SetLcdFilterWeightsProc setLcdFilterWeights; - //The following cast is non-standard, but safe for POSIX. - *reinterpret_cast(&setLcdFilterWeights) = - dlsym(self, "FT_Library_SetLcdFilterWeights"); + // The following cast is non-standard, but safe for POSIX. + // Cannot write this with reinterpret_cast, because clang has not implemented DR573. + // See http://clang.llvm.org/cxx_dr_status.html . + //*reinterpret_cast(&setLcdFilterWeights) = + // dlsym(self, "FT_Library_SetLcdFilterWeights"); + *(void**)(&setLcdFilterWeights) = dlsym(self, "FT_Library_SetLcdFilterWeights"); dlclose(self); if (setLcdFilterWeights) { setLcdFilterWeights(fLibrary, gGaussianLikeHeavyWeights); } } -# endif +# endif #endif } } @@ -132,6 +171,13 @@ public: bool isLCDSupported() { return fIsLCDSupported; } int lcdExtra() { return fLCDExtra; } + // FT_Get_{MM,Var}_{Blend,Design}_Coordinates were added in FreeType 2.7.1. + // Prior to this there was no way to get the coordinates out of the FT_Face. + // This wasn't too bad because you needed to specify them anyway, and the clamp was provided. + // However, this doesn't work when face_index specifies named variations as introduced in 2.6.1. + using FT_Get_Var_Blend_CoordinatesProc = FT_Error (*)(FT_Face, FT_UInt, FT_Fixed*); + FT_Get_Var_Blend_CoordinatesProc fGetVarDesignCoordinates; + private: FT_Library fLibrary; bool fIsLCDSupported; @@ -144,7 +190,8 @@ private: // OpenSuse >= 11.4 (previous deprecated January 2012 / Nov 2013 for Evergreen 11.2) // Fedora >= 14 (good) // Android >= Gingerbread (good) - typedef FT_Error (*FT_Library_SetLcdFilterWeightsProc)(FT_Library, unsigned char*); + // RHEL >= 7 (6 has 2.3.11, EOL Nov 2020, Phase 3 May 2017) + using FT_Library_SetLcdFilterWeightsProc = FT_Error (*)(FT_Library, unsigned char*); }; struct SkFaceRec; @@ -183,6 +230,223 @@ static void unref_ft_library() { } } +/////////////////////////////////////////////////////////////////////////// + +struct SkFaceRec { + SkFaceRec* fNext; + std::unique_ptr> fFace; + FT_StreamRec fFTStream; + std::unique_ptr fSkStream; + uint32_t fRefCnt; + uint32_t fFontID; + + // FreeType prior to 2.7.1 does not implement retreiving variation design metrics. + // Cache the variation design metrics used to create the font if the user specifies them. + SkAutoSTMalloc<4, SkFixed> fAxes; + int fAxesCount; + + // FreeType from 2.6.1 (14d6b5d7) until 2.7.0 (ee3f36f6b38) uses font_index for both font index + // and named variation index on input, but masks the named variation index part on output. + // Manually keep track of when a named variation is requested for 2.6.1 until 2.7.1. + bool fNamedVariationSpecified; + + SkFaceRec(std::unique_ptr stream, uint32_t fontID); +}; + +extern "C" { + static unsigned long sk_ft_stream_io(FT_Stream ftStream, + unsigned long offset, + unsigned char* buffer, + unsigned long count) + { + SkStreamAsset* stream = static_cast(ftStream->descriptor.pointer); + + if (count) { + if (!stream->seek(offset)) { + return 0; + } + count = stream->read(buffer, count); + } + return count; + } + + static void sk_ft_stream_close(FT_Stream) {} +} + +SkFaceRec::SkFaceRec(std::unique_ptr stream, uint32_t fontID) + : fNext(nullptr), fSkStream(std::move(stream)), fRefCnt(1), fFontID(fontID) + , fAxesCount(0), fNamedVariationSpecified(false) +{ + sk_bzero(&fFTStream, sizeof(fFTStream)); + fFTStream.size = fSkStream->getLength(); + fFTStream.descriptor.pointer = fSkStream.get(); + fFTStream.read = sk_ft_stream_io; + fFTStream.close = sk_ft_stream_close; +} + +static void ft_face_setup_axes(SkFaceRec* rec, const SkFontData& data) { + if (!(rec->fFace->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS)) { + return; + } + + // If a named variation is requested, don't overwrite the named variation's position. + if (data.getIndex() > 0xFFFF) { + rec->fNamedVariationSpecified = true; + return; + } + + SkDEBUGCODE( + FT_MM_Var* variations = nullptr; + if (FT_Get_MM_Var(rec->fFace.get(), &variations)) { + SkDEBUGF(("INFO: font %s claims variations, but none found.\n", + rec->fFace->family_name)); + return; + } + SkAutoFree autoFreeVariations(variations); + + if (static_cast(data.getAxisCount()) != variations->num_axis) { + SkDEBUGF(("INFO: font %s has %d variations, but %d were specified.\n", + rec->fFace->family_name, variations->num_axis, data.getAxisCount())); + return; + } + ) + + SkAutoSTMalloc<4, FT_Fixed> coords(data.getAxisCount()); + for (int i = 0; i < data.getAxisCount(); ++i) { + coords[i] = data.getAxis()[i]; + } + if (FT_Set_Var_Design_Coordinates(rec->fFace.get(), data.getAxisCount(), coords.get())) { + SkDEBUGF(("INFO: font %s has variations, but specified variations could not be set.\n", + rec->fFace->family_name)); + return; + } + + rec->fAxesCount = data.getAxisCount(); + rec->fAxes.reset(rec->fAxesCount); + for (int i = 0; i < rec->fAxesCount; ++i) { + rec->fAxes[i] = data.getAxis()[i]; + } +} + +// Will return nullptr on failure +// Caller must lock gFTMutex before calling this function. +static SkFaceRec* ref_ft_face(const SkTypeface* typeface) { + gFTMutex.assertHeld(); + + const SkFontID fontID = typeface->uniqueID(); + SkFaceRec* cachedRec = gFaceRecHead; + while (cachedRec) { + if (cachedRec->fFontID == fontID) { + SkASSERT(cachedRec->fFace); + cachedRec->fRefCnt += 1; + return cachedRec; + } + cachedRec = cachedRec->fNext; + } + + std::unique_ptr data = typeface->makeFontData(); + if (nullptr == data || !data->hasStream()) { + return nullptr; + } + + std::unique_ptr rec(new SkFaceRec(data->detachStream(), fontID)); + + FT_Open_Args args; + memset(&args, 0, sizeof(args)); + const void* memoryBase = rec->fSkStream->getMemoryBase(); + if (memoryBase) { + args.flags = FT_OPEN_MEMORY; + args.memory_base = (const FT_Byte*)memoryBase; + args.memory_size = rec->fSkStream->getLength(); + } else { + args.flags = FT_OPEN_STREAM; + args.stream = &rec->fFTStream; + } + + { + FT_Face rawFace; + FT_Error err = FT_Open_Face(gFTLibrary->library(), &args, data->getIndex(), &rawFace); + if (err) { + SkDEBUGF(("ERROR: unable to open font '%x'\n", fontID)); + return nullptr; + } + rec->fFace.reset(rawFace); + } + SkASSERT(rec->fFace); + + ft_face_setup_axes(rec.get(), *data); + + // FreeType will set the charmap to the "most unicode" cmap if it exists. + // If there are no unicode cmaps, the charmap is set to nullptr. + // However, "symbol" cmaps should also be considered "fallback unicode" cmaps + // because they are effectively private use area only (even if they aren't). + // This is the last on the fallback list at + // https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6cmap.html + if (!rec->fFace->charmap) { + FT_Select_Charmap(rec->fFace.get(), FT_ENCODING_MS_SYMBOL); + } + + rec->fNext = gFaceRecHead; + gFaceRecHead = rec.get(); + return rec.release(); +} + +// Caller must lock gFTMutex before calling this function. +static void unref_ft_face(SkFaceRec* faceRec) { + gFTMutex.assertHeld(); + + SkFaceRec* rec = gFaceRecHead; + SkFaceRec* prev = nullptr; + while (rec) { + SkFaceRec* next = rec->fNext; + if (rec->fFace == faceRec->fFace) { + if (--rec->fRefCnt == 0) { + if (prev) { + prev->fNext = next; + } else { + gFaceRecHead = next; + } + delete rec; + } + return; + } + prev = rec; + rec = next; + } + SkDEBUGFAIL("shouldn't get here, face not in list"); +} + +class AutoFTAccess { +public: + AutoFTAccess(const SkTypeface* tf) : fFaceRec(nullptr) { + gFTMutex.acquire(); + if (!ref_ft_library()) { + sk_throw(); + } + fFaceRec = ref_ft_face(tf); + } + + ~AutoFTAccess() { + if (fFaceRec) { + unref_ft_face(fFaceRec); + } + unref_ft_library(); + gFTMutex.release(); + } + + FT_Face face() { return fFaceRec ? fFaceRec->fFace.get() : nullptr; } + int getAxesCount() { return fFaceRec ? fFaceRec->fAxesCount : 0; } + SkFixed* getAxes() { return fFaceRec ? fFaceRec->fAxes.get() : nullptr; } + bool isNamedVariationSpecified() { + return fFaceRec ? fFaceRec->fNamedVariationSpecified : false; + } + +private: + SkFaceRec* fFaceRec; +}; + +/////////////////////////////////////////////////////////////////////////// + class SkScalerContext_FreeType : public SkScalerContext_FreeType_Base { public: SkScalerContext_FreeType(sk_sp, @@ -205,7 +469,10 @@ protected: SkUnichar generateGlyphToChar(uint16_t glyph) override; private: - FT_Face fFace; // Shared face from gFaceRecHead. + using UnrefFTFace = SkFunctionWrapper; + std::unique_ptr fFaceRec; + + FT_Face fFace; // Borrowed face from gFaceRecHead. FT_Size fFTSize; // The size on the fFace for this scaler. FT_Int fStrikeIndex; @@ -235,192 +502,6 @@ private: bool shouldSubpixelBitmap(const SkGlyph&, const SkMatrix&); }; -/////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////// - -struct SkFaceRec { - SkFaceRec* fNext; - FT_Face fFace; - FT_StreamRec fFTStream; - std::unique_ptr fSkStream; - uint32_t fRefCnt; - uint32_t fFontID; - - SkFaceRec(std::unique_ptr stream, uint32_t fontID); -}; - -extern "C" { - static unsigned long sk_ft_stream_io(FT_Stream ftStream, - unsigned long offset, - unsigned char* buffer, - unsigned long count) - { - SkStreamAsset* stream = static_cast(ftStream->descriptor.pointer); - - if (count) { - if (!stream->seek(offset)) { - return 0; - } - count = stream->read(buffer, count); - } - return count; - } - - static void sk_ft_stream_close(FT_Stream) {} -} - -SkFaceRec::SkFaceRec(std::unique_ptr stream, uint32_t fontID) - : fNext(nullptr), fSkStream(std::move(stream)), fRefCnt(1), fFontID(fontID) -{ - sk_bzero(&fFTStream, sizeof(fFTStream)); - fFTStream.size = fSkStream->getLength(); - fFTStream.descriptor.pointer = fSkStream.get(); - fFTStream.read = sk_ft_stream_io; - fFTStream.close = sk_ft_stream_close; -} - -static void ft_face_setup_axes(FT_Face face, const SkFontData& data) { - if (!(face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS)) { - return; - } - - SkDEBUGCODE( - FT_MM_Var* variations = nullptr; - if (FT_Get_MM_Var(face, &variations)) { - SkDEBUGF(("INFO: font %s claims variations, but none found.\n", face->family_name)); - return; - } - SkAutoFree autoFreeVariations(variations); - - if (static_cast(data.getAxisCount()) != variations->num_axis) { - SkDEBUGF(("INFO: font %s has %d variations, but %d were specified.\n", - face->family_name, variations->num_axis, data.getAxisCount())); - return; - } - ) - - SkAutoSTMalloc<4, FT_Fixed> coords(data.getAxisCount()); - for (int i = 0; i < data.getAxisCount(); ++i) { - coords[i] = data.getAxis()[i]; - } - if (FT_Set_Var_Design_Coordinates(face, data.getAxisCount(), coords.get())) { - SkDEBUGF(("INFO: font %s has variations, but specified variations could not be set.\n", - face->family_name)); - return; - } -} - -// Will return 0 on failure -// Caller must lock gFTMutex before calling this function. -static FT_Face ref_ft_face(const SkTypeface* typeface) { - gFTMutex.assertHeld(); - - const SkFontID fontID = typeface->uniqueID(); - SkFaceRec* rec = gFaceRecHead; - while (rec) { - if (rec->fFontID == fontID) { - SkASSERT(rec->fFace); - rec->fRefCnt += 1; - return rec->fFace; - } - rec = rec->fNext; - } - - std::unique_ptr data = typeface->makeFontData(); - if (nullptr == data || !data->hasStream()) { - return nullptr; - } - - rec = new SkFaceRec(data->detachStream(), fontID); - - FT_Open_Args args; - memset(&args, 0, sizeof(args)); - const void* memoryBase = rec->fSkStream->getMemoryBase(); - if (memoryBase) { - args.flags = FT_OPEN_MEMORY; - args.memory_base = (const FT_Byte*)memoryBase; - args.memory_size = rec->fSkStream->getLength(); - } else { - args.flags = FT_OPEN_STREAM; - args.stream = &rec->fFTStream; - } - - FT_Error err = FT_Open_Face(gFTLibrary->library(), &args, data->getIndex(), &rec->fFace); - if (err) { - SkDEBUGF(("ERROR: unable to open font '%x'\n", fontID)); - delete rec; - return nullptr; - } - SkASSERT(rec->fFace); - - ft_face_setup_axes(rec->fFace, *data); - - // FreeType will set the charmap to the "most unicode" cmap if it exists. - // If there are no unicode cmaps, the charmap is set to nullptr. - // However, "symbol" cmaps should also be considered "fallback unicode" cmaps - // because they are effectively private use area only (even if they aren't). - // This is the last on the fallback list at - // https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6cmap.html - if (!rec->fFace->charmap) { - FT_Select_Charmap(rec->fFace, FT_ENCODING_MS_SYMBOL); - } - - rec->fNext = gFaceRecHead; - gFaceRecHead = rec; - return rec->fFace; -} - -// Caller must lock gFTMutex before calling this function. -extern void unref_ft_face(FT_Face face); -void unref_ft_face(FT_Face face) { - gFTMutex.assertHeld(); - - SkFaceRec* rec = gFaceRecHead; - SkFaceRec* prev = nullptr; - while (rec) { - SkFaceRec* next = rec->fNext; - if (rec->fFace == face) { - if (--rec->fRefCnt == 0) { - if (prev) { - prev->fNext = next; - } else { - gFaceRecHead = next; - } - FT_Done_Face(face); - delete rec; - } - return; - } - prev = rec; - rec = next; - } - SkDEBUGFAIL("shouldn't get here, face not in list"); -} - -class AutoFTAccess { -public: - AutoFTAccess(const SkTypeface* tf) : fFace(nullptr) { - gFTMutex.acquire(); - if (!ref_ft_library()) { - sk_throw(); - } - fFace = ref_ft_face(tf); - } - - ~AutoFTAccess() { - if (fFace) { - unref_ft_face(fFace); - } - unref_ft_library(); - gFTMutex.release(); - } - - FT_Face face() { return fFace; } - -private: - FT_Face fFace; -}; - /////////////////////////////////////////////////////////////////////////// static bool canEmbed(FT_Face face) { @@ -746,11 +827,10 @@ SkScalerContext_FreeType::SkScalerContext_FreeType(sk_sp typeface, sk_throw(); } + fFaceRec.reset(ref_ft_face(this->getTypeface())); + // load the font file - using UnrefFTFace = SkFunctionWrapper, unref_ft_face>; - using FT_FaceRef = skstd::remove_pointer_t; - std::unique_ptr ftFace(ref_ft_face(this->getTypeface())); - if (nullptr == ftFace) { + if (nullptr == fFaceRec) { SkDEBUGF(("Could not create FT_Face.\n")); return; } @@ -836,11 +916,11 @@ SkScalerContext_FreeType::SkScalerContext_FreeType(sk_sp typeface, } using DoneFTSize = SkFunctionWrapper, FT_Done_Size>; - std::unique_ptr, DoneFTSize> ftSize([&ftFace]() -> FT_Size { + std::unique_ptr, DoneFTSize> ftSize([this]() -> FT_Size { FT_Size size; - FT_Error err = FT_New_Size(ftFace.get(), &size); + FT_Error err = FT_New_Size(fFaceRec->fFace.get(), &size); if (err != 0) { - SkDEBUGF(("FT_New_Size(%s) returned 0x%x.\n", ftFace->family_name, err)); + SkDEBUGF(("FT_New_Size(%s) returned 0x%x.\n", fFaceRec->fFace->family_name, err)); return nullptr; } return size; @@ -852,36 +932,37 @@ SkScalerContext_FreeType::SkScalerContext_FreeType(sk_sp typeface, FT_Error err = FT_Activate_Size(ftSize.get()); if (err != 0) { - SkDEBUGF(("FT_Activate_Size(%s) returned 0x%x.\n", ftFace->family_name, err)); + SkDEBUGF(("FT_Activate_Size(%s) returned 0x%x.\n", fFaceRec->fFace->family_name, err)); return; } - if (FT_IS_SCALABLE(ftFace)) { - err = FT_Set_Char_Size(ftFace.get(), scaleX, scaleY, 72, 72); + if (FT_IS_SCALABLE(fFaceRec->fFace)) { + err = FT_Set_Char_Size(fFaceRec->fFace.get(), scaleX, scaleY, 72, 72); if (err != 0) { SkDEBUGF(("FT_Set_CharSize(%s, %f, %f) returned 0x%x.\n", - ftFace->family_name, fScale.fX, fScale.fY, err)); + fFaceRec->fFace->family_name, fScale.fX, fScale.fY, err)); return; } - } else if (FT_HAS_FIXED_SIZES(ftFace)) { - fStrikeIndex = chooseBitmapStrike(ftFace.get(), scaleY); + } else if (FT_HAS_FIXED_SIZES(fFaceRec->fFace)) { + fStrikeIndex = chooseBitmapStrike(fFaceRec->fFace.get(), scaleY); if (fStrikeIndex == -1) { - SkDEBUGF(("No glyphs for font \"%s\" size %f.\n", ftFace->family_name, fScale.fY)); + SkDEBUGF(("No glyphs for font \"%s\" size %f.\n", + fFaceRec->fFace->family_name, fScale.fY)); return; } - err = FT_Select_Size(ftFace.get(), fStrikeIndex); + err = FT_Select_Size(fFaceRec->fFace.get(), fStrikeIndex); if (err != 0) { SkDEBUGF(("FT_Select_Size(%s, %d) returned 0x%x.\n", - ftFace->family_name, fStrikeIndex, err)); + fFaceRec->fFace->family_name, fStrikeIndex, err)); fStrikeIndex = -1; return; } // A non-ideal size was picked, so recompute the matrix. // This adjusts for the difference between FT_Set_Char_Size and FT_Select_Size. - fMatrix22Scalar.preScale(fScale.x() / ftFace->size->metrics.x_ppem, - fScale.y() / ftFace->size->metrics.y_ppem); + fMatrix22Scalar.preScale(fScale.x() / fFaceRec->fFace->size->metrics.x_ppem, + fScale.y() / fFaceRec->fFace->size->metrics.y_ppem); fMatrix22.xx = SkScalarToFixed(fMatrix22Scalar.getScaleX()); fMatrix22.xy = SkScalarToFixed(-fMatrix22Scalar.getSkewX()); fMatrix22.yx = SkScalarToFixed(-fMatrix22Scalar.getSkewY()); @@ -898,12 +979,12 @@ SkScalerContext_FreeType::SkScalerContext_FreeType(sk_sp typeface, // Force this flag off for bitmap only fonts. fLoadGlyphFlags &= ~FT_LOAD_NO_BITMAP; } else { - SkDEBUGF(("Unknown kind of font \"%s\" size %f.\n", fFace->family_name, fScale.fY)); + SkDEBUGF(("Unknown kind of font \"%s\" size %f.\n", fFaceRec->fFace->family_name, fScale.fY)); return; } fFTSize = ftSize.release(); - fFace = ftFace.release(); + fFace = fFaceRec->fFace.get(); fDoLinearMetrics = linearMetrics; } @@ -914,9 +995,7 @@ SkScalerContext_FreeType::~SkScalerContext_FreeType() { FT_Done_Size(fFTSize); } - if (fFace != nullptr) { - unref_ft_face(fFace); - } + fFaceRec = nullptr; unref_ft_library(); } @@ -1500,6 +1579,51 @@ SkTypeface::LocalizedStrings* SkTypeface_FreeType::onCreateFamilyNameIterator() return nameIter; } +int SkTypeface_FreeType::onGetVariationDesignPosition( + SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) const +{ + AutoFTAccess fta(this); + FT_Face face = fta.face(); + + if (!(face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS)) { + return 0; + } + + FT_MM_Var* variations = nullptr; + if (FT_Get_MM_Var(face, &variations)) { + return 0; + } + SkAutoFree autoFreeVariations(variations); + + if (!coordinates || coordinateCount < SkToInt(variations->num_axis)) { + return variations->num_axis; + } + + SkAutoSTMalloc<4, FT_Fixed> coords(variations->num_axis); + // FT_Get_{MM,Var}_{Blend,Design}_Coordinates were added in FreeType 2.7.1. + if (gFTLibrary->fGetVarDesignCoordinates && + !gFTLibrary->fGetVarDesignCoordinates(face, variations->num_axis, coords.get())) + { + for (FT_UInt i = 0; i < variations->num_axis; ++i) { + coordinates[i].axis = variations->axis[i].tag; + coordinates[i].value = SkFixedToScalar(coords[i]); + } + } else if (static_cast(fta.getAxesCount()) == variations->num_axis) { + for (FT_UInt i = 0; i < variations->num_axis; ++i) { + coordinates[i].axis = variations->axis[i].tag; + coordinates[i].value = SkFixedToScalar(fta.getAxes()[i]); + } + } else if (fta.isNamedVariationSpecified()) { + // The font has axes, they cannot be retrieved, and some named axis was specified. + return -1; + } else { + // The font has axes, they cannot be retrieved, but no named instance was specified. + return 0; + } + + return variations->num_axis; +} + int SkTypeface_FreeType::onGetTableTags(SkFontTableTag tags[]) const { AutoFTAccess fta(this); FT_Face face = fta.face(); @@ -1730,7 +1854,7 @@ bool SkTypeface_FreeType::Scanner::scanFont( /*static*/ void SkTypeface_FreeType::Scanner::computeAxisValues( AxisDefinitions axisDefinitions, - const SkFontMgr::FontParameters::Axis* requestedAxes, int requestedAxisCount, + const SkFontArguments::VariationPosition position, SkFixed* axisValues, const SkString& name) { @@ -1739,11 +1863,11 @@ bool SkTypeface_FreeType::Scanner::scanFont( const SkScalar axisMin = SkFixedToScalar(axisDefinition.fMinimum); const SkScalar axisMax = SkFixedToScalar(axisDefinition.fMaximum); axisValues[i] = axisDefinition.fDefault; - for (int j = 0; j < requestedAxisCount; ++j) { - const SkFontMgr::FontParameters::Axis& axisSpecified = requestedAxes[j]; - if (axisDefinition.fTag == axisSpecified.fTag) { - const SkScalar axisValue = SkTPin(axisSpecified.fStyleValue, axisMin, axisMax); - if (axisSpecified.fStyleValue != axisValue) { + for (int j = 0; j < position.coordinateCount; ++j) { + const auto& coordinate = position.coordinates[j]; + if (axisDefinition.fTag == coordinate.axis) { + const SkScalar axisValue = SkTPin(coordinate.value, axisMin, axisMax); + if (coordinate.value != axisValue) { SkDEBUGF(("Requested font axis value out of range: " "%s '%c%c%c%c' %f; pinned to %f.\n", name.c_str(), @@ -1751,7 +1875,7 @@ bool SkTypeface_FreeType::Scanner::scanFont( (axisDefinition.fTag >> 16) & 0xFF, (axisDefinition.fTag >> 8) & 0xFF, (axisDefinition.fTag ) & 0xFF, - SkScalarToDouble(axisSpecified.fStyleValue), + SkScalarToDouble(coordinate.value), SkScalarToDouble(axisValue))); } axisValues[i] = SkScalarToFixed(axisValue); @@ -1763,8 +1887,8 @@ bool SkTypeface_FreeType::Scanner::scanFont( SkDEBUGCODE( // Check for axis specified, but not matched in font. - for (int i = 0; i < requestedAxisCount; ++i) { - SkFourByteTag skTag = requestedAxes[i].fTag; + for (int i = 0; i < position.coordinateCount; ++i) { + SkFourByteTag skTag = position.coordinates[i].axis; bool found = false; for (int j = 0; j < axisDefinitions.count(); ++j) { if (skTag == axisDefinitions[j].fTag) { diff --git a/src/ports/SkFontHost_FreeType_common.h b/src/ports/SkFontHost_FreeType_common.h index 867e139c21..b9e8b13d7f 100644 --- a/src/ports/SkFontHost_FreeType_common.h +++ b/src/ports/SkFontHost_FreeType_common.h @@ -17,8 +17,11 @@ #include "SkFontMgr.h" -#include -#include FT_FREETYPE_H +// These are forward declared to avoid pimpl but also hide the FreeType implementation. +typedef struct FT_LibraryRec_* FT_Library; +typedef struct FT_FaceRec_* FT_Face; +typedef struct FT_StreamRec_* FT_Stream; +typedef signed long FT_Pos; class SkScalerContext_FreeType_Base : public SkScalerContext { protected: @@ -59,7 +62,7 @@ public: AxisDefinitions* axes) const; static void computeAxisValues( AxisDefinitions axisDefinitions, - const SkFontMgr::FontParameters::Axis* requestedAxis, int requestedAxisCount, + const SkFontArguments::VariationPosition position, SkFixed* axisValues, const SkString& name); @@ -88,6 +91,8 @@ protected: LocalizedStrings* onCreateFamilyNameIterator() const override; + int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], + int coordinateCount) const override; int onGetTableTags(SkFontTableTag tags[]) const override; size_t onGetTableData(SkFontTableTag, size_t offset, size_t length, void* data) const override; diff --git a/src/ports/SkFontHost_mac.cpp b/src/ports/SkFontHost_mac.cpp index 7a46d40ec6..b5022d9af9 100644 --- a/src/ports/SkFontHost_mac.cpp +++ b/src/ports/SkFontHost_mac.cpp @@ -492,6 +492,8 @@ protected: int onGetUPEM() const override; SkStreamAsset* onOpenStream(int* ttcIndex) const override; std::unique_ptr onMakeFontData() const override; + int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], + int coordinateCount) const override; void onGetFamilyName(SkString* familyName) const override; SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override; int onGetTableTags(SkFontTableTag tags[]) const override; @@ -1374,7 +1376,10 @@ void SkScalerContext_Mac::CTPathElement(void *info, const CGPathElement *element // Returns nullptr on failure // Call must still manage its ownership of provider -static SkTypeface* create_from_dataProvider(UniqueCFRef provider) { +static SkTypeface* create_from_dataProvider(UniqueCFRef provider, int ttcIndex) { + if (ttcIndex != 0) { + return nullptr; + } UniqueCFRef cg(CGFontCreateWithDataProvider(provider.get())); if (!cg) { return nullptr; @@ -1695,13 +1700,14 @@ static void set_non_default_axes(CFTypeRef key, CFTypeRef value, void* context) } self->axisValue[keyIndex] = SkDoubleToFixed(valueDouble); } -static bool get_variations(CTFontRef fFontRef, CFIndex* cgAxisCount, +static bool get_variations(CTFontRef ctFont, CFIndex* cgAxisCount, SkAutoSTMalloc<4, SkFixed>* axisValues) { - // CTFontCopyVariationAxes and CTFontCopyVariation do not work when applied to fonts which - // started life with CGFontCreateWithDataProvider (they simply always return nullptr). - // As a result, we are limited to CGFontCopyVariationAxes and CGFontCopyVariations. - UniqueCFRef cgFont(CTFontCopyGraphicsFont(fFontRef, nullptr)); + // In 10.10 and earlier, CTFontCopyVariationAxes and CTFontCopyVariation do not work when + // applied to fonts which started life with CGFontCreateWithDataProvider (they simply always + // return nullptr). As a result, we are limited to CGFontCopyVariationAxes and + // CGFontCopyVariations here until support for 10.10 and earlier is removed. + UniqueCFRef cgFont(CTFontCopyGraphicsFont(ctFont, nullptr)); if (!cgFont) { return false; } @@ -1766,6 +1772,126 @@ std::unique_ptr SkTypeface_Mac::onMakeFontData() const { return skstd::make_unique(std::move(stream), index, nullptr, 0); } +/** Creates a CT variation dictionary {tag, value} from a CG variation dictionary {name, value}. */ +static UniqueCFRef ct_variation_from_cg_variation(CFDictionaryRef cgVariations, + CFArrayRef ctAxes) { + + UniqueCFRef ctVariations( + CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks)); + + CFIndex axisCount = CFArrayGetCount(ctAxes); + for (CFIndex i = 0; i < axisCount; ++i) { + CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes, i); + if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) { + return nullptr; + } + CFDictionaryRef axisInfoDict = static_cast(axisInfo); + + // The assumption is that values produced by kCTFontVariationAxisNameKey and + // kCGFontVariationAxisName will always be equal. + CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisNameKey); + if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) { + return nullptr; + } + + CFTypeRef axisValue = CFDictionaryGetValue(cgVariations, axisName); + if (!axisValue || CFGetTypeID(axisValue) != CFNumberGetTypeID()) { + return nullptr; + } + + CFTypeRef axisTag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey); + if (!axisTag || CFGetTypeID(axisTag) != CFNumberGetTypeID()) { + return nullptr; + } + + CFDictionaryAddValue(ctVariations.get(), axisTag, axisValue); + } + return std::move(ctVariations); +} + +int SkTypeface_Mac::onGetVariationDesignPosition( + SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) const +{ + // The CGFont variation data does not contain the tag. + + // This call always returns nullptr on 10.10 and under for CGFontCreateWithDataProvider fonts. + // When this happens, there is no API to provide the tag. + UniqueCFRef ctAxes(CTFontCopyVariationAxes(fFontRef.get())); + if (!ctAxes) { + return -1; + } + CFIndex axisCount = CFArrayGetCount(ctAxes.get()); + if (!coordinates || coordinateCount < axisCount) { + return axisCount; + } + + // This call always returns nullptr on 10.11 and under for CGFontCreateWithDataProvider fonts. + // When this happens, try converting the CG variation to a CT variation. + // On 10.12 and later, this only returns non-default variations. + UniqueCFRef ctVariations(CTFontCopyVariation(fFontRef.get())); + if (!ctVariations) { + // When 10.11 and earlier are no longer supported, the following code can be replaced with + // return -1 and ct_variation_from_cg_variation can be removed. + UniqueCFRef cgFont(CTFontCopyGraphicsFont(fFontRef.get(), nullptr)); + if (!cgFont) { + return -1; + } + UniqueCFRef cgVariations(CGFontCopyVariations(cgFont.get())); + if (!cgVariations) { + return -1; + } + ctVariations = ct_variation_from_cg_variation(cgVariations.get(), ctAxes.get()); + if (!ctVariations) { + return -1; + } + } + + for (int i = 0; i < axisCount; ++i) { + CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes.get(), i); + if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) { + return -1; + } + CFDictionaryRef axisInfoDict = static_cast(axisInfo); + + CFTypeRef tag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey); + if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) { + return -1; + } + CFNumberRef tagNumber = static_cast(tag); + int64_t tagLong; + if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) { + return -1; + } + coordinates[i].axis = tagLong; + + CGFloat variationCGFloat; + CFTypeRef variationValue = CFDictionaryGetValue(ctVariations.get(), tagNumber); + if (variationValue) { + if (CFGetTypeID(variationValue) != CFNumberGetTypeID()) { + return -1; + } + CFNumberRef variationNumber = static_cast(variationValue); + if (!CFNumberGetValue(variationNumber, kCFNumberCGFloatType, &variationCGFloat)) { + return -1; + } + } else { + CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisDefaultValueKey); + if (!def || CFGetTypeID(def) != CFNumberGetTypeID()) { + return -1; + } + CFNumberRef defNumber = static_cast(def); + if (!CFNumberGetValue(defNumber, kCFNumberCGFloatType, &variationCGFloat)) { + return -1; + } + } + coordinates[i].value = CGToScalar(variationCGFloat); + + } + return axisCount; +} + /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// @@ -2247,7 +2373,7 @@ protected: if (!pr) { return nullptr; } - return create_from_dataProvider(std::move(pr)); + return create_from_dataProvider(std::move(pr), ttcIndex); } SkTypeface* onCreateFromStream(SkStreamAsset* bareStream, int ttcIndex) const override { @@ -2256,55 +2382,26 @@ protected: if (!pr) { return nullptr; } - return create_from_dataProvider(std::move(pr)); + return create_from_dataProvider(std::move(pr), ttcIndex); } - 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(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(tag); - } - } - return nullptr; - } - static UniqueCFRef copy_axes(CGFontRef cg, const FontParameters& params) { - UniqueCFRef cgAxes(CGFontCopyVariationAxes(cg)); - if (!cgAxes) { - return nullptr; - } - CFIndex axisCount = CFArrayGetCount(cgAxes.get()); - - // The CGFont variation data is keyed by name, and lacks the tag. + /** Creates a dictionary suitable for setting the axes on a CGFont. */ + static UniqueCFRef copy_axes(CGFontRef cg, const SkFontArguments& args) { + // The CGFont variation data is keyed by name, but 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 + // We would like to work with CTFont variations, 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. UniqueCFRef ct(CTFontCreateWithGraphicsFont(cg, 0, nullptr, nullptr)); + // This call always returns nullptr on 10.10 and under. + // When this happens, there is no API to provide the tag. UniqueCFRef ctAxes(CTFontCopyVariationAxes(ct.get())); - if (!ctAxes || CFArrayGetCount(ctAxes.get()) != axisCount) { + if (!ctAxes) { return nullptr; } + CFIndex axisCount = CFArrayGetCount(ctAxes.get()); - int paramAxisCount; - const FontParameters::Axis* paramAxes = params.getAxes(¶mAxisCount); + const SkFontArguments::VariationPosition position = args.getVariationDesignPosition(); UniqueCFRef dict( CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount, @@ -2312,24 +2409,25 @@ protected: &kCFTypeDictionaryValueCallBacks)); for (int i = 0; i < axisCount; ++i) { - CFTypeRef axisInfo = CFArrayGetValueAtIndex(cgAxes.get(), i); + CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes.get(), i); if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) { return nullptr; } CFDictionaryRef axisInfoDict = static_cast(axisInfo); - CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisName); + // The assumption is that values produced by kCTFontVariationAxisNameKey and + // kCGFontVariationAxisName will always be equal. + // If they are ever not, seach the project history for "get_tag_for_name". + CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisNameKey); if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) { return nullptr; } - CFNumberRef tagNumber = - get_tag_for_name(static_cast(axisName), ctAxes.get()); - if (!tagNumber) { - // Could not find a tag to go with the name of this index. - // This would be a bug in CG/CT. - continue; + CFTypeRef tag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey); + if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) { + return nullptr; } + CFNumberRef tagNumber = static_cast(tag); int64_t tagLong; if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) { return nullptr; @@ -2337,9 +2435,9 @@ protected: // 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); + CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMinimumValueKey); + CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMaximumValueKey); + CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisDefaultValueKey); if (!min || CFGetTypeID(min) != CFNumberGetTypeID() || !max || CFGetTypeID(max) != CFNumberGetTypeID() || !def || CFGetTypeID(def) != CFNumberGetTypeID()) @@ -2360,9 +2458,10 @@ protected: } double value = defDouble; - for (int j = 0; j < paramAxisCount; ++j) { - if (paramAxes[j].fTag == tagLong) { - value = SkTPin(SkScalarToDouble(paramAxes[j].fStyleValue),minDouble,maxDouble); + for (int j = 0; j < position.coordinateCount; ++j) { + if (position.coordinates[j].axis == tagLong) { + value = SkTPin(SkScalarToDouble(position.coordinates[j].value), + minDouble, maxDouble); break; } } @@ -2372,8 +2471,11 @@ protected: } return std::move(dict); } - SkTypeface* onCreateFromStream(SkStreamAsset* bs, const FontParameters& params) const override { + SkTypeface* onCreateFromStream(SkStreamAsset* bs, const SkFontArguments& args) const override { std::unique_ptr s(bs); + if (args.getCollectionIndex() != 0) { + return nullptr; + } UniqueCFRef provider(SkCreateDataProviderFromStream(std::move(s))); if (!provider) { return nullptr; @@ -2383,7 +2485,7 @@ protected: return nullptr; } - UniqueCFRef cgVariations = copy_axes(cg.get(), params); + UniqueCFRef cgVariations = copy_axes(cg.get(), args); // 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. @@ -2402,6 +2504,7 @@ protected: return create_from_CTFontRef(std::move(ct), std::move(cg), true); } + /** Creates a dictionary suitable for setting the axes on a CGFont. */ static UniqueCFRef copy_axes(CGFontRef cg, SkFontData* fontData) { UniqueCFRef cgAxes(CGFontCopyVariationAxes(cg)); if (!cgAxes) { @@ -2456,6 +2559,9 @@ protected: return std::move(dict); } SkTypeface* onCreateFromFontData(std::unique_ptr fontData) const override { + if (fontData->getIndex() != 0) { + return nullptr; + } UniqueCFRef provider( SkCreateDataProviderFromStream(fontData->detachStream())); if (!provider) { @@ -2490,7 +2596,7 @@ protected: if (!pr) { return nullptr; } - return create_from_dataProvider(std::move(pr)); + return create_from_dataProvider(std::move(pr), ttcIndex); } SkTypeface* onLegacyCreateTypeface(const char familyName[], SkFontStyle style) const override { diff --git a/src/ports/SkFontHost_win.cpp b/src/ports/SkFontHost_win.cpp index e6e07159c4..fbcd01a0ff 100644 --- a/src/ports/SkFontHost_win.cpp +++ b/src/ports/SkFontHost_win.cpp @@ -262,15 +262,19 @@ protected: SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics( PerGlyphInfo, const uint32_t*, uint32_t) const override; void onGetFontDescriptor(SkFontDescriptor*, bool*) const override; - virtual int onCharsToGlyphs(const void* chars, Encoding encoding, - uint16_t glyphs[], int glyphCount) const override; + int onCharsToGlyphs(const void* chars, Encoding encoding, + uint16_t glyphs[], int glyphCount) const override; int onCountGlyphs() const override; int onGetUPEM() const override; void onGetFamilyName(SkString* familyName) const override; SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override; + int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], + int coordinateCount) const override + { + return -1; + } int onGetTableTags(SkFontTableTag tags[]) const override; - virtual size_t onGetTableData(SkFontTableTag, size_t offset, - size_t length, void* data) const override; + size_t onGetTableData(SkFontTableTag, size_t offset, size_t length, void* data) const override; }; class FontMemResourceTypeface : public LogFontTypeface { @@ -2455,17 +2459,20 @@ protected: SkTypeface* onCreateFromStream(SkStreamAsset* bareStream, int ttcIndex) const override { std::unique_ptr stream(bareStream); + if (ttcIndex != 0) { + return nullptr; + } return create_from_stream(stream.get()); } SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const override { // could be in base impl - return this->createFromStream(new SkMemoryStream(sk_ref_sp(data))); + return this->createFromStream(new SkMemoryStream(sk_ref_sp(data)), ttcIndex); } SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const override { // could be in base impl - return this->createFromStream(SkStream::MakeFromFile(path).release()); + return this->createFromStream(SkStream::MakeFromFile(path).release(), ttcIndex); } SkTypeface* onLegacyCreateTypeface(const char familyName[], SkFontStyle style) const override { diff --git a/src/ports/SkFontMgr_FontConfigInterface.cpp b/src/ports/SkFontMgr_FontConfigInterface.cpp index 81c4868e5d..6264710008 100644 --- a/src/ports/SkFontMgr_FontConfigInterface.cpp +++ b/src/ports/SkFontMgr_FontConfigInterface.cpp @@ -222,7 +222,7 @@ protected: return SkTypeface_FCI::Create(std::move(fontData), std::move(name), style, isFixedPitch); } - SkTypeface* onCreateFromStream(SkStreamAsset* s, const FontParameters& params) const override { + SkTypeface* onCreateFromStream(SkStreamAsset* s, const SkFontArguments& args) const override { using Scanner = SkTypeface_FreeType::Scanner; std::unique_ptr stream(s); const size_t length = stream->getLength(); @@ -237,19 +237,18 @@ protected: SkFontStyle style; SkString name; Scanner::AxisDefinitions axisDefinitions; - if (!fScanner.scanFont(stream.get(), params.getCollectionIndex(), + if (!fScanner.scanFont(stream.get(), args.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); + Scanner::computeAxisValues(axisDefinitions, args.getVariationDesignPosition(), + axisValues, name); auto fontData = skstd::make_unique(std::move(stream), - params.getCollectionIndex(), + args.getCollectionIndex(), axisValues.get(), axisDefinitions.count()); return SkTypeface_FCI::Create(std::move(fontData), std::move(name), style, isFixedPitch); diff --git a/src/ports/SkFontMgr_android.cpp b/src/ports/SkFontMgr_android.cpp index d4d7967dc5..a180215e88 100644 --- a/src/ports/SkFontMgr_android.cpp +++ b/src/ports/SkFontMgr_android.cpp @@ -201,8 +201,11 @@ public: } SkAutoSTMalloc<4, SkFixed> axisValues(axisDefinitions.count()); - Scanner::computeAxisValues(axisDefinitions, - fontFile.fAxes.begin(), fontFile.fAxes.count(), + SkFontArguments::VariationPosition position = { + fontFile.fVariationDesignPosition.begin(), + fontFile.fVariationDesignPosition.count() + }; + Scanner::computeAxisValues(axisDefinitions, position, axisValues, familyName); fStyles.push_back().reset(new SkTypeface_AndroidSystem( @@ -428,25 +431,24 @@ protected: return new SkTypeface_AndroidStream(std::move(data), style, isFixedPitch, name); } - SkTypeface* onCreateFromStream(SkStreamAsset* s, const FontParameters& params) const override { + SkTypeface* onCreateFromStream(SkStreamAsset* s, const SkFontArguments& args) const override { using Scanner = SkTypeface_FreeType::Scanner; std::unique_ptr stream(s); bool isFixedPitch; SkFontStyle style; SkString name; Scanner::AxisDefinitions axisDefinitions; - if (!fScanner.scanFont(stream.get(), params.getCollectionIndex(), + if (!fScanner.scanFont(stream.get(), args.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); + Scanner::computeAxisValues(axisDefinitions, args.getVariationDesignPosition(), + axisValues, name); - auto data = skstd::make_unique(std::move(stream), params.getCollectionIndex(), + auto data = skstd::make_unique(std::move(stream), args.getCollectionIndex(), axisValues.get(), axisDefinitions.count()); return new SkTypeface_AndroidStream(std::move(data), style, isFixedPitch, name); } diff --git a/src/ports/SkFontMgr_android_parser.cpp b/src/ports/SkFontMgr_android_parser.cpp index e5fdcd0dbf..e50a73946a 100644 --- a/src/ports/SkFontMgr_android_parser.cpp +++ b/src/ports/SkFontMgr_android_parser.cpp @@ -167,8 +167,8 @@ static const TagHandler axisHandler = { if (valueLen == 4) { axisTag = SkSetFourByteTag(value[0], value[1], value[2], value[3]); axisTagIsValid = true; - for (int j = 0; j < file.fAxes.count() - 1; ++j) { - if (file.fAxes[j].fTag == axisTag) { + for (int j = 0; j < file.fVariationDesignPosition.count() - 1; ++j) { + if (file.fVariationDesignPosition[j].axis == axisTag) { axisTagIsValid = false; SK_FONTCONFIGPARSER_WARNING("'%c%c%c%c' axis specified more than once", (axisTag >> 24) & 0xFF, @@ -189,9 +189,9 @@ static const TagHandler axisHandler = { } } if (axisTagIsValid && axisStyleValueIsValid) { - SkFontMgr::FontParameters::Axis& axis = file.fAxes.push_back(); - axis.fTag = axisTag; - axis.fStyleValue = SkFixedToScalar(axisStyleValue); + auto& coordinate = file.fVariationDesignPosition.push_back(); + coordinate.axis = axisTag; + coordinate.value = SkFixedToScalar(axisStyleValue); } }, /*end*/nullptr, diff --git a/src/ports/SkFontMgr_android_parser.h b/src/ports/SkFontMgr_android_parser.h index efd8144f14..75b31c300e 100644 --- a/src/ports/SkFontMgr_android_parser.h +++ b/src/ports/SkFontMgr_android_parser.h @@ -73,7 +73,7 @@ struct FontFileInfo { int fIndex; int fWeight; enum class Style { kAuto, kNormal, kItalic } fStyle; - SkTArray fAxes; + SkTArray fVariationDesignPosition; }; /** diff --git a/src/ports/SkFontMgr_custom.cpp b/src/ports/SkFontMgr_custom.cpp index 4e4887d27d..91a590827c 100644 --- a/src/ports/SkFontMgr_custom.cpp +++ b/src/ports/SkFontMgr_custom.cpp @@ -5,6 +5,7 @@ * found in the LICENSE file. */ +#include "SkFontArguments.h" #include "SkFontDescriptor.h" #include "SkFontHost_FreeType_common.h" #include "SkFontMgr.h" @@ -200,7 +201,7 @@ SkTypeface* SkFontMgr_Custom::onCreateFromStream(SkStreamAsset* bareStream, int } SkTypeface* SkFontMgr_Custom::onCreateFromStream(SkStreamAsset* s, - const FontParameters& params) const + const SkFontArguments& args) const { using Scanner = SkTypeface_FreeType::Scanner; std::unique_ptr stream(s); @@ -208,19 +209,18 @@ SkTypeface* SkFontMgr_Custom::onCreateFromStream(SkStreamAsset* s, SkFontStyle style; SkString name; Scanner::AxisDefinitions axisDefinitions; - if (!fScanner.scanFont(stream.get(), params.getCollectionIndex(), + if (!fScanner.scanFont(stream.get(), args.getCollectionIndex(), &name, &style, &isFixedPitch, &axisDefinitions)) { return nullptr; } - int paramAxisCount; - const FontParameters::Axis* paramAxes = params.getAxes(¶mAxisCount); + const SkFontArguments::VariationPosition position = args.getVariationDesignPosition(); SkAutoSTMalloc<4, SkFixed> axisValues(axisDefinitions.count()); - Scanner::computeAxisValues(axisDefinitions, paramAxes, paramAxisCount, axisValues, name); + Scanner::computeAxisValues(axisDefinitions, position, axisValues, name); - auto data = skstd::make_unique(std::move(stream), params.getCollectionIndex(), - axisValues.get(), axisDefinitions.count()); + auto data = skstd::make_unique(std::move(stream), args.getCollectionIndex(), + axisValues.get(), axisDefinitions.count()); return new SkTypeface_Stream(std::move(data), style, isFixedPitch, false, name); } diff --git a/src/ports/SkFontMgr_custom.h b/src/ports/SkFontMgr_custom.h index 9149378a50..f8d083c049 100644 --- a/src/ports/SkFontMgr_custom.h +++ b/src/ports/SkFontMgr_custom.h @@ -144,7 +144,7 @@ protected: const SkFontStyle& fontStyle) const override; SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const override; SkTypeface* onCreateFromStream(SkStreamAsset* bareStream, int ttcIndex) const override; - SkTypeface* onCreateFromStream(SkStreamAsset* s, const FontParameters& params) const override; + SkTypeface* onCreateFromStream(SkStreamAsset* s, const SkFontArguments& args) const override; SkTypeface* onCreateFromFontData(std::unique_ptr data) const override; SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const override; SkTypeface* onLegacyCreateTypeface(const char familyName[], SkFontStyle style) const override; diff --git a/src/ports/SkFontMgr_fontconfig.cpp b/src/ports/SkFontMgr_fontconfig.cpp index 63d720b390..85230b394e 100644 --- a/src/ports/SkFontMgr_fontconfig.cpp +++ b/src/ports/SkFontMgr_fontconfig.cpp @@ -899,25 +899,24 @@ protected: return new SkTypeface_stream(std::move(data), std::move(name), style, isFixedWidth); } - SkTypeface* onCreateFromStream(SkStreamAsset* s, const FontParameters& params) const override { + SkTypeface* onCreateFromStream(SkStreamAsset* s, const SkFontArguments& args) const override { using Scanner = SkTypeface_FreeType::Scanner; std::unique_ptr stream(s); bool isFixedPitch; SkFontStyle style; SkString name; Scanner::AxisDefinitions axisDefinitions; - if (!fScanner.scanFont(stream.get(), params.getCollectionIndex(), + if (!fScanner.scanFont(stream.get(), args.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); + Scanner::computeAxisValues(axisDefinitions, args.getVariationDesignPosition(), + axisValues, name); - auto data = skstd::make_unique(std::move(stream), params.getCollectionIndex(), + auto data = skstd::make_unique(std::move(stream), args.getCollectionIndex(), axisValues.get(), axisDefinitions.count()); return new SkTypeface_stream(std::move(data), std::move(name), style, isFixedPitch); } diff --git a/src/ports/SkTypeface_win_dw.h b/src/ports/SkTypeface_win_dw.h index 1119841e00..ffc49f3d86 100644 --- a/src/ports/SkTypeface_win_dw.h +++ b/src/ports/SkTypeface_win_dw.h @@ -104,15 +104,19 @@ protected: SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics( PerGlyphInfo, const uint32_t*, uint32_t) const override; void onGetFontDescriptor(SkFontDescriptor*, bool*) const override; - virtual int onCharsToGlyphs(const void* chars, Encoding encoding, - uint16_t glyphs[], int glyphCount) const override; + int onCharsToGlyphs(const void* chars, Encoding encoding, + uint16_t glyphs[], int glyphCount) const override; int onCountGlyphs() const override; int onGetUPEM() const override; void onGetFamilyName(SkString* familyName) const override; SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override; + int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], + int coordinateCount) const override + { + return -1; + } int onGetTableTags(SkFontTableTag tags[]) const override; - virtual size_t onGetTableData(SkFontTableTag, size_t offset, - size_t length, void* data) const override; + size_t onGetTableData(SkFontTableTag, size_t offset, size_t length, void* data) const override; private: typedef SkTypeface INHERITED; diff --git a/tests/FontMgrAndroidParserTest.cpp b/tests/FontMgrAndroidParserTest.cpp index 92dbd951c0..cbcfb3be2a 100644 --- a/tests/FontMgrAndroidParserTest.cpp +++ b/tests/FontMgrAndroidParserTest.cpp @@ -90,13 +90,13 @@ void DumpLoadedFonts(SkTDArray fontFamilies, const char* label) { for (int j = 0; j < fontFamilies[i]->fFonts.count(); ++j) { const FontFileInfo& ffi = fontFamilies[i]->fFonts[j]; SkDebugf(" file (%d) %s#%d", ffi.fWeight, ffi.fFileName.c_str(), ffi.fIndex); - for (const auto& axis : ffi.fAxes) { + for (const auto& coordinate : ffi.fVariationDesignPosition) { SkDebugf(" @'%c%c%c%c'=%f", - (axis.fTag >> 24) & 0xFF, - (axis.fTag >> 16) & 0xFF, - (axis.fTag >> 8) & 0xFF, - (axis.fTag ) & 0xFF, - axis.fStyleValue); + (coordinate.axis >> 24) & 0xFF, + (coordinate.axis >> 16) & 0xFF, + (coordinate.axis >> 8) & 0xFF, + (coordinate.axis ) & 0xFF, + coordinate.value); } SkDebugf("\n"); } diff --git a/tests/FontMgrTest.cpp b/tests/FontMgrTest.cpp index 50e2d5a606..b6a0cc5a74 100644 --- a/tests/FontMgrTest.cpp +++ b/tests/FontMgrTest.cpp @@ -150,6 +150,12 @@ static void test_matchStyleCSS3(skiatest::Reporter* reporter) { SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override { return new EmptyLocalizedStrings; } + int onGetVariationDesignPosition( + SkFontArguments::VariationPosition::Coordinate coordinates[], + int coordinateCount) const override + { + return 0; + } int onGetTableTags(SkFontTableTag tags[]) const override { return 0; } size_t onGetTableData(SkFontTableTag, size_t, size_t, void*) const override { return 0; diff --git a/tests/TypefaceTest.cpp b/tests/TypefaceTest.cpp index 2a3b32d2cf..4786cc278c 100644 --- a/tests/TypefaceTest.cpp +++ b/tests/TypefaceTest.cpp @@ -6,6 +6,9 @@ */ #include "SkData.h" +#include "SkFixed.h" +#include "SkFontMgr.h" +#include "SkMakeUnique.h" #include "SkOTTable_OS_2.h" #include "SkSFNTHeader.h" #include "SkStream.h" @@ -87,6 +90,77 @@ DEF_TEST(TypefaceStyle, reporter) { } } +DEF_TEST(TypefaceAxes, reporter) { + std::unique_ptr distortable(GetResourceAsStream("/fonts/Distortable.ttf")); + if (!distortable) { + REPORT_FAILURE(reporter, "distortable", SkString()); + return; + } + + sk_sp fm = SkFontMgr::RefDefault(); + const SkFontArguments::VariationPosition::Coordinate position[] = { + { SkSetFourByteTag('w','g','h','t'), SK_ScalarSqrt2 } + }; + SkFontArguments params; + params.setVariationDesignPosition({position, SK_ARRAY_COUNT(position)}); + // TODO: if axes are set and the back-end doesn't support them, should we create the typeface? + sk_sp typeface(fm->createFromStream(distortable.release(), params)); + + int count = typeface->getVariationDesignPosition(nullptr, 0); + if (count == -1) { + return; + } + REPORTER_ASSERT(reporter, count == SK_ARRAY_COUNT(position)); + + SkFontArguments::VariationPosition::Coordinate positionRead[SK_ARRAY_COUNT(position)]; + count = typeface->getVariationDesignPosition(positionRead, SK_ARRAY_COUNT(positionRead)); + REPORTER_ASSERT(reporter, count == SK_ARRAY_COUNT(position)); + + REPORTER_ASSERT(reporter, positionRead[0].axis == position[0].axis); + + // Convert to fixed for "almost equal". + SkFixed fixedRead = SkScalarToFixed(positionRead[0].value); + SkFixed fixedOriginal = SkScalarToFixed(position[0].value); + REPORTER_ASSERT(reporter, fixedRead == fixedOriginal); +} + +DEF_TEST(TypefaceVariationIndex, reporter) { + std::unique_ptr distortable(GetResourceAsStream("/fonts/Distortable.ttf")); + if (!distortable) { + REPORT_FAILURE(reporter, "distortable", SkString()); + return; + } + + sk_sp fm = SkFontMgr::RefDefault(); + SkFontArguments params; + // The first named variation position in Distortable is 'Thin'. + params.setCollectionIndex(0x00010000); + sk_sp typeface(fm->createFromStream(distortable.release(), params)); + if (!typeface) { + // FreeType is the only weird thing that supports this, Skia just needs to make sure if it + // gets one of these things make sense. + return; + } + + int count = typeface->getVariationDesignPosition(nullptr, 0); + if (!(count == 1)) { + REPORT_FAILURE(reporter, "count == 1", SkString()); + return; + } + + SkFontArguments::VariationPosition::Coordinate positionRead[1]; + count = typeface->getVariationDesignPosition(positionRead, SK_ARRAY_COUNT(positionRead)); + if (count == -1) { + return; + } + if (!(count == 1)) { + REPORT_FAILURE(reporter, "count == 1", SkString()); + return; + } + REPORTER_ASSERT(reporter, positionRead[0].axis == SkSetFourByteTag('w','g','h','t')); + REPORTER_ASSERT(reporter, positionRead[0].value == 0.5); +} + DEF_TEST(Typeface, reporter) { sk_sp t1(SkTypeface::MakeFromName(nullptr, SkFontStyle())); @@ -134,6 +208,11 @@ protected: SK_ABORT("unimplemented"); return nullptr; } + int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], + int coordinateCount) const override + { + return 0; + } int onGetTableTags(SkFontTableTag tags[]) const override { return 0; } size_t onGetTableData(SkFontTableTag, size_t, size_t, void*) const override { return 0; } }; diff --git a/third_party/freetype2/BUILD.gn b/third_party/freetype2/BUILD.gn index 89b64e4ed9..d543dac347 100644 --- a/third_party/freetype2/BUILD.gn +++ b/third_party/freetype2/BUILD.gn @@ -17,6 +17,7 @@ if (skia_use_system_freetype2) { } } else { third_party("freetype2") { + public_defines = [ "SK_FREETYPE_MINIMUM_RUNTIME_VERSION=(((FREETYPE_MAJOR) << 24) | ((FREETYPE_MINOR) << 16) | ((FREETYPE_PATCH) << 8))" ] public_include_dirs = [ "../externals/freetype/include" ] deps = [