Flutter Engine
The Flutter Engine
PDFPrimitivesTest.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2010 The Android Open Source Project
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 "tests/Test.h"
9
10#ifdef SK_SUPPORT_PDF
11
16#include "include/core/SkFont.h"
21#include "include/core/SkPath.h"
23#include "include/core/SkRect.h"
26#include "include/core/SkSpan.h"
36#include "src/base/SkRandom.h"
41#include "src/pdf/SkPDFFont.h"
42#include "src/pdf/SkPDFTypes.h"
43#include "src/pdf/SkPDFUnion.h"
44#include "src/pdf/SkPDFUtils.h"
45#include "src/text/GlyphRun.h"
47#include "tools/Resources.h"
48#include "tools/ToolUtils.h"
50
51#include <cfloat>
52#include <cmath>
53#include <cstdint>
54#include <cstdio>
55#include <cstdlib>
56#include <cstring>
57#include <memory>
58#include <optional>
59#include <utility>
60
61class SkTypeface;
62
63template <typename T>
64static SkString emit_to_string(T& obj) {
66 obj.emitObject(&buffer);
67 SkString tmp(buffer.bytesWritten());
68 buffer.copyTo(tmp.data());
69 return tmp;
70}
71
72static bool eq(const SkString& str, const char* strPtr, size_t len) {
73 return len == str.size() && 0 == memcmp(str.c_str(), strPtr, len);
74}
75
76static void assert_eql(skiatest::Reporter* reporter,
77 const SkString& skString,
78 const char* str,
79 size_t len) {
80 if (!eq(skString, str, len)) {
82 "'%*s' != '%s'", (int)len, str, skString.c_str()));
83 }
84}
85
86static void assert_eq(skiatest::Reporter* reporter,
87 const SkString& skString,
88 const char* str) {
89 assert_eql(reporter, skString, str, strlen(str));
90}
91
92
93template <typename T>
94static void assert_emit_eq(skiatest::Reporter* reporter,
95 T& object,
96 const char* string) {
97 SkString result = emit_to_string(object);
98 assert_eq(reporter, result, string);
99}
100
101// This test used to assert without the fix submitted for
102// http://code.google.com/p/skia/issues/detail?id=1083.
103// SKP files might have invalid glyph ids. This test ensures they are ignored,
104// and there is no assert on input data in Debug mode.
105static void test_issue1083() {
106 SkDynamicMemoryWStream outStream;
107 auto doc = SkPDF::MakeDocument(&outStream);
108 SkCanvas* canvas = doc->beginPage(100.0f, 100.0f);
109
110 uint16_t glyphID = 65000;
112 canvas->drawSimpleText(&glyphID, 2, SkTextEncoding::kGlyphID, 0, 0, font, SkPaint());
113
114 doc->close();
115}
116
117static void assert_emit_eq_number(skiatest::Reporter* reporter, float number) {
118 SkPDFUnion pdfUnion = SkPDFUnion::Scalar(number);
119 SkString result = emit_to_string(pdfUnion);
120 float value = static_cast<float>(std::atof(result.c_str()));
121 if (value != number) {
122 ERRORF(reporter, "%.9g != %s", number, result.c_str());
123 }
124}
125
126
127static void TestPDFUnion(skiatest::Reporter* reporter) {
128 SkPDFUnion boolTrue = SkPDFUnion::Bool(true);
129 assert_emit_eq(reporter, boolTrue, "true");
130
131 SkPDFUnion boolFalse = SkPDFUnion::Bool(false);
132 assert_emit_eq(reporter, boolFalse, "false");
133
134 SkPDFUnion int42 = SkPDFUnion::Int(42);
135 assert_emit_eq(reporter, int42, "42");
136
137 assert_emit_eq_number(reporter, SK_ScalarHalf);
138 assert_emit_eq_number(reporter, 110999.75f); // bigScalar
139 assert_emit_eq_number(reporter, 50000000.1f); // biggerScalar
140 assert_emit_eq_number(reporter, 1.0f / 65536); // smallScalar
141
142 SkPDFUnion stringSimple = SkPDFUnion::TextString("test ) string ( foo");
143 assert_emit_eq(reporter, stringSimple, "(test \\) string \\( foo)");
144
145 SkString stringComplexInput("\ttest ) string ( foo");
146 SkPDFUnion stringComplex = SkPDFUnion::TextString(stringComplexInput);
147 assert_emit_eq(reporter, stringComplex, "(\\011test \\) string \\( foo)");
148
149 SkString binaryStringInput("\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20");
150 SkPDFUnion binaryString = SkPDFUnion::ByteString(binaryStringInput);
151 assert_emit_eq(reporter, binaryString, "<0102030405060708090A0B0C0D0E0F10>");
152
153 SkString nameInput("Test name\twith#tab");
154 SkPDFUnion name = SkPDFUnion::Name(nameInput);
155 assert_emit_eq(reporter, name, "/Test#20name#09with#23tab");
156
157 SkString nameInput2("A#/%()<>[]{}B");
158 SkPDFUnion name2 = SkPDFUnion::Name(nameInput2);
159 assert_emit_eq(reporter, name2, "/A#23#2F#25#28#29#3C#3E#5B#5D#7B#7DB");
160
161 SkPDFUnion name3 = SkPDFUnion::Name("SimpleNameWithOnlyPrintableASCII");
162 assert_emit_eq(reporter, name3, "/SimpleNameWithOnlyPrintableASCII");
163
164 // Test that we correctly handle characters with the high-bit set.
165 SkString highBitString("\xDE\xAD" "be\xEF");
166 SkPDFUnion highBitName = SkPDFUnion::Name(highBitString);
167 assert_emit_eq(reporter, highBitName, "/#DE#ADbe#EF");
168
169 // https://bugs.skia.org/9508
170 // https://crbug.com/494913
171 // Trailing '\0' characters must be removed.
172 const char nameInput4[] = "Test name with nil\0";
173 SkPDFUnion name4 = SkPDFUnion::Name(SkString(nameInput4, strlen(nameInput4) + 1));
174 assert_emit_eq(reporter, name4, "/Test#20name#20with#20nil");
175}
176
177static void TestPDFArray(skiatest::Reporter* reporter) {
178 std::unique_ptr<SkPDFArray> array(new SkPDFArray);
179 assert_emit_eq(reporter, *array, "[]");
180
181 array->appendInt(42);
182 assert_emit_eq(reporter, *array, "[42]");
183
184 array->appendScalar(SK_ScalarHalf);
185 assert_emit_eq(reporter, *array, "[42 .5]");
186
187 array->appendInt(0);
188 assert_emit_eq(reporter, *array, "[42 .5 0]");
189
190 array->appendBool(true);
191 assert_emit_eq(reporter, *array, "[42 .5 0 true]");
192
193 array->appendName("ThisName");
194 assert_emit_eq(reporter, *array, "[42 .5 0 true /ThisName]");
195
196 array->appendName(SkString("AnotherName"));
197 assert_emit_eq(reporter, *array, "[42 .5 0 true /ThisName /AnotherName]");
198
199 array->appendTextString("This String");
200 assert_emit_eq(reporter, *array,
201 "[42 .5 0 true /ThisName /AnotherName (This String)]");
202
203 array->appendByteString(SkString("Another String"));
204 assert_emit_eq(reporter, *array,
205 "[42 .5 0 true /ThisName /AnotherName (This String) "
206 "(Another String)]");
207
208 std::unique_ptr<SkPDFArray> innerArray(new SkPDFArray);
209 innerArray->appendInt(-1);
210 array->appendObject(std::move(innerArray));
211 assert_emit_eq(reporter, *array,
212 "[42 .5 0 true /ThisName /AnotherName (This String) "
213 "(Another String) [-1]]");
214}
215
216static void TestPDFDict(skiatest::Reporter* reporter) {
217 std::unique_ptr<SkPDFDict> dict(new SkPDFDict);
218 assert_emit_eq(reporter, *dict, "<<>>");
219
220 dict->insertInt("n1", SkToSizeT(42));
221 assert_emit_eq(reporter, *dict, "<</n1 42>>");
222
223 dict = std::make_unique<SkPDFDict>();
224 assert_emit_eq(reporter, *dict, "<<>>");
225
226 dict->insertInt("n1", 42);
227 assert_emit_eq(reporter, *dict, "<</n1 42>>");
228
229 dict->insertScalar("n2", SK_ScalarHalf);
230
231 SkString n3("n3");
232 std::unique_ptr<SkPDFArray> innerArray(new SkPDFArray);
233 innerArray->appendInt(-100);
234 dict->insertObject(n3, std::move(innerArray));
235 assert_emit_eq(reporter, *dict, "<</n1 42\n/n2 .5\n/n3 [-100]>>");
236
237 dict = std::make_unique<SkPDFDict>();
238 assert_emit_eq(reporter, *dict, "<<>>");
239
240 dict->insertInt("n1", 24);
241 assert_emit_eq(reporter, *dict, "<</n1 24>>");
242
243 dict->insertInt("n2", SkToSizeT(99));
244 assert_emit_eq(reporter, *dict, "<</n1 24\n/n2 99>>");
245
246 dict->insertScalar("n3", SK_ScalarHalf);
247 assert_emit_eq(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5>>");
248
249 dict->insertName("n4", "AName");
250 assert_emit_eq(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5\n/n4 /AName>>");
251
252 dict->insertName("n5", SkString("AnotherName"));
253 assert_emit_eq(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5\n/n4 /AName\n"
254 "/n5 /AnotherName>>");
255
256 dict->insertTextString("n6", "A String");
257 assert_emit_eq(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5\n/n4 /AName\n"
258 "/n5 /AnotherName\n/n6 (A String)>>");
259
260 dict->insertByteString("n7", SkString("Another String"));
261 assert_emit_eq(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5\n/n4 /AName\n"
262 "/n5 /AnotherName\n/n6 (A String)\n/n7 (Another String)>>");
263
264 dict = std::make_unique<SkPDFDict>("DType");
265 assert_emit_eq(reporter, *dict, "<</Type /DType>>");
266}
267
268DEF_TEST(SkPDF_Primitives, reporter) {
269 TestPDFUnion(reporter);
270 TestPDFArray(reporter);
271 TestPDFDict(reporter);
272 test_issue1083();
273}
274
275namespace {
276
277class TestImageFilter : public SkImageFilter_Base {
278public:
279 TestImageFilter() : SkImageFilter_Base(nullptr, 0), fVisited(false) {}
280
281 bool visited() const { return fVisited; }
282
283private:
284 Factory getFactory() const override {
285 SK_ABORT("Does not participate in serialization");
286 return nullptr;
287 }
288 const char* getTypeName() const override { return "TestImageFilter"; }
289
290 skif::FilterResult onFilterImage(const skif::Context& ctx) const override {
291 fVisited = true;
292 return ctx.source();
293 }
294
295 skif::LayerSpace<SkIRect> onGetInputLayerBounds(
296 const skif::Mapping& mapping,
297 const skif::LayerSpace<SkIRect>& desiredOutput,
298 std::optional<skif::LayerSpace<SkIRect>> contentBounds) const override {
299 return desiredOutput;
300 }
301
302 std::optional<skif::LayerSpace<SkIRect>> onGetOutputLayerBounds(
303 const skif::Mapping& mapping,
304 std::optional<skif::LayerSpace<SkIRect>> contentBounds) const override {
305 return contentBounds;
306 }
307
308 mutable bool fVisited;
309};
310
311} // namespace
312
313// Check that PDF rendering of image filters successfully falls back to
314// CPU rasterization.
315DEF_TEST(SkPDF_ImageFilter, reporter) {
316 REQUIRE_PDF_DOCUMENT(SkPDF_ImageFilter, reporter);
318 auto doc = SkPDF::MakeDocument(&stream);
319 SkCanvas* canvas = doc->beginPage(100.0f, 100.0f);
320
321 sk_sp<TestImageFilter> filter(new TestImageFilter());
322
323 // Filter just created; should be unvisited.
324 REPORTER_ASSERT(reporter, !filter->visited());
326 paint.setImageFilter(filter);
327 canvas->drawRect(SkRect::MakeWH(100, 100), paint);
328 doc->close();
329
330 // Filter was used in rendering; should be visited.
331 REPORTER_ASSERT(reporter, filter->visited());
332}
333
334// Check that PDF rendering of image filters successfully falls back to
335// CPU rasterization.
336DEF_TEST(SkPDF_FontCanEmbedTypeface, reporter) {
337 SkNullWStream nullWStream;
338 SkPDFDocument doc(&nullWStream, SkPDF::Metadata());
339
340 const char resource[] = "fonts/Roboto2-Regular_NoEmbed.ttf";
342 if (noEmbedTypeface) {
344 !SkPDFFont::CanEmbedTypeface(noEmbedTypeface.get(), &doc));
345 }
348 SkPDFFont::CanEmbedTypeface(portableTypeface.get(), &doc));
349}
350
351
352// test to see that all finite scalars round trip via scanf().
353static void check_pdf_scalar_serialization(
354 skiatest::Reporter* reporter, float inputFloat) {
355 char floatString[kMaximumSkFloatToDecimalLength];
356 size_t len = SkFloatToDecimal(inputFloat, floatString);
357 if (len >= sizeof(floatString)) {
358 ERRORF(reporter, "string too long: %u", (unsigned)len);
359 return;
360 }
361 if (floatString[len] != '\0' || strlen(floatString) != len) {
362 ERRORF(reporter, "terminator misplaced.");
363 return; // The terminator is needed for sscanf().
364 }
365 if (reporter->verbose()) {
366 SkDebugf("%15.9g = \"%s\"\n", inputFloat, floatString);
367 }
368 float roundTripFloat;
369 if (1 != sscanf(floatString, "%f", &roundTripFloat)) {
370 ERRORF(reporter, "unscannable result: %s", floatString);
371 return;
372 }
373 if (SkIsFinite(inputFloat) && roundTripFloat != inputFloat) {
374 ERRORF(reporter, "roundTripFloat (%.9g) != inputFloat (%.9g)",
375 roundTripFloat, inputFloat);
376 }
377}
378
379// Test SkPDFUtils::AppendScalar for accuracy.
380DEF_TEST(SkPDF_Primitives_Scalar, reporter) {
381 SkRandom random(0x5EED);
382 int iterationCount = 512;
383 while (iterationCount-- > 0) {
384 union { uint32_t u; float f; };
385 u = random.nextU();
386 static_assert(sizeof(float) == sizeof(uint32_t), "");
387 check_pdf_scalar_serialization(reporter, f);
388 }
389 float alwaysCheck[] = {
390 0.0f, -0.0f, 1.0f, -1.0f, SK_ScalarPI, 0.1f, FLT_MIN, FLT_MAX,
391 -FLT_MIN, -FLT_MAX, FLT_MIN / 16.0f, -FLT_MIN / 16.0f,
393 -FLT_MIN / 8388608.0
394 };
395 for (float inputFloat: alwaysCheck) {
396 check_pdf_scalar_serialization(reporter, inputFloat);
397 }
398}
399
400// Test SkPDFUtils:: for accuracy.
401DEF_TEST(SkPDF_Primitives_Color, reporter) {
402 char buffer[5];
403 for (int i = 0; i < 256; ++i) {
405 REPORTER_ASSERT(reporter, len == strlen(buffer));
406 float f;
407 REPORTER_ASSERT(reporter, 1 == sscanf(buffer, "%f", &f));
408 int roundTrip = (int)(0.5 + f * 255);
409 REPORTER_ASSERT(reporter, roundTrip == i);
410 }
411}
412
413static sktext::GlyphRun make_run(size_t len, const SkGlyphID* glyphs, SkPoint* pos,
414 const SkFont& font, const uint32_t* clusters,
415 size_t utf8TextByteLength, const char* utf8Text) {
416 return sktext::GlyphRun(font,
419 SkSpan<const char>{utf8Text, utf8TextByteLength},
420 SkSpan<const uint32_t>{clusters, len},
422}
423
424DEF_TEST(SkPDF_Clusterator, reporter) {
426 {
427 constexpr unsigned len = 11;
428 const uint32_t clusters[len] = { 3, 2, 2, 1, 0, 4, 4, 7, 6, 6, 5 };
429 const SkGlyphID glyphs[len] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
430 SkPoint pos[len] = {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
431 {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}};
432 const char text[] = "abcdefgh";
433 sktext::GlyphRun run = make_run(len, glyphs, pos, font, clusters, strlen(text), text);
434 SkClusterator clusterator(run);
435 SkClusterator::Cluster expectations[] = {
436 {&text[3], 1, 0, 1},
437 {&text[2], 1, 1, 2},
438 {&text[1], 1, 3, 1},
439 {&text[0], 1, 4, 1},
440 {&text[4], 1, 5, 2},
441 {&text[7], 1, 7, 1},
442 {&text[6], 1, 8, 2},
443 {&text[5], 1, 10, 1},
444 {nullptr, 0, 0, 0},
445 };
446 for (const auto& expectation : expectations) {
447 REPORTER_ASSERT(reporter, clusterator.next() == expectation);
448 }
449 }
450 {
451 constexpr unsigned len = 5;
452 const uint32_t clusters[len] = { 0, 1, 4, 5, 6 };
453 const SkGlyphID glyphs[len] = { 43, 167, 79, 79, 82, };
454 SkPoint pos[len] = {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}};
455 const char text[] = "Ha\xCC\x8A" "llo";
456 sktext::GlyphRun run = make_run(len, glyphs, pos, font, clusters, strlen(text), text);
457 SkClusterator clusterator(run);
458 SkClusterator::Cluster expectations[] = {
459 {&text[0], 1, 0, 1},
460 {&text[1], 3, 1, 1},
461 {&text[4], 1, 2, 1},
462 {&text[5], 1, 3, 1},
463 {&text[6], 1, 4, 1},
464 {nullptr, 0, 0, 0},
465 };
466 for (const auto& expectation : expectations) {
467 REPORTER_ASSERT(reporter, clusterator.next() == expectation);
468 }
469 }
470}
471
472DEF_TEST(fuzz875632f0, reporter) {
474 auto doc = SkPDF::MakeDocument(&stream);
476 SkCanvas* canvas = doc->beginPage(128, 160);
477
478 SkAutoCanvasRestore autoCanvasRestore(canvas, false);
479
480 SkPaint layerPaint({0, 0, 0, 0});
481 layerPaint.setImageFilter(SkImageFilters::Dilate(536870912, 0, nullptr, nullptr));
482 layerPaint.setBlendMode(SkBlendMode::kClear);
483
484 canvas->saveLayer(nullptr, &layerPaint);
485 canvas->saveLayer(nullptr, nullptr);
486
488 paint.setBlendMode(SkBlendMode::kDarken);
489 paint.setShader(SkShaders::MakeFractalNoise(0, 0, 2, 0, nullptr));
490 paint.setColor4f(SkColor4f{0, 0, 0 ,0});
491
492 canvas->drawPath(SkPath(), paint);
493}
494#endif
reporter
Definition: FontMgrTest.cpp:39
uint16_t glyphs[5]
Definition: FontMgrTest.cpp:46
SkPoint pos
static bool eq(const SkM44 &a, const SkM44 &b, float tol)
Definition: M44Test.cpp:18
#define SK_ABORT(message,...)
Definition: SkAssert.h:70
@ kDarken
rc = s + d - max(s*da, d*sa), ra = kSrcOver
@ kClear
r = 0
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
unsigned SkFloatToDecimal(float value, char output[kMaximumSkFloatToDecimalLength])
constexpr unsigned kMaximumSkFloatToDecimalLength
constexpr float SK_FloatInfinity
static bool SkIsFinite(T x, Pack... values)
constexpr float SK_FloatNaN
constexpr float SK_FloatNegativeInfinity
@ kGlyphID
uses two byte words to represent glyph indices
static SkString resource(SkPDFResourceType type, int index)
#define SK_ScalarHalf
Definition: SkScalar.h:19
#define SK_ScalarPI
Definition: SkScalar.h:21
SK_API SkString SkStringPrintf(const char *format,...) SK_PRINTF_LIKE(1
Creates a new string and writes into it using a printf()-style format.
constexpr size_t SkToSizeT(S x)
Definition: SkTo.h:31
uint16_t SkGlyphID
Definition: SkTypes.h:179
#define DEF_TEST(name, reporter)
Definition: Test.h:312
#define REPORTER_ASSERT(r, cond,...)
Definition: Test.h:286
#define REQUIRE_PDF_DOCUMENT(TEST_NAME, REPORTER)
Definition: Test.h:485
#define ERRORF(r,...)
Definition: Test.h:293
#define REPORT_FAILURE(reporter, cond, message)
Definition: Test.h:90
int saveLayer(const SkRect *bounds, const SkPaint *paint)
Definition: SkCanvas.cpp:496
void drawRect(const SkRect &rect, const SkPaint &paint)
Definition: SkCanvas.cpp:1673
void drawSimpleText(const void *text, size_t byteLength, SkTextEncoding encoding, SkScalar x, SkScalar y, const SkFont &font, const SkPaint &paint)
Definition: SkCanvas.cpp:2413
void drawPath(const SkPath &path, const SkPaint &paint)
Definition: SkCanvas.cpp:1747
Definition: SkFont.h:35
static sk_sp< SkImageFilter > Dilate(SkScalar radiusX, SkScalar radiusY, sk_sp< SkImageFilter > input, const CropRect &cropRect={})
static bool CanEmbedTypeface(SkTypeface *, SkPDFDocument *)
Definition: SkPDFFont.cpp:743
static SkPDFUnion Bool(bool)
Definition: SkPDFTypes.cpp:317
static SkPDFUnion Int(int32_t)
Definition: SkPDFTypes.cpp:305
static SkPDFUnion TextString(const char *)
Definition: SkPDFTypes.cpp:336
static SkPDFUnion ByteString(const char *)
Definition: SkPDFTypes.cpp:331
static SkPDFUnion Name(const char *)
Definition: SkPDFTypes.cpp:325
static SkPDFUnion Scalar(SkScalar)
Definition: SkPDFTypes.cpp:321
void setImageFilter(sk_sp< SkImageFilter > imageFilter)
Definition: SkPath.h:59
size_t size() const
Definition: SkString.h:131
const char * c_str() const
Definition: SkString.h:133
const FilterResult & source() const
const Paint & paint
Definition: color_source.cc:38
uint8_t value
GAsyncResult * result
std::u16string text
size_t ColorToDecimal(uint8_t value, char result[5])
Definition: SkPDFUtils.cpp:307
SK_API sk_sp< SkDocument > MakeDocument(SkWStream *stream, const Metadata &metadata)
SK_API sk_sp< SkShader > MakeFractalNoise(SkScalar baseFrequencyX, SkScalar baseFrequencyY, int numOctaves, SkScalar seed, const SkISize *tileSize=nullptr)
SKSHAPER_API sk_sp< Factory > Factory()
SkFont DefaultFont()
sk_sp< SkTypeface > CreateTypefaceFromResource(const char *resource, int ttcIndex)
sk_sp< SkTypeface > DefaultTypeface()
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
font
Font Metadata and Metrics.
Definition: run.py:1
#define T
Definition: precompiler.cc:65
static constexpr SkRect MakeWH(float w, float h)
Definition: SkRect.h:609