From: bungeman Date: Mon, 25 Jul 2016 22:11:49 +0000 (-0700) Subject: Add test for typeface style round trip. X-Git-Tag: accepted/tizen/5.0/unified/20181102.025319~116^2~611 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=6e45bda29edef867468cbdd7c062d0d99e884656;p=platform%2Fupstream%2FlibSkiaSharp.git Add test for typeface style round trip. This also fixes the CG and GDI ports so they pass the test. GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2171163002 Review-Url: https://codereview.chromium.org/2171163002 --- diff --git a/include/core/SkFontStyle.h b/include/core/SkFontStyle.h index f4ffbb9..643b69b 100644 --- a/include/core/SkFontStyle.h +++ b/include/core/SkFontStyle.h @@ -13,15 +13,17 @@ class SK_API SkFontStyle { public: enum Weight { - kThin_Weight = 100, - kExtraLight_Weight = 200, - kLight_Weight = 300, - kNormal_Weight = 400, - kMedium_Weight = 500, - kSemiBold_Weight = 600, - kBold_Weight = 700, - kExtraBold_Weight = 800, - kBlack_Weight = 900 + kInvisible_Weight = 0, + kThin_Weight = 100, + kExtraLight_Weight = 200, + kLight_Weight = 300, + kNormal_Weight = 400, + kMedium_Weight = 500, + kSemiBold_Weight = 600, + kBold_Weight = 700, + kExtraBold_Weight = 800, + kBlack_Weight = 900, + kExtraBlack_Weight = 1000 }; enum Width { diff --git a/include/core/SkTypeface.h b/include/core/SkTypeface.h index 4f3879c..83008d3 100644 --- a/include/core/SkTypeface.h +++ b/include/core/SkTypeface.h @@ -346,6 +346,8 @@ protected: /** Sets the fixedPitch bit. If used, must be called in the constructor. */ void setIsFixedPitch(bool isFixedPitch) { fIsFixedPitch = isFixedPitch; } + /** Sets the font style. If used, must be called in the constructor. */ + void setFontStyle(SkFontStyle style) { fStyle = style; } friend class SkScalerContext; static SkTypeface* GetDefaultTypeface(Style style = SkTypeface::kNormal); diff --git a/src/core/SkFontStyle.cpp b/src/core/SkFontStyle.cpp index 01628ae..fc50f1f 100644 --- a/src/core/SkFontStyle.cpp +++ b/src/core/SkFontStyle.cpp @@ -18,7 +18,7 @@ SkFontStyle::SkFontStyle() { SkFontStyle::SkFontStyle(int weight, int width, Slant slant) { fUnion.fU32 = 0; - fUnion.fR.fWeight = SkTPin(weight, kThin_Weight, kBlack_Weight); + fUnion.fR.fWeight = SkTPin(weight, kInvisible_Weight, kExtraBlack_Weight); fUnion.fR.fWidth = SkTPin(width, kUltraCondensed_Width, kUltaExpanded_Width); fUnion.fR.fSlant = SkTPin(slant, kUpright_Slant, kOblique_Slant); } diff --git a/src/ports/SkFontConfigInterface_direct.cpp b/src/ports/SkFontConfigInterface_direct.cpp index ccea794..6e133bd 100644 --- a/src/ports/SkFontConfigInterface_direct.cpp +++ b/src/ports/SkFontConfigInterface_direct.cpp @@ -418,7 +418,7 @@ static SkFontStyle skfontstyle_from_fcpattern(FcPattern* pattern) { { SkTFixed::value, SkTFixed::value }, { SkTFixed::value, SkTFixed::value }, { SkTFixed::value, SkTFixed::value }, - { SkTFixed::value, SkTFixed<1000>::value }, + { SkTFixed::value, SkTFixed::value }, }; int weight = map_ranges(get_int(pattern, FC_WEIGHT, FC_WEIGHT_REGULAR), weightRanges, SK_ARRAY_COUNT(weightRanges)); @@ -461,7 +461,7 @@ static void fcpattern_from_skfontstyle(SkFontStyle style, FcPattern* pattern) { { SkTFixed::value, SkTFixed::value }, { SkTFixed::value, SkTFixed::value }, { SkTFixed::value, SkTFixed::value }, - { SkTFixed<1000>::value, SkTFixed::value }, + { SkTFixed::value, SkTFixed::value }, }; int weight = map_ranges(style.weight(), weightRanges, SK_ARRAY_COUNT(weightRanges)); diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp index 4a1f12b..e093bf0 100644 --- a/src/ports/SkFontHost_FreeType.cpp +++ b/src/ports/SkFontHost_FreeType.cpp @@ -1696,9 +1696,9 @@ bool SkTypeface_FreeType::Scanner::scanFont( { "standard", SkFontStyle::kNormal_Weight }, { "thin", SkFontStyle::kThin_Weight }, { "ultra", SkFontStyle::kExtraBold_Weight }, - { "ultrablack", 1000 }, + { "ultrablack", SkFontStyle::kExtraBlack_Weight }, { "ultrabold", SkFontStyle::kExtraBold_Weight }, - { "ultraheavy", 1000 }, + { "ultraheavy", SkFontStyle::kExtraBlack_Weight }, { "ultralight", SkFontStyle::kExtraLight_Weight }, }; int const index = SkStrLCSearch(&commonWeights[0].name, SK_ARRAY_COUNT(commonWeights), diff --git a/src/ports/SkFontHost_mac.cpp b/src/ports/SkFontHost_mac.cpp index 68668c2..41cb52b 100644 --- a/src/ports/SkFontHost_mac.cpp +++ b/src/ports/SkFontHost_mac.cpp @@ -370,31 +370,90 @@ private: /////////////////////////////////////////////////////////////////////////////// -static bool find_dict_float(CFDictionaryRef dict, CFStringRef name, float* value) { +static bool find_dict_CGFloat(CFDictionaryRef dict, CFStringRef name, CGFloat* value) { CFNumberRef num; return CFDictionaryGetValueIfPresent(dict, name, (const void**)&num) && CFNumberIsFloatType(num) - && CFNumberGetValue(num, kCFNumberFloatType, value); + && CFNumberGetValue(num, kCFNumberCGFloatType, value); } -static int unit_weight_to_fontstyle(float unit) { - float value; - if (unit < 0) { - value = 100 + (1 + unit) * 300; - } else { - value = 400 + unit * 500; +template struct LinearInterpolater { + struct Mapping { + S src_val; + D dst_val; + }; + constexpr LinearInterpolater(Mapping const mapping[], int mappingCount) + : fMapping(mapping), fMappingCount(mappingCount) {} + + static D map(S value, S src_min, S src_max, D dst_min, D dst_max) { + SkASSERT(src_min < src_max); + SkASSERT(dst_min <= dst_max); + return C()(dst_min + (((value - src_min) * (dst_max - dst_min)) / (src_max - src_min))); } - return sk_float_round2int(value); -} -static int unit_width_to_fontstyle(float unit) { - float value; - if (unit < 0) { - value = 1 + (1 + unit) * 4; - } else { - value = 5 + unit * 4; + int map(S val) const { + // -Inf to [0] + if (val < fMapping[0].src_val) { + return fMapping[0].dst_val; + } + + // Linear from [i] to [i+1] + for (int i = 0; i < fMappingCount - 1; ++i) { + if (val < fMapping[i+1].src_val) { + return map(val, fMapping[i].src_val, fMapping[i+1].src_val, + fMapping[i].dst_val, fMapping[i+1].dst_val); + } + } + + // From [n] to +Inf + // if (fcweight < Inf) + return fMapping[fMappingCount - 1].dst_val; } - return sk_float_round2int(value); + + Mapping const * fMapping; + int fMappingCount; +}; + +struct RoundCGFloatToInt { + int operator()(CGFloat s) { return s + 0.5; } +}; + +static int ct_weight_to_fontstyle(CGFloat cgWeight) { + using Interpolator = LinearInterpolater; + + // Values determined by creating font data with every weight, creating a CTFont, + // and asking the CTFont for its weight. See TypefaceStyle test for basics. + + // Note that Mac supports the old OS2 version A so 0 through 10 are as if multiplied by 100. + // However, on this end we can't tell. + static constexpr Interpolator::Mapping weightMappings[] = { + { -1.00, 0 }, + { -0.70, 100 }, + { -0.50, 200 }, + { -0.23, 300 }, + { 0.00, 400 }, + { 0.20, 500 }, + { 0.30, 600 }, + { 0.40, 700 }, + { 0.60, 800 }, + { 0.80, 900 }, + { 1.00, 1000 }, + }; + static constexpr Interpolator interpolater(weightMappings, SK_ARRAY_COUNT(weightMappings)); + return interpolater.map(cgWeight); +} + +static int ct_width_to_fontstyle(CGFloat cgWidth) { + using Interpolator = LinearInterpolater; + + // Values determined by creating font data with every width, creating a CTFont, + // and asking the CTFont for its width. See TypefaceStyle test for basics. + static constexpr Interpolator::Mapping widthMappings[] = { + { -0.5, 0 }, + { 0.5, 10 }, + }; + static constexpr Interpolator interpolater(widthMappings, SK_ARRAY_COUNT(widthMappings)); + return interpolater.map(cgWidth); } static SkFontStyle fontstyle_from_descriptor(CTFontDescriptorRef desc) { @@ -404,19 +463,19 @@ static SkFontStyle fontstyle_from_descriptor(CTFontDescriptorRef desc) { return SkFontStyle(); } - float weight, width, slant; - if (!find_dict_float(dict, kCTFontWeightTrait, &weight)) { + CGFloat weight, width, slant; + if (!find_dict_CGFloat(dict, kCTFontWeightTrait, &weight)) { weight = 0; } - if (!find_dict_float(dict, kCTFontWidthTrait, &width)) { + if (!find_dict_CGFloat(dict, kCTFontWidthTrait, &width)) { width = 0; } - if (!find_dict_float(dict, kCTFontSlantTrait, &slant)) { + if (!find_dict_CGFloat(dict, kCTFontSlantTrait, &slant)) { slant = 0; } - return SkFontStyle(unit_weight_to_fontstyle(weight), - unit_width_to_fontstyle(width), + return SkFontStyle(ct_weight_to_fontstyle(weight), + ct_width_to_fontstyle(width), slant ? SkFontStyle::kItalic_Slant : SkFontStyle::kUpright_Slant); } diff --git a/src/ports/SkFontHost_win.cpp b/src/ports/SkFontHost_win.cpp index 22262a5..226f219 100644 --- a/src/ports/SkFontHost_win.cpp +++ b/src/ports/SkFontHost_win.cpp @@ -210,8 +210,6 @@ public: , fLogFont(lf) , fSerializeAsStream(serializeAsStream) { - - // If the font has cubic outlines, it will not be rendered with ClearType. HFONT font = CreateFontIndirect(&lf); HDC deviceContext = ::CreateCompatibleDC(nullptr); @@ -234,9 +232,11 @@ public: // The fixed pitch bit is set if the font is *not* fixed pitch. this->setIsFixedPitch((textMetric.tmPitchAndFamily & TMPF_FIXED_PITCH) == 0); + this->setFontStyle(SkFontStyle(textMetric.tmWeight, style.width(), style.slant())); // Used a logfont on a memory context, should never get a device font. // Therefore all TMPF_DEVICE will be PostScript (cubic) fonts. + // If the font has cubic outlines, it will not be rendered with ClearType. fCanBeLCD = !((textMetric.tmPitchAndFamily & TMPF_VECTOR) && (textMetric.tmPitchAndFamily & TMPF_DEVICE)); } diff --git a/src/ports/SkFontMgr_fontconfig.cpp b/src/ports/SkFontMgr_fontconfig.cpp index 0876fb6..d4d8737 100644 --- a/src/ports/SkFontMgr_fontconfig.cpp +++ b/src/ports/SkFontMgr_fontconfig.cpp @@ -326,7 +326,7 @@ static SkFontStyle skfontstyle_from_fcpattern(FcPattern* pattern) { { SkTFixed::value, SkTFixed::value }, { SkTFixed::value, SkTFixed::value }, { SkTFixed::value, SkTFixed::value }, - { SkTFixed::value, SkTFixed<1000>::value }, + { SkTFixed::value, SkTFixed::value }, }; int weight = map_ranges(get_int(pattern, FC_WEIGHT, FC_WEIGHT_REGULAR), weightRanges, SK_ARRAY_COUNT(weightRanges)); @@ -371,7 +371,7 @@ static void fcpattern_from_skfontstyle(SkFontStyle style, FcPattern* pattern) { { SkTFixed::value, SkTFixed::value }, { SkTFixed::value, SkTFixed::value }, { SkTFixed::value, SkTFixed::value }, - { SkTFixed<1000>::value, SkTFixed::value }, + { SkTFixed::value, SkTFixed::value }, }; int weight = map_ranges(style.weight(), weightRanges, SK_ARRAY_COUNT(weightRanges)); diff --git a/tests/TypefaceTest.cpp b/tests/TypefaceTest.cpp index 4c65fa3..33fa887 100644 --- a/tests/TypefaceTest.cpp +++ b/tests/TypefaceTest.cpp @@ -5,11 +5,89 @@ * found in the LICENSE file. */ +#include "SkData.h" +#include "SkOTTable_OS_2.h" +#include "SkSFNTHeader.h" +#include "SkStream.h" #include "SkRefCnt.h" #include "SkTypeface.h" #include "SkTypefaceCache.h" +#include "Resources.h" #include "Test.h" +#include + +static void TypefaceStyle_test(skiatest::Reporter* reporter, + uint16_t weight, uint16_t width, SkData* data) +{ + sk_sp dataCopy; + SkData* dataToUse = data; + if (!dataToUse->unique()) { + dataCopy = SkData::MakeWithCopy(data->data(), data->size()); + dataToUse = dataCopy.get(); + } + SkSFNTHeader* sfntHeader = static_cast(dataToUse->writable_data()); + + SkSFNTHeader::TableDirectoryEntry* tableEntry = + SkTAfter(sfntHeader); + SkSFNTHeader::TableDirectoryEntry* os2TableEntry = nullptr; + int numTables = SkEndian_SwapBE16(sfntHeader->numTables); + for (int tableEntryIndex = 0; tableEntryIndex < numTables; ++tableEntryIndex) { + if (SkOTTableOS2::TAG == tableEntry[tableEntryIndex].tag) { + os2TableEntry = tableEntry + tableEntryIndex; + break; + } + } + SkASSERT_RELEASE(os2TableEntry); + + size_t os2TableOffset = SkEndian_SwapBE32(os2TableEntry->offset); + SkOTTableOS2_V0* os2Table = SkTAddOffset(sfntHeader, os2TableOffset); + os2Table->usWeightClass.value = SkEndian_SwapBE16(weight); + using WidthType = SkOTTableOS2_V0::WidthClass::Value; + os2Table->usWidthClass.value = static_cast(SkEndian_SwapBE16(width)); + + sk_sp newTypeface(SkTypeface::MakeFromStream(new SkMemoryStream(dataToUse))); + SkASSERT_RELEASE(newTypeface); + + SkFontStyle newStyle = newTypeface->fontStyle(); + + //printf("%d, %f\n", weight, (newStyle.weight() - (float)0x7FFF) / (float)0x7FFF); + //printf("%d, %f\n", width , (newStyle.width() - (float)0x7F) / (float)0x7F); + //printf("%d, %d\n", weight, newStyle.weight()); + //printf("%d, %d\n", width , newStyle.width()); + + // Some back-ends (CG, GDI, DW) support OS/2 version A which uses 0 - 10 (but all differently). + REPORTER_ASSERT(reporter, + newStyle.weight() == weight || + (weight <= 10 && newStyle.weight() == 100 * weight) || + (weight == 4 && newStyle.weight() == 350) || // GDI weirdness + (weight == 5 && newStyle.weight() == 400) || // GDI weirdness + (weight == 0 && newStyle.weight() == 1) || // DW weirdness + (weight == 1000 && newStyle.weight() == 999) // DW weirdness + ); + + // Some back-ends (GDI) don't support width, ensure these always report 'medium'. + REPORTER_ASSERT(reporter, + newStyle.width() == width || + newStyle.width() == 5); +} +DEF_TEST(TypefaceStyle, reporter) { + std::unique_ptr stream(GetResourceAsStream("/fonts/Em.ttf")); + if (!stream) { + REPORT_FAILURE(reporter, "/fonts/Em.ttf", SkString("Cannot load resource")); + return; + } + sk_sp data(SkData::MakeFromStream(stream.get(), stream->getLength())); + + using SkFS = SkFontStyle; + for (int weight = SkFS::kInvisible_Weight; weight <= SkFS::kExtraBlack_Weight; ++weight) { + TypefaceStyle_test(reporter, weight, 5, data.get()); + } + for (int width = SkFS::kUltraCondensed_Width; width <= SkFS::kUltaExpanded_Width; ++width) { + TypefaceStyle_test(reporter, 400, width, data.get()); + } +} + DEF_TEST(Typeface, reporter) { sk_sp t1(SkTypeface::MakeFromName(nullptr, SkFontStyle()));