Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
fontations_ft_compare.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2023 Google LLC
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 "gm/gm.h"
12#include "include/core/SkData.h"
13#include "include/core/SkFont.h"
21#include "tools/Resources.h"
23
24namespace skiagm {
25
26namespace {
27
28constexpr int kGmWidth = 1000;
29constexpr int kMargin = 15;
30constexpr float kFontSize = 24;
31constexpr float kLangYIncrementScale = 1.9;
32
33/** Compare bitmap A and B, in this case originating from text rendering results with FreeType and
34 * Fontations + Skia path rendering, compute individual pixel differences for the rectangles that
35 * must match in size. Produce a highlighted difference bitmap, in which any pixel becomes white for
36 * which a difference was determined. */
37void comparePixels(const SkBitmap& bitmapA,
38 const SkBitmap& bitmapB,
39 SkBitmap* outPixelDiffBitmap,
40 SkBitmap* outHighlightDiffBitmap) {
41 SkASSERT(bitmapA.colorType() == bitmapB.colorType() &&
42 bitmapA.colorType() == outPixelDiffBitmap->colorType() &&
43 bitmapA.dimensions() == bitmapB.dimensions() &&
44 bitmapA.dimensions() == outPixelDiffBitmap->dimensions());
45
46 SkASSERT(bitmapA.bytesPerPixel() == 4);
47 SkISize dimensions = bitmapA.dimensions();
48 for (int32_t x = 0; x < dimensions.fWidth; x++) {
49 for (int32_t y = 0; y < dimensions.fHeight; y++) {
50 SkPMColor c0 = *bitmapA.getAddr32(x, y);
51 SkPMColor c1 = *bitmapB.getAddr32(x, y);
52 int dr = SkGetPackedR32(c0) - SkGetPackedR32(c1);
53 int dg = SkGetPackedG32(c0) - SkGetPackedG32(c1);
54 int db = SkGetPackedB32(c0) - SkGetPackedB32(c1);
55
56 *(outPixelDiffBitmap->getAddr32(x, y)) =
57 SkPackARGB32(0xFF, SkAbs32(dr), SkAbs32(dg), SkAbs32(db));
58
59 if (dr != 0 || dg != 0 || db != 0) {
60 *(outHighlightDiffBitmap->getAddr32(x, y)) = SK_ColorWHITE;
61 } else {
62 *(outHighlightDiffBitmap->getAddr32(x, y)) = SK_ColorBLACK;
63 }
64 }
65 }
66}
67
68} // namespace
69
70class FontationsFtCompareGM : public GM {
71public:
72 FontationsFtCompareGM(std::string testName,
73 std::string fontNameFilterRegexp,
74 std::string langFilterRegexp)
75 : fTestDataIterator(fontNameFilterRegexp, langFilterRegexp)
76 , fTestName(testName.c_str()) {
78 }
79
80protected:
81 SkString getName() const override {
82 return SkStringPrintf("fontations_compare_ft_%s", fTestName.c_str());
83 }
84
85 SkISize getISize() override {
87 fTestDataIterator.rewind();
88 fTestDataIterator.next(&testSet);
89
90 return SkISize::Make(kGmWidth,
91 testSet.langSamples.size() * kFontSize * kLangYIncrementScale + 100);
92 }
93
94 DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override {
96 paint.setColor(SK_ColorBLACK);
97
98 fTestDataIterator.rewind();
100
101 while (fTestDataIterator.next(&testSet)) {
103 SkStream::MakeFromFile(testSet.fontFilename.c_str()), SkFontArguments());
105 SkStream::MakeFromFile(testSet.fontFilename.c_str()), SkFontArguments());
106
107 if (!testTypeface || !ftTypeface) {
108 *errorMsg = "Unable to initialize typeface.";
109 return DrawResult::kSkip;
110 }
111
112 auto configureFont = [](SkFont& font) {
113 font.setSize(kFontSize);
115 font.setSubpixel(true);
116 font.setHinting(SkFontHinting::kNone);
117 };
118
119 SkFont font(testTypeface);
120 configureFont(font);
121
122 SkFont ftFont(ftTypeface);
123 configureFont(ftFont);
124 enum class DrawPhase { Fontations, FreeType, Comparison };
125
126 SkRect maxBounds = SkRect::MakeEmpty();
127 for (auto phase : {DrawPhase::Fontations, DrawPhase::FreeType, DrawPhase::Comparison}) {
128 SkScalar yCoord = kFontSize * 1.5f;
129
130 for (auto& langEntry : testSet.langSamples) {
131 auto shapeAndDrawToCanvas = [canvas, paint, langEntry](const SkFont& font,
132 SkPoint coord) {
133 std::string testString(langEntry.sampleShort.c_str(),
134 langEntry.sampleShort.size());
135 SkTextBlobBuilderRunHandler textBlobBuilder(testString.c_str(), {0, 0});
136 std::unique_ptr<SkShaper> shaper = SkShaper::Make();
137 shaper->shape(testString.c_str(),
138 testString.size(),
139 font,
140 true,
141 999999, /* Don't linebreak. */
142 &textBlobBuilder);
143 sk_sp<const SkTextBlob> blob = textBlobBuilder.makeBlob();
144 canvas->drawTextBlob(blob.get(), coord.x(), coord.y(), paint);
145 return blob->bounds();
146 };
147
148 auto roundToDevicePixels = [canvas](SkPoint& point) {
149 SkMatrix ctm = canvas->getLocalToDeviceAs3x3();
150 SkPoint mapped = ctm.mapPoint(point);
151 SkPoint mappedRounded =
152 SkPoint::Make(roundf(mapped.x()), roundf(mapped.y()));
153 SkMatrix inverse;
154 bool inverseExists = ctm.invert(&inverse);
155 SkASSERT(inverseExists);
156 if (inverseExists) {
157 point = inverse.mapPoint(mappedRounded);
158 }
159 };
160
161 auto fontationsCoord = [yCoord, roundToDevicePixels]() {
162 SkPoint fontationsCoord = SkPoint::Make(kMargin, yCoord);
163 roundToDevicePixels(fontationsCoord);
164 return fontationsCoord;
165 };
166
167 auto freetypeCoord = [yCoord, maxBounds, roundToDevicePixels]() {
168 SkPoint freetypeCoord = SkPoint::Make(
169 2 * kMargin + maxBounds.left() + maxBounds.width(), yCoord);
170 roundToDevicePixels(freetypeCoord);
171 return freetypeCoord;
172 };
173
174 switch (phase) {
175 case DrawPhase::Fontations: {
176 SkRect boundsFontations = shapeAndDrawToCanvas(font, fontationsCoord());
177 /* Determine maximum of column width across all language samples. */
178 boundsFontations.roundOut();
179 maxBounds.join(boundsFontations);
180 break;
181 }
182 case DrawPhase::FreeType: {
183 shapeAndDrawToCanvas(ftFont, freetypeCoord());
184 break;
185 }
186 case DrawPhase::Comparison: {
187 /* Read back pixels from equally sized rectangles from the space in
188 * SkCanvas where Fontations and FreeType sample texts were drawn,
189 * compare them using pixel comparisons similar to SkDiff, draw a
190 * comparison as faint pixel differences, and as an amplified
191 * visualization in which each differing pixel is drawn as white. */
192 SkPoint copyBoxFontationsCoord = fontationsCoord();
193 SkPoint copyBoxFreetypeCoord = freetypeCoord();
194 SkRect copyBoxFontations(maxBounds);
195 copyBoxFontations.roundOut(&copyBoxFontations);
196 SkRect copyBoxFreetype(copyBoxFontations);
197 copyBoxFontations.offset(copyBoxFontationsCoord.x(),
198 copyBoxFontationsCoord.y());
199 copyBoxFreetype.offset(copyBoxFreetypeCoord.x(),
200 copyBoxFreetypeCoord.y());
201
202 SkMatrix ctm = canvas->getLocalToDeviceAs3x3();
203 ctm.mapRect(&copyBoxFontations, copyBoxFontations);
204 ctm.mapRect(&copyBoxFreetype, copyBoxFreetype);
205
206 SkISize pixelDimensions(copyBoxFontations.roundOut().size());
207 SkImageInfo canvasImageInfo = canvas->imageInfo();
208 SkImageInfo copyImageInfo =
209 SkImageInfo::Make(pixelDimensions,
210 canvasImageInfo.colorType(),
211 canvasImageInfo.alphaType());
212
213 SkBitmap fontationsBitmap, freetypeBitmap, diffBitmap,
214 highlightDiffBitmap;
215 fontationsBitmap.allocPixels(copyImageInfo, 0);
216 freetypeBitmap.allocPixels(copyImageInfo, 0);
217 diffBitmap.allocPixels(copyImageInfo, 0);
218 highlightDiffBitmap.allocPixels(copyImageInfo, 0);
219
220 canvas->readPixels(
221 fontationsBitmap, copyBoxFontations.x(), copyBoxFontations.y());
222 canvas->readPixels(
223 freetypeBitmap, copyBoxFreetype.x(), copyBoxFreetype.y());
224
225 comparePixels(fontationsBitmap,
226 freetypeBitmap,
227 &diffBitmap,
228 &highlightDiffBitmap);
229
230 /* Place comparison results as two extra columns, shift up to account
231 for placement of rectangles vs. SkTextBlobs (baseline shift). */
232 SkPoint comparisonCoord = ctm.mapPoint(SkPoint::Make(
233 3 * kMargin + maxBounds.width() * 2, yCoord + maxBounds.top()));
234 SkPoint whiteCoord = ctm.mapPoint(SkPoint::Make(
235 4 * kMargin + maxBounds.width() * 3, yCoord + maxBounds.top()));
236
237 canvas->writePixels(
238 diffBitmap, comparisonCoord.x(), comparisonCoord.y());
239 canvas->writePixels(
240 highlightDiffBitmap, whiteCoord.x(), whiteCoord.y());
241 break;
242 }
243 }
244
245 yCoord += font.getSize() * kLangYIncrementScale;
246 }
247 }
248 }
249
250 return DrawResult::kOk;
251 }
252
253private:
254 using INHERITED = GM;
255
256 TestFontDataProvider fTestDataIterator;
257 SkString fTestName;
258 sk_sp<SkTypeface> fReportTypeface;
259 std::unique_ptr<SkFontArguments::VariationPosition::Coordinate[]> fCoordinates;
260};
261
263 "NotoSans",
264 "Noto Sans",
265 "en_Latn|es_Latn|pt_Latn|id_Latn|ru_Cyrl|fr_Latn|tr_Latn|vi_Latn|de_"
266 "Latn|it_Latn|pl_Latn|nl_Latn|uk_Cyrl|gl_Latn|ro_Latn|cs_Latn|hu_Latn|"
267 "el_Grek|se_Latn|da_Latn|bg_Latn|sk_Latn|fi_Latn|bs_Latn|ca_Latn|no_"
268 "Latn|sr_Latn|sr_Cyrl|lt_Latn|hr_Latn|sl_Latn|uz_Latn|uz_Cyrl|lv_Latn|"
269 "et_Latn|az_Latn|az_Cyrl|la_Latn|tg_Latn|tg_Cyrl|sw_Latn|mn_Cyrl|kk_"
270 "Latn|kk_Cyrl|sq_Latn|af_Latn|ha_Latn|ky_Cyrl"));
271
272DEF_GM(return new FontationsFtCompareGM("NotoSans_Deva",
273 "Noto Sans Devanagari",
274 "hi_Deva|mr_Deva"));
275
276DEF_GM(return new FontationsFtCompareGM("NotoSans_ar_Arab",
277 "Noto Sans Arabic",
278 "ar_Arab|uz_Arab|kk_Arab|ky_Arab"));
279
280DEF_GM(return new FontationsFtCompareGM("NotoSans_Beng", "Noto Sans Bengali", "bn_Beng"));
281
282DEF_GM(return new FontationsFtCompareGM("NotoSans_Jpan", "Noto Sans JP", "ja_Jpan"));
283
284DEF_GM(return new FontationsFtCompareGM("NotoSans_Thai", "Noto Sans Thai", "th_Thai"));
285
286DEF_GM(return new FontationsFtCompareGM("NotoSans_Hans", "Noto Sans SC", "zh_Hans"));
287
288DEF_GM(return new FontationsFtCompareGM("NotoSans_Hant", "Noto Sans TC", "zh_Hant"));
289
290DEF_GM(return new FontationsFtCompareGM("NotoSans_Kore", "Noto Sans KR", "ko_Kore"));
291
292DEF_GM(return new FontationsFtCompareGM("NotoSans_Taml", "Noto Sans Tamil", "ta_Taml"));
293
294DEF_GM(return new FontationsFtCompareGM("NotoSans_Newa", "Noto Sans Newa", "new_Newa"));
295
296DEF_GM(return new FontationsFtCompareGM("NotoSans_Knda", "Noto Sans Kannada", "kn_Knda"));
297
298DEF_GM(return new FontationsFtCompareGM("NotoSans_Tglg", "Noto Sans Tagalog", "fil_Tglg"));
299
300DEF_GM(return new FontationsFtCompareGM("NotoSans_Telu", "Noto Sans Telugu", "te_Telu"));
301
302DEF_GM(return new FontationsFtCompareGM("NotoSans_Gujr", "Noto Sans Gujarati", "gu_Gujr"));
303
304DEF_GM(return new FontationsFtCompareGM("NotoSans_Geor", "Noto Sans Georgian", "ka_Geor"));
305
306DEF_GM(return new FontationsFtCompareGM("NotoSans_Mlym", "Noto Sans Malayalam", "ml_Mlym"));
307
308DEF_GM(return new FontationsFtCompareGM("NotoSans_Khmr", "Noto Sans Khmer", "km_Khmr"));
309
310DEF_GM(return new FontationsFtCompareGM("NotoSans_Sinh", "Noto Sans Sinhala", "si_Sinh"));
311
312DEF_GM(return new FontationsFtCompareGM("NotoSans_Mymr", "Noto Sans Myanmar", "my_Mymr"));
313
314DEF_GM(return new FontationsFtCompareGM("NotoSans_Java", "Noto Sans Javanese", "jv_Java"));
315
316DEF_GM(return new FontationsFtCompareGM("NotoSans_Mong", "Noto Sans Mongolian", "mn_Mong"));
317
318DEF_GM(return new FontationsFtCompareGM("NotoSans_Armn", "Noto Sans Armenian", "hy_Armn"));
319
320DEF_GM(return new FontationsFtCompareGM("NotoSans_Elba", "Noto Sans Elbasan", "sq_Elba"));
321
322DEF_GM(return new FontationsFtCompareGM("NotoSans_Vith", "Noto Sans Vithkuqi", "sq_Vith"));
323
324DEF_GM(return new FontationsFtCompareGM("NotoSans_Guru", "Noto Sans Gurmukhi", "pa_Guru"));
325
326} // namespace skiagm
static const ConicPts testSet[]
#define SkASSERT(cond)
Definition SkAssert.h:116
#define SkGetPackedB32(packed)
Definition SkColorPriv.h:95
#define SkGetPackedR32(packed)
Definition SkColorPriv.h:93
#define SkGetPackedG32(packed)
Definition SkColorPriv.h:94
static SkPMColor SkPackARGB32(U8CPU a, U8CPU r, U8CPU g, U8CPU b)
uint32_t SkPMColor
Definition SkColor.h:205
constexpr SkColor SK_ColorBLACK
Definition SkColor.h:103
constexpr SkColor SK_ColorWHITE
Definition SkColor.h:122
@ kNone
glyph outlines unchanged
static int32_t SkAbs32(int32_t value)
Definition SkSafe32.h:41
SK_API SkString static SkString SkStringPrintf()
Definition SkString.h:287
SK_API sk_sp< SkTypeface > SkTypeface_Make_Fontations(std::unique_ptr< SkStreamAsset > fontData, const SkFontArguments &args)
@ kMargin
void allocPixels(const SkImageInfo &info, size_t rowBytes)
Definition SkBitmap.cpp:258
SkISize dimensions() const
Definition SkBitmap.h:388
SkColorType colorType() const
Definition SkBitmap.h:160
int bytesPerPixel() const
Definition SkBitmap.h:187
uint32_t * getAddr32(int x, int y) const
Definition SkBitmap.h:1260
SkMatrix getLocalToDeviceAs3x3() const
Definition SkCanvas.h:2222
bool writePixels(const SkImageInfo &info, const void *pixels, size_t rowBytes, int x, int y)
Definition SkCanvas.cpp:403
SkImageInfo imageInfo() const
bool readPixels(const SkImageInfo &dstInfo, void *dstPixels, size_t dstRowBytes, int srcX, int srcY)
Definition SkCanvas.cpp:386
void drawTextBlob(const SkTextBlob *blob, SkScalar x, SkScalar y, const SkPaint &paint)
@ kSubpixelAntiAlias
glyph positioned in pixel using transparency
SkPoint mapPoint(SkPoint pt) const
Definition SkMatrix.h:1374
bool invert(SkMatrix *inverse) const
Definition SkMatrix.h:1206
bool mapRect(SkRect *dst, const SkRect &src, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
static std::unique_ptr< SkShaper > Make(sk_sp< SkFontMgr > fallback=nullptr)
Definition SkShaper.cpp:36
static std::unique_ptr< SkStreamAsset > MakeFromFile(const char path[])
Definition SkStream.cpp:922
const char * c_str() const
Definition SkString.h:133
sk_sp< SkTextBlob > makeBlob()
Definition SkShaper.cpp:257
static sk_sp< SkTypeface > MakeFromStream(std::unique_ptr< SkStreamAsset >, const SkFontArguments &)
bool next(TestSet *testSet)
T * get() const
Definition SkRefCnt.h:303
SkString getName() const override
FontationsFtCompareGM(std::string testName, std::string fontNameFilterRegexp, std::string langFilterRegexp)
DrawResult onDraw(SkCanvas *canvas, SkString *errorMsg) override
void setBGColor(SkColor)
Definition gm.cpp:159
const Paint & paint
float SkScalar
Definition extension.cpp:12
#define DEF_GM(CODE)
Definition gm.h:40
double y
double x
DrawResult
Definition gm.h:104
static constexpr SkISize Make(int32_t w, int32_t h)
Definition SkSize.h:20
int32_t fHeight
Definition SkSize.h:18
int32_t fWidth
Definition SkSize.h:17
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
SkAlphaType alphaType() const
SkColorType colorType() const
static constexpr SkPoint Make(float x, float y)
constexpr float y() const
constexpr float x() const
static constexpr SkRect MakeEmpty()
Definition SkRect.h:595
constexpr float x() const
Definition SkRect.h:720
constexpr float y() const
Definition SkRect.h:727
void roundOut(SkIRect *dst) const
Definition SkRect.h:1241
void offset(float dx, float dy)
Definition SkRect.h:1016