Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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 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, 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 BoundsTolerance DefaultTolerance
 

Detailed Description

Definition at line 1105 of file dl_rendering_unittests.cc.

Member Enumeration Documentation

◆ DirectoryStatus

Member Function Documentation

◆ AddProvider()

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

Definition at line 1124 of file dl_rendering_unittests.cc.

1124 {
1125 auto provider = GetProvider(type);
1126 if (!provider) {
1127 return false;
1128 }
1130 return true;
1131 }
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 2224 of file dl_rendering_unittests.cc.

2224 {
2225 auto ret =
2227 if (ret.is_valid()) {
2229 }
2230 ret =
2232 if (ret.is_valid()) {
2234 }
2235 FML_LOG(ERROR) << "Could not create directory (" << dir
2236 << ") for impeller failure images" << ", ret = " << ret.get()
2237 << ", errno = " << errno;
2239 }
#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)

◆ 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 2497 of file dl_rendering_unittests.cc.

2501 {
2502 SkScalar opacity = 128.0 / 255.0;
2503
2504 DisplayListJobRenderer opacity_job(display_list);
2505 RenderJobInfo opacity_info = {
2506 .bg = bg,
2507 .opacity = opacity,
2508 };
2509 auto group_opacity_result = env.getResult(opacity_info, opacity_job);
2510
2511 ASSERT_EQ(group_opacity_result->width(), kTestWidth) << info;
2512 ASSERT_EQ(group_opacity_result->height(), kTestHeight) << info;
2513
2514 ASSERT_EQ(ref_result->width(), kTestWidth) << info;
2515 ASSERT_EQ(ref_result->height(), kTestHeight) << info;
2516
2517 int pixels_touched = 0;
2518 int pixels_different = 0;
2519 int max_diff = 0;
2520 // We need to allow some slight differences per component due to the
2521 // fact that rearranging discrete calculations can compound round off
2522 // errors. Off-by-2 is enough for 8 bit components, but for the 565
2523 // tests we allow at least 9 which is the maximum distance between
2524 // samples when converted to 8 bits. (You might think it would be a
2525 // max step of 8 converting 5 bits to 8 bits, but it is really
2526 // converting 31 steps to 255 steps with an average step size of
2527 // 8.23 - 24 of the steps are by 8, but 7 of them are by 9.)
2528 int fudge = groupOpacityFudgeFactor(env);
2529 for (int y = 0; y < kTestHeight; y++) {
2530 const uint32_t* ref_row = ref_result->addr32(0, y);
2531 const uint32_t* test_row = group_opacity_result->addr32(0, y);
2532 for (int x = 0; x < kTestWidth; x++) {
2533 uint32_t ref_pixel = ref_row[x];
2534 uint32_t test_pixel = test_row[x];
2535 if (ref_pixel != bg.argb() || test_pixel != bg.argb()) {
2536 pixels_touched++;
2537 for (int i = 0; i < 32; i += 8) {
2538 int ref_comp = (ref_pixel >> i) & 0xff;
2539 int bg_comp = (bg.argb() >> i) & 0xff;
2540 SkScalar faded_comp = bg_comp + (ref_comp - bg_comp) * opacity;
2541 int test_comp = (test_pixel >> i) & 0xff;
2542 if (std::abs(faded_comp - test_comp) > fudge) {
2543 int diff = std::abs(faded_comp - test_comp);
2544 if (max_diff < diff) {
2545 max_diff = diff;
2546 }
2547 pixels_different++;
2548 break;
2549 }
2550 }
2551 }
2552 }
2553 }
2554 ASSERT_GT(pixels_touched, 20) << info;
2555 if (pixels_different > 1) {
2556 FML_LOG(ERROR) << "max diff == " << max_diff << " for " << info;
2557 }
2558 ASSERT_LE(pixels_different, 1) << info;
2559 }
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

◆ 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 2561 of file dl_rendering_unittests.cc.

2564 {
2565 uint32_t untouched = bg.premultipliedArgb();
2566 int pixels_touched = 0;
2567 int pixels_oob = 0;
2568 SkIRect i_bounds = ref_bounds.roundOut();
2569 EXPECT_EQ(ref_result->width(), kTestWidth) << info;
2570 EXPECT_EQ(ref_result->height(), kTestWidth) << info;
2571 for (int y = 0; y < kTestHeight; y++) {
2572 const uint32_t* ref_row = ref_result->addr32(0, y);
2573 for (int x = 0; x < kTestWidth; x++) {
2574 if (ref_row[x] != untouched) {
2575 pixels_touched++;
2576 if (!i_bounds.contains(x, y)) {
2577 pixels_oob++;
2578 }
2579 }
2580 }
2581 }
2582 EXPECT_EQ(pixels_oob, 0) << info;
2583 EXPECT_GT(pixels_touched, 0) << info;
2584 return pixels_oob == 0 && pixels_touched > 0;
2585 }
bool contains(int32_t x, int32_t y) const
Definition SkRect.h:463
void roundOut(SkIRect *dst) const
Definition SkRect.h:1241

◆ compareToReference()

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

Definition at line 2640 of file dl_rendering_unittests.cc.

2649 {
2650 uint32_t untouched = bg.premultipliedArgb();
2651 ASSERT_EQ(test_result->width(), width) << info;
2652 ASSERT_EQ(test_result->height(), height) << info;
2653 SkIRect i_bounds =
2654 bounds ? bounds->roundOut() : SkIRect::MakeWH(width, height);
2655
2656 int pixels_different = 0;
2657 int pixels_oob = 0;
2658 int min_x = width;
2659 int min_y = height;
2660 int max_x = 0;
2661 int max_y = 0;
2662 for (int y = 0; y < height; y++) {
2663 const uint32_t* ref_row = ref_result->addr32(0, y);
2664 const uint32_t* test_row = test_result->addr32(0, y);
2665 for (int x = 0; x < width; x++) {
2666 if (bounds && test_row[x] != untouched) {
2667 if (min_x > x) {
2668 min_x = x;
2669 }
2670 if (min_y > y) {
2671 min_y = y;
2672 }
2673 if (max_x <= x) {
2674 max_x = x + 1;
2675 }
2676 if (max_y <= y) {
2677 max_y = y + 1;
2678 }
2679 if (!i_bounds.contains(x, y)) {
2680 pixels_oob++;
2681 }
2682 }
2683 bool match = fuzzyCompares ? fuzzyCompare(test_row[x], ref_row[x], 1)
2684 : test_row[x] == ref_row[x];
2685 if (!match) {
2686 if (printMismatches && pixels_different < 5) {
2687 FML_LOG(ERROR) << "pix[" << x << ", " << y
2688 << "] mismatch: " << std::hex << test_row[x]
2689 << "(test) != (ref)" << ref_row[x] << std::dec;
2690 }
2691 pixels_different++;
2692 }
2693 }
2694 }
2695 if (pixels_oob > 0) {
2696 FML_LOG(ERROR) << "pix bounds[" //
2697 << min_x << ", " << min_y << " => " << max_x << ", "
2698 << max_y << "]";
2699 FML_LOG(ERROR) << "dl_bounds[" //
2700 << bounds->fLeft << ", " << bounds->fTop //
2701 << " => " //
2702 << bounds->fRight << ", " << bounds->fBottom //
2703 << "]";
2704 } else if (bounds) {
2705 showBoundsOverflow(info, i_bounds, tolerance, min_x, min_y, max_x, max_y);
2706 }
2707 ASSERT_EQ(pixels_oob, 0) << info;
2708 ASSERT_EQ(pixels_different, 0) << info;
2709 }
static bool match(const char *needle, const char *haystack)
Definition DM.cpp:1132
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
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 2587 of file dl_rendering_unittests.cc.

2588 {
2589 int count = 0;
2590 for (int y = 0; y < kTestHeight; y++) {
2591 const uint32_t* ref_row = ref_result->addr32(0, y);
2592 const uint32_t* test_row = test_result->addr32(0, y);
2593 for (int x = 0; x < kTestWidth; x++) {
2594 if (ref_row[x] != test_row[x]) {
2595 if (ref_row[x] == 0) {
2596 count++;
2597 }
2598 }
2599 }
2600 }
2601 return count;
2602 }
int count

◆ fuzzyCompare()

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

Definition at line 2475 of file dl_rendering_unittests.cc.

2475 {
2476 for (int i = 0; i < 32; i += 8) {
2477 int comp_a = (pixel_a >> i) & 0xff;
2478 int comp_b = (pixel_b >> i) & 0xff;
2479 if (std::abs(comp_a - comp_b) > fudge) {
2480 return false;
2481 }
2482 }
2483 return true;
2484 }

◆ GetProvider()

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

Definition at line 1112 of file dl_rendering_unittests.cc.

1112 {
1113 auto provider = DlSurfaceProvider::Create(type);
1114 if (provider == nullptr) {
1116 << " not supported (ignoring)";
1117 return nullptr;
1118 }
1119 provider->InitializeSurface(kTestWidth, kTestHeight,
1120 PixelFormat::kN32PremulPixelFormat);
1121 return provider;
1122 }
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 2486 of file dl_rendering_unittests.cc.

2486 {
2487 if (env.format() == PixelFormat::k565PixelFormat) {
2488 return 9;
2489 }
2490 if (env.provider()->backend_type() == BackendType::kOpenGlBackend) {
2491 // OpenGL gets a little fuzzy at times. Still, "within 5" (aka +/-4)
2492 // for byte samples is not bad, though the other backends give +/-1
2493 return 5;
2494 }
2495 return 2;
2496 }

◆ MakeTextBlob()

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

Definition at line 2753 of file dl_rendering_unittests.cc.

2754 {
2755 SkFont font = CreateTestFontOfSize(font_height);
2756 sk_sp<SkTypeface> face = font.refTypeface();
2757 FML_CHECK(face);
2758 FML_CHECK(face->countGlyphs() > 0) << "No glyphs in font";
2759 return SkTextBlob::MakeFromText(string.c_str(), string.size(), font,
2761 }
@ kUTF8
uses bytes to represent UTF-8 or ASCII
static sk_sp< SkTextBlob > MakeFromText(const void *text, size_t byteLength, const SkFont &font, SkTextEncoding encoding=SkTextEncoding::kUTF8)
#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 2604 of file dl_rendering_unittests.cc.

2605 {
2606 quickCompareToReference(env.ref_sk_result(), env.ref_dl_result(), true,
2607 info + " reference rendering");
2608 }
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 2610 of file dl_rendering_unittests.cc.

2613 {
2614 int w = test_result->width();
2615 int h = test_result->height();
2616 EXPECT_EQ(w, ref_result->width()) << info;
2617 EXPECT_EQ(h, ref_result->height()) << info;
2618 int pixels_different = 0;
2619 for (int y = 0; y < h; y++) {
2620 const uint32_t* ref_row = ref_result->addr32(0, y);
2621 const uint32_t* test_row = test_result->addr32(0, y);
2622 for (int x = 0; x < w; x++) {
2623 if (ref_row[x] != test_row[x]) {
2624 if (should_match && pixels_different < 5) {
2625 FML_LOG(ERROR) << std::hex << ref_row[x] << " != " << test_row[x];
2626 }
2627 pixels_different++;
2628 }
2629 }
2630 }
2631 if (should_match) {
2632 EXPECT_EQ(pixels_different, 0) << info;
2633 return pixels_different == 0;
2634 } else {
2635 EXPECT_NE(pixels_different, 0) << info;
2636 return pixels_different != 0;
2637 }
2638 }
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 1135 of file dl_rendering_unittests.cc.

1136 {
1137 for (auto& back_end : TestBackends) {
1138 auto provider = GetProvider(back_end);
1139 RenderEnvironment env = RenderEnvironment::MakeN32(provider.get());
1140 env.init_ref(kEmptySkSetup, params.sk_renderer(), //
1141 kEmptyDlSetup, params.dl_renderer(), params.imp_renderer());
1142 quickCompareToReference(env, "default");
1143 if (env.supports_impeller()) {
1144 auto impeller_result = env.ref_impeller_result();
1145 if (!checkPixels(impeller_result, impeller_result->render_bounds(),
1146 "Impeller reference")) {
1147 std::string test_name =
1148 ::testing::UnitTest::GetInstance()->current_test_info()->name();
1149 save_to_png(impeller_result, test_name + " (Impeller reference)",
1150 "base rendering was blank or out of bounds");
1151 }
1152 } else {
1153 static OncePerBackendWarning warnings("No Impeller output tests");
1154 warnings.warn(env.backend_name());
1155 }
1156
1157 RenderWithTransforms(params, env, tolerance);
1158 RenderWithClips(params, env, tolerance);
1159 RenderWithSaveRestore(params, env, tolerance);
1160 // Only test attributes if the canvas version uses the paint object
1161 if (params.uses_paint()) {
1162 RenderWithAttributes(params, env, tolerance);
1163 }
1164 }
1165 }
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 2289 of file dl_rendering_unittests.cc.

2292 {
2293 std::string test_name =
2294 ::testing::UnitTest::GetInstance()->current_test_info()->name();
2295 const std::string info =
2296 env.backend_name() + ": " + test_name + " (" + caseP.info() + ")";
2297 const DlColor bg = caseP.bg();
2298 RenderJobInfo base_info = {
2299 .bg = bg,
2300 };
2301
2302 // sk_result is a direct rendering via SkCanvas to SkSurface
2303 // DisplayList mechanisms are not involved in this operation
2304 SkJobRenderer sk_job(caseP.sk_setup(), //
2305 testP.sk_renderer(), //
2306 caseP.sk_restore(), //
2307 env.sk_image());
2308 auto sk_result = env.getResult(base_info, sk_job);
2309
2310 DlJobRenderer dl_job(caseP.dl_setup(), //
2311 testP.dl_renderer(), //
2312 caseP.dl_restore(), //
2313 env.dl_image());
2314 auto dl_result = env.getResult(base_info, dl_job);
2315
2316 EXPECT_EQ(sk_job.setup_matrix(), dl_job.setup_matrix());
2317 EXPECT_EQ(sk_job.setup_clip_bounds(), dl_job.setup_clip_bounds());
2318 ASSERT_EQ(sk_result->width(), kTestWidth) << info;
2319 ASSERT_EQ(sk_result->height(), kTestHeight) << info;
2320 ASSERT_EQ(dl_result->width(), kTestWidth) << info;
2321 ASSERT_EQ(dl_result->height(), kTestHeight) << info;
2322
2323 const BoundsTolerance tolerance =
2324 testP.adjust(tolerance_in, dl_job.setup_paint(), dl_job.setup_matrix());
2325 const sk_sp<SkPicture> sk_picture = sk_job.MakePicture(base_info);
2326 const sk_sp<DisplayList> display_list = dl_job.MakeDisplayList(base_info);
2327
2328 SkRect sk_bounds = sk_picture->cullRect();
2329 checkPixels(sk_result.get(), sk_bounds, info + " (Skia reference)", bg);
2330
2331 if (testP.should_match(env, caseP, dl_job.setup_paint(), dl_job)) {
2332 quickCompareToReference(env.ref_sk_result(), sk_result.get(), true,
2333 info + " (attribute should not have effect)");
2334 } else {
2335 quickCompareToReference(env.ref_sk_result(), sk_result.get(), false,
2336 info + " (attribute should affect rendering)");
2337 }
2338
2339 // If either the reference setup or the test setup contain attributes
2340 // that Impeller doesn't support, we skip the Impeller testing. This
2341 // is mostly stroked or patterned text which is vectored through drawPath
2342 // for Impeller.
2343 if (env.supports_impeller() &&
2344 testP.impeller_compatible(dl_job.setup_paint()) &&
2345 testP.impeller_compatible(env.ref_dl_paint())) {
2346 DlJobRenderer imp_job(caseP.dl_setup(), //
2347 testP.imp_renderer(), //
2348 caseP.dl_restore(), //
2349 env.impeller_image());
2350 auto imp_result = env.getImpellerResult(base_info, imp_job);
2351 std::string imp_info = info + " (Impeller)";
2352 bool success = checkPixels(imp_result.get(), imp_result->render_bounds(),
2353 imp_info, bg);
2354 if (testP.should_match(env, caseP, imp_job.setup_paint(), imp_job)) {
2355 success = success && //
2357 env.ref_impeller_result(), imp_result.get(), true,
2358 imp_info + " (attribute should not have effect)");
2359 } else {
2360 success = success && //
2362 env.ref_impeller_result(), imp_result.get(), false,
2363 imp_info + " (attribute should affect rendering)");
2364 }
2365 if (SaveImpellerFailureImages && !success) {
2366 FML_LOG(ERROR) << "Impeller issue encountered for: "
2367 << *imp_job.MakeDisplayList(base_info);
2368 save_to_png(imp_result.get(), info + " (Impeller Result)",
2369 "output saved in");
2370 save_to_png(env.ref_impeller_result(), info + " (Impeller Reference)",
2371 "compare to reference without attributes");
2372 save_to_png(sk_result.get(), info + " (Skia Result)",
2373 "and to Skia reference with attributes");
2374 save_to_png(env.ref_sk_result(), info + " (Skia Reference)",
2375 "and to Skia reference without attributes");
2376 }
2377 }
2378
2379 quickCompareToReference(sk_result.get(), dl_result.get(), true,
2380 info + " (DlCanvas output matches SkCanvas)");
2381
2382 {
2383 SkRect dl_bounds = display_list->bounds();
2384 if (!sk_bounds.roundOut().contains(dl_bounds)) {
2385 FML_LOG(ERROR) << "For " << info;
2386 FML_LOG(ERROR) << "sk ref: " //
2387 << sk_bounds.fLeft << ", " << sk_bounds.fTop << " => "
2388 << sk_bounds.fRight << ", " << sk_bounds.fBottom;
2389 FML_LOG(ERROR) << "dl: " //
2390 << dl_bounds.fLeft << ", " << dl_bounds.fTop << " => "
2391 << dl_bounds.fRight << ", " << dl_bounds.fBottom;
2392 if (!dl_bounds.contains(sk_bounds)) {
2393 FML_LOG(ERROR) << "DisplayList bounds are too small!";
2394 }
2395 if (!dl_bounds.isEmpty() &&
2396 !sk_bounds.roundOut().contains(dl_bounds.roundOut())) {
2397 FML_LOG(ERROR) << "###### DisplayList bounds larger than reference!";
2398 }
2399 }
2400
2401 // This EXPECT sometimes triggers, but when it triggers and I examine
2402 // the ref_bounds, they are always unnecessarily large and since the
2403 // pixel OOB tests in the compare method do not trigger, we will trust
2404 // the DL bounds.
2405 // EXPECT_TRUE(dl_bounds.contains(ref_bounds)) << info;
2406
2407 // When we are drawing a DisplayList, the display_list built above
2408 // will contain just a single drawDisplayList call plus the case
2409 // attribute. The sk_picture will, however, contain a list of all
2410 // of the embedded calls in the display list and so the op counts
2411 // will not be equal between the two.
2412 if (!testP.is_draw_display_list()) {
2413 EXPECT_EQ(static_cast<int>(display_list->op_count()),
2414 sk_picture->approximateOpCount())
2415 << info;
2416 EXPECT_EQ(static_cast<int>(display_list->op_count()),
2417 sk_picture->approximateOpCount())
2418 << info;
2419 }
2420
2421 DisplayListJobRenderer dl_builder_job(display_list);
2422 auto dl_builder_result = env.getResult(base_info, dl_builder_job);
2423 if (caseP.fuzzy_compare_components()) {
2425 dl_builder_result.get(), dl_result.get(),
2426 info + " (DlCanvas DL output close to Builder Dl output)",
2427 &dl_bounds, &tolerance, bg, true);
2428 } else {
2430 dl_builder_result.get(), dl_result.get(), true,
2431 info + " (DlCanvas DL output matches Builder Dl output)");
2432 }
2433
2434 compareToReference(dl_result.get(), sk_result.get(),
2435 info + " (DisplayList built directly -> surface)",
2436 &dl_bounds, &tolerance, bg,
2437 caseP.fuzzy_compare_components());
2438
2439 if (display_list->can_apply_group_opacity()) {
2440 checkGroupOpacity(env, display_list, dl_result.get(),
2441 info + " with Group Opacity", bg);
2442 }
2443 }
2444
2445 {
2446 // This sequence uses an SkPicture generated previously from the SkCanvas
2447 // calls and a DisplayList generated previously from the DlCanvas calls
2448 // and renders both back under a transform (scale(2x)) to see if their
2449 // rendering is affected differently by a change of matrix between
2450 // recording time and rendering time.
2451 const int test_width_2 = kTestWidth * 2;
2452 const int test_height_2 = kTestHeight * 2;
2453 const SkScalar test_scale = 2.0;
2454
2455 SkPictureJobRenderer sk_job_x2(sk_picture);
2456 RenderJobInfo info_2x = {
2457 .width = test_width_2,
2458 .height = test_height_2,
2459 .bg = bg,
2460 .scale = test_scale,
2461 };
2462 auto ref_x2_result = env.getResult(info_2x, sk_job_x2);
2463 ASSERT_EQ(ref_x2_result->width(), test_width_2) << info;
2464 ASSERT_EQ(ref_x2_result->height(), test_height_2) << info;
2465
2466 DisplayListJobRenderer dl_job_x2(display_list);
2467 auto test_x2_result = env.getResult(info_2x, dl_job_x2);
2468 compareToReference(test_x2_result.get(), ref_x2_result.get(),
2469 info + " (Both rendered scaled 2x)", nullptr, nullptr,
2470 bg, caseP.fuzzy_compare_components(), //
2471 test_width_2, test_height_2, false);
2472 }
2473 }
static void test_scale(skiatest::Reporter *reporter, const Geo &geo)
static void compareToReference(const RenderResult *test_result, const RenderResult *ref_result, const std::string &info, SkRect *bounds, const BoundsTolerance *tolerance, const DlColor bg, bool fuzzyCompares=false, int width=kTestWidth, int height=kTestHeight, bool printMismatches=false)
static void checkGroupOpacity(const RenderEnvironment &env, const sk_sp< DisplayList > &display_list, const RenderResult *ref_result, const std::string &info, DlColor bg)
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 1469 of file dl_rendering_unittests.cc.

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

2105 {
2106 // We used to use an inset of 15.5 pixels here, but since Skia's rounding
2107 // behavior at the center of pixels does not match between HW and SW, we
2108 // ended up with some clips including different pixels between the two
2109 // destinations and this interacted poorly with the carefully chosen
2110 // geometry in some of the tests which was designed to have just the
2111 // right features fully filling the clips based on the SW rounding. By
2112 // moving to a 15.4 inset, the edge of the clip is never on the "rounding
2113 // edge" of a pixel.
2114 SkRect r_clip = kRenderBounds.makeInset(15.4, 15.4);
2115 BoundsTolerance intersect_tolerance = diff_tolerance.clip(r_clip);
2116 intersect_tolerance = intersect_tolerance.addPostClipPadding(1, 1);
2117 RenderWith(testP, env, intersect_tolerance,
2118 CaseParameters(
2119 "Hard ClipRect inset by 15.4",
2120 [=](const SkSetupContext& ctx) {
2121 ctx.canvas->clipRect(r_clip, SkClipOp::kIntersect, false);
2122 },
2123 [=](const DlSetupContext& ctx) {
2124 ctx.canvas->ClipRect(r_clip, ClipOp::kIntersect, false);
2125 }));
2126 RenderWith(testP, env, intersect_tolerance,
2127 CaseParameters(
2128 "AntiAlias ClipRect inset by 15.4",
2129 [=](const SkSetupContext& ctx) {
2130 ctx.canvas->clipRect(r_clip, SkClipOp::kIntersect, true);
2131 },
2132 [=](const DlSetupContext& ctx) {
2133 ctx.canvas->ClipRect(r_clip, ClipOp::kIntersect, true);
2134 }));
2135 RenderWith(testP, env, diff_tolerance,
2136 CaseParameters(
2137 "Hard ClipRect Diff, inset by 15.4",
2138 [=](const SkSetupContext& ctx) {
2139 ctx.canvas->clipRect(r_clip, SkClipOp::kDifference, false);
2140 },
2141 [=](const DlSetupContext& ctx) {
2142 ctx.canvas->ClipRect(r_clip, ClipOp::kDifference, false);
2143 })
2144 .with_diff_clip());
2145 // This test RR clip used to use very small radii, but due to
2146 // optimizations in the HW rrect rasterization, this caused small
2147 // bulges in the corners of the RRect which were interpreted as
2148 // "clip overruns" by the clip OOB pixel testing code. Using less
2149 // abusively small radii fixes the problem.
2150 SkRRect rr_clip = SkRRect::MakeRectXY(r_clip, 9, 9);
2151 RenderWith(testP, env, intersect_tolerance,
2152 CaseParameters(
2153 "Hard ClipRRect with radius of 15.4",
2154 [=](const SkSetupContext& ctx) {
2155 ctx.canvas->clipRRect(rr_clip, SkClipOp::kIntersect,
2156 false);
2157 },
2158 [=](const DlSetupContext& ctx) {
2159 ctx.canvas->ClipRRect(rr_clip, ClipOp::kIntersect, false);
2160 }));
2161 RenderWith(testP, env, intersect_tolerance,
2162 CaseParameters(
2163 "AntiAlias ClipRRect with radius of 15.4",
2164 [=](const SkSetupContext& ctx) {
2165 ctx.canvas->clipRRect(rr_clip, SkClipOp::kIntersect, true);
2166 },
2167 [=](const DlSetupContext& ctx) {
2168 ctx.canvas->ClipRRect(rr_clip, ClipOp::kIntersect, true);
2169 }));
2170 RenderWith(testP, env, diff_tolerance,
2171 CaseParameters(
2172 "Hard ClipRRect Diff, with radius of 15.4",
2173 [=](const SkSetupContext& ctx) {
2174 ctx.canvas->clipRRect(rr_clip, SkClipOp::kDifference,
2175 false);
2176 },
2177 [=](const DlSetupContext& ctx) {
2178 ctx.canvas->ClipRRect(rr_clip, ClipOp::kDifference, false);
2179 })
2180 .with_diff_clip());
2181 SkPath path_clip = SkPath();
2182 path_clip.setFillType(SkPathFillType::kEvenOdd);
2183 path_clip.addRect(r_clip);
2184 path_clip.addCircle(kRenderCenterX, kRenderCenterY, 1.0);
2185 RenderWith(testP, env, intersect_tolerance,
2186 CaseParameters(
2187 "Hard ClipPath inset by 15.4",
2188 [=](const SkSetupContext& ctx) {
2189 ctx.canvas->clipPath(path_clip, SkClipOp::kIntersect,
2190 false);
2191 },
2192 [=](const DlSetupContext& ctx) {
2193 ctx.canvas->ClipPath(path_clip, ClipOp::kIntersect, false);
2194 }));
2195 RenderWith(testP, env, intersect_tolerance,
2196 CaseParameters(
2197 "AntiAlias ClipPath inset by 15.4",
2198 [=](const SkSetupContext& ctx) {
2199 ctx.canvas->clipPath(path_clip, SkClipOp::kIntersect,
2200 true);
2201 },
2202 [=](const DlSetupContext& ctx) {
2203 ctx.canvas->ClipPath(path_clip, ClipOp::kIntersect, true);
2204 }));
2205 RenderWith(
2206 testP, env, diff_tolerance,
2207 CaseParameters(
2208 "Hard ClipPath Diff, inset by 15.4",
2209 [=](const SkSetupContext& ctx) {
2210 ctx.canvas->clipPath(path_clip, SkClipOp::kDifference, false);
2211 },
2212 [=](const DlSetupContext& ctx) {
2213 ctx.canvas->ClipPath(path_clip, ClipOp::kDifference, false);
2214 })
2215 .with_diff_clip());
2216 }
static SkRRect MakeRectXY(const SkRect &rect, SkScalar xRad, SkScalar yRad)
Definition SkRRect.h:180
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 1167 of file dl_rendering_unittests.cc.

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

1795 {
1796 // The test cases were generated with geometry that will try to fill
1797 // out the various miter limits used for testing, but they can be off
1798 // by a couple of pixels so we will relax bounds testing for strokes by
1799 // a couple of pixels.
1800 BoundsTolerance tolerance = tolerance_in.addBoundsPadding(2, 2);
1801 RenderWith(testP, env, tolerance,
1802 CaseParameters(
1803 "Fill",
1804 [=](const SkSetupContext& ctx) {
1805 ctx.paint.setStyle(SkPaint::kFill_Style);
1806 },
1807 [=](const DlSetupContext& ctx) {
1808 ctx.paint.setDrawStyle(DlDrawStyle::kFill);
1809 }));
1810 // Skia on HW produces a strong miter consistent with width=1.0
1811 // for any width less than a pixel, but the bounds computations of
1812 // both DL and SkPicture do not account for this. We will get
1813 // OOB pixel errors for the highly mitered drawPath geometry if
1814 // we don't set stroke width to 1.0 for that test on HW.
1815 // See https://bugs.chromium.org/p/skia/issues/detail?id=14046
1816 bool no_hairlines =
1817 testP.is_draw_path() &&
1818 env.provider()->backend_type() != BackendType::kSoftwareBackend;
1819 RenderWith(testP, env, tolerance,
1820 CaseParameters(
1821 "Stroke + defaults",
1822 [=](const SkSetupContext& ctx) {
1823 if (no_hairlines) {
1824 ctx.paint.setStrokeWidth(1.0);
1825 }
1826 ctx.paint.setStyle(SkPaint::kStroke_Style);
1827 },
1828 [=](const DlSetupContext& ctx) {
1829 if (no_hairlines) {
1830 ctx.paint.setStrokeWidth(1.0);
1831 }
1832 ctx.paint.setDrawStyle(DlDrawStyle::kStroke);
1833 }));
1834
1835 RenderWith(testP, env, tolerance,
1836 CaseParameters(
1837 "Fill + unnecessary StrokeWidth 10",
1838 [=](const SkSetupContext& ctx) {
1839 ctx.paint.setStyle(SkPaint::kFill_Style);
1840 ctx.paint.setStrokeWidth(10.0);
1841 },
1842 [=](const DlSetupContext& ctx) {
1843 ctx.paint.setDrawStyle(DlDrawStyle::kFill);
1844 ctx.paint.setStrokeWidth(10.0);
1845 }));
1846
1847 RenderEnvironment stroke_base_env =
1848 RenderEnvironment::MakeN32(env.provider());
1849 SkSetup sk_stroke_setup = [=](const SkSetupContext& ctx) {
1850 ctx.paint.setStyle(SkPaint::kStroke_Style);
1851 ctx.paint.setStrokeWidth(5.0);
1852 };
1853 DlSetup dl_stroke_setup = [=](const DlSetupContext& ctx) {
1854 ctx.paint.setDrawStyle(DlDrawStyle::kStroke);
1855 ctx.paint.setStrokeWidth(5.0);
1856 };
1857 stroke_base_env.init_ref(sk_stroke_setup, testP.sk_renderer(),
1858 dl_stroke_setup, testP.dl_renderer(),
1859 testP.imp_renderer());
1860 quickCompareToReference(stroke_base_env, "stroke");
1861
1862 RenderWith(testP, stroke_base_env, tolerance,
1863 CaseParameters(
1864 "Stroke Width 10",
1865 [=](const SkSetupContext& ctx) {
1866 ctx.paint.setStyle(SkPaint::kStroke_Style);
1867 ctx.paint.setStrokeWidth(10.0);
1868 },
1869 [=](const DlSetupContext& ctx) {
1870 ctx.paint.setDrawStyle(DlDrawStyle::kStroke);
1871 ctx.paint.setStrokeWidth(10.0);
1872 }));
1873 RenderWith(testP, stroke_base_env, tolerance,
1874 CaseParameters(
1875 "Stroke Width 5",
1876 [=](const SkSetupContext& ctx) {
1877 ctx.paint.setStyle(SkPaint::kStroke_Style);
1878 ctx.paint.setStrokeWidth(5.0);
1879 },
1880 [=](const DlSetupContext& ctx) {
1881 ctx.paint.setDrawStyle(DlDrawStyle::kStroke);
1882 ctx.paint.setStrokeWidth(5.0);
1883 }));
1884
1885 RenderWith(testP, stroke_base_env, tolerance,
1886 CaseParameters(
1887 "Stroke Width 5, Square Cap",
1888 [=](const SkSetupContext& ctx) {
1889 ctx.paint.setStyle(SkPaint::kStroke_Style);
1890 ctx.paint.setStrokeWidth(5.0);
1891 ctx.paint.setStrokeCap(SkPaint::kSquare_Cap);
1892 },
1893 [=](const DlSetupContext& ctx) {
1894 ctx.paint.setDrawStyle(DlDrawStyle::kStroke);
1895 ctx.paint.setStrokeWidth(5.0);
1896 ctx.paint.setStrokeCap(DlStrokeCap::kSquare);
1897 }));
1898 RenderWith(testP, stroke_base_env, tolerance,
1899 CaseParameters(
1900 "Stroke Width 5, Round Cap",
1901 [=](const SkSetupContext& ctx) {
1902 ctx.paint.setStyle(SkPaint::kStroke_Style);
1903 ctx.paint.setStrokeWidth(5.0);
1904 ctx.paint.setStrokeCap(SkPaint::kRound_Cap);
1905 },
1906 [=](const DlSetupContext& ctx) {
1907 ctx.paint.setDrawStyle(DlDrawStyle::kStroke);
1908 ctx.paint.setStrokeWidth(5.0);
1909 ctx.paint.setStrokeCap(DlStrokeCap::kRound);
1910 }));
1911
1912 RenderWith(testP, stroke_base_env, tolerance,
1913 CaseParameters(
1914 "Stroke Width 5, Bevel Join",
1915 [=](const SkSetupContext& ctx) {
1916 ctx.paint.setStyle(SkPaint::kStroke_Style);
1917 ctx.paint.setStrokeWidth(5.0);
1918 ctx.paint.setStrokeJoin(SkPaint::kBevel_Join);
1919 },
1920 [=](const DlSetupContext& ctx) {
1921 ctx.paint.setDrawStyle(DlDrawStyle::kStroke);
1922 ctx.paint.setStrokeWidth(5.0);
1923 ctx.paint.setStrokeJoin(DlStrokeJoin::kBevel);
1924 }));
1925 RenderWith(testP, stroke_base_env, tolerance,
1926 CaseParameters(
1927 "Stroke Width 5, Round Join",
1928 [=](const SkSetupContext& ctx) {
1929 ctx.paint.setStyle(SkPaint::kStroke_Style);
1930 ctx.paint.setStrokeWidth(5.0);
1931 ctx.paint.setStrokeJoin(SkPaint::kRound_Join);
1932 },
1933 [=](const DlSetupContext& ctx) {
1934 ctx.paint.setDrawStyle(DlDrawStyle::kStroke);
1935 ctx.paint.setStrokeWidth(5.0);
1936 ctx.paint.setStrokeJoin(DlStrokeJoin::kRound);
1937 }));
1938
1939 RenderWith(testP, stroke_base_env, tolerance,
1940 CaseParameters(
1941 "Stroke Width 5, Miter 10",
1942 [=](const SkSetupContext& ctx) {
1943 ctx.paint.setStyle(SkPaint::kStroke_Style);
1944 ctx.paint.setStrokeWidth(5.0);
1945 ctx.paint.setStrokeMiter(10.0);
1946 ctx.paint.setStrokeJoin(SkPaint::kMiter_Join);
1947 },
1948 [=](const DlSetupContext& ctx) {
1949 ctx.paint.setDrawStyle(DlDrawStyle::kStroke);
1950 ctx.paint.setStrokeWidth(5.0);
1951 ctx.paint.setStrokeMiter(10.0);
1952 ctx.paint.setStrokeJoin(DlStrokeJoin::kMiter);
1953 }));
1954
1955 RenderWith(testP, stroke_base_env, tolerance,
1956 CaseParameters(
1957 "Stroke Width 5, Miter 0",
1958 [=](const SkSetupContext& ctx) {
1959 ctx.paint.setStyle(SkPaint::kStroke_Style);
1960 ctx.paint.setStrokeWidth(5.0);
1961 ctx.paint.setStrokeMiter(0.0);
1962 ctx.paint.setStrokeJoin(SkPaint::kMiter_Join);
1963 },
1964 [=](const DlSetupContext& ctx) {
1965 ctx.paint.setDrawStyle(DlDrawStyle::kStroke);
1966 ctx.paint.setStrokeWidth(5.0);
1967 ctx.paint.setStrokeMiter(0.0);
1968 ctx.paint.setStrokeJoin(DlStrokeJoin::kMiter);
1969 }));
1970
1971 {
1972 const SkScalar test_dashes_1[] = {29.0, 2.0};
1973 const SkScalar test_dashes_2[] = {17.0, 1.5};
1974 auto dl_dash_effect = DlDashPathEffect::Make(test_dashes_1, 2, 0.0f);
1975 auto sk_dash_effect = SkDashPathEffect::Make(test_dashes_1, 2, 0.0f);
1976 {
1977 RenderWith(testP, stroke_base_env, tolerance,
1978 CaseParameters(
1979 "PathEffect without forced stroking == Dash-29-2",
1980 [=](const SkSetupContext& ctx) {
1981 // Provide some non-trivial stroke size to get dashed
1982 ctx.paint.setStrokeWidth(5.0);
1983 ctx.paint.setPathEffect(sk_dash_effect);
1984 },
1985 [=](const DlSetupContext& ctx) {
1986 // Provide some non-trivial stroke size to get dashed
1987 ctx.paint.setStrokeWidth(5.0);
1988 ctx.paint.setPathEffect(dl_dash_effect);
1989 }));
1990 }
1991 {
1992 RenderWith(testP, stroke_base_env, tolerance,
1993 CaseParameters(
1994 "PathEffect == Dash-29-2",
1995 [=](const SkSetupContext& ctx) {
1996 // Need stroke style to see dashing properly
1997 ctx.paint.setStyle(SkPaint::kStroke_Style);
1998 // Provide some non-trivial stroke size to get dashed
1999 ctx.paint.setStrokeWidth(5.0);
2000 ctx.paint.setPathEffect(sk_dash_effect);
2001 },
2002 [=](const DlSetupContext& ctx) {
2003 // Need stroke style to see dashing properly
2004 ctx.paint.setDrawStyle(DlDrawStyle::kStroke);
2005 // Provide some non-trivial stroke size to get dashed
2006 ctx.paint.setStrokeWidth(5.0);
2007 ctx.paint.setPathEffect(dl_dash_effect);
2008 }));
2009 }
2010 dl_dash_effect = DlDashPathEffect::Make(test_dashes_2, 2, 0.0f);
2011 sk_dash_effect = SkDashPathEffect::Make(test_dashes_2, 2, 0.0f);
2012 {
2013 RenderWith(testP, stroke_base_env, tolerance,
2014 CaseParameters(
2015 "PathEffect == Dash-17-1.5",
2016 [=](const SkSetupContext& ctx) {
2017 // Need stroke style to see dashing properly
2018 ctx.paint.setStyle(SkPaint::kStroke_Style);
2019 // Provide some non-trivial stroke size to get dashed
2020 ctx.paint.setStrokeWidth(5.0);
2021 ctx.paint.setPathEffect(sk_dash_effect);
2022 },
2023 [=](const DlSetupContext& ctx) {
2024 // Need stroke style to see dashing properly
2025 ctx.paint.setDrawStyle(DlDrawStyle::kStroke);
2026 // Provide some non-trivial stroke size to get dashed
2027 ctx.paint.setStrokeWidth(5.0);
2028 ctx.paint.setPathEffect(dl_dash_effect);
2029 }));
2030 }
2031 }
2032 }
static sk_sp< SkPathEffect > Make(const SkScalar intervals[], int count, SkScalar phase)
@ 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
static std::shared_ptr< DlPathEffect > Make(const SkScalar intervals[], int count, SkScalar phase)
@ 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 2034 of file dl_rendering_unittests.cc.

2036 {
2037 // If the rendering method does not fill the corners of the original
2038 // bounds, then the estimate under rotation or skewing will be off
2039 // so we scale the padding by about 5% to compensate.
2040 BoundsTolerance skewed_tolerance = tolerance.mulScale(1.05, 1.05);
2041 RenderWith( //
2042 testP, env, tolerance,
2043 CaseParameters(
2044 "Translate 5, 10", //
2045 [=](const SkSetupContext& ctx) { ctx.canvas->translate(5, 10); },
2046 [=](const DlSetupContext& ctx) { ctx.canvas->Translate(5, 10); }));
2047 RenderWith( //
2048 testP, env, tolerance,
2049 CaseParameters(
2050 "Scale +5%", //
2051 [=](const SkSetupContext& ctx) { ctx.canvas->scale(1.05, 1.05); },
2052 [=](const DlSetupContext& ctx) { ctx.canvas->Scale(1.05, 1.05); }));
2053 RenderWith( //
2054 testP, env, skewed_tolerance,
2055 CaseParameters(
2056 "Rotate 5 degrees", //
2057 [=](const SkSetupContext& ctx) { ctx.canvas->rotate(5); },
2058 [=](const DlSetupContext& ctx) { ctx.canvas->Rotate(5); }));
2059 RenderWith( //
2060 testP, env, skewed_tolerance,
2061 CaseParameters(
2062 "Skew 5%", //
2063 [=](const SkSetupContext& ctx) { ctx.canvas->skew(0.05, 0.05); },
2064 [=](const DlSetupContext& ctx) { ctx.canvas->Skew(0.05, 0.05); }));
2065 {
2066 // This rather odd transform can cause slight differences in
2067 // computing in-bounds samples depending on which base rendering
2068 // routine Skia uses. Making sure our matrix values are powers
2069 // of 2 reduces, but does not eliminate, these slight differences
2070 // in calculation when we are comparing rendering with an alpha
2071 // to rendering opaque colors in the group opacity tests, for
2072 // example.
2073 SkScalar tweak = 1.0 / 16.0;
2074 SkMatrix tx = SkMatrix::MakeAll(1.0 + tweak, tweak, 5, //
2075 tweak, 1.0 + tweak, 10, //
2076 0, 0, 1);
2077 RenderWith( //
2078 testP, env, skewed_tolerance,
2079 CaseParameters(
2080 "Transform 2D Affine",
2081 [=](const SkSetupContext& ctx) { ctx.canvas->concat(tx); },
2082 [=](const DlSetupContext& ctx) { ctx.canvas->Transform(tx); }));
2083 }
2084 {
2085 SkM44 m44 = SkM44(1, 0, 0, kRenderCenterX, //
2086 0, 1, 0, kRenderCenterY, //
2087 0, 0, 1, 0, //
2088 0, 0, .001, 1);
2089 m44.preConcat(
2090 SkM44::Rotate({1, 0, 0}, math::kPi / 60)); // 3 degrees around X
2091 m44.preConcat(
2092 SkM44::Rotate({0, 1, 0}, math::kPi / 45)); // 4 degrees around Y
2094 RenderWith( //
2095 testP, env, skewed_tolerance,
2096 CaseParameters(
2097 "Transform Full Perspective",
2098 [=](const SkSetupContext& ctx) { ctx.canvas->concat(m44); },
2099 [=](const DlSetupContext& ctx) { ctx.canvas->Transform(m44); }));
2100 }
2101 }
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 2265 of file dl_rendering_unittests.cc.

2267 {
2269 return;
2270 }
2271 if (ImpellerFailureImageDirectory.length() == 0) {
2273 if (ImpellerFailureImageDirectory.length() == 0) {
2275 return;
2276 }
2277 }
2278
2279 std::string filename = ImpellerFailureImageDirectory + "/";
2280 for (const char& ch : op_desc) {
2281 filename += (ch == ':' || ch == ' ') ? '_' : ch;
2282 }
2283 filename = filename + ".png";
2284 result->write(filename);
2285 ImpellerFailureImages.push_back(filename);
2286 FML_LOG(ERROR) << reason << ": " << filename;
2287 }
static std::vector< std::string > ImpellerFailureImages
GAsyncResult * result

◆ SetupImpellerFailureImageDirectory()

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

Definition at line 2241 of file dl_rendering_unittests.cc.

2241 {
2242 std::string base_dir = "./impeller_failure_images";
2243 if (CheckDir(base_dir) == DirectoryStatus::kFailed) {
2244 return;
2245 }
2246 for (int i = 0; i < 10000; i++) {
2247 std::string sub_dir = std::to_string(i);
2248 while (sub_dir.length() < 4) {
2249 sub_dir = "0" + sub_dir;
2250 }
2251 std::string try_dir = base_dir + "/" + sub_dir;
2252 switch (CheckDir(try_dir)) {
2254 break;
2257 return;
2259 return;
2260 }
2261 }
2262 FML_LOG(ERROR) << "Too many output directories for Impeller failure images";
2263 }
static DirectoryStatus CheckDir(const std::string &dir)

◆ 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 2711 of file dl_rendering_unittests.cc.

2717 {
2718 int pad_left = std::max(0, pixLeft - bounds.fLeft);
2719 int pad_top = std::max(0, pixTop - bounds.fTop);
2720 int pad_right = std::max(0, bounds.fRight - pixRight);
2721 int pad_bottom = std::max(0, bounds.fBottom - pixBottom);
2722 SkIRect pix_bounds =
2723 SkIRect::MakeLTRB(pixLeft, pixTop, pixRight, pixBottom);
2724 SkISize pix_size = pix_bounds.size();
2725 int pix_width = pix_size.width();
2726 int pix_height = pix_size.height();
2727 int worst_pad_x = std::max(pad_left, pad_right);
2728 int worst_pad_y = std::max(pad_top, pad_bottom);
2729 if (tolerance->overflows(pix_bounds, worst_pad_x, worst_pad_y)) {
2730 FML_LOG(ERROR) << "Computed bounds for " << info;
2731 FML_LOG(ERROR) << "pix bounds[" //
2732 << pixLeft << ", " << pixTop << " => " //
2733 << pixRight << ", " << pixBottom //
2734 << "]";
2735 FML_LOG(ERROR) << "dl_bounds[" //
2736 << bounds.fLeft << ", " << bounds.fTop //
2737 << " => " //
2738 << bounds.fRight << ", " << bounds.fBottom //
2739 << "]";
2740 FML_LOG(ERROR) << "Bounds overly conservative by up to " //
2741 << worst_pad_x << ", " << worst_pad_y //
2742 << " (" << (worst_pad_x * 100.0 / pix_width) //
2743 << "%, " << (worst_pad_y * 100.0 / pix_height) << "%)";
2744 int pix_area = pix_size.area();
2745 int dl_area = bounds.width() * bounds.height();
2746 FML_LOG(ERROR) << "Total overflow area: " << (dl_area - pix_area) //
2747 << " (+" << (dl_area * 100.0 / pix_area - 100.0) //
2748 << "% larger)";
2749 FML_LOG(ERROR);
2750 }
2751 }
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
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 1133 of file dl_rendering_unittests.cc.

◆ ImpellerFailureImageDirectory

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

Definition at line 1108 of file dl_rendering_unittests.cc.

◆ ImpellerFailureImages

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

Definition at line 1110 of file dl_rendering_unittests.cc.

◆ SaveImpellerFailureImages

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

Definition at line 1109 of file dl_rendering_unittests.cc.

◆ TestBackends

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

Definition at line 1107 of file dl_rendering_unittests.cc.


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