Flutter Engine
The Flutter Engine
Public Types | Static Public Member Functions | Static Public Attributes | List of all members
flutter::testing::CanvasCompareTester Class Reference

Public Types

enum class  DirectoryStatus { kExisted , kCreated , kFailed }
 

Static Public Member Functions

static std::unique_ptr< DlSurfaceProviderGetProvider (BackendType type)
 
static void ClearProviders ()
 
static bool AddProvider (BackendType type)
 
static void RenderAll (const TestParameters &params, const BoundsTolerance &tolerance=DefaultTolerance)
 
static void RenderWithSaveRestore (const TestParameters &testP, const RenderEnvironment &env, const BoundsTolerance &tolerance)
 
static void RenderWithAttributes (const TestParameters &testP, const RenderEnvironment &env, const BoundsTolerance &tolerance)
 
static void RenderWithStrokes (const TestParameters &testP, const RenderEnvironment &env, const BoundsTolerance &tolerance_in)
 
static void RenderWithTransforms (const TestParameters &testP, const RenderEnvironment &env, const BoundsTolerance &tolerance)
 
static void RenderWithClips (const TestParameters &testP, const RenderEnvironment &env, const BoundsTolerance &diff_tolerance)
 
static DirectoryStatus CheckDir (const std::string &dir)
 
static void SetupImpellerFailureImageDirectory ()
 
static void save_to_png (const RenderResult *result, const std::string &op_desc, const std::string &reason)
 
static void RenderWith (const TestParameters &testP, const RenderEnvironment &env, const BoundsTolerance &tolerance_in, const CaseParameters &caseP)
 
static bool fuzzyCompare (uint32_t pixel_a, uint32_t pixel_b, int fudge)
 
static int groupOpacityFudgeFactor (const RenderEnvironment &env)
 
static void checkGroupOpacity (const RenderEnvironment &env, const sk_sp< DisplayList > &display_list, const RenderResult *ref_result, const std::string &info, DlColor bg)
 
static bool checkPixels (const RenderResult *ref_result, const SkRect ref_bounds, const std::string &info, const DlColor bg=DlColor::kTransparent())
 
static int countModifiedTransparentPixels (const RenderResult *ref_result, const RenderResult *test_result)
 
static void quickCompareToReference (const RenderEnvironment &env, const std::string &info)
 
static bool quickCompareToReference (const RenderResult *ref_result, const RenderResult *test_result, bool should_match, const std::string &info)
 
static void compareToReference (const RenderResult *test_result, const RenderResult *ref_result, const std::string &info, const SkRect *bounds, const BoundsTolerance *tolerance, const DlColor bg, bool fuzzyCompares=false, int width=kTestWidth, int height=kTestHeight, bool printMismatches=false)
 
static void showBoundsOverflow (const std::string &info, SkIRect &bounds, const BoundsTolerance *tolerance, int pixLeft, int pixTop, int pixRight, int pixBottom)
 
static sk_sp< SkTextBlobMakeTextBlob (const std::string &string, SkScalar font_height)
 

Static Public Attributes

static std::vector< BackendTypeTestBackends
 
static std::string ImpellerFailureImageDirectory = ""
 
static bool SaveImpellerFailureImages = false
 
static std::vector< std::string > ImpellerFailureImages
 
static bool ImpellerSupported = false
 
static BoundsTolerance DefaultTolerance
 

Detailed Description

Definition at line 1092 of file dl_rendering_unittests.cc.

Member Enumeration Documentation

◆ DirectoryStatus

Enumerator
kExisted 
kCreated 
kFailed 

Definition at line 2149 of file dl_rendering_unittests.cc.

2149 {
2150 kExisted,
2151 kCreated,
2152 kFailed,
2153 };

Member Function Documentation

◆ AddProvider()

static bool flutter::testing::CanvasCompareTester::AddProvider ( BackendType  type)
inlinestatic

Definition at line 1114 of file dl_rendering_unittests.cc.

1114 {
1115 auto provider = GetProvider(type);
1116 if (!provider) {
1117 return false;
1118 }
1119 if (provider->supports_impeller()) {
1120 ImpellerSupported = true;
1121 }
1122 TestBackends.push_back(type);
1123 return true;
1124 }
GLenum type
static std::vector< BackendType > TestBackends
static std::unique_ptr< DlSurfaceProvider > GetProvider(BackendType type)

◆ CheckDir()

static DirectoryStatus flutter::testing::CanvasCompareTester::CheckDir ( const std::string &  dir)
inlinestatic

Definition at line 2155 of file dl_rendering_unittests.cc.

2155 {
2156 auto ret =
2158 if (ret.is_valid()) {
2160 }
2161 ret =
2163 if (ret.is_valid()) {
2165 }
2166 FML_LOG(ERROR) << "Could not create directory (" << dir
2167 << ") for impeller failure images" << ", ret = " << ret.get()
2168 << ", errno = " << errno;
2170 }
#define FML_LOG(severity)
Definition: logging.h:82
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 Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets dir
Definition: switches.h:145
fml::UniqueFD OpenDirectory(const char *path, bool create_if_necessary, FilePermission permission)
Definition: file_posix.cc:97
#define ERROR(message)
Definition: elf_loader.cc:260

◆ checkGroupOpacity()

static void flutter::testing::CanvasCompareTester::checkGroupOpacity ( const RenderEnvironment env,
const sk_sp< DisplayList > &  display_list,
const RenderResult ref_result,
const std::string &  info,
DlColor  bg 
)
inlinestatic

Definition at line 2428 of file dl_rendering_unittests.cc.

2432 {
2433 SkScalar opacity = 128.0 / 255.0;
2434
2435 DisplayListJobRenderer opacity_job(display_list);
2436 RenderJobInfo opacity_info = {
2437 .bg = bg,
2438 .opacity = opacity,
2439 };
2440 auto group_opacity_result = env.getResult(opacity_info, opacity_job);
2441
2442 ASSERT_EQ(group_opacity_result->width(), kTestWidth) << info;
2443 ASSERT_EQ(group_opacity_result->height(), kTestHeight) << info;
2444
2445 ASSERT_EQ(ref_result->width(), kTestWidth) << info;
2446 ASSERT_EQ(ref_result->height(), kTestHeight) << info;
2447
2448 int pixels_touched = 0;
2449 int pixels_different = 0;
2450 int max_diff = 0;
2451 // We need to allow some slight differences per component due to the
2452 // fact that rearranging discrete calculations can compound round off
2453 // errors. Off-by-2 is enough for 8 bit components, but for the 565
2454 // tests we allow at least 9 which is the maximum distance between
2455 // samples when converted to 8 bits. (You might think it would be a
2456 // max step of 8 converting 5 bits to 8 bits, but it is really
2457 // converting 31 steps to 255 steps with an average step size of
2458 // 8.23 - 24 of the steps are by 8, but 7 of them are by 9.)
2459 int fudge = groupOpacityFudgeFactor(env);
2460 for (int y = 0; y < kTestHeight; y++) {
2461 const uint32_t* ref_row = ref_result->addr32(0, y);
2462 const uint32_t* test_row = group_opacity_result->addr32(0, y);
2463 for (int x = 0; x < kTestWidth; x++) {
2464 uint32_t ref_pixel = ref_row[x];
2465 uint32_t test_pixel = test_row[x];
2466 if (ref_pixel != bg.argb() || test_pixel != bg.argb()) {
2467 pixels_touched++;
2468 for (int i = 0; i < 32; i += 8) {
2469 int ref_comp = (ref_pixel >> i) & 0xff;
2470 int bg_comp = (bg.argb() >> i) & 0xff;
2471 SkScalar faded_comp = bg_comp + (ref_comp - bg_comp) * opacity;
2472 int test_comp = (test_pixel >> i) & 0xff;
2473 if (std::abs(faded_comp - test_comp) > fudge) {
2474 int diff = std::abs(faded_comp - test_comp);
2475 if (max_diff < diff) {
2476 max_diff = diff;
2477 }
2478 pixels_different++;
2479 break;
2480 }
2481 }
2482 }
2483 }
2484 }
2485 ASSERT_GT(pixels_touched, 20) << info;
2486 if (pixels_different > 1) {
2487 FML_LOG(ERROR) << "max diff == " << max_diff << " for " << info;
2488 }
2489 ASSERT_LE(pixels_different, 1) << info;
2490 }
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
static int groupOpacityFudgeFactor(const RenderEnvironment &env)
float SkScalar
Definition: extension.cpp:12
double y
double x
Definition: __init__.py:1
SIN Vec< N, float > abs(const Vec< N, float > &x)
Definition: SkVx.h:707

◆ checkPixels()

static bool flutter::testing::CanvasCompareTester::checkPixels ( const RenderResult ref_result,
const SkRect  ref_bounds,
const std::string &  info,
const DlColor  bg = DlColor::kTransparent() 
)
inlinestatic

Definition at line 2492 of file dl_rendering_unittests.cc.

2495 {
2496 uint32_t untouched = bg.premultipliedArgb();
2497 int pixels_touched = 0;
2498 int pixels_oob = 0;
2499 SkIRect i_bounds = ref_bounds.roundOut();
2500 EXPECT_EQ(ref_result->width(), kTestWidth) << info;
2501 EXPECT_EQ(ref_result->height(), kTestWidth) << info;
2502 for (int y = 0; y < kTestHeight; y++) {
2503 const uint32_t* ref_row = ref_result->addr32(0, y);
2504 for (int x = 0; x < kTestWidth; x++) {
2505 if (ref_row[x] != untouched) {
2506 pixels_touched++;
2507 if (!i_bounds.contains(x, y)) {
2508 pixels_oob++;
2509 }
2510 }
2511 }
2512 }
2513 EXPECT_EQ(pixels_oob, 0) << info;
2514 EXPECT_GT(pixels_touched, 0) << info;
2515 return pixels_oob == 0 && pixels_touched > 0;
2516 }
Definition: SkRect.h:32
bool contains(int32_t x, int32_t y) const
Definition: SkRect.h:463
void roundOut(SkIRect *dst) const
Definition: SkRect.h:1241

◆ ClearProviders()

static void flutter::testing::CanvasCompareTester::ClearProviders ( )
inlinestatic

Definition at line 1112 of file dl_rendering_unittests.cc.

1112{ TestBackends.clear(); }

◆ compareToReference()

static void flutter::testing::CanvasCompareTester::compareToReference ( const RenderResult test_result,
const RenderResult ref_result,
const std::string &  info,
const SkRect bounds,
const BoundsTolerance tolerance,
const DlColor  bg,
bool  fuzzyCompares = false,
int  width = kTestWidth,
int  height = kTestHeight,
bool  printMismatches = false 
)
inlinestatic

Definition at line 2571 of file dl_rendering_unittests.cc.

2580 {
2581 uint32_t untouched = bg.premultipliedArgb();
2582 ASSERT_EQ(test_result->width(), width) << info;
2583 ASSERT_EQ(test_result->height(), height) << info;
2584 SkIRect i_bounds =
2585 bounds ? bounds->roundOut() : SkIRect::MakeWH(width, height);
2586
2587 int pixels_different = 0;
2588 int pixels_oob = 0;
2589 int min_x = width;
2590 int min_y = height;
2591 int max_x = 0;
2592 int max_y = 0;
2593 for (int y = 0; y < height; y++) {
2594 const uint32_t* ref_row = ref_result->addr32(0, y);
2595 const uint32_t* test_row = test_result->addr32(0, y);
2596 for (int x = 0; x < width; x++) {
2597 if (bounds && test_row[x] != untouched) {
2598 if (min_x > x) {
2599 min_x = x;
2600 }
2601 if (min_y > y) {
2602 min_y = y;
2603 }
2604 if (max_x <= x) {
2605 max_x = x + 1;
2606 }
2607 if (max_y <= y) {
2608 max_y = y + 1;
2609 }
2610 if (!i_bounds.contains(x, y)) {
2611 pixels_oob++;
2612 }
2613 }
2614 bool match = fuzzyCompares ? fuzzyCompare(test_row[x], ref_row[x], 1)
2615 : test_row[x] == ref_row[x];
2616 if (!match) {
2617 if (printMismatches && pixels_different < 5) {
2618 FML_LOG(ERROR) << "pix[" << x << ", " << y
2619 << "] mismatch: " << std::hex << test_row[x]
2620 << "(test) != (ref)" << ref_row[x] << std::dec;
2621 }
2622 pixels_different++;
2623 }
2624 }
2625 }
2626 if (pixels_oob > 0) {
2627 FML_LOG(ERROR) << "pix bounds[" //
2628 << min_x << ", " << min_y << " => " << max_x << ", "
2629 << max_y << "]";
2630 FML_LOG(ERROR) << "dl_bounds[" //
2631 << bounds->fLeft << ", " << bounds->fTop //
2632 << " => " //
2633 << bounds->fRight << ", " << bounds->fBottom //
2634 << "]";
2635 } else if (bounds) {
2636 showBoundsOverflow(info, i_bounds, tolerance, min_x, min_y, max_x, max_y);
2637 }
2638 ASSERT_EQ(pixels_oob, 0) << info;
2639 ASSERT_EQ(pixels_different, 0) << info;
2640 }
static void showBoundsOverflow(const std::string &info, SkIRect &bounds, const BoundsTolerance *tolerance, int pixLeft, int pixTop, int pixRight, int pixBottom)
static bool fuzzyCompare(uint32_t pixel_a, uint32_t pixel_b, int fudge)
Optional< SkRect > bounds
Definition: SkRecords.h:189
def match(bench, filt)
Definition: benchmark.py:23
int32_t height
int32_t width
static constexpr SkIRect MakeWH(int32_t w, int32_t h)
Definition: SkRect.h:56

◆ countModifiedTransparentPixels()

static int flutter::testing::CanvasCompareTester::countModifiedTransparentPixels ( const RenderResult ref_result,
const RenderResult test_result 
)
inlinestatic

Definition at line 2518 of file dl_rendering_unittests.cc.

2519 {
2520 int count = 0;
2521 for (int y = 0; y < kTestHeight; y++) {
2522 const uint32_t* ref_row = ref_result->addr32(0, y);
2523 const uint32_t* test_row = test_result->addr32(0, y);
2524 for (int x = 0; x < kTestWidth; x++) {
2525 if (ref_row[x] != test_row[x]) {
2526 if (ref_row[x] == 0) {
2527 count++;
2528 }
2529 }
2530 }
2531 }
2532 return count;
2533 }
int count
Definition: FontMgrTest.cpp:50

◆ fuzzyCompare()

static bool flutter::testing::CanvasCompareTester::fuzzyCompare ( uint32_t  pixel_a,
uint32_t  pixel_b,
int  fudge 
)
inlinestatic

Definition at line 2406 of file dl_rendering_unittests.cc.

2406 {
2407 for (int i = 0; i < 32; i += 8) {
2408 int comp_a = (pixel_a >> i) & 0xff;
2409 int comp_b = (pixel_b >> i) & 0xff;
2410 if (std::abs(comp_a - comp_b) > fudge) {
2411 return false;
2412 }
2413 }
2414 return true;
2415 }

◆ GetProvider()

static std::unique_ptr< DlSurfaceProvider > flutter::testing::CanvasCompareTester::GetProvider ( BackendType  type)
inlinestatic

Definition at line 1100 of file dl_rendering_unittests.cc.

1100 {
1101 auto provider = DlSurfaceProvider::Create(type);
1102 if (provider == nullptr) {
1104 << " not supported (ignoring)";
1105 return nullptr;
1106 }
1107 provider->InitializeSurface(kTestWidth, kTestHeight,
1108 PixelFormat::kN32PremulPixelFormat);
1109 return provider;
1110 }
static std::string BackendName(BackendType type)
static std::unique_ptr< DlSurfaceProvider > Create(BackendType backend_type)

◆ groupOpacityFudgeFactor()

static int flutter::testing::CanvasCompareTester::groupOpacityFudgeFactor ( const RenderEnvironment env)
inlinestatic

Definition at line 2417 of file dl_rendering_unittests.cc.

2417 {
2418 if (env.format() == PixelFormat::k565PixelFormat) {
2419 return 9;
2420 }
2421 if (env.provider()->backend_type() == BackendType::kOpenGlBackend) {
2422 // OpenGL gets a little fuzzy at times. Still, "within 5" (aka +/-4)
2423 // for byte samples is not bad, though the other backends give +/-1
2424 return 5;
2425 }
2426 return 2;
2427 }

◆ MakeTextBlob()

static sk_sp< SkTextBlob > flutter::testing::CanvasCompareTester::MakeTextBlob ( const std::string &  string,
SkScalar  font_height 
)
inlinestatic

Definition at line 2684 of file dl_rendering_unittests.cc.

2685 {
2686 SkFont font = CreateTestFontOfSize(font_height);
2687 sk_sp<SkTypeface> face = font.refTypeface();
2688 FML_CHECK(face);
2689 FML_CHECK(face->countGlyphs() > 0) << "No glyphs in font";
2690 return SkTextBlob::MakeFromText(string.c_str(), string.size(), font,
2692 }
@ kUTF8
uses bytes to represent UTF-8 or ASCII
Definition: SkFont.h:35
static sk_sp< SkTextBlob > MakeFromText(const void *text, size_t byteLength, const SkFont &font, SkTextEncoding encoding=SkTextEncoding::kUTF8)
Definition: SkTextBlob.cpp:788
int countGlyphs() const
Definition: SkTypeface.cpp:432
#define FML_CHECK(condition)
Definition: logging.h:85
SkFont CreateTestFontOfSize(SkScalar scalar)
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
font
Font Metadata and Metrics.

◆ quickCompareToReference() [1/2]

static void flutter::testing::CanvasCompareTester::quickCompareToReference ( const RenderEnvironment env,
const std::string &  info 
)
inlinestatic

Definition at line 2535 of file dl_rendering_unittests.cc.

2536 {
2537 quickCompareToReference(env.ref_sk_result(), env.ref_dl_result(), true,
2538 info + " reference rendering");
2539 }
static void quickCompareToReference(const RenderEnvironment &env, const std::string &info)

◆ quickCompareToReference() [2/2]

static bool flutter::testing::CanvasCompareTester::quickCompareToReference ( const RenderResult ref_result,
const RenderResult test_result,
bool  should_match,
const std::string &  info 
)
inlinestatic

Definition at line 2541 of file dl_rendering_unittests.cc.

2544 {
2545 int w = test_result->width();
2546 int h = test_result->height();
2547 EXPECT_EQ(w, ref_result->width()) << info;
2548 EXPECT_EQ(h, ref_result->height()) << info;
2549 int pixels_different = 0;
2550 for (int y = 0; y < h; y++) {
2551 const uint32_t* ref_row = ref_result->addr32(0, y);
2552 const uint32_t* test_row = test_result->addr32(0, y);
2553 for (int x = 0; x < w; x++) {
2554 if (ref_row[x] != test_row[x]) {
2555 if (should_match && pixels_different < 5) {
2556 FML_LOG(ERROR) << std::hex << ref_row[x] << " != " << test_row[x];
2557 }
2558 pixels_different++;
2559 }
2560 }
2561 }
2562 if (should_match) {
2563 EXPECT_EQ(pixels_different, 0) << info;
2564 return pixels_different == 0;
2565 } else {
2566 EXPECT_NE(pixels_different, 0) << info;
2567 return pixels_different != 0;
2568 }
2569 }
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 h
Definition: switches.h:59
SkScalar w

◆ RenderAll()

static void flutter::testing::CanvasCompareTester::RenderAll ( const TestParameters params,
const BoundsTolerance tolerance = DefaultTolerance 
)
inlinestatic

Definition at line 1128 of file dl_rendering_unittests.cc.

1129 {
1130 for (auto& back_end : TestBackends) {
1131 auto provider = GetProvider(back_end);
1132 RenderEnvironment env = RenderEnvironment::MakeN32(provider.get());
1133 env.init_ref(kEmptySkSetup, params.sk_renderer(), //
1134 kEmptyDlSetup, params.dl_renderer(), params.imp_renderer());
1135 quickCompareToReference(env, "default");
1136 if (env.supports_impeller()) {
1137 auto impeller_result = env.ref_impeller_result();
1138 if (!checkPixels(impeller_result, impeller_result->render_bounds(),
1139 "Impeller reference")) {
1140 std::string test_name =
1141 ::testing::UnitTest::GetInstance()->current_test_info()->name();
1142 save_to_png(impeller_result, test_name + " (Impeller reference)",
1143 "base rendering was blank or out of bounds");
1144 }
1145 } else {
1146 static OncePerBackendWarning warnings("No Impeller output tests");
1147 warnings.warn(env.backend_name());
1148 }
1149
1150 RenderWithTransforms(params, env, tolerance);
1151 RenderWithClips(params, env, tolerance);
1152 RenderWithSaveRestore(params, env, tolerance);
1153 // Only test attributes if the canvas version uses the paint object
1154 if (params.uses_paint()) {
1155 RenderWithAttributes(params, env, tolerance);
1156 }
1157 }
1158 }
static void RenderWithAttributes(const TestParameters &testP, const RenderEnvironment &env, const BoundsTolerance &tolerance)
static bool checkPixels(const RenderResult *ref_result, const SkRect ref_bounds, const std::string &info, const DlColor bg=DlColor::kTransparent())
static void RenderWithSaveRestore(const TestParameters &testP, const RenderEnvironment &env, const BoundsTolerance &tolerance)
static void RenderWithClips(const TestParameters &testP, const RenderEnvironment &env, const BoundsTolerance &diff_tolerance)
static void save_to_png(const RenderResult *result, const std::string &op_desc, const std::string &reason)
static void RenderWithTransforms(const TestParameters &testP, const RenderEnvironment &env, const BoundsTolerance &tolerance)
static RenderEnvironment MakeN32(const DlSurfaceProvider *provider)
const EmbeddedViewParams * params
static const DlSetup kEmptyDlSetup
static const SkSetup kEmptySkSetup

◆ RenderWith()

static void flutter::testing::CanvasCompareTester::RenderWith ( const TestParameters testP,
const RenderEnvironment env,
const BoundsTolerance tolerance_in,
const CaseParameters caseP 
)
inlinestatic

Definition at line 2220 of file dl_rendering_unittests.cc.

2223 {
2224 std::string test_name =
2225 ::testing::UnitTest::GetInstance()->current_test_info()->name();
2226 const std::string info =
2227 env.backend_name() + ": " + test_name + " (" + caseP.info() + ")";
2228 const DlColor bg = caseP.bg();
2229 RenderJobInfo base_info = {
2230 .bg = bg,
2231 };
2232
2233 // sk_result is a direct rendering via SkCanvas to SkSurface
2234 // DisplayList mechanisms are not involved in this operation
2235 SkJobRenderer sk_job(caseP.sk_setup(), //
2236 testP.sk_renderer(), //
2237 caseP.sk_restore(), //
2238 env.sk_image());
2239 auto sk_result = env.getResult(base_info, sk_job);
2240
2241 DlJobRenderer dl_job(caseP.dl_setup(), //
2242 testP.dl_renderer(), //
2243 caseP.dl_restore(), //
2244 env.dl_image());
2245 auto dl_result = env.getResult(base_info, dl_job);
2246
2247 EXPECT_EQ(sk_job.setup_matrix(), dl_job.setup_matrix());
2248 EXPECT_EQ(sk_job.setup_clip_bounds(), dl_job.setup_clip_bounds());
2249 ASSERT_EQ(sk_result->width(), kTestWidth) << info;
2250 ASSERT_EQ(sk_result->height(), kTestHeight) << info;
2251 ASSERT_EQ(dl_result->width(), kTestWidth) << info;
2252 ASSERT_EQ(dl_result->height(), kTestHeight) << info;
2253
2254 const BoundsTolerance tolerance =
2255 testP.adjust(tolerance_in, dl_job.setup_paint(), dl_job.setup_matrix());
2256 const sk_sp<SkPicture> sk_picture = sk_job.MakePicture(base_info);
2257 const sk_sp<DisplayList> display_list = dl_job.MakeDisplayList(base_info);
2258
2259 SkRect sk_bounds = sk_picture->cullRect();
2260 checkPixels(sk_result.get(), sk_bounds, info + " (Skia reference)", bg);
2261
2262 if (testP.should_match(env, caseP, dl_job.setup_paint(), dl_job)) {
2263 quickCompareToReference(env.ref_sk_result(), sk_result.get(), true,
2264 info + " (attribute should not have effect)");
2265 } else {
2266 quickCompareToReference(env.ref_sk_result(), sk_result.get(), false,
2267 info + " (attribute should affect rendering)");
2268 }
2269
2270 // If either the reference setup or the test setup contain attributes
2271 // that Impeller doesn't support, we skip the Impeller testing. This
2272 // is mostly stroked or patterned text which is vectored through drawPath
2273 // for Impeller.
2274 if (env.supports_impeller() &&
2275 testP.impeller_compatible(dl_job.setup_paint()) &&
2276 testP.impeller_compatible(env.ref_dl_paint())) {
2277 DlJobRenderer imp_job(caseP.dl_setup(), //
2278 testP.imp_renderer(), //
2279 caseP.dl_restore(), //
2280 env.impeller_image());
2281 auto imp_result = env.getImpellerResult(base_info, imp_job);
2282 std::string imp_info = info + " (Impeller)";
2283 bool success = checkPixels(imp_result.get(), imp_result->render_bounds(),
2284 imp_info, bg);
2285 if (testP.should_match(env, caseP, imp_job.setup_paint(), imp_job)) {
2286 success = success && //
2288 env.ref_impeller_result(), imp_result.get(), true,
2289 imp_info + " (attribute should not have effect)");
2290 } else {
2291 success = success && //
2293 env.ref_impeller_result(), imp_result.get(), false,
2294 imp_info + " (attribute should affect rendering)");
2295 }
2296 if (SaveImpellerFailureImages && !success) {
2297 FML_LOG(ERROR) << "Impeller issue encountered for: "
2298 << *imp_job.MakeDisplayList(base_info);
2299 save_to_png(imp_result.get(), info + " (Impeller Result)",
2300 "output saved in");
2301 save_to_png(env.ref_impeller_result(), info + " (Impeller Reference)",
2302 "compare to reference without attributes");
2303 save_to_png(sk_result.get(), info + " (Skia Result)",
2304 "and to Skia reference with attributes");
2305 save_to_png(env.ref_sk_result(), info + " (Skia Reference)",
2306 "and to Skia reference without attributes");
2307 }
2308 }
2309
2310 quickCompareToReference(sk_result.get(), dl_result.get(), true,
2311 info + " (DlCanvas output matches SkCanvas)");
2312
2313 {
2314 SkRect dl_bounds = display_list->bounds();
2315 if (!sk_bounds.roundOut().contains(dl_bounds)) {
2316 FML_LOG(ERROR) << "For " << info;
2317 FML_LOG(ERROR) << "sk ref: " //
2318 << sk_bounds.fLeft << ", " << sk_bounds.fTop << " => "
2319 << sk_bounds.fRight << ", " << sk_bounds.fBottom;
2320 FML_LOG(ERROR) << "dl: " //
2321 << dl_bounds.fLeft << ", " << dl_bounds.fTop << " => "
2322 << dl_bounds.fRight << ", " << dl_bounds.fBottom;
2323 if (!dl_bounds.contains(sk_bounds)) {
2324 FML_LOG(ERROR) << "DisplayList bounds are too small!";
2325 }
2326 if (!dl_bounds.isEmpty() &&
2327 !sk_bounds.roundOut().contains(dl_bounds.roundOut())) {
2328 FML_LOG(ERROR) << "###### DisplayList bounds larger than reference!";
2329 }
2330 }
2331
2332 // This EXPECT sometimes triggers, but when it triggers and I examine
2333 // the ref_bounds, they are always unnecessarily large and since the
2334 // pixel OOB tests in the compare method do not trigger, we will trust
2335 // the DL bounds.
2336 // EXPECT_TRUE(dl_bounds.contains(ref_bounds)) << info;
2337
2338 // When we are drawing a DisplayList, the display_list built above
2339 // will contain just a single drawDisplayList call plus the case
2340 // attribute. The sk_picture will, however, contain a list of all
2341 // of the embedded calls in the display list and so the op counts
2342 // will not be equal between the two.
2343 if (!testP.is_draw_display_list()) {
2344 EXPECT_EQ(static_cast<int>(display_list->op_count()),
2345 sk_picture->approximateOpCount())
2346 << info;
2347 EXPECT_EQ(static_cast<int>(display_list->op_count()),
2348 sk_picture->approximateOpCount())
2349 << info;
2350 }
2351
2352 DisplayListJobRenderer dl_builder_job(display_list);
2353 auto dl_builder_result = env.getResult(base_info, dl_builder_job);
2354 if (caseP.fuzzy_compare_components()) {
2356 dl_builder_result.get(), dl_result.get(),
2357 info + " (DlCanvas DL output close to Builder Dl output)",
2358 &dl_bounds, &tolerance, bg, true);
2359 } else {
2361 dl_builder_result.get(), dl_result.get(), true,
2362 info + " (DlCanvas DL output matches Builder Dl output)");
2363 }
2364
2365 compareToReference(dl_result.get(), sk_result.get(),
2366 info + " (DisplayList built directly -> surface)",
2367 &dl_bounds, &tolerance, bg,
2368 caseP.fuzzy_compare_components());
2369
2370 if (display_list->can_apply_group_opacity()) {
2371 checkGroupOpacity(env, display_list, dl_result.get(),
2372 info + " with Group Opacity", bg);
2373 }
2374 }
2375
2376 {
2377 // This sequence uses an SkPicture generated previously from the SkCanvas
2378 // calls and a DisplayList generated previously from the DlCanvas calls
2379 // and renders both back under a transform (scale(2x)) to see if their
2380 // rendering is affected differently by a change of matrix between
2381 // recording time and rendering time.
2382 const int test_width_2 = kTestWidth * 2;
2383 const int test_height_2 = kTestHeight * 2;
2384 const SkScalar test_scale = 2.0;
2385
2386 SkPictureJobRenderer sk_job_x2(sk_picture);
2387 RenderJobInfo info_2x = {
2388 .width = test_width_2,
2389 .height = test_height_2,
2390 .bg = bg,
2391 .scale = test_scale,
2392 };
2393 auto ref_x2_result = env.getResult(info_2x, sk_job_x2);
2394 ASSERT_EQ(ref_x2_result->width(), test_width_2) << info;
2395 ASSERT_EQ(ref_x2_result->height(), test_height_2) << info;
2396
2397 DisplayListJobRenderer dl_job_x2(display_list);
2398 auto test_x2_result = env.getResult(info_2x, dl_job_x2);
2399 compareToReference(test_x2_result.get(), ref_x2_result.get(),
2400 info + " (Both rendered scaled 2x)", nullptr, nullptr,
2401 bg, caseP.fuzzy_compare_components(), //
2402 test_width_2, test_height_2, false);
2403 }
2404 }
static void test_scale(skiatest::Reporter *reporter, const Geo &geo)
virtual SkRect cullRect() const =0
virtual int approximateOpCount(bool nested=false) const =0
static void checkGroupOpacity(const RenderEnvironment &env, const sk_sp< DisplayList > &display_list, const RenderResult *ref_result, const std::string &info, DlColor bg)
static void compareToReference(const RenderResult *test_result, const RenderResult *ref_result, const std::string &info, const SkRect *bounds, const BoundsTolerance *tolerance, const DlColor bg, bool fuzzyCompares=false, int width=kTestWidth, int height=kTestHeight, bool printMismatches=false)
flutter::DlColor DlColor
SkScalar fBottom
larger y-axis bounds
Definition: extension.cpp:17
SkScalar fLeft
smaller x-axis bounds
Definition: extension.cpp:14
SkScalar fRight
larger x-axis bounds
Definition: extension.cpp:16
bool contains(SkScalar x, SkScalar y) const
Definition: extension.cpp:19
bool isEmpty() const
Definition: SkRect.h:693
SkScalar fTop
smaller y-axis bounds
Definition: extension.cpp:15

◆ RenderWithAttributes()

static void flutter::testing::CanvasCompareTester::RenderWithAttributes ( const TestParameters testP,
const RenderEnvironment env,
const BoundsTolerance tolerance 
)
inlinestatic

Definition at line 1462 of file dl_rendering_unittests.cc.

1464 {
1465 RenderWith(testP, env, tolerance, CaseParameters("Defaults Test"));
1466
1467 {
1468 // CPU renderer with default line width of 0 does not show antialiasing
1469 // for stroked primitives, so we make a new reference with a non-trivial
1470 // stroke width to demonstrate the differences
1471 RenderEnvironment aa_env = RenderEnvironment::MakeN32(env.provider());
1472 // Tweak the bounds tolerance for the displacement of 1/10 of a pixel
1473 const BoundsTolerance aa_tolerance = tolerance.addBoundsPadding(1, 1);
1474 auto sk_aa_setup = [=](SkSetupContext ctx, bool is_aa) {
1475 ctx.canvas->translate(0.1, 0.1);
1476 ctx.paint.setAntiAlias(is_aa);
1477 ctx.paint.setStrokeWidth(5.0);
1478 };
1479 auto dl_aa_setup = [=](DlSetupContext ctx, bool is_aa) {
1480 ctx.canvas->Translate(0.1, 0.1);
1481 ctx.paint.setAntiAlias(is_aa);
1482 ctx.paint.setStrokeWidth(5.0);
1483 };
1484 aa_env.init_ref(
1485 [=](const SkSetupContext& ctx) { sk_aa_setup(ctx, false); },
1486 testP.sk_renderer(),
1487 [=](const DlSetupContext& ctx) { dl_aa_setup(ctx, false); },
1488 testP.dl_renderer(), testP.imp_renderer());
1489 quickCompareToReference(aa_env, "AntiAlias");
1490 RenderWith(
1491 testP, aa_env, aa_tolerance,
1492 CaseParameters(
1493 "AntiAlias == True",
1494 [=](const SkSetupContext& ctx) { sk_aa_setup(ctx, true); },
1495 [=](const DlSetupContext& ctx) { dl_aa_setup(ctx, true); }));
1496 RenderWith(
1497 testP, aa_env, aa_tolerance,
1498 CaseParameters(
1499 "AntiAlias == False",
1500 [=](const SkSetupContext& ctx) { sk_aa_setup(ctx, false); },
1501 [=](const DlSetupContext& ctx) { dl_aa_setup(ctx, false); }));
1502 }
1503
1504 RenderWith( //
1505 testP, env, tolerance,
1506 CaseParameters(
1507 "Color == Blue",
1508 [=](const SkSetupContext& ctx) {
1509 ctx.paint.setColor(SK_ColorBLUE);
1510 },
1511 [=](const DlSetupContext& ctx) {
1512 ctx.paint.setColor(DlColor::kBlue());
1513 }));
1514 RenderWith( //
1515 testP, env, tolerance,
1516 CaseParameters(
1517 "Color == Green",
1518 [=](const SkSetupContext& ctx) {
1519 ctx.paint.setColor(SK_ColorGREEN);
1520 },
1521 [=](const DlSetupContext& ctx) {
1522 ctx.paint.setColor(DlColor::kGreen());
1523 }));
1524
1525 RenderWithStrokes(testP, env, tolerance);
1526
1527 {
1528 // half opaque cyan
1529 DlColor blendable_color = DlColor::kCyan().withAlpha(0x7f);
1530 DlColor bg = DlColor::kWhite();
1531
1532 RenderWith(testP, env, tolerance,
1533 CaseParameters(
1534 "Blend == SrcIn",
1535 [=](const SkSetupContext& ctx) {
1536 ctx.paint.setBlendMode(SkBlendMode::kSrcIn);
1537 ctx.paint.setColor(blendable_color.argb());
1538 },
1539 [=](const DlSetupContext& ctx) {
1540 ctx.paint.setBlendMode(DlBlendMode::kSrcIn);
1541 ctx.paint.setColor(blendable_color);
1542 })
1543 .with_bg(bg));
1544 RenderWith(testP, env, tolerance,
1545 CaseParameters(
1546 "Blend == DstIn",
1547 [=](const SkSetupContext& ctx) {
1548 ctx.paint.setBlendMode(SkBlendMode::kDstIn);
1549 ctx.paint.setColor(blendable_color.argb());
1550 },
1551 [=](const DlSetupContext& ctx) {
1552 ctx.paint.setBlendMode(DlBlendMode::kDstIn);
1553 ctx.paint.setColor(blendable_color);
1554 })
1555 .with_bg(bg));
1556 }
1557
1558 {
1559 // Being able to see a blur requires some non-default attributes,
1560 // like a non-trivial stroke width and a shader rather than a color
1561 // (for drawPaint) so we create a new environment for these tests.
1562 RenderEnvironment blur_env = RenderEnvironment::MakeN32(env.provider());
1563 SkSetup sk_blur_setup = [=](const SkSetupContext& ctx) {
1564 ctx.paint.setShader(MakeColorSource(ctx.image));
1565 ctx.paint.setStrokeWidth(5.0);
1566 };
1567 DlSetup dl_blur_setup = [=](const DlSetupContext& ctx) {
1568 ctx.paint.setColorSource(MakeColorSource(ctx.image));
1569 ctx.paint.setStrokeWidth(5.0);
1570 };
1571 blur_env.init_ref(sk_blur_setup, testP.sk_renderer(), //
1572 dl_blur_setup, testP.dl_renderer(),
1573 testP.imp_renderer());
1574 quickCompareToReference(blur_env, "blur");
1575 DlBlurImageFilter dl_filter_decal_5(5.0, 5.0, DlTileMode::kDecal);
1576 auto sk_filter_decal_5 =
1577 SkImageFilters::Blur(5.0, 5.0, SkTileMode::kDecal, nullptr);
1578 BoundsTolerance blur_5_tolerance = tolerance.addBoundsPadding(4, 4);
1579 {
1580 RenderWith(testP, blur_env, blur_5_tolerance,
1581 CaseParameters(
1582 "ImageFilter == Decal Blur 5",
1583 [=](const SkSetupContext& ctx) {
1584 sk_blur_setup(ctx);
1585 ctx.paint.setImageFilter(sk_filter_decal_5);
1586 },
1587 [=](const DlSetupContext& ctx) {
1588 dl_blur_setup(ctx);
1589 ctx.paint.setImageFilter(&dl_filter_decal_5);
1590 }));
1591 }
1592 DlBlurImageFilter dl_filter_clamp_5(5.0, 5.0, DlTileMode::kClamp);
1593 auto sk_filter_clamp_5 =
1594 SkImageFilters::Blur(5.0, 5.0, SkTileMode::kClamp, nullptr);
1595 {
1596 RenderWith(testP, blur_env, blur_5_tolerance,
1597 CaseParameters(
1598 "ImageFilter == Clamp Blur 5",
1599 [=](const SkSetupContext& ctx) {
1600 sk_blur_setup(ctx);
1601 ctx.paint.setImageFilter(sk_filter_clamp_5);
1602 },
1603 [=](const DlSetupContext& ctx) {
1604 dl_blur_setup(ctx);
1605 ctx.paint.setImageFilter(&dl_filter_clamp_5);
1606 }));
1607 }
1608 }
1609
1610 {
1611 // Being able to see a dilate requires some non-default attributes,
1612 // like a non-trivial stroke width and a shader rather than a color
1613 // (for drawPaint) so we create a new environment for these tests.
1614 RenderEnvironment dilate_env = RenderEnvironment::MakeN32(env.provider());
1615 SkSetup sk_dilate_setup = [=](const SkSetupContext& ctx) {
1616 ctx.paint.setShader(MakeColorSource(ctx.image));
1617 ctx.paint.setStrokeWidth(5.0);
1618 };
1619 DlSetup dl_dilate_setup = [=](const DlSetupContext& ctx) {
1620 ctx.paint.setColorSource(MakeColorSource(ctx.image));
1621 ctx.paint.setStrokeWidth(5.0);
1622 };
1623 dilate_env.init_ref(sk_dilate_setup, testP.sk_renderer(), //
1624 dl_dilate_setup, testP.dl_renderer(),
1625 testP.imp_renderer());
1626 quickCompareToReference(dilate_env, "dilate");
1627 DlDilateImageFilter dl_dilate_filter_5(5.0, 5.0);
1628 auto sk_dilate_filter_5 = SkImageFilters::Dilate(5.0, 5.0, nullptr);
1629 RenderWith(testP, dilate_env, tolerance,
1630 CaseParameters(
1631 "ImageFilter == Dilate 5",
1632 [=](const SkSetupContext& ctx) {
1633 sk_dilate_setup(ctx);
1634 ctx.paint.setImageFilter(sk_dilate_filter_5);
1635 },
1636 [=](const DlSetupContext& ctx) {
1637 dl_dilate_setup(ctx);
1638 ctx.paint.setImageFilter(&dl_dilate_filter_5);
1639 }));
1640 }
1641
1642 {
1643 // Being able to see an erode requires some non-default attributes,
1644 // like a non-trivial stroke width and a shader rather than a color
1645 // (for drawPaint) so we create a new environment for these tests.
1646 RenderEnvironment erode_env = RenderEnvironment::MakeN32(env.provider());
1647 SkSetup sk_erode_setup = [=](const SkSetupContext& ctx) {
1648 ctx.paint.setShader(MakeColorSource(ctx.image));
1649 ctx.paint.setStrokeWidth(6.0);
1650 };
1651 DlSetup dl_erode_setup = [=](const DlSetupContext& ctx) {
1652 ctx.paint.setColorSource(MakeColorSource(ctx.image));
1653 ctx.paint.setStrokeWidth(6.0);
1654 };
1655 erode_env.init_ref(sk_erode_setup, testP.sk_renderer(), //
1656 dl_erode_setup, testP.dl_renderer(),
1657 testP.imp_renderer());
1658 quickCompareToReference(erode_env, "erode");
1659 // do not erode too much, because some tests assert there are enough
1660 // pixels that are changed.
1661 DlErodeImageFilter dl_erode_filter_1(1.0, 1.0);
1662 auto sk_erode_filter_1 = SkImageFilters::Erode(1.0, 1.0, nullptr);
1663 RenderWith(testP, erode_env, tolerance,
1664 CaseParameters(
1665 "ImageFilter == Erode 1",
1666 [=](const SkSetupContext& ctx) {
1667 sk_erode_setup(ctx);
1668 ctx.paint.setImageFilter(sk_erode_filter_1);
1669 },
1670 [=](const DlSetupContext& ctx) {
1671 dl_erode_setup(ctx);
1672 ctx.paint.setImageFilter(&dl_erode_filter_1);
1673 }));
1674 }
1675
1676 {
1677 // clang-format off
1678 constexpr float rotate_color_matrix[20] = {
1679 0, 1, 0, 0, 0,
1680 0, 0, 1, 0, 0,
1681 1, 0, 0, 0, 0,
1682 0, 0, 0, 1, 0,
1683 };
1684 constexpr float invert_color_matrix[20] = {
1685 -1.0, 0, 0, 1.0, 0,
1686 0, -1.0, 0, 1.0, 0,
1687 0, 0, -1.0, 1.0, 0,
1688 1.0, 1.0, 1.0, 1.0, 0,
1689 };
1690 // clang-format on
1691 DlMatrixColorFilter dl_color_filter(rotate_color_matrix);
1692 auto sk_color_filter = SkColorFilters::Matrix(rotate_color_matrix);
1693 {
1694 DlColor bg = DlColor::kWhite();
1695 RenderWith(testP, env, tolerance,
1696 CaseParameters(
1697 "ColorFilter == RotateRGB",
1698 [=](const SkSetupContext& ctx) {
1699 ctx.paint.setColor(SK_ColorYELLOW);
1700 ctx.paint.setColorFilter(sk_color_filter);
1701 },
1702 [=](const DlSetupContext& ctx) {
1703 ctx.paint.setColor(DlColor::kYellow());
1704 ctx.paint.setColorFilter(&dl_color_filter);
1705 })
1706 .with_bg(bg));
1707 }
1708 {
1709 DlColor bg = DlColor::kWhite();
1710 RenderWith(testP, env, tolerance,
1711 CaseParameters(
1712 "ColorFilter == Invert",
1713 [=](const SkSetupContext& ctx) {
1714 ctx.paint.setColor(SK_ColorYELLOW);
1715 ctx.paint.setColorFilter(
1716 SkColorFilters::Matrix(invert_color_matrix));
1717 },
1718 [=](const DlSetupContext& ctx) {
1719 ctx.paint.setColor(DlColor::kYellow());
1720 ctx.paint.setInvertColors(true);
1721 })
1722 .with_bg(bg));
1723 }
1724 }
1725
1726 {
1727 const DlBlurMaskFilter dl_mask_filter(DlBlurStyle::kNormal, 5.0);
1728 auto sk_mask_filter = SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 5.0);
1729 BoundsTolerance blur_5_tolerance = tolerance.addBoundsPadding(4, 4);
1730 {
1731 // Stroked primitives need some non-trivial stroke size to be blurred
1732 RenderWith(testP, env, blur_5_tolerance,
1733 CaseParameters(
1734 "MaskFilter == Blur 5",
1735 [=](const SkSetupContext& ctx) {
1736 ctx.paint.setStrokeWidth(5.0);
1737 ctx.paint.setMaskFilter(sk_mask_filter);
1738 },
1739 [=](const DlSetupContext& ctx) {
1740 ctx.paint.setStrokeWidth(5.0);
1741 ctx.paint.setMaskFilter(&dl_mask_filter);
1742 }));
1743 }
1744 }
1745
1746 {
1747 SkPoint end_points[] = {
1750 };
1751 DlColor dl_colors[] = {
1755 };
1756 SkColor sk_colors[] = {
1760 };
1761 float stops[] = {
1762 0.0,
1763 0.5,
1764 1.0,
1765 };
1766 auto dl_gradient =
1767 DlColorSource::MakeLinear(end_points[0], end_points[1], 3, dl_colors,
1768 stops, DlTileMode::kMirror);
1769 auto sk_gradient = SkGradientShader::MakeLinear(
1770 end_points, sk_colors, stops, 3, SkTileMode::kMirror, 0, nullptr);
1771 {
1772 RenderWith(testP, env, tolerance,
1773 CaseParameters(
1774 "LinearGradient GYB",
1775 [=](const SkSetupContext& ctx) {
1776 ctx.paint.setShader(sk_gradient);
1777 ctx.paint.setDither(testP.uses_gradient());
1778 },
1779 [=](const DlSetupContext& ctx) {
1780 ctx.paint.setColorSource(dl_gradient);
1781 }));
1782 }
1783 }
1784 }
@ kDstIn
r = d * sa
@ kSrcIn
r = s * da
@ kNormal_SkBlurStyle
fuzzy inside and outside
Definition: SkBlurTypes.h:12
constexpr SkColor SK_ColorYELLOW
Definition: SkColor.h:139
uint32_t SkColor
Definition: SkColor.h:37
constexpr SkColor SK_ColorBLUE
Definition: SkColor.h:135
static constexpr SkColor SkColorSetA(SkColor c, U8CPU a)
Definition: SkColor.h:82
constexpr SkColor SK_ColorGREEN
Definition: SkColor.h:131
static sk_sp< SkColorFilter > Matrix(const SkColorMatrix &)
static sk_sp< SkShader > MakeLinear(const SkPoint pts[2], const SkColor colors[], const SkScalar pos[], int count, SkTileMode mode, uint32_t flags=0, const SkMatrix *localMatrix=nullptr)
static sk_sp< SkImageFilter > Erode(SkScalar radiusX, SkScalar radiusY, sk_sp< SkImageFilter > input, const CropRect &cropRect={})
static sk_sp< SkImageFilter > Blur(SkScalar sigmaX, SkScalar sigmaY, SkTileMode tileMode, sk_sp< SkImageFilter > input, const CropRect &cropRect={})
static sk_sp< SkImageFilter > Dilate(SkScalar radiusX, SkScalar radiusY, sk_sp< SkImageFilter > input, const CropRect &cropRect={})
static sk_sp< SkMaskFilter > MakeBlur(SkBlurStyle style, SkScalar sigma, bool respectCTM=true)
static std::shared_ptr< DlLinearGradientColorSource > MakeLinear(const SkPoint start_point, const SkPoint end_point, uint32_t stop_count, const DlColor *colors, const float *stops, DlTileMode tile_mode, const SkMatrix *matrix=nullptr)
static void RenderWithStrokes(const TestParameters &testP, const RenderEnvironment &env, const BoundsTolerance &tolerance_in)
static void RenderWith(const TestParameters &testP, const RenderEnvironment &env, const BoundsTolerance &tolerance_in, const CaseParameters &caseP)
const std::function< void(const DlSetupContext &)> DlSetup
RenderContext< DlCanvas *, DlPaint &, sk_sp< DlImage > > DlSetupContext
RenderContext< SkCanvas *, SkPaint &, sk_sp< SkImage > > SkSetupContext
static std::shared_ptr< DlImageColorSource > MakeColorSource(const sk_sp< DlImage > &image)
const std::function< void(const SkSetupContext &)> SkSetup
constexpr SkRect kRenderBounds
@ kNormal
fuzzy inside and outside
static constexpr SkPoint Make(float x, float y)
Definition: SkPoint_impl.h:173
static constexpr DlColor kWhite()
Definition: dl_color.h:23
static constexpr DlColor kBlue()
Definition: dl_color.h:26
static constexpr DlColor kYellow()
Definition: dl_color.h:29
constexpr DlColor withAlpha(uint8_t alpha) const
Definition: dl_color.h:63
static constexpr DlColor kGreen()
Definition: dl_color.h:25
static constexpr DlColor kCyan()
Definition: dl_color.h:27

◆ RenderWithClips()

static void flutter::testing::CanvasCompareTester::RenderWithClips ( const TestParameters testP,
const RenderEnvironment env,
const BoundsTolerance diff_tolerance 
)
inlinestatic

Definition at line 2034 of file dl_rendering_unittests.cc.

2036 {
2037 // We used to use an inset of 15.5 pixels here, but since Skia's rounding
2038 // behavior at the center of pixels does not match between HW and SW, we
2039 // ended up with some clips including different pixels between the two
2040 // destinations and this interacted poorly with the carefully chosen
2041 // geometry in some of the tests which was designed to have just the
2042 // right features fully filling the clips based on the SW rounding. By
2043 // moving to a 15.4 inset, the edge of the clip is never on the "rounding
2044 // edge" of a pixel.
2045 SkRect r_clip = kRenderBounds.makeInset(15.4, 15.4);
2046 BoundsTolerance intersect_tolerance = diff_tolerance.clip(r_clip);
2047 intersect_tolerance = intersect_tolerance.addPostClipPadding(1, 1);
2048 RenderWith(testP, env, intersect_tolerance,
2049 CaseParameters(
2050 "Hard ClipRect inset by 15.4",
2051 [=](const SkSetupContext& ctx) {
2052 ctx.canvas->clipRect(r_clip, SkClipOp::kIntersect, false);
2053 },
2054 [=](const DlSetupContext& ctx) {
2055 ctx.canvas->ClipRect(r_clip, ClipOp::kIntersect, false);
2056 }));
2057 RenderWith(testP, env, intersect_tolerance,
2058 CaseParameters(
2059 "AntiAlias ClipRect inset by 15.4",
2060 [=](const SkSetupContext& ctx) {
2061 ctx.canvas->clipRect(r_clip, SkClipOp::kIntersect, true);
2062 },
2063 [=](const DlSetupContext& ctx) {
2064 ctx.canvas->ClipRect(r_clip, ClipOp::kIntersect, true);
2065 }));
2066 RenderWith(testP, env, diff_tolerance,
2067 CaseParameters(
2068 "Hard ClipRect Diff, inset by 15.4",
2069 [=](const SkSetupContext& ctx) {
2070 ctx.canvas->clipRect(r_clip, SkClipOp::kDifference, false);
2071 },
2072 [=](const DlSetupContext& ctx) {
2073 ctx.canvas->ClipRect(r_clip, ClipOp::kDifference, false);
2074 })
2075 .with_diff_clip());
2076 // This test RR clip used to use very small radii, but due to
2077 // optimizations in the HW rrect rasterization, this caused small
2078 // bulges in the corners of the RRect which were interpreted as
2079 // "clip overruns" by the clip OOB pixel testing code. Using less
2080 // abusively small radii fixes the problem.
2081 SkRRect rr_clip = SkRRect::MakeRectXY(r_clip, 9, 9);
2082 RenderWith(testP, env, intersect_tolerance,
2083 CaseParameters(
2084 "Hard ClipRRect with radius of 15.4",
2085 [=](const SkSetupContext& ctx) {
2086 ctx.canvas->clipRRect(rr_clip, SkClipOp::kIntersect,
2087 false);
2088 },
2089 [=](const DlSetupContext& ctx) {
2090 ctx.canvas->ClipRRect(rr_clip, ClipOp::kIntersect, false);
2091 }));
2092 RenderWith(testP, env, intersect_tolerance,
2093 CaseParameters(
2094 "AntiAlias ClipRRect with radius of 15.4",
2095 [=](const SkSetupContext& ctx) {
2096 ctx.canvas->clipRRect(rr_clip, SkClipOp::kIntersect, true);
2097 },
2098 [=](const DlSetupContext& ctx) {
2099 ctx.canvas->ClipRRect(rr_clip, ClipOp::kIntersect, true);
2100 }));
2101 RenderWith(testP, env, diff_tolerance,
2102 CaseParameters(
2103 "Hard ClipRRect Diff, with radius of 15.4",
2104 [=](const SkSetupContext& ctx) {
2105 ctx.canvas->clipRRect(rr_clip, SkClipOp::kDifference,
2106 false);
2107 },
2108 [=](const DlSetupContext& ctx) {
2109 ctx.canvas->ClipRRect(rr_clip, ClipOp::kDifference, false);
2110 })
2111 .with_diff_clip());
2112 SkPath path_clip = SkPath();
2113 path_clip.setFillType(SkPathFillType::kEvenOdd);
2114 path_clip.addRect(r_clip);
2115 path_clip.addCircle(kRenderCenterX, kRenderCenterY, 1.0);
2116 RenderWith(testP, env, intersect_tolerance,
2117 CaseParameters(
2118 "Hard ClipPath inset by 15.4",
2119 [=](const SkSetupContext& ctx) {
2120 ctx.canvas->clipPath(path_clip, SkClipOp::kIntersect,
2121 false);
2122 },
2123 [=](const DlSetupContext& ctx) {
2124 ctx.canvas->ClipPath(path_clip, ClipOp::kIntersect, false);
2125 }));
2126 RenderWith(testP, env, intersect_tolerance,
2127 CaseParameters(
2128 "AntiAlias ClipPath inset by 15.4",
2129 [=](const SkSetupContext& ctx) {
2130 ctx.canvas->clipPath(path_clip, SkClipOp::kIntersect,
2131 true);
2132 },
2133 [=](const DlSetupContext& ctx) {
2134 ctx.canvas->ClipPath(path_clip, ClipOp::kIntersect, true);
2135 }));
2136 RenderWith(
2137 testP, env, diff_tolerance,
2138 CaseParameters(
2139 "Hard ClipPath Diff, inset by 15.4",
2140 [=](const SkSetupContext& ctx) {
2141 ctx.canvas->clipPath(path_clip, SkClipOp::kDifference, false);
2142 },
2143 [=](const DlSetupContext& ctx) {
2144 ctx.canvas->ClipPath(path_clip, ClipOp::kDifference, false);
2145 })
2146 .with_diff_clip());
2147 }
Definition: SkPath.h:59
static SkRRect MakeRectXY(const SkRect &rect, SkScalar xRad, SkScalar yRad)
Definition: SkRRect.h:180
constexpr int kRenderCenterY
constexpr int kRenderCenterX
SkRect makeInset(float dx, float dy) const
Definition: SkRect.h:987

◆ RenderWithSaveRestore()

static void flutter::testing::CanvasCompareTester::RenderWithSaveRestore ( const TestParameters testP,
const RenderEnvironment env,
const BoundsTolerance tolerance 
)
inlinestatic

Definition at line 1160 of file dl_rendering_unittests.cc.

1162 {
1163 SkRect clip =
1166 DlColor alpha_layer_color = DlColor::kCyan().withAlpha(0x7f);
1167 SkRenderer sk_safe_restore = [=](const SkRenderContext& ctx) {
1168 // Draw another primitive to disable peephole optimizations
1169 ctx.canvas->drawRect(kRenderBounds.makeOffset(500, 500), SkPaint());
1170 ctx.canvas->restore();
1171 };
1172 DlRenderer dl_safe_restore = [=](const DlRenderContext& ctx) {
1173 // Draw another primitive to disable peephole optimizations
1174 // As the rendering op rejection in the DisplayList Builder
1175 // gets smarter and smarter, this operation has had to get
1176 // sneakier and sneakier about specifying an operation that
1177 // won't practically show up in the output, but technically
1178 // can't be culled.
1179 ctx.canvas->DrawRect(
1181 DlPaint());
1182 ctx.canvas->Restore();
1183 };
1184 SkRenderer sk_opt_restore = [=](const SkRenderContext& ctx) {
1185 // Just a simple restore to allow peephole optimizations to occur
1186 ctx.canvas->restore();
1187 };
1188 DlRenderer dl_opt_restore = [=](const DlRenderContext& ctx) {
1189 // Just a simple restore to allow peephole optimizations to occur
1190 ctx.canvas->Restore();
1191 };
1192 SkRect layer_bounds = kRenderBounds.makeInset(15, 15);
1193 RenderWith(testP, env, tolerance,
1194 CaseParameters(
1195 "With prior save/clip/restore",
1196 [=](const SkSetupContext& ctx) {
1197 ctx.canvas->save();
1198 ctx.canvas->clipRect(clip, SkClipOp::kIntersect, false);
1199 SkPaint p2;
1200 ctx.canvas->drawRect(rect, p2);
1202 ctx.canvas->drawRect(rect, p2);
1203 ctx.canvas->restore();
1204 },
1205 [=](const DlSetupContext& ctx) {
1206 ctx.canvas->Save();
1207 ctx.canvas->ClipRect(clip, ClipOp::kIntersect, false);
1208 DlPaint p2;
1209 ctx.canvas->DrawRect(rect, p2);
1211 ctx.canvas->DrawRect(rect, p2);
1212 ctx.canvas->Restore();
1213 }));
1214 RenderWith(testP, env, tolerance,
1215 CaseParameters(
1216 "saveLayer no paint, no bounds",
1217 [=](const SkSetupContext& ctx) {
1218 ctx.canvas->saveLayer(nullptr, nullptr);
1219 },
1220 [=](const DlSetupContext& ctx) {
1221 ctx.canvas->SaveLayer(nullptr, nullptr);
1222 })
1223 .with_restore(sk_safe_restore, dl_safe_restore, false));
1224 RenderWith(testP, env, tolerance,
1225 CaseParameters(
1226 "saveLayer no paint, with bounds",
1227 [=](const SkSetupContext& ctx) {
1228 ctx.canvas->saveLayer(layer_bounds, nullptr);
1229 },
1230 [=](const DlSetupContext& ctx) {
1231 ctx.canvas->SaveLayer(&layer_bounds, nullptr);
1232 })
1233 .with_restore(sk_safe_restore, dl_safe_restore, true));
1234 RenderWith(testP, env, tolerance,
1235 CaseParameters(
1236 "saveLayer with alpha, no bounds",
1237 [=](const SkSetupContext& ctx) {
1238 SkPaint save_p;
1239 save_p.setColor(ToSk(alpha_layer_color));
1240 ctx.canvas->saveLayer(nullptr, &save_p);
1241 },
1242 [=](const DlSetupContext& ctx) {
1243 DlPaint save_p;
1244 save_p.setColor(alpha_layer_color);
1245 ctx.canvas->SaveLayer(nullptr, &save_p);
1246 })
1247 .with_restore(sk_safe_restore, dl_safe_restore, true));
1248 RenderWith(testP, env, tolerance,
1249 CaseParameters(
1250 "saveLayer with peephole alpha, no bounds",
1251 [=](const SkSetupContext& ctx) {
1252 SkPaint save_p;
1253 save_p.setColor(ToSk(alpha_layer_color));
1254 ctx.canvas->saveLayer(nullptr, &save_p);
1255 },
1256 [=](const DlSetupContext& ctx) {
1257 DlPaint save_p;
1258 save_p.setColor(alpha_layer_color);
1259 ctx.canvas->SaveLayer(nullptr, &save_p);
1260 })
1261 .with_restore(sk_opt_restore, dl_opt_restore, true, true));
1262 RenderWith(testP, env, tolerance,
1263 CaseParameters(
1264 "saveLayer with alpha and bounds",
1265 [=](const SkSetupContext& ctx) {
1266 SkPaint save_p;
1267 save_p.setColor(ToSk(alpha_layer_color));
1268 ctx.canvas->saveLayer(layer_bounds, &save_p);
1269 },
1270 [=](const DlSetupContext& ctx) {
1271 DlPaint save_p;
1272 save_p.setColor(alpha_layer_color);
1273 ctx.canvas->SaveLayer(&layer_bounds, &save_p);
1274 })
1275 .with_restore(sk_safe_restore, dl_safe_restore, true));
1276 {
1277 // Being able to see a backdrop blur requires a non-default background
1278 // so we create a new environment for these tests that has a checkerboard
1279 // background that can be blurred by the backdrop filter. We also want
1280 // to avoid the rendered primitive from obscuring the blurred background
1281 // so we set an alpha value which works for all primitives except for
1282 // drawColor which can override the alpha with its color, but it now uses
1283 // a non-opaque color to avoid that problem.
1284 RenderEnvironment backdrop_env =
1285 RenderEnvironment::MakeN32(env.provider());
1286 SkSetup sk_backdrop_setup = [=](const SkSetupContext& ctx) {
1287 SkPaint setup_p;
1288 setup_p.setShader(MakeColorSource(ctx.image));
1289 ctx.canvas->drawPaint(setup_p);
1290 };
1291 DlSetup dl_backdrop_setup = [=](const DlSetupContext& ctx) {
1292 DlPaint setup_p;
1293 setup_p.setColorSource(MakeColorSource(ctx.image));
1294 ctx.canvas->DrawPaint(setup_p);
1295 };
1296 SkSetup sk_content_setup = [=](const SkSetupContext& ctx) {
1297 ctx.paint.setAlpha(ctx.paint.getAlpha() / 2);
1298 };
1299 DlSetup dl_content_setup = [=](const DlSetupContext& ctx) {
1300 ctx.paint.setAlpha(ctx.paint.getAlpha() / 2);
1301 };
1302 backdrop_env.init_ref(sk_backdrop_setup, testP.sk_renderer(),
1303 dl_backdrop_setup, testP.dl_renderer(),
1304 testP.imp_renderer());
1305 quickCompareToReference(backdrop_env, "backdrop");
1306
1307 DlBlurImageFilter dl_backdrop(5, 5, DlTileMode::kDecal);
1308 auto sk_backdrop =
1310 RenderWith(testP, backdrop_env, tolerance,
1311 CaseParameters(
1312 "saveLayer with backdrop",
1313 [=](const SkSetupContext& ctx) {
1314 sk_backdrop_setup(ctx);
1315 ctx.canvas->saveLayer(SkCanvas::SaveLayerRec(
1316 nullptr, nullptr, sk_backdrop.get(), 0));
1317 sk_content_setup(ctx);
1318 },
1319 [=](const DlSetupContext& ctx) {
1320 dl_backdrop_setup(ctx);
1321 ctx.canvas->SaveLayer(nullptr, nullptr, &dl_backdrop);
1322 dl_content_setup(ctx);
1323 })
1324 .with_restore(sk_safe_restore, dl_safe_restore, true));
1325 RenderWith(testP, backdrop_env, tolerance,
1326 CaseParameters(
1327 "saveLayer with bounds and backdrop",
1328 [=](const SkSetupContext& ctx) {
1329 sk_backdrop_setup(ctx);
1330 ctx.canvas->saveLayer(SkCanvas::SaveLayerRec(
1331 &layer_bounds, nullptr, sk_backdrop.get(), 0));
1332 sk_content_setup(ctx);
1333 },
1334 [=](const DlSetupContext& ctx) {
1335 dl_backdrop_setup(ctx);
1336 ctx.canvas->SaveLayer(&layer_bounds, nullptr,
1337 &dl_backdrop);
1338 dl_content_setup(ctx);
1339 })
1340 .with_restore(sk_safe_restore, dl_safe_restore, true));
1341 RenderWith(testP, backdrop_env, tolerance,
1342 CaseParameters(
1343 "clipped saveLayer with backdrop",
1344 [=](const SkSetupContext& ctx) {
1345 sk_backdrop_setup(ctx);
1346 ctx.canvas->clipRect(layer_bounds);
1347 ctx.canvas->saveLayer(SkCanvas::SaveLayerRec(
1348 nullptr, nullptr, sk_backdrop.get(), 0));
1349 sk_content_setup(ctx);
1350 },
1351 [=](const DlSetupContext& ctx) {
1352 dl_backdrop_setup(ctx);
1353 ctx.canvas->ClipRect(layer_bounds);
1354 ctx.canvas->SaveLayer(nullptr, nullptr, &dl_backdrop);
1355 dl_content_setup(ctx);
1356 })
1357 .with_restore(sk_safe_restore, dl_safe_restore, true));
1358 }
1359
1360 {
1361 // clang-format off
1362 constexpr float rotate_alpha_color_matrix[20] = {
1363 0, 1, 0, 0 , 0,
1364 0, 0, 1, 0 , 0,
1365 1, 0, 0, 0 , 0,
1366 0, 0, 0, 0.5, 0,
1367 };
1368 // clang-format on
1369 DlMatrixColorFilter dl_alpha_rotate_filter(rotate_alpha_color_matrix);
1370 auto sk_alpha_rotate_filter =
1371 SkColorFilters::Matrix(rotate_alpha_color_matrix);
1372 {
1373 RenderWith(testP, env, tolerance,
1374 CaseParameters(
1375 "saveLayer ColorFilter, no bounds",
1376 [=](const SkSetupContext& ctx) {
1377 SkPaint save_p;
1378 save_p.setColorFilter(sk_alpha_rotate_filter);
1379 ctx.canvas->saveLayer(nullptr, &save_p);
1380 ctx.paint.setStrokeWidth(5.0);
1381 },
1382 [=](const DlSetupContext& ctx) {
1383 DlPaint save_p;
1384 save_p.setColorFilter(&dl_alpha_rotate_filter);
1385 ctx.canvas->SaveLayer(nullptr, &save_p);
1386 ctx.paint.setStrokeWidth(5.0);
1387 })
1388 .with_restore(sk_safe_restore, dl_safe_restore, true));
1389 }
1390 {
1391 RenderWith(testP, env, tolerance,
1392 CaseParameters(
1393 "saveLayer ColorFilter and bounds",
1394 [=](const SkSetupContext& ctx) {
1395 SkPaint save_p;
1396 save_p.setColorFilter(sk_alpha_rotate_filter);
1397 ctx.canvas->saveLayer(kRenderBounds, &save_p);
1398 ctx.paint.setStrokeWidth(5.0);
1399 },
1400 [=](const DlSetupContext& ctx) {
1401 DlPaint save_p;
1402 save_p.setColorFilter(&dl_alpha_rotate_filter);
1403 ctx.canvas->SaveLayer(&kRenderBounds, &save_p);
1404 ctx.paint.setStrokeWidth(5.0);
1405 })
1406 .with_restore(sk_safe_restore, dl_safe_restore, true));
1407 }
1408 }
1409
1410 {
1411 // clang-format off
1412 constexpr float color_matrix[20] = {
1413 0.5, 0, 0, 0, 0.5,
1414 0, 0.5, 0, 0, 0.5,
1415 0, 0, 0.5, 0, 0.5,
1416 0, 0, 0, 1, 0,
1417 };
1418 // clang-format on
1419 DlMatrixColorFilter dl_color_filter(color_matrix);
1420 DlColorFilterImageFilter dl_cf_image_filter(dl_color_filter);
1421 auto sk_cf_image_filter = SkImageFilters::ColorFilter(
1422 SkColorFilters::Matrix(color_matrix), nullptr);
1423 {
1424 RenderWith(testP, env, tolerance,
1425 CaseParameters(
1426 "saveLayer ImageFilter, no bounds",
1427 [=](const SkSetupContext& ctx) {
1428 SkPaint save_p;
1429 save_p.setImageFilter(sk_cf_image_filter);
1430 ctx.canvas->saveLayer(nullptr, &save_p);
1431 ctx.paint.setStrokeWidth(5.0);
1432 },
1433 [=](const DlSetupContext& ctx) {
1434 DlPaint save_p;
1435 save_p.setImageFilter(&dl_cf_image_filter);
1436 ctx.canvas->SaveLayer(nullptr, &save_p);
1437 ctx.paint.setStrokeWidth(5.0);
1438 })
1439 .with_restore(sk_safe_restore, dl_safe_restore, true));
1440 }
1441 {
1442 RenderWith(testP, env, tolerance,
1443 CaseParameters(
1444 "saveLayer ImageFilter and bounds",
1445 [=](const SkSetupContext& ctx) {
1446 SkPaint save_p;
1447 save_p.setImageFilter(sk_cf_image_filter);
1448 ctx.canvas->saveLayer(kRenderBounds, &save_p);
1449 ctx.paint.setStrokeWidth(5.0);
1450 },
1451 [=](const DlSetupContext& ctx) {
1452 DlPaint save_p;
1453 save_p.setImageFilter(&dl_cf_image_filter);
1454 ctx.canvas->SaveLayer(&kRenderBounds, &save_p);
1455 ctx.paint.setStrokeWidth(5.0);
1456 })
1457 .with_restore(sk_safe_restore, dl_safe_restore, true));
1458 }
1459 }
1460 }
@ kClear
r = 0
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition: SkPath.cpp:3892
static sk_sp< SkImageFilter > ColorFilter(sk_sp< SkColorFilter > cf, sk_sp< SkImageFilter > input, const CropRect &cropRect={})
void setColor(SkColor color)
Definition: SkPaint.cpp:119
void setImageFilter(sk_sp< SkImageFilter > imageFilter)
void setShader(sk_sp< SkShader > shader)
void setBlendMode(SkBlendMode mode)
Definition: SkPaint.cpp:151
void setColorFilter(sk_sp< SkColorFilter > colorFilter)
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
const std::function< void(const SkRenderContext &)> SkRenderer
RenderContext< DlCanvas *, const DlPaint &, sk_sp< DlImage > > DlRenderContext
RenderContext< SkCanvas *, const SkPaint &, sk_sp< SkImage > > SkRenderContext
const std::function< void(const DlRenderContext &)> DlRenderer
SkPaint ToSk(const DlPaint &paint)
flutter::DlPaint DlPaint
constexpr SkRect makeOffset(float dx, float dy) const
Definition: SkRect.h:965
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition: SkRect.h:659

◆ RenderWithStrokes()

static void flutter::testing::CanvasCompareTester::RenderWithStrokes ( const TestParameters testP,
const RenderEnvironment env,
const BoundsTolerance tolerance_in 
)
inlinestatic

Definition at line 1786 of file dl_rendering_unittests.cc.

1788 {
1789 // The test cases were generated with geometry that will try to fill
1790 // out the various miter limits used for testing, but they can be off
1791 // by a couple of pixels so we will relax bounds testing for strokes by
1792 // a couple of pixels.
1793 BoundsTolerance tolerance = tolerance_in.addBoundsPadding(2, 2);
1794 RenderWith(testP, env, tolerance,
1795 CaseParameters(
1796 "Fill",
1797 [=](const SkSetupContext& ctx) {
1798 ctx.paint.setStyle(SkPaint::kFill_Style);
1799 },
1800 [=](const DlSetupContext& ctx) {
1801 ctx.paint.setDrawStyle(DlDrawStyle::kFill);
1802 }));
1803 // Skia on HW produces a strong miter consistent with width=1.0
1804 // for any width less than a pixel, but the bounds computations of
1805 // both DL and SkPicture do not account for this. We will get
1806 // OOB pixel errors for the highly mitered drawPath geometry if
1807 // we don't set stroke width to 1.0 for that test on HW.
1808 // See https://bugs.chromium.org/p/skia/issues/detail?id=14046
1809 bool no_hairlines =
1810 testP.is_draw_path() &&
1811 env.provider()->backend_type() != BackendType::kSoftwareBackend;
1812 RenderWith(testP, env, tolerance,
1813 CaseParameters(
1814 "Stroke + defaults",
1815 [=](const SkSetupContext& ctx) {
1816 if (no_hairlines) {
1817 ctx.paint.setStrokeWidth(1.0);
1818 }
1819 ctx.paint.setStyle(SkPaint::kStroke_Style);
1820 },
1821 [=](const DlSetupContext& ctx) {
1822 if (no_hairlines) {
1823 ctx.paint.setStrokeWidth(1.0);
1824 }
1825 ctx.paint.setDrawStyle(DlDrawStyle::kStroke);
1826 }));
1827
1828 RenderWith(testP, env, tolerance,
1829 CaseParameters(
1830 "Fill + unnecessary StrokeWidth 10",
1831 [=](const SkSetupContext& ctx) {
1832 ctx.paint.setStyle(SkPaint::kFill_Style);
1833 ctx.paint.setStrokeWidth(10.0);
1834 },
1835 [=](const DlSetupContext& ctx) {
1836 ctx.paint.setDrawStyle(DlDrawStyle::kFill);
1837 ctx.paint.setStrokeWidth(10.0);
1838 }));
1839
1840 RenderEnvironment stroke_base_env =
1841 RenderEnvironment::MakeN32(env.provider());
1842 SkSetup sk_stroke_setup = [=](const SkSetupContext& ctx) {
1843 ctx.paint.setStyle(SkPaint::kStroke_Style);
1844 ctx.paint.setStrokeWidth(5.0);
1845 };
1846 DlSetup dl_stroke_setup = [=](const DlSetupContext& ctx) {
1847 ctx.paint.setDrawStyle(DlDrawStyle::kStroke);
1848 ctx.paint.setStrokeWidth(5.0);
1849 };
1850 stroke_base_env.init_ref(sk_stroke_setup, testP.sk_renderer(),
1851 dl_stroke_setup, testP.dl_renderer(),
1852 testP.imp_renderer());
1853 quickCompareToReference(stroke_base_env, "stroke");
1854
1855 RenderWith(testP, stroke_base_env, tolerance,
1856 CaseParameters(
1857 "Stroke Width 10",
1858 [=](const SkSetupContext& ctx) {
1859 ctx.paint.setStyle(SkPaint::kStroke_Style);
1860 ctx.paint.setStrokeWidth(10.0);
1861 },
1862 [=](const DlSetupContext& ctx) {
1863 ctx.paint.setDrawStyle(DlDrawStyle::kStroke);
1864 ctx.paint.setStrokeWidth(10.0);
1865 }));
1866 RenderWith(testP, stroke_base_env, tolerance,
1867 CaseParameters(
1868 "Stroke Width 5",
1869 [=](const SkSetupContext& ctx) {
1870 ctx.paint.setStyle(SkPaint::kStroke_Style);
1871 ctx.paint.setStrokeWidth(5.0);
1872 },
1873 [=](const DlSetupContext& ctx) {
1874 ctx.paint.setDrawStyle(DlDrawStyle::kStroke);
1875 ctx.paint.setStrokeWidth(5.0);
1876 }));
1877
1878 RenderWith(testP, stroke_base_env, tolerance,
1879 CaseParameters(
1880 "Stroke Width 5, Square Cap",
1881 [=](const SkSetupContext& ctx) {
1882 ctx.paint.setStyle(SkPaint::kStroke_Style);
1883 ctx.paint.setStrokeWidth(5.0);
1884 ctx.paint.setStrokeCap(SkPaint::kSquare_Cap);
1885 },
1886 [=](const DlSetupContext& ctx) {
1887 ctx.paint.setDrawStyle(DlDrawStyle::kStroke);
1888 ctx.paint.setStrokeWidth(5.0);
1889 ctx.paint.setStrokeCap(DlStrokeCap::kSquare);
1890 }));
1891 RenderWith(testP, stroke_base_env, tolerance,
1892 CaseParameters(
1893 "Stroke Width 5, Round Cap",
1894 [=](const SkSetupContext& ctx) {
1895 ctx.paint.setStyle(SkPaint::kStroke_Style);
1896 ctx.paint.setStrokeWidth(5.0);
1897 ctx.paint.setStrokeCap(SkPaint::kRound_Cap);
1898 },
1899 [=](const DlSetupContext& ctx) {
1900 ctx.paint.setDrawStyle(DlDrawStyle::kStroke);
1901 ctx.paint.setStrokeWidth(5.0);
1902 ctx.paint.setStrokeCap(DlStrokeCap::kRound);
1903 }));
1904
1905 RenderWith(testP, stroke_base_env, tolerance,
1906 CaseParameters(
1907 "Stroke Width 5, Bevel Join",
1908 [=](const SkSetupContext& ctx) {
1909 ctx.paint.setStyle(SkPaint::kStroke_Style);
1910 ctx.paint.setStrokeWidth(5.0);
1911 ctx.paint.setStrokeJoin(SkPaint::kBevel_Join);
1912 },
1913 [=](const DlSetupContext& ctx) {
1914 ctx.paint.setDrawStyle(DlDrawStyle::kStroke);
1915 ctx.paint.setStrokeWidth(5.0);
1916 ctx.paint.setStrokeJoin(DlStrokeJoin::kBevel);
1917 }));
1918 RenderWith(testP, stroke_base_env, tolerance,
1919 CaseParameters(
1920 "Stroke Width 5, Round Join",
1921 [=](const SkSetupContext& ctx) {
1922 ctx.paint.setStyle(SkPaint::kStroke_Style);
1923 ctx.paint.setStrokeWidth(5.0);
1924 ctx.paint.setStrokeJoin(SkPaint::kRound_Join);
1925 },
1926 [=](const DlSetupContext& ctx) {
1927 ctx.paint.setDrawStyle(DlDrawStyle::kStroke);
1928 ctx.paint.setStrokeWidth(5.0);
1929 ctx.paint.setStrokeJoin(DlStrokeJoin::kRound);
1930 }));
1931
1932 RenderWith(testP, stroke_base_env, tolerance,
1933 CaseParameters(
1934 "Stroke Width 5, Miter 10",
1935 [=](const SkSetupContext& ctx) {
1936 ctx.paint.setStyle(SkPaint::kStroke_Style);
1937 ctx.paint.setStrokeWidth(5.0);
1938 ctx.paint.setStrokeMiter(10.0);
1939 ctx.paint.setStrokeJoin(SkPaint::kMiter_Join);
1940 },
1941 [=](const DlSetupContext& ctx) {
1942 ctx.paint.setDrawStyle(DlDrawStyle::kStroke);
1943 ctx.paint.setStrokeWidth(5.0);
1944 ctx.paint.setStrokeMiter(10.0);
1945 ctx.paint.setStrokeJoin(DlStrokeJoin::kMiter);
1946 }));
1947
1948 RenderWith(testP, stroke_base_env, tolerance,
1949 CaseParameters(
1950 "Stroke Width 5, Miter 0",
1951 [=](const SkSetupContext& ctx) {
1952 ctx.paint.setStyle(SkPaint::kStroke_Style);
1953 ctx.paint.setStrokeWidth(5.0);
1954 ctx.paint.setStrokeMiter(0.0);
1955 ctx.paint.setStrokeJoin(SkPaint::kMiter_Join);
1956 },
1957 [=](const DlSetupContext& ctx) {
1958 ctx.paint.setDrawStyle(DlDrawStyle::kStroke);
1959 ctx.paint.setStrokeWidth(5.0);
1960 ctx.paint.setStrokeMiter(0.0);
1961 ctx.paint.setStrokeJoin(DlStrokeJoin::kMiter);
1962 }));
1963 }
@ kRound_Cap
adds circle
Definition: SkPaint.h:335
@ kSquare_Cap
adds square
Definition: SkPaint.h:336
@ kStroke_Style
set to stroke geometry
Definition: SkPaint.h:194
@ kFill_Style
set to fill geometry
Definition: SkPaint.h:193
@ kRound_Join
adds circle
Definition: SkPaint.h:360
@ kMiter_Join
extends to miter limit
Definition: SkPaint.h:359
@ kBevel_Join
connects outside edges
Definition: SkPaint.h:361
@ kMiter
extends to miter limit
@ kBevel
connects outside edges
@ kRound
adds circle
@ kSquare
adds square
@ kStroke
strokes boundary of shapes
@ kFill
fills interior of shapes

◆ RenderWithTransforms()

static void flutter::testing::CanvasCompareTester::RenderWithTransforms ( const TestParameters testP,
const RenderEnvironment env,
const BoundsTolerance tolerance 
)
inlinestatic

Definition at line 1965 of file dl_rendering_unittests.cc.

1967 {
1968 // If the rendering method does not fill the corners of the original
1969 // bounds, then the estimate under rotation or skewing will be off
1970 // so we scale the padding by about 5% to compensate.
1971 BoundsTolerance skewed_tolerance = tolerance.mulScale(1.05, 1.05);
1972 RenderWith( //
1973 testP, env, tolerance,
1974 CaseParameters(
1975 "Translate 5, 10", //
1976 [=](const SkSetupContext& ctx) { ctx.canvas->translate(5, 10); },
1977 [=](const DlSetupContext& ctx) { ctx.canvas->Translate(5, 10); }));
1978 RenderWith( //
1979 testP, env, tolerance,
1980 CaseParameters(
1981 "Scale +5%", //
1982 [=](const SkSetupContext& ctx) { ctx.canvas->scale(1.05, 1.05); },
1983 [=](const DlSetupContext& ctx) { ctx.canvas->Scale(1.05, 1.05); }));
1984 RenderWith( //
1985 testP, env, skewed_tolerance,
1986 CaseParameters(
1987 "Rotate 5 degrees", //
1988 [=](const SkSetupContext& ctx) { ctx.canvas->rotate(5); },
1989 [=](const DlSetupContext& ctx) { ctx.canvas->Rotate(5); }));
1990 RenderWith( //
1991 testP, env, skewed_tolerance,
1992 CaseParameters(
1993 "Skew 5%", //
1994 [=](const SkSetupContext& ctx) { ctx.canvas->skew(0.05, 0.05); },
1995 [=](const DlSetupContext& ctx) { ctx.canvas->Skew(0.05, 0.05); }));
1996 {
1997 // This rather odd transform can cause slight differences in
1998 // computing in-bounds samples depending on which base rendering
1999 // routine Skia uses. Making sure our matrix values are powers
2000 // of 2 reduces, but does not eliminate, these slight differences
2001 // in calculation when we are comparing rendering with an alpha
2002 // to rendering opaque colors in the group opacity tests, for
2003 // example.
2004 SkScalar tweak = 1.0 / 16.0;
2005 SkMatrix tx = SkMatrix::MakeAll(1.0 + tweak, tweak, 5, //
2006 tweak, 1.0 + tweak, 10, //
2007 0, 0, 1);
2008 RenderWith( //
2009 testP, env, skewed_tolerance,
2010 CaseParameters(
2011 "Transform 2D Affine",
2012 [=](const SkSetupContext& ctx) { ctx.canvas->concat(tx); },
2013 [=](const DlSetupContext& ctx) { ctx.canvas->Transform(tx); }));
2014 }
2015 {
2016 SkM44 m44 = SkM44(1, 0, 0, kRenderCenterX, //
2017 0, 1, 0, kRenderCenterY, //
2018 0, 0, 1, 0, //
2019 0, 0, .001, 1);
2020 m44.preConcat(
2021 SkM44::Rotate({1, 0, 0}, math::kPi / 60)); // 3 degrees around X
2022 m44.preConcat(
2023 SkM44::Rotate({0, 1, 0}, math::kPi / 45)); // 4 degrees around Y
2025 RenderWith( //
2026 testP, env, skewed_tolerance,
2027 CaseParameters(
2028 "Transform Full Perspective",
2029 [=](const SkSetupContext& ctx) { ctx.canvas->concat(m44); },
2030 [=](const DlSetupContext& ctx) { ctx.canvas->Transform(m44); }));
2031 }
2032 }
Definition: SkM44.h:150
static SkM44 Rotate(SkV3 axis, SkScalar radians)
Definition: SkM44.h:239
SkM44 & preConcat(const SkM44 &m)
Definition: SkM44.h:351
SkM44 & preTranslate(SkScalar x, SkScalar y, SkScalar z=0)
Definition: SkM44.cpp:89
static SkMatrix MakeAll(SkScalar scaleX, SkScalar skewX, SkScalar transX, SkScalar skewY, SkScalar scaleY, SkScalar transY, SkScalar pers0, SkScalar pers1, SkScalar pers2)
Definition: SkMatrix.h:179
constexpr float kPi
Definition: math.h:27

◆ save_to_png()

static void flutter::testing::CanvasCompareTester::save_to_png ( const RenderResult result,
const std::string &  op_desc,
const std::string &  reason 
)
inlinestatic

Definition at line 2196 of file dl_rendering_unittests.cc.

2198 {
2200 return;
2201 }
2202 if (ImpellerFailureImageDirectory.length() == 0) {
2204 if (ImpellerFailureImageDirectory.length() == 0) {
2206 return;
2207 }
2208 }
2209
2210 std::string filename = ImpellerFailureImageDirectory + "/";
2211 for (const char& ch : op_desc) {
2212 filename += (ch == ':' || ch == ' ') ? '_' : ch;
2213 }
2214 filename = filename + ".png";
2215 result->write(filename);
2216 ImpellerFailureImages.push_back(filename);
2217 FML_LOG(ERROR) << reason << ": " << filename;
2218 }
static std::vector< std::string > ImpellerFailureImages
GAsyncResult * result

◆ SetupImpellerFailureImageDirectory()

static void flutter::testing::CanvasCompareTester::SetupImpellerFailureImageDirectory ( )
inlinestatic

Definition at line 2172 of file dl_rendering_unittests.cc.

2172 {
2173 std::string base_dir = "./impeller_failure_images";
2174 if (CheckDir(base_dir) == DirectoryStatus::kFailed) {
2175 return;
2176 }
2177 for (int i = 0; i < 10000; i++) {
2178 std::string sub_dir = std::to_string(i);
2179 while (sub_dir.length() < 4) {
2180 sub_dir = "0" + sub_dir;
2181 }
2182 std::string try_dir = base_dir + "/" + sub_dir;
2183 switch (CheckDir(try_dir)) {
2185 break;
2188 return;
2190 return;
2191 }
2192 }
2193 FML_LOG(ERROR) << "Too many output directories for Impeller failure images";
2194 }
static DirectoryStatus CheckDir(const std::string &dir)
static SkString to_string(int n)
Definition: nanobench.cpp:119

◆ showBoundsOverflow()

static void flutter::testing::CanvasCompareTester::showBoundsOverflow ( const std::string &  info,
SkIRect bounds,
const BoundsTolerance tolerance,
int  pixLeft,
int  pixTop,
int  pixRight,
int  pixBottom 
)
inlinestatic

Definition at line 2642 of file dl_rendering_unittests.cc.

2648 {
2649 int pad_left = std::max(0, pixLeft - bounds.fLeft);
2650 int pad_top = std::max(0, pixTop - bounds.fTop);
2651 int pad_right = std::max(0, bounds.fRight - pixRight);
2652 int pad_bottom = std::max(0, bounds.fBottom - pixBottom);
2653 SkIRect pix_bounds =
2654 SkIRect::MakeLTRB(pixLeft, pixTop, pixRight, pixBottom);
2655 SkISize pix_size = pix_bounds.size();
2656 int pix_width = pix_size.width();
2657 int pix_height = pix_size.height();
2658 int worst_pad_x = std::max(pad_left, pad_right);
2659 int worst_pad_y = std::max(pad_top, pad_bottom);
2660 if (tolerance->overflows(pix_bounds, worst_pad_x, worst_pad_y)) {
2661 FML_LOG(ERROR) << "Computed bounds for " << info;
2662 FML_LOG(ERROR) << "pix bounds[" //
2663 << pixLeft << ", " << pixTop << " => " //
2664 << pixRight << ", " << pixBottom //
2665 << "]";
2666 FML_LOG(ERROR) << "dl_bounds[" //
2667 << bounds.fLeft << ", " << bounds.fTop //
2668 << " => " //
2669 << bounds.fRight << ", " << bounds.fBottom //
2670 << "]";
2671 FML_LOG(ERROR) << "Bounds overly conservative by up to " //
2672 << worst_pad_x << ", " << worst_pad_y //
2673 << " (" << (worst_pad_x * 100.0 / pix_width) //
2674 << "%, " << (worst_pad_y * 100.0 / pix_height) << "%)";
2675 int pix_area = pix_size.area();
2676 int dl_area = bounds.width() * bounds.height();
2677 FML_LOG(ERROR) << "Total overflow area: " << (dl_area - pix_area) //
2678 << " (+" << (dl_area * 100.0 / pix_area - 100.0) //
2679 << "% larger)";
2680 FML_LOG(ERROR);
2681 }
2682 }
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static constexpr SkIRect MakeLTRB(int32_t l, int32_t t, int32_t r, int32_t b)
Definition: SkRect.h:91
constexpr SkISize size() const
Definition: SkRect.h:172
Definition: SkSize.h:16
constexpr int32_t width() const
Definition: SkSize.h:36
constexpr int32_t height() const
Definition: SkSize.h:37
constexpr int64_t area() const
Definition: SkSize.h:39

Member Data Documentation

◆ DefaultTolerance

BoundsTolerance flutter::testing::CanvasCompareTester::DefaultTolerance
static
Initial value:
=
BoundsTolerance().addAbsolutePadding(1, 1)

Definition at line 1126 of file dl_rendering_unittests.cc.

◆ ImpellerFailureImageDirectory

std::string flutter::testing::CanvasCompareTester::ImpellerFailureImageDirectory = ""
static

Definition at line 1095 of file dl_rendering_unittests.cc.

◆ ImpellerFailureImages

std::vector< std::string > flutter::testing::CanvasCompareTester::ImpellerFailureImages
static

Definition at line 1097 of file dl_rendering_unittests.cc.

◆ ImpellerSupported

bool flutter::testing::CanvasCompareTester::ImpellerSupported = false
static

Definition at line 1098 of file dl_rendering_unittests.cc.

◆ SaveImpellerFailureImages

bool flutter::testing::CanvasCompareTester::SaveImpellerFailureImages = false
static

Definition at line 1096 of file dl_rendering_unittests.cc.

◆ TestBackends

std::vector< BackendType > flutter::testing::CanvasCompareTester::TestBackends
static

Definition at line 1094 of file dl_rendering_unittests.cc.


The documentation for this class was generated from the following file: