Ben Wagner | a3e5e55 | 2021-02-22 15:37:33 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2021 Google Inc. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license that can be |
| 5 | * found in the LICENSE file. |
| 6 | */ |
| 7 | |
| 8 | #include "include/core/SkFontMgr.h" |
| 9 | #include "include/core/SkRefCnt.h" |
| 10 | #include "include/core/SkTypeface.h" |
| 11 | #include "include/ports/SkTypeface_mac.h" |
| 12 | #include "src/core/SkZip.h" |
| 13 | #include "tests/Test.h" |
| 14 | |
| 15 | #include <stdarg.h> |
| 16 | #include <string> |
| 17 | #include <vector> |
| 18 | |
| 19 | #if 0 |
| 20 | static void SkMaybeDebugf(const char format[], ...) { |
| 21 | va_list args; |
| 22 | va_start(args, format); |
| 23 | vprintf(format, args); |
| 24 | va_end(args); |
| 25 | } |
| 26 | #else |
| 27 | static void SkMaybeDebugf(const char format[], ...) { } |
| 28 | #endif |
| 29 | |
| 30 | DEF_TEST(TypefaceMacVariation, reporter) { |
| 31 | auto makeSystemFont = [](float size) -> CTFontRef { |
| 32 | // kCTFontUIFontSystem, kCTFontUIFontMessage |
| 33 | return CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, size, nullptr); |
| 34 | }; |
| 35 | |
| 36 | auto tagToString = [](SkFourByteTag tag) -> std::string { |
| 37 | char buffer[5]; |
| 38 | buffer[0] = (tag & 0xff000000) >> 24; |
| 39 | buffer[1] = (tag & 0xff0000) >> 16; |
| 40 | buffer[2] = (tag & 0xff00) >> 8; |
| 41 | buffer[3] = tag & 0xff; |
| 42 | buffer[4] = 0; |
| 43 | return std::string(buffer); |
| 44 | }; |
| 45 | |
| 46 | // This typeface has the issue. |
| 47 | sk_sp<SkTypeface> typeface(SkMakeTypefaceFromCTFont(makeSystemFont(30))); |
| 48 | |
| 49 | // Since MakeFromFile creates at default size 12, these two are more comparable. |
| 50 | // The first one has the issue and the second does not. |
| 51 | //typeface = SkMakeTypefaceFromCTFont(makeSystemFont(12)); |
| 52 | //typeface = SkTypeface::MakeFromFile("/System/Library/Fonts/SFNS.ttf"); |
| 53 | |
| 54 | // Setting the initial opsz <= min, the reported wght axis is strange, but draws the same? |
| 55 | //typeface = SkMakeTypefaceFromCTFont(makeSystemFont(17)); |
| 56 | //typeface = SkMakeTypefaceFromCTFont(makeSystemFont(17.01)); |
| 57 | |
| 58 | // Setting the initial opsz >= max, the requested variation doesn't take effect? |
| 59 | //typeface = SkMakeTypefaceFromCTFont(makeSystemFont(95.9)); |
| 60 | //typeface = SkMakeTypefaceFromCTFont(makeSystemFont(96)); |
| 61 | |
| 62 | if (!typeface) { |
| 63 | REPORTER_ASSERT(reporter, typeface); |
| 64 | return; |
| 65 | } |
| 66 | using Coordinate = SkFontArguments::VariationPosition::Coordinate; |
| 67 | using Axis = SkFontParameters::Variation::Axis; |
| 68 | |
| 69 | const int originalPositionCount = typeface->getVariationDesignPosition(nullptr, 0); |
| 70 | std::vector<Coordinate> originalPosition(originalPositionCount); |
| 71 | const int retrievedOriginalPositionCount = |
| 72 | typeface->getVariationDesignPosition(originalPosition.data(), originalPosition.size()); |
| 73 | if (!(retrievedOriginalPositionCount == originalPositionCount)) { |
| 74 | REPORTER_ASSERT(reporter, retrievedOriginalPositionCount == originalPositionCount); |
| 75 | return; |
| 76 | } |
| 77 | |
| 78 | constexpr SkFourByteTag kGRADTag = SkSetFourByteTag('G', 'R', 'A', 'D'); |
| 79 | constexpr SkFourByteTag kWghtTag = SkSetFourByteTag('w', 'g', 'h', 't'); |
| 80 | constexpr SkFourByteTag kWdthTag = SkSetFourByteTag('w', 'd', 't', 'h'); |
| 81 | constexpr SkFourByteTag kOpszTag = SkSetFourByteTag('o', 'p', 's', 'z'); |
| 82 | |
| 83 | SkMaybeDebugf("Original: "); |
| 84 | for (auto& originalCoordinate : originalPosition) { |
| 85 | SkMaybeDebugf("(%s: %f) ", tagToString(originalCoordinate.axis).c_str(), |
| 86 | originalCoordinate.value); |
| 87 | } |
| 88 | SkMaybeDebugf("\n\n"); |
| 89 | |
| 90 | const int originalAxisCount = typeface->getVariationDesignParameters(nullptr, 0); |
| 91 | std::vector<Axis> originalAxes(originalAxisCount); |
| 92 | const int returnedOriginalAxisCount = |
| 93 | typeface->getVariationDesignParameters(originalAxes.data(), originalAxes.size()); |
| 94 | if (!(returnedOriginalAxisCount == originalAxisCount)) { |
| 95 | REPORTER_ASSERT(reporter, returnedOriginalAxisCount == originalAxisCount); |
| 96 | return; |
| 97 | } |
| 98 | |
| 99 | for (bool omitOpsz : {false, true}) { |
| 100 | for (SkFourByteTag axisToBump : { 0u, kOpszTag, kWdthTag, kGRADTag }) { |
| 101 | for (float testCoordinate : {100, 300, 400, 500, 700, 900}) { |
| 102 | std::vector<Coordinate> expectedPosition; |
| 103 | std::vector<Coordinate> requestPosition; |
| 104 | SkMaybeDebugf("Request : "); |
| 105 | for (auto& originalCoordinate : originalPosition) { |
| 106 | float requestValue = originalCoordinate.value; |
| 107 | if (originalCoordinate.axis == kOpszTag && omitOpsz) { |
| 108 | SkMaybeDebugf("#%s: %f# ", tagToString(originalCoordinate.axis).c_str(), |
| 109 | requestValue); |
| 110 | } else { |
| 111 | if (originalCoordinate.axis == axisToBump) { |
| 112 | // CoreText floats for the variation coordinates have limited precision. |
| 113 | // 'opsz' sems to have more precision since it is set somewhat independently. |
| 114 | //requestValue = nextafter(requestValue, HUGE_VALF); // Does not work. |
| 115 | requestValue += requestValue / 1024.0f; // Expect at least 10 bits. |
| 116 | } |
| 117 | if (originalCoordinate.axis == kWghtTag) { |
| 118 | requestValue = testCoordinate; |
| 119 | } |
| 120 | SkMaybeDebugf("(%s: %f) ", tagToString(originalCoordinate.axis).c_str(), |
| 121 | requestValue); |
| 122 | requestPosition.push_back({originalCoordinate.axis, requestValue}); |
| 123 | } |
| 124 | |
| 125 | float expectedValue = requestValue; |
| 126 | for (auto& originalAxis : originalAxes) { |
| 127 | if (originalAxis.tag == originalCoordinate.axis) { |
| 128 | expectedValue = std::min(expectedValue, originalAxis.max); |
| 129 | expectedValue = std::max(expectedValue, originalAxis.min); |
| 130 | } |
| 131 | } |
| 132 | expectedPosition.push_back({originalCoordinate.axis, expectedValue}); |
| 133 | } |
| 134 | SkMaybeDebugf("\n"); |
| 135 | |
| 136 | SkMaybeDebugf("Expected: "); |
| 137 | for (auto& expectedCoordinate : expectedPosition) { |
| 138 | SkMaybeDebugf("(%s: %f) ", tagToString(expectedCoordinate.axis).c_str(), |
| 139 | expectedCoordinate.value); |
| 140 | } |
| 141 | SkMaybeDebugf("\n"); |
| 142 | |
| 143 | SkFontArguments::VariationPosition variationPosition = |
| 144 | { requestPosition.data(), (int)requestPosition.size() }; |
| 145 | sk_sp<SkTypeface> cloneTypeface( |
| 146 | typeface->makeClone(SkFontArguments().setVariationDesignPosition(variationPosition))); |
| 147 | |
| 148 | const int cloneAxisCount = cloneTypeface->getVariationDesignPosition(nullptr, 0); |
| 149 | std::vector<Coordinate> clonePosition(cloneAxisCount); |
| 150 | const int retrievedCloneAxisCount = |
| 151 | cloneTypeface->getVariationDesignPosition(clonePosition.data(), clonePosition.size()); |
| 152 | if (!(retrievedCloneAxisCount == cloneAxisCount)) { |
| 153 | REPORTER_ASSERT(reporter, retrievedCloneAxisCount == cloneAxisCount); |
| 154 | continue; |
| 155 | } |
| 156 | |
| 157 | SkMaybeDebugf("Result : "); |
| 158 | for (auto& cloneCoordinate : clonePosition) { |
| 159 | SkMaybeDebugf("(%s: %f) ", tagToString(cloneCoordinate.axis).c_str(), |
| 160 | cloneCoordinate.value); |
| 161 | } |
| 162 | SkMaybeDebugf("\n"); |
| 163 | |
| 164 | if (clonePosition.size() != expectedPosition.size()) { |
| 165 | REPORTER_ASSERT(reporter, clonePosition.size() == expectedPosition.size()); |
| 166 | continue; |
| 167 | } |
| 168 | |
| 169 | auto compareCoordinate = [](const Coordinate& a, const Coordinate& b) -> bool { |
| 170 | return a.axis < b.axis; |
| 171 | }; |
| 172 | std::sort(clonePosition.begin(), clonePosition.end(), compareCoordinate); |
| 173 | std::sort(expectedPosition.begin(), expectedPosition.end(), compareCoordinate); |
| 174 | for (const auto&& [clone, expected] : SkMakeZip(clonePosition, expectedPosition)) { |
| 175 | REPORTER_ASSERT(reporter, clone.axis == expected.axis); |
| 176 | REPORTER_ASSERT(reporter, clone.value == expected.value); |
| 177 | } |
| 178 | |
| 179 | SkMaybeDebugf("\n"); |
| 180 | } |
| 181 | } |
| 182 | } |
| 183 | } |