diff --git a/display_list/display_list.h b/display_list/display_list.h index 35c19ef9d4a0b..bb67c435cc6d7 100644 --- a/display_list/display_list.h +++ b/display_list/display_list.h @@ -98,9 +98,11 @@ namespace flutter { V(TransformReset) \ \ V(ClipIntersectRect) \ + V(ClipIntersectOval) \ V(ClipIntersectRRect) \ V(ClipIntersectPath) \ V(ClipDifferenceRect) \ + V(ClipDifferenceOval) \ V(ClipDifferenceRRect) \ V(ClipDifferencePath) \ \ diff --git a/display_list/display_list_unittests.cc b/display_list/display_list_unittests.cc index 635d38af7c8a9..13902b621a241 100644 --- a/display_list/display_list_unittests.cc +++ b/display_list/display_list_unittests.cc @@ -3439,11 +3439,9 @@ TEST_F(DisplayListTest, ImpellerPathPreferenceIsHonored) { }; DisplayListBuilder builder; - builder.DrawPath(SkPath::Rect(SkRect::MakeLTRB(0, 0, 100, 100)), DlPaint()); - builder.ClipPath(SkPath::Rect(SkRect::MakeLTRB(0, 0, 100, 100)), - ClipOp::kIntersect, true); - builder.DrawShadow(SkPath::Rect(SkRect::MakeLTRB(20, 20, 80, 80)), - DlColor::kBlue(), 1.0f, true, 1.0f); + builder.DrawPath(kTestPath1, DlPaint()); + builder.ClipPath(kTestPath1, ClipOp::kIntersect, true); + builder.DrawShadow(kTestPath1, DlColor::kBlue(), 1.0f, true, 1.0f); auto display_list = builder.Build(); { @@ -4332,36 +4330,66 @@ TEST_F(DisplayListTest, DrawDisplayListForwardsBackdropFlag) { #define CLIP_EXPECTOR(name) ClipExpector name(__FILE__, __LINE__) +struct ClipExpectation { + std::variant shape; + bool is_oval; + ClipOp clip_op; + bool is_aa; + + std::string shape_name() { + switch (shape.index()) { + case 0: + return is_oval ? "SkOval" : "SkRect"; + case 1: + return "SkRRect"; + case 2: + return "SkPath"; + default: + return "Unknown"; + } + } +}; + +::std::ostream& operator<<(::std::ostream& os, const ClipExpectation& expect) { + os << "Expectation("; + switch (expect.shape.index()) { + case 0: + os << std::get(expect.shape); + if (expect.is_oval) { + os << " (oval)"; + } + break; + case 1: + os << std::get(expect.shape); + break; + case 2: + os << std::get(expect.shape); + break; + case 3: + os << "Unknown"; + } + os << ", " << expect.clip_op; + os << ", " << expect.is_aa; + os << ")"; + return os; +} + class ClipExpector : public virtual DlOpReceiver, virtual IgnoreAttributeDispatchHelper, virtual IgnoreTransformDispatchHelper, virtual IgnoreDrawDispatchHelper { public: - struct Expectation { - std::variant shape; - ClipOp clip_op; - bool is_aa; - - std::string shape_name() { - switch (shape.index()) { - case 0: - return "SkRect"; - case 1: - return "SkRRect"; - case 2: - return "SkPath"; - default: - return "Unknown"; - } - } - }; - // file and line supplied automatically from CLIP_EXPECTOR macro explicit ClipExpector(const std::string& file, int line) : file_(file), line_(line) {} ~ClipExpector() { // EXPECT_EQ(index_, clip_expectations_.size()) << label(); + while (index_ < clip_expectations_.size()) { + auto expect = clip_expectations_[index_]; + FML_LOG(ERROR) << "leftover clip shape[" << index_ << "] = " << expect; + index_++; + } } ClipExpector& addExpectation(const SkRect& rect, @@ -4369,6 +4397,19 @@ class ClipExpector : public virtual DlOpReceiver, bool is_aa = false) { clip_expectations_.push_back({ .shape = rect, + .is_oval = false, + .clip_op = clip_op, + .is_aa = is_aa, + }); + return *this; + } + + ClipExpector& addOvalExpectation(const SkRect& rect, + ClipOp clip_op = ClipOp::kIntersect, + bool is_aa = false) { + clip_expectations_.push_back({ + .shape = rect, + .is_oval = true, .clip_op = clip_op, .is_aa = is_aa, }); @@ -4380,6 +4421,7 @@ class ClipExpector : public virtual DlOpReceiver, bool is_aa = false) { clip_expectations_.push_back({ .shape = rrect, + .is_oval = false, .clip_op = clip_op, .is_aa = is_aa, }); @@ -4391,6 +4433,7 @@ class ClipExpector : public virtual DlOpReceiver, bool is_aa = false) { clip_expectations_.push_back({ .shape = path, + .is_oval = false, .clip_op = clip_op, .is_aa = is_aa, }); @@ -4402,6 +4445,11 @@ class ClipExpector : public virtual DlOpReceiver, bool is_aa) override { check(rect, clip_op, is_aa); } + void clipOval(const SkRect& bounds, + DlCanvas::ClipOp clip_op, + bool is_aa) override { + check(bounds, clip_op, is_aa, true); + } void clipRRect(const SkRRect& rrect, DlCanvas::ClipOp clip_op, bool is_aa) override { @@ -4415,22 +4463,23 @@ class ClipExpector : public virtual DlOpReceiver, private: size_t index_ = 0; - std::vector clip_expectations_; + std::vector clip_expectations_; template - void check(T shape, ClipOp clip_op, bool is_aa) { + void check(T shape, ClipOp clip_op, bool is_aa, bool is_oval = false) { ASSERT_LT(index_, clip_expectations_.size()) << label() << std::endl - << "extra clip shape = " << shape; + << "extra clip shape = " << shape << (is_oval ? " (oval)" : ""); auto expected = clip_expectations_[index_]; - EXPECT_EQ(expected.clip_op, clip_op) << label(); - EXPECT_EQ(expected.is_aa, is_aa) << label(); if (!std::holds_alternative(expected.shape)) { EXPECT_TRUE(std::holds_alternative(expected.shape)) << label() << ", expected type: " << expected.shape_name(); } else { EXPECT_EQ(std::get(expected.shape), shape) << label(); } + EXPECT_EQ(expected.is_oval, is_oval) << label(); + EXPECT_EQ(expected.clip_op, clip_op) << label(); + EXPECT_EQ(expected.is_aa, is_aa) << label(); index_++; } @@ -4570,9 +4619,47 @@ TEST_F(DisplayListTest, ClipRectNestedNonCullingComplex) { cull_dl->Dispatch(expector); } +TEST_F(DisplayListTest, ClipOvalCulling) { + auto clip = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f); + // A 10x10 rectangle extends 5x5 from the center to each corner. To have + // an oval that encompasses that rectangle, the radius must be at least + // length(5, 5), or 7.071+ so we expand the radius 5 square clip by 2.072 + // on each side to barely contain the corners of the square. + auto encompassing_oval = clip.makeOutset(2.072f, 2.072f); + + DisplayListBuilder cull_builder; + cull_builder.ClipRect(clip, ClipOp::kIntersect, false); + cull_builder.ClipOval(encompassing_oval, ClipOp::kIntersect, false); + auto cull_dl = cull_builder.Build(); + + CLIP_EXPECTOR(expector); + expector.addExpectation(clip, ClipOp::kIntersect, false); + cull_dl->Dispatch(expector); +} + +TEST_F(DisplayListTest, ClipOvalNonCulling) { + auto clip = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f); + // A 10x10 rectangle extends 5x5 from the center to each corner. To have + // an oval that encompasses that rectangle, the radius must be at least + // length(5, 5), or 7.071+ so we expand the radius 5 square clip by 2.072 + // on each side to barely exclude the corners of the square. + auto non_encompassing_oval = clip.makeOutset(2.071f, 2.071f); + + DisplayListBuilder cull_builder; + cull_builder.ClipRect(clip, ClipOp::kIntersect, false); + cull_builder.ClipOval(non_encompassing_oval, ClipOp::kIntersect, false); + auto cull_dl = cull_builder.Build(); + + CLIP_EXPECTOR(expector); + expector.addExpectation(clip, ClipOp::kIntersect, false); + expector.addOvalExpectation(non_encompassing_oval, ClipOp::kIntersect, false); + cull_dl->Dispatch(expector); +} + TEST_F(DisplayListTest, ClipRRectCulling) { auto clip = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f); auto rrect = SkRRect::MakeRectXY(clip.makeOutset(2.0f, 2.0f), 2.0f, 2.0f); + ASSERT_FALSE(rrect.isOval()); DisplayListBuilder cull_builder; cull_builder.ClipRect(clip, ClipOp::kIntersect, false); @@ -4586,7 +4673,8 @@ TEST_F(DisplayListTest, ClipRRectCulling) { TEST_F(DisplayListTest, ClipRRectNonCulling) { auto clip = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f); - auto rrect = SkRRect::MakeRectXY(clip.makeOutset(2.0f, 2.0f), 12.0f, 12.0f); + auto rrect = SkRRect::MakeRectXY(clip.makeOutset(1.0f, 1.0f), 4.0f, 4.0f); + ASSERT_FALSE(rrect.isOval()); DisplayListBuilder cull_builder; cull_builder.ClipRect(clip, ClipOp::kIntersect, false); @@ -4661,9 +4749,52 @@ TEST_F(DisplayListTest, ClipPathRectNonCulling) { cull_dl->Dispatch(expector); } +TEST_F(DisplayListTest, ClipPathOvalCulling) { + auto clip = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f); + // A 10x10 rectangle extends 5x5 from the center to each corner. To have + // an oval that encompasses that rectangle, the radius must be at least + // length(5, 5), or 7.071+ so we expand the radius 5 square clip by 2.072 + // on each side to barely contain the corners of the square. + auto encompassing_oval = clip.makeOutset(2.072f, 2.072f); + SkPath path; + path.addOval(encompassing_oval); + + DisplayListBuilder cull_builder; + cull_builder.ClipRect(clip, ClipOp::kIntersect, false); + cull_builder.ClipPath(path, ClipOp::kIntersect, false); + auto cull_dl = cull_builder.Build(); + + CLIP_EXPECTOR(expector); + expector.addExpectation(clip, ClipOp::kIntersect, false); + cull_dl->Dispatch(expector); +} + +TEST_F(DisplayListTest, ClipPathOvalNonCulling) { + auto clip = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f); + // A 10x10 rectangle extends 5x5 from the center to each corner. To have + // an oval that encompasses that rectangle, the radius must be at least + // length(5, 5), or 7.071+ so we expand the radius 5 square clip by 2.072 + // on each side to barely exclude the corners of the square. + auto non_encompassing_oval = clip.makeOutset(2.071f, 2.071f); + SkPath path; + path.addOval(non_encompassing_oval); + + DisplayListBuilder cull_builder; + cull_builder.ClipRect(clip, ClipOp::kIntersect, false); + cull_builder.ClipPath(path, ClipOp::kIntersect, false); + auto cull_dl = cull_builder.Build(); + + CLIP_EXPECTOR(expector); + expector.addExpectation(clip, ClipOp::kIntersect, false); + // Builder will not cull this clip, but it will turn it into a ClipOval + expector.addOvalExpectation(non_encompassing_oval, ClipOp::kIntersect, false); + cull_dl->Dispatch(expector); +} + TEST_F(DisplayListTest, ClipPathRRectCulling) { auto clip = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f); auto rrect = SkRRect::MakeRectXY(clip.makeOutset(2.0f, 2.0f), 2.0f, 2.0f); + ASSERT_FALSE(rrect.isOval()); SkPath path; path.addRRect(rrect); @@ -4679,7 +4810,8 @@ TEST_F(DisplayListTest, ClipPathRRectCulling) { TEST_F(DisplayListTest, ClipPathRRectNonCulling) { auto clip = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f); - auto rrect = SkRRect::MakeRectXY(clip.makeOutset(2.0f, 2.0f), 12.0f, 12.0f); + auto rrect = SkRRect::MakeRectXY(clip.makeOutset(1.0f, 1.0f), 4.0f, 4.0f); + ASSERT_FALSE(rrect.isOval()); SkPath path; path.addRRect(rrect); @@ -4724,5 +4856,235 @@ TEST_F(DisplayListTest, RecordLargeVertices) { } } +TEST_F(DisplayListTest, DrawRectRRectPromoteToDrawRect) { + SkRect rect = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f); + + DisplayListBuilder builder; + builder.DrawRRect(SkRRect::MakeRect(rect), DlPaint()); + auto dl = builder.Build(); + + DisplayListBuilder expected; + expected.DrawRect(rect, DlPaint()); + auto expect_dl = expected.Build(); + + DisplayListsEQ_Verbose(dl, expect_dl); +} + +TEST_F(DisplayListTest, DrawOvalRRectPromoteToDrawOval) { + SkRect rect = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f); + + DisplayListBuilder builder; + builder.DrawRRect(SkRRect::MakeOval(rect), DlPaint()); + auto dl = builder.Build(); + + DisplayListBuilder expected; + expected.DrawOval(rect, DlPaint()); + auto expect_dl = expected.Build(); + + DisplayListsEQ_Verbose(dl, expect_dl); +} + +TEST_F(DisplayListTest, DrawRectPathPromoteToDrawRect) { + SkRect rect = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f); + + DisplayListBuilder builder; + builder.DrawPath(SkPath::Rect(rect), DlPaint()); + auto dl = builder.Build(); + + DisplayListBuilder expected; + expected.DrawRect(rect, DlPaint()); + auto expect_dl = expected.Build(); + + DisplayListsEQ_Verbose(dl, expect_dl); +} + +TEST_F(DisplayListTest, DrawOvalPathPromoteToDrawOval) { + SkRect rect = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f); + + DisplayListBuilder builder; + builder.DrawPath(SkPath::Oval(rect), DlPaint()); + auto dl = builder.Build(); + + DisplayListBuilder expected; + expected.DrawOval(rect, DlPaint()); + auto expect_dl = expected.Build(); + + DisplayListsEQ_Verbose(dl, expect_dl); +} + +TEST_F(DisplayListTest, DrawRRectPathPromoteToDrawRRect) { + SkRect rect = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f); + SkRRect rrect = SkRRect::MakeRectXY(rect, 2.0f, 2.0f); + + DisplayListBuilder builder; + builder.DrawPath(SkPath::RRect(rrect), DlPaint()); + auto dl = builder.Build(); + + DisplayListBuilder expected; + expected.DrawRRect(rrect, DlPaint()); + auto expect_dl = expected.Build(); + + DisplayListsEQ_Verbose(dl, expect_dl); +} + +TEST_F(DisplayListTest, DrawRectRRectPathPromoteToDrawRect) { + SkRect rect = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f); + SkRRect rrect = SkRRect::MakeRect(rect); + + DisplayListBuilder builder; + builder.DrawPath(SkPath::RRect(rrect), DlPaint()); + auto dl = builder.Build(); + + DisplayListBuilder expected; + expected.DrawRect(rect, DlPaint()); + auto expect_dl = expected.Build(); + + DisplayListsEQ_Verbose(dl, expect_dl); +} + +TEST_F(DisplayListTest, DrawOvalRRectPathPromoteToDrawOval) { + SkRect rect = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f); + SkRRect rrect = SkRRect::MakeOval(rect); + + DisplayListBuilder builder; + builder.DrawPath(SkPath::RRect(rrect), DlPaint()); + auto dl = builder.Build(); + + DisplayListBuilder expected; + expected.DrawOval(rect, DlPaint()); + auto expect_dl = expected.Build(); + + DisplayListsEQ_Verbose(dl, expect_dl); +} + +TEST_F(DisplayListTest, ClipRectRRectPromoteToClipRect) { + SkRect clip_rect = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f); + SkRect draw_rect = clip_rect.makeOutset(2.0f, 2.0f); + + DisplayListBuilder builder; + builder.ClipRRect(SkRRect::MakeRect(clip_rect), ClipOp::kIntersect, false); + // Include a rendering op in case DlBuilder ever removes unneeded clips + builder.DrawRect(draw_rect, DlPaint()); + auto dl = builder.Build(); + + DisplayListBuilder expected; + expected.ClipRect(clip_rect, ClipOp::kIntersect, false); + expected.DrawRect(draw_rect, DlPaint()); + auto expect_dl = expected.Build(); + + DisplayListsEQ_Verbose(dl, expect_dl); +} + +TEST_F(DisplayListTest, ClipOvalRRectPromoteToClipOval) { + SkRect clip_rect = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f); + SkRect draw_rect = clip_rect.makeOutset(2.0f, 2.0f); + + DisplayListBuilder builder; + builder.ClipRRect(SkRRect::MakeOval(clip_rect), ClipOp::kIntersect, false); + // Include a rendering op in case DlBuilder ever removes unneeded clips + builder.DrawRect(draw_rect, DlPaint()); + auto dl = builder.Build(); + + DisplayListBuilder expected; + expected.ClipOval(clip_rect, ClipOp::kIntersect, false); + expected.DrawRect(draw_rect, DlPaint()); + auto expect_dl = expected.Build(); + + DisplayListsEQ_Verbose(dl, expect_dl); +} + +TEST_F(DisplayListTest, ClipRectPathPromoteToClipRect) { + SkRect clip_rect = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f); + SkRect draw_rect = clip_rect.makeOutset(2.0f, 2.0f); + + DisplayListBuilder builder; + builder.ClipPath(SkPath::Rect(clip_rect), ClipOp::kIntersect, false); + // Include a rendering op in case DlBuilder ever removes unneeded clips + builder.DrawRect(draw_rect, DlPaint()); + auto dl = builder.Build(); + + DisplayListBuilder expected; + expected.ClipRect(clip_rect, ClipOp::kIntersect, false); + expected.DrawRect(draw_rect, DlPaint()); + auto expect_dl = expected.Build(); + + DisplayListsEQ_Verbose(dl, expect_dl); +} + +TEST_F(DisplayListTest, ClipOvalPathPromoteToClipOval) { + SkRect clip_rect = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f); + SkRect draw_rect = clip_rect.makeOutset(2.0f, 2.0f); + + DisplayListBuilder builder; + builder.ClipPath(SkPath::Oval(clip_rect), ClipOp::kIntersect, false); + // Include a rendering op in case DlBuilder ever removes unneeded clips + builder.DrawRect(draw_rect, DlPaint()); + auto dl = builder.Build(); + + DisplayListBuilder expected; + expected.ClipOval(clip_rect, ClipOp::kIntersect, false); + expected.DrawRect(draw_rect, DlPaint()); + auto expect_dl = expected.Build(); + + DisplayListsEQ_Verbose(dl, expect_dl); +} + +TEST_F(DisplayListTest, ClipRRectPathPromoteToClipRRect) { + SkRect clip_rect = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f); + SkRRect clip_rrect = SkRRect::MakeRectXY(clip_rect, 2.0f, 2.0f); + SkRect draw_rect = clip_rect.makeOutset(2.0f, 2.0f); + + DisplayListBuilder builder; + builder.ClipPath(SkPath::RRect(clip_rrect), ClipOp::kIntersect, false); + // Include a rendering op in case DlBuilder ever removes unneeded clips + builder.DrawRect(draw_rect, DlPaint()); + auto dl = builder.Build(); + + DisplayListBuilder expected; + expected.ClipRRect(clip_rrect, ClipOp::kIntersect, false); + expected.DrawRect(draw_rect, DlPaint()); + auto expect_dl = expected.Build(); + + DisplayListsEQ_Verbose(dl, expect_dl); +} + +TEST_F(DisplayListTest, ClipRectRRectPathPromoteToClipRect) { + SkRect clip_rect = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f); + SkRRect clip_rrect = SkRRect::MakeRect(clip_rect); + SkRect draw_rect = clip_rect.makeOutset(2.0f, 2.0f); + + DisplayListBuilder builder; + builder.ClipPath(SkPath::RRect(clip_rrect), ClipOp::kIntersect, false); + // Include a rendering op in case DlBuilder ever removes unneeded clips + builder.DrawRect(draw_rect, DlPaint()); + auto dl = builder.Build(); + + DisplayListBuilder expected; + expected.ClipRect(clip_rect, ClipOp::kIntersect, false); + expected.DrawRect(draw_rect, DlPaint()); + auto expect_dl = expected.Build(); + + DisplayListsEQ_Verbose(dl, expect_dl); +} + +TEST_F(DisplayListTest, ClipOvalRRectPathPromoteToClipOval) { + SkRect clip_rect = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f); + SkRRect clip_rrect = SkRRect::MakeOval(clip_rect); + SkRect draw_rect = clip_rect.makeOutset(2.0f, 2.0f); + + DisplayListBuilder builder; + builder.ClipPath(SkPath::RRect(clip_rrect), ClipOp::kIntersect, false); + // Include a rendering op in case DlBuilder ever removes unneeded clips + builder.DrawRect(draw_rect, DlPaint()); + auto dl = builder.Build(); + + DisplayListBuilder expected; + expected.ClipOval(clip_rect, ClipOp::kIntersect, false); + expected.DrawRect(draw_rect, DlPaint()); + auto expect_dl = expected.Build(); + + DisplayListsEQ_Verbose(dl, expect_dl); +} + } // namespace testing } // namespace flutter diff --git a/display_list/dl_builder.cc b/display_list/dl_builder.cc index 98772c0d7a3ab..9147661de26f1 100644 --- a/display_list/dl_builder.cc +++ b/display_list/dl_builder.cc @@ -963,14 +963,50 @@ void DisplayListBuilder::ClipRect(const SkRect& rect, break; } } +void DisplayListBuilder::ClipOval(const SkRect& bounds, + ClipOp clip_op, + bool is_aa) { + if (!bounds.isFinite()) { + return; + } + if (current_info().is_nop) { + return; + } + if (current_info().has_valid_clip && + clip_op == DlCanvas::ClipOp::kIntersect && + layer_local_state().oval_covers_cull(bounds)) { + return; + } + global_state().clipOval(bounds, clip_op, is_aa); + layer_local_state().clipOval(bounds, clip_op, is_aa); + if (global_state().is_cull_rect_empty() || + layer_local_state().is_cull_rect_empty()) { + current_info().is_nop = true; + return; + } + current_info().has_valid_clip = true; + checkForDeferredSave(); + switch (clip_op) { + case ClipOp::kIntersect: + Push(0, bounds, is_aa); + break; + case ClipOp::kDifference: + Push(0, bounds, is_aa); + break; + } +} void DisplayListBuilder::ClipRRect(const SkRRect& rrect, ClipOp clip_op, bool is_aa) { + if (current_info().is_nop) { + return; + } if (rrect.isRect()) { clipRect(rrect.rect(), clip_op, is_aa); return; } - if (current_info().is_nop) { + if (rrect.isOval()) { + clipOval(rrect.rect(), clip_op, is_aa); return; } if (current_info().has_valid_clip && @@ -1008,12 +1044,11 @@ void DisplayListBuilder::ClipPath(const SkPath& path, this->clipRect(rect, clip_op, is_aa); return; } - SkRRect rrect; if (path.isOval(&rect)) { - rrect.setOval(rect); - this->clipRRect(rrect, clip_op, is_aa); + this->clipOval(rect, clip_op, is_aa); return; } + SkRRect rrect; if (path.isRRect(&rrect)) { this->clipRRect(rrect, clip_op, is_aa); return; @@ -1188,6 +1223,24 @@ void DisplayListBuilder::DrawDRRect(const SkRRect& outer, drawDRRect(outer, inner); } void DisplayListBuilder::drawPath(const SkPath& path) { + { + SkRect rect; + if (path.isRect(&rect)) { + drawRect(rect); + return; + } + if (path.isOval(&rect)) { + drawOval(rect); + return; + } + } + { + SkRRect rrect; + if (path.isRRect(&rrect)) { + drawRRect(rrect); + return; + } + } DisplayListAttributeFlags flags = kDrawPathFlags; OpResult result = PaintResult(current_, flags); if (result != OpResult::kNoEffect) { diff --git a/display_list/dl_builder.h b/display_list/dl_builder.h index b1a458f68eec9..dc75e2d37f6af 100644 --- a/display_list/dl_builder.h +++ b/display_list/dl_builder.h @@ -117,6 +117,10 @@ class DisplayListBuilder final : public virtual DlCanvas, ClipOp clip_op = ClipOp::kIntersect, bool is_aa = false) override; // |DlCanvas| + void ClipOval(const SkRect& bounds, + ClipOp clip_op = ClipOp::kIntersect, + bool is_aa = false) override; + // |DlCanvas| void ClipRRect(const SkRRect& rrect, ClipOp clip_op = ClipOp::kIntersect, bool is_aa = false) override; @@ -400,6 +404,10 @@ class DisplayListBuilder final : public virtual DlCanvas, ClipRect(rect, clip_op, is_aa); } // |DlOpReceiver| + void clipOval(const SkRect& bounds, ClipOp clip_op, bool is_aa) override { + ClipOval(bounds, clip_op, is_aa); + } + // |DlOpReceiver| void clipRRect(const SkRRect& rrect, ClipOp clip_op, bool is_aa) override { ClipRRect(rrect, clip_op, is_aa); } diff --git a/display_list/dl_canvas.h b/display_list/dl_canvas.h index ab505777aeae2..8fa4ad923f307 100644 --- a/display_list/dl_canvas.h +++ b/display_list/dl_canvas.h @@ -105,6 +105,9 @@ class DlCanvas { virtual void ClipRect(const SkRect& rect, ClipOp clip_op = ClipOp::kIntersect, bool is_aa = false) = 0; + virtual void ClipOval(const SkRect& bounds, + ClipOp clip_op = ClipOp::kIntersect, + bool is_aa = false) = 0; virtual void ClipRRect(const SkRRect& rrect, ClipOp clip_op = ClipOp::kIntersect, bool is_aa = false) = 0; diff --git a/display_list/dl_op_receiver.h b/display_list/dl_op_receiver.h index 8de5dae62c4d7..dc85e379e4176 100644 --- a/display_list/dl_op_receiver.h +++ b/display_list/dl_op_receiver.h @@ -328,6 +328,7 @@ class DlOpReceiver { virtual void transformReset() = 0; virtual void clipRect(const SkRect& rect, ClipOp clip_op, bool is_aa) = 0; + virtual void clipOval(const SkRect& bounds, ClipOp clip_op, bool is_aa) = 0; virtual void clipRRect(const SkRRect& rrect, ClipOp clip_op, bool is_aa) = 0; virtual void clipPath(const SkPath& path, ClipOp clip_op, bool is_aa) = 0; diff --git a/display_list/dl_op_records.h b/display_list/dl_op_records.h index 3ad6eebe8fc54..aeb0379e2371f 100644 --- a/display_list/dl_op_records.h +++ b/display_list/dl_op_records.h @@ -566,11 +566,11 @@ struct TransformResetOp final : TransformClipOpBase { // the header, but the Windows compiler keeps wanting to expand that // packing into more bytes than needed (even when they are declared as // packed bit fields!) -#define DEFINE_CLIP_SHAPE_OP(shapetype, clipop) \ - struct Clip##clipop##shapetype##Op final : TransformClipOpBase { \ - static constexpr auto kType = DisplayListOpType::kClip##clipop##shapetype; \ +#define DEFINE_CLIP_SHAPE_OP(shapename, shapetype, clipop) \ + struct Clip##clipop##shapename##Op final : TransformClipOpBase { \ + static constexpr auto kType = DisplayListOpType::kClip##clipop##shapename; \ \ - Clip##clipop##shapetype##Op(Sk##shapetype shape, bool is_aa) \ + Clip##clipop##shapename##Op(Sk##shapetype shape, bool is_aa) \ : is_aa(is_aa), shape(shape) {} \ \ const bool is_aa; \ @@ -578,15 +578,17 @@ struct TransformResetOp final : TransformClipOpBase { \ void dispatch(DispatchContext& ctx) const { \ if (op_needed(ctx)) { \ - ctx.receiver.clip##shapetype(shape, DlCanvas::ClipOp::k##clipop, \ + ctx.receiver.clip##shapename(shape, DlCanvas::ClipOp::k##clipop, \ is_aa); \ } \ } \ }; -DEFINE_CLIP_SHAPE_OP(Rect, Intersect) -DEFINE_CLIP_SHAPE_OP(RRect, Intersect) -DEFINE_CLIP_SHAPE_OP(Rect, Difference) -DEFINE_CLIP_SHAPE_OP(RRect, Difference) +DEFINE_CLIP_SHAPE_OP(Rect, Rect, Intersect) +DEFINE_CLIP_SHAPE_OP(Oval, Rect, Intersect) +DEFINE_CLIP_SHAPE_OP(RRect, RRect, Intersect) +DEFINE_CLIP_SHAPE_OP(Rect, Rect, Difference) +DEFINE_CLIP_SHAPE_OP(Oval, Rect, Difference) +DEFINE_CLIP_SHAPE_OP(RRect, RRect, Difference) #undef DEFINE_CLIP_SHAPE_OP #define DEFINE_CLIP_PATH_OP(clipop) \ diff --git a/display_list/skia/dl_sk_canvas.cc b/display_list/skia/dl_sk_canvas.cc index a7334f303ec4b..235b654f41bca 100644 --- a/display_list/skia/dl_sk_canvas.cc +++ b/display_list/skia/dl_sk_canvas.cc @@ -153,6 +153,12 @@ void DlSkCanvasAdapter::ClipRect(const SkRect& rect, delegate_->clipRect(rect, ToSk(clip_op), is_aa); } +void DlSkCanvasAdapter::ClipOval(const SkRect& bounds, + ClipOp clip_op, + bool is_aa) { + delegate_->clipRRect(SkRRect::MakeOval(bounds), ToSk(clip_op), is_aa); +} + void DlSkCanvasAdapter::ClipRRect(const SkRRect& rrect, ClipOp clip_op, bool is_aa) { diff --git a/display_list/skia/dl_sk_canvas.h b/display_list/skia/dl_sk_canvas.h index c61f3f465455e..5ceebe561ef6b 100644 --- a/display_list/skia/dl_sk_canvas.h +++ b/display_list/skia/dl_sk_canvas.h @@ -72,6 +72,7 @@ class DlSkCanvasAdapter final : public virtual DlCanvas { SkMatrix GetTransform() const override; void ClipRect(const SkRect& rect, ClipOp clip_op, bool is_aa) override; + void ClipOval(const SkRect& bounds, ClipOp clip_op, bool is_aa) override; void ClipRRect(const SkRRect& rrect, ClipOp clip_op, bool is_aa) override; void ClipPath(const SkPath& path, ClipOp clip_op, bool is_aa) override; diff --git a/display_list/skia/dl_sk_dispatcher.cc b/display_list/skia/dl_sk_dispatcher.cc index 84c7115fb5fa4..819860d3fb184 100644 --- a/display_list/skia/dl_sk_dispatcher.cc +++ b/display_list/skia/dl_sk_dispatcher.cc @@ -122,6 +122,11 @@ void DlSkCanvasDispatcher::clipRect(const SkRect& rect, bool is_aa) { canvas_->clipRect(rect, ToSk(clip_op), is_aa); } +void DlSkCanvasDispatcher::clipOval(const SkRect& bounds, + ClipOp clip_op, + bool is_aa) { + canvas_->clipRRect(SkRRect::MakeOval(bounds), ToSk(clip_op), is_aa); +} void DlSkCanvasDispatcher::clipRRect(const SkRRect& rrect, ClipOp clip_op, bool is_aa) { diff --git a/display_list/skia/dl_sk_dispatcher.h b/display_list/skia/dl_sk_dispatcher.h index ef15f8152e736..384f722633677 100644 --- a/display_list/skia/dl_sk_dispatcher.h +++ b/display_list/skia/dl_sk_dispatcher.h @@ -51,6 +51,7 @@ class DlSkCanvasDispatcher : public virtual DlOpReceiver, void transformReset() override; void clipRect(const SkRect& rect, ClipOp clip_op, bool is_aa) override; + void clipOval(const SkRect& bounds, ClipOp clip_op, bool is_aa) override; void clipRRect(const SkRRect& rrect, ClipOp clip_op, bool is_aa) override; void clipPath(const SkPath& path, ClipOp clip_op, bool is_aa) override; diff --git a/display_list/testing/dl_rendering_unittests.cc b/display_list/testing/dl_rendering_unittests.cc index 89102029e175b..1368f3e921fbe 100644 --- a/display_list/testing/dl_rendering_unittests.cc +++ b/display_list/testing/dl_rendering_unittests.cc @@ -2073,6 +2073,39 @@ class CanvasCompareTester { ctx.canvas->ClipRect(r_clip, ClipOp::kDifference, false); }) .with_diff_clip()); + // Skia lacks clipOval and requires us to make an oval SkRRect + SkRRect rr_oval_clip = SkRRect::MakeOval(r_clip); + RenderWith(testP, env, intersect_tolerance, + CaseParameters( + "Hard ClipOval", + [=](const SkSetupContext& ctx) { + ctx.canvas->clipRRect(rr_oval_clip, SkClipOp::kIntersect, + false); + }, + [=](const DlSetupContext& ctx) { + ctx.canvas->ClipOval(r_clip, ClipOp::kIntersect, false); + })); + RenderWith(testP, env, intersect_tolerance, + CaseParameters( + "AntiAlias ClipOval", + [=](const SkSetupContext& ctx) { + ctx.canvas->clipRRect(rr_oval_clip, SkClipOp::kIntersect, + true); + }, + [=](const DlSetupContext& ctx) { + ctx.canvas->ClipOval(r_clip, ClipOp::kIntersect, true); + })); + RenderWith(testP, env, diff_tolerance, + CaseParameters( + "Hard ClipOval Diff", + [=](const SkSetupContext& ctx) { + ctx.canvas->clipRRect(rr_oval_clip, SkClipOp::kDifference, + false); + }, + [=](const DlSetupContext& ctx) { + ctx.canvas->ClipOval(r_clip, ClipOp::kDifference, false); + }) + .with_diff_clip()); // This test RR clip used to use very small radii, but due to // optimizations in the HW rrect rasterization, this caused small // bulges in the corners of the RRect which were interpreted as @@ -2850,12 +2883,14 @@ TEST_F(DisplayListRendering, DrawDiagonalLines) { SkPoint p2 = SkPoint::Make(kRenderRight, kRenderBottom); SkPoint p3 = SkPoint::Make(kRenderLeft, kRenderBottom); SkPoint p4 = SkPoint::Make(kRenderRight, kRenderTop); + // Adding some edge to edge diagonals that run through the points about + // 16 units in from the center of that edge. // Adding some edge center to edge center diagonals to better fill // out the RRect Clip so bounds checking sees less empty bounds space. - SkPoint p5 = SkPoint::Make(kRenderCenterX, kRenderTop); - SkPoint p6 = SkPoint::Make(kRenderRight, kRenderCenterY); - SkPoint p7 = SkPoint::Make(kRenderLeft, kRenderCenterY); - SkPoint p8 = SkPoint::Make(kRenderCenterX, kRenderBottom); + SkPoint p5 = SkPoint::Make(kRenderCenterX, kRenderTop + 15); + SkPoint p6 = SkPoint::Make(kRenderRight - 15, kRenderCenterY); + SkPoint p7 = SkPoint::Make(kRenderCenterX, kRenderBottom - 15); + SkPoint p8 = SkPoint::Make(kRenderLeft + 15, kRenderCenterY); CanvasCompareTester::RenderAll( // TestParameters( @@ -2880,9 +2915,13 @@ TEST_F(DisplayListRendering, DrawDiagonalLines) { .set_draw_line()); } -TEST_F(DisplayListRendering, DrawHorizontalLine) { - SkPoint p1 = SkPoint::Make(kRenderLeft, kRenderCenterY); - SkPoint p2 = SkPoint::Make(kRenderRight, kRenderCenterY); +TEST_F(DisplayListRendering, DrawHorizontalLines) { + SkPoint p1 = SkPoint::Make(kRenderLeft, kRenderTop + 16); + SkPoint p2 = SkPoint::Make(kRenderRight, kRenderTop + 16); + SkPoint p3 = SkPoint::Make(kRenderLeft, kRenderCenterY); + SkPoint p4 = SkPoint::Make(kRenderRight, kRenderCenterY); + SkPoint p5 = SkPoint::Make(kRenderLeft, kRenderBottom - 16); + SkPoint p6 = SkPoint::Make(kRenderRight, kRenderBottom - 16); CanvasCompareTester::RenderAll( // TestParameters( @@ -2893,18 +2932,26 @@ TEST_F(DisplayListRendering, DrawHorizontalLine) { SkPaint p = ctx.paint; p.setStyle(SkPaint::kStroke_Style); ctx.canvas->drawLine(p1, p2, p); + ctx.canvas->drawLine(p3, p4, p); + ctx.canvas->drawLine(p5, p6, p); }, [=](const DlRenderContext& ctx) { // ctx.canvas->DrawLine(p1, p2, ctx.paint); + ctx.canvas->DrawLine(p3, p4, ctx.paint); + ctx.canvas->DrawLine(p5, p6, ctx.paint); }, kDrawHVLineFlags) .set_draw_line() .set_horizontal_line()); } -TEST_F(DisplayListRendering, DrawVerticalLine) { - SkPoint p1 = SkPoint::Make(kRenderCenterX, kRenderTop); - SkPoint p2 = SkPoint::Make(kRenderCenterY, kRenderBottom); +TEST_F(DisplayListRendering, DrawVerticalLines) { + SkPoint p1 = SkPoint::Make(kRenderLeft + 16, kRenderTop); + SkPoint p2 = SkPoint::Make(kRenderLeft + 16, kRenderBottom); + SkPoint p3 = SkPoint::Make(kRenderCenterX, kRenderTop); + SkPoint p4 = SkPoint::Make(kRenderCenterX, kRenderBottom); + SkPoint p5 = SkPoint::Make(kRenderRight - 16, kRenderTop); + SkPoint p6 = SkPoint::Make(kRenderRight - 16, kRenderBottom); CanvasCompareTester::RenderAll( // TestParameters( @@ -2915,9 +2962,13 @@ TEST_F(DisplayListRendering, DrawVerticalLine) { SkPaint p = ctx.paint; p.setStyle(SkPaint::kStroke_Style); ctx.canvas->drawLine(p1, p2, p); + ctx.canvas->drawLine(p3, p4, p); + ctx.canvas->drawLine(p5, p6, p); }, [=](const DlRenderContext& ctx) { // ctx.canvas->DrawLine(p1, p2, ctx.paint); + ctx.canvas->DrawLine(p3, p4, ctx.paint); + ctx.canvas->DrawLine(p5, p6, ctx.paint); }, kDrawHVLineFlags) .set_draw_line() @@ -2929,12 +2980,14 @@ TEST_F(DisplayListRendering, DrawDiagonalDashedLines) { SkPoint p2 = SkPoint::Make(kRenderRight, kRenderBottom); SkPoint p3 = SkPoint::Make(kRenderLeft, kRenderBottom); SkPoint p4 = SkPoint::Make(kRenderRight, kRenderTop); + // Adding some edge to edge diagonals that run through the points about + // 16 units in from the center of that edge. // Adding some edge center to edge center diagonals to better fill // out the RRect Clip so bounds checking sees less empty bounds space. - SkPoint p5 = SkPoint::Make(kRenderCenterX, kRenderTop); - SkPoint p6 = SkPoint::Make(kRenderRight, kRenderCenterY); - SkPoint p7 = SkPoint::Make(kRenderLeft, kRenderCenterY); - SkPoint p8 = SkPoint::Make(kRenderCenterX, kRenderBottom); + SkPoint p5 = SkPoint::Make(kRenderCenterX, kRenderTop + 15); + SkPoint p6 = SkPoint::Make(kRenderRight - 15, kRenderCenterY); + SkPoint p7 = SkPoint::Make(kRenderCenterX, kRenderBottom - 15); + SkPoint p8 = SkPoint::Make(kRenderLeft + 15, kRenderCenterY); // Full diagonals are 100x100 which are 140 in length // Dashing them with 25 on, 5 off means that the last @@ -2955,7 +3008,7 @@ TEST_F(DisplayListRendering, DrawDiagonalDashedLines) { SkPaint p = ctx.paint; p.setStyle(SkPaint::kStroke_Style); SkScalar intervals[2] = {25.0f, 5.0f}; - p.setPathEffect(SkDashPathEffect::Make(intervals, 2.0f, 0.0f)); + p.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0.0f)); ctx.canvas->drawLine(p1, p2, p); ctx.canvas->drawLine(p3, p4, p); ctx.canvas->drawLine(p5, p6, p); @@ -3125,7 +3178,7 @@ TEST_F(DisplayListRendering, DrawPointsAsPoints) { const SkScalar x3 = kRenderCenterX + 0.1; const SkScalar x4 = (kRenderRight + kRenderCenterX) * 0.5; const SkScalar x5 = kRenderRight - 16; - const SkScalar x6 = kRenderRight; + const SkScalar x6 = kRenderRight - 1; const SkScalar y0 = kRenderTop; const SkScalar y1 = kRenderTop + 16; @@ -3133,7 +3186,7 @@ TEST_F(DisplayListRendering, DrawPointsAsPoints) { const SkScalar y3 = kRenderCenterY + 0.1; const SkScalar y4 = (kRenderBottom + kRenderCenterY) * 0.5; const SkScalar y5 = kRenderBottom - 16; - const SkScalar y6 = kRenderBottom; + const SkScalar y6 = kRenderBottom - 1; // clang-format off const SkPoint points[] = { @@ -3177,7 +3230,7 @@ TEST_F(DisplayListRendering, DrawPointsAsLines) { const SkScalar y0 = kRenderTop; const SkScalar y1 = kRenderTop + 16; const SkScalar y2 = kRenderBottom - 16; - const SkScalar y3 = kRenderBottom; + const SkScalar y3 = kRenderBottom - 1; // clang-format off const SkPoint points[] = { @@ -3226,10 +3279,12 @@ TEST_F(DisplayListRendering, DrawPointsAsPolygon) { SkPoint::Make(kRenderRight, kRenderBottom), SkPoint::Make(kRenderLeft, kRenderBottom), SkPoint::Make(kRenderLeft, kRenderTop), - SkPoint::Make(kRenderCenterX, kRenderTop), - SkPoint::Make(kRenderRight, kRenderCenterY), - SkPoint::Make(kRenderCenterX, kRenderBottom), - SkPoint::Make(kRenderLeft, kRenderCenterY), + + SkPoint::Make(kRenderCenterX, kRenderTop + 15), + SkPoint::Make(kRenderRight - 15, kRenderCenterY), + SkPoint::Make(kRenderCenterX, kRenderBottom - 15), + SkPoint::Make(kRenderLeft + 15, kRenderCenterY), + SkPoint::Make(kRenderCenterX, kRenderTop + 15), }; const int count1 = sizeof(points1) / sizeof(points1[0]); @@ -3730,7 +3785,7 @@ TEST_F(DisplayListRendering, DrawShadow) { }, kRenderCornerRadius, kRenderCornerRadius); const DlColor color = DlColor::kDarkGrey(); - const SkScalar elevation = 5; + const SkScalar elevation = 7; CanvasCompareTester::RenderAll( // TestParameters( @@ -3756,7 +3811,7 @@ TEST_F(DisplayListRendering, DrawShadowTransparentOccluder) { }, kRenderCornerRadius, kRenderCornerRadius); const DlColor color = DlColor::kDarkGrey(); - const SkScalar elevation = 5; + const SkScalar elevation = 7; CanvasCompareTester::RenderAll( // TestParameters( @@ -3782,7 +3837,7 @@ TEST_F(DisplayListRendering, DrawShadowDpr) { }, kRenderCornerRadius, kRenderCornerRadius); const DlColor color = DlColor::kDarkGrey(); - const SkScalar elevation = 5; + const SkScalar elevation = 7; CanvasCompareTester::RenderAll( // TestParameters( diff --git a/display_list/testing/dl_test_snippets.cc b/display_list/testing/dl_test_snippets.cc index e5ad4b2f365dc..16c0edb5d4468 100644 --- a/display_list/testing/dl_test_snippets.cc +++ b/display_list/testing/dl_test_snippets.cc @@ -424,6 +424,30 @@ std::vector CreateAllClipOps() { r.clipRect(kTestBounds, DlCanvas::ClipOp::kDifference, false); }}, }}, + {"ClipOval", + { + {1, 24, 0, + [](DlOpReceiver& r) { + r.clipOval(kTestBounds, DlCanvas::ClipOp::kIntersect, true); + }}, + {1, 24, 0, + [](DlOpReceiver& r) { + r.clipOval(kTestBounds.makeOffset(1, 1), + DlCanvas::ClipOp::kIntersect, true); + }}, + {1, 24, 0, + [](DlOpReceiver& r) { + r.clipOval(kTestBounds, DlCanvas::ClipOp::kIntersect, false); + }}, + {1, 24, 0, + [](DlOpReceiver& r) { + r.clipOval(kTestBounds, DlCanvas::ClipOp::kDifference, true); + }}, + {1, 24, 0, + [](DlOpReceiver& r) { + r.clipOval(kTestBounds, DlCanvas::ClipOp::kDifference, false); + }}, + }}, {"ClipRRect", { {1, 64, 0, @@ -479,11 +503,16 @@ std::vector CreateAllClipOps() { [](DlOpReceiver& r) { r.clipPath(kTestPathRect, DlCanvas::ClipOp::kIntersect, true); }}, - // clipPath(oval) becomes clipRRect - {1, 64, 0, + // clipPath(oval) becomes clipOval + {1, 24, 0, [](DlOpReceiver& r) { r.clipPath(kTestPathOval, DlCanvas::ClipOp::kIntersect, true); }}, + // clipPath(rrect) becomes clipRRect + {1, 64, 0, + [](DlOpReceiver& r) { + r.clipPath(kTestPathRRect, DlCanvas::ClipOp::kIntersect, true); + }}, }}, }; } @@ -637,8 +666,11 @@ std::vector CreateAllRenderingOps() { {1, 40, 1, [](DlOpReceiver& r) { r.drawPath(kTestPath1); }}, {1, 40, 1, [](DlOpReceiver& r) { r.drawPath(kTestPath2); }}, {1, 40, 1, [](DlOpReceiver& r) { r.drawPath(kTestPath3); }}, - {1, 40, 1, [](DlOpReceiver& r) { r.drawPath(kTestPathRect); }}, - {1, 40, 1, [](DlOpReceiver& r) { r.drawPath(kTestPathOval); }}, + // oval and rect paths are redirected to drawRect and drawOval + {1, 24, 1, [](DlOpReceiver& r) { r.drawPath(kTestPathRect); }}, + {1, 24, 1, [](DlOpReceiver& r) { r.drawPath(kTestPathOval); }}, + // rrect path is redirected to drawRRect + {1, 56, 1, [](DlOpReceiver& r) { r.drawPath(kTestPathRRect); }}, }}, {"DrawArc", { diff --git a/display_list/testing/dl_test_snippets.h b/display_list/testing/dl_test_snippets.h index 16d740c2fb6a0..733ce67364034 100644 --- a/display_list/testing/dl_test_snippets.h +++ b/display_list/testing/dl_test_snippets.h @@ -183,6 +183,7 @@ static const SkRRect kTestInnerRRect = SkRRect::MakeRectXY(kTestBounds.makeInset(5, 5), 2, 2); static const SkPath kTestPathRect = SkPath::Rect(kTestBounds); static const SkPath kTestPathOval = SkPath::Oval(kTestBounds); +static const SkPath kTestPathRRect = SkPath::RRect(kTestRRect); static const SkPath kTestPath1 = SkPath::Polygon({{0, 0}, {10, 10}, {10, 0}, {0, 10}}, true); static const SkPath kTestPath2 = diff --git a/display_list/utils/dl_matrix_clip_tracker.cc b/display_list/utils/dl_matrix_clip_tracker.cc index 748cf07a8385a..86f3cff46a6d9 100644 --- a/display_list/utils/dl_matrix_clip_tracker.cc +++ b/display_list/utils/dl_matrix_clip_tracker.cc @@ -74,6 +74,24 @@ void DisplayListMatrixClipState::clipRect(const DlRect& rect, } } +void DisplayListMatrixClipState::clipOval(const DlRect& bounds, + ClipOp op, + bool is_aa) { + if (!bounds.IsFinite()) { + return; + } + switch (op) { + case DlCanvas::ClipOp::kIntersect: + adjustCullRect(bounds, op, is_aa); + break; + case DlCanvas::ClipOp::kDifference: + if (oval_covers_cull(bounds)) { + cull_rect_ = DlRect(); + } + break; + } +} + void DisplayListMatrixClipState::clipRRect(const SkRRect& rrect, ClipOp op, bool is_aa) { diff --git a/display_list/utils/dl_matrix_clip_tracker.h b/display_list/utils/dl_matrix_clip_tracker.h index 59c9880bd3bd7..4a6198d75a3e1 100644 --- a/display_list/utils/dl_matrix_clip_tracker.h +++ b/display_list/utils/dl_matrix_clip_tracker.h @@ -149,6 +149,10 @@ class DisplayListMatrixClipState { void clipRect(const SkRect& rect, ClipOp op, bool is_aa) { clipRect(ToDlRect(rect), op, is_aa); } + void clipOval(const DlRect& bounds, ClipOp op, bool is_aa); + void clipOval(const SkRect& bounds, ClipOp op, bool is_aa) { + clipRect(ToDlRect(bounds), op, is_aa); + } void clipRRect(const SkRRect& rrect, ClipOp op, bool is_aa); void clipPath(const SkPath& path, ClipOp op, bool is_aa); diff --git a/display_list/utils/dl_receiver_utils.h b/display_list/utils/dl_receiver_utils.h index d50cb00cba057..bb6ce6539f653 100644 --- a/display_list/utils/dl_receiver_utils.h +++ b/display_list/utils/dl_receiver_utils.h @@ -44,6 +44,9 @@ class IgnoreClipDispatchHelper : public virtual DlOpReceiver { void clipRect(const SkRect& rect, DlCanvas::ClipOp clip_op, bool is_aa) override {} + void clipOval(const SkRect& bounds, + DlCanvas::ClipOp clip_op, + bool is_aa) override {} void clipRRect(const SkRRect& rrect, DlCanvas::ClipOp clip_op, bool is_aa) override {} diff --git a/impeller/display_list/dl_dispatcher.cc b/impeller/display_list/dl_dispatcher.cc index aaed5e44f647b..089e5259ffa5a 100644 --- a/impeller/display_list/dl_dispatcher.cc +++ b/impeller/display_list/dl_dispatcher.cc @@ -723,6 +723,14 @@ void DlDispatcherBase::clipRect(const SkRect& rect, ToClipOperation(clip_op)); } +// |flutter::DlOpReceiver| +void DlDispatcherBase::clipOval(const SkRect& bounds, + ClipOp clip_op, + bool is_aa) { + GetCanvas().ClipOval(skia_conversions::ToRect(bounds), + ToClipOperation(clip_op)); +} + // |flutter::DlOpReceiver| void DlDispatcherBase::clipRRect(const SkRRect& rrect, ClipOp sk_op, diff --git a/impeller/display_list/dl_dispatcher.h b/impeller/display_list/dl_dispatcher.h index 7349ca7e4fd84..294616b748776 100644 --- a/impeller/display_list/dl_dispatcher.h +++ b/impeller/display_list/dl_dispatcher.h @@ -123,6 +123,9 @@ class DlDispatcherBase : public flutter::DlOpReceiver { // |flutter::DlOpReceiver| void clipRect(const SkRect& rect, ClipOp clip_op, bool is_aa) override; + // |flutter::DlOpReceiver| + void clipOval(const SkRect& bounds, ClipOp clip_op, bool is_aa) override; + // |flutter::DlOpReceiver| void clipRRect(const SkRRect& rrect, ClipOp clip_op, bool is_aa) override; diff --git a/testing/display_list_testing.cc b/testing/display_list_testing.cc index 8cf84bc270242..ce831ca247813 100644 --- a/testing/display_list_testing.cc +++ b/testing/display_list_testing.cc @@ -742,6 +742,14 @@ void DisplayListStreamDispatcher::clipRect(const SkRect& rect, ClipOp clip_op, << "isaa: " << is_aa << ");" << std::endl; } +void DisplayListStreamDispatcher::clipOval(const SkRect& bounds, ClipOp clip_op, + bool is_aa) { + startl() << "clipOval(" + << bounds << ", " + << clip_op << ", " + << "isaa: " << is_aa + << ");" << std::endl; +} void DisplayListStreamDispatcher::clipRRect(const SkRRect& rrect, ClipOp clip_op, bool is_aa) { diff --git a/testing/display_list_testing.h b/testing/display_list_testing.h index b710e1e22af45..2ccb011dedcf0 100644 --- a/testing/display_list_testing.h +++ b/testing/display_list_testing.h @@ -118,6 +118,7 @@ class DisplayListStreamDispatcher final : public DlOpReceiver { void transformReset() override; void clipRect(const SkRect& rect, ClipOp clip_op, bool is_aa) override; + void clipOval(const SkRect& bounds, ClipOp clip_op, bool is_aa) override; void clipRRect(const SkRRect& rrect, ClipOp clip_op, bool is_aa) override; void clipPath(const SkPath& path, ClipOp clip_op, bool is_aa) override; diff --git a/testing/mock_canvas.cc b/testing/mock_canvas.cc index 695678e23a0ee..03627f722fbaa 100644 --- a/testing/mock_canvas.cc +++ b/testing/mock_canvas.cc @@ -214,6 +214,13 @@ void MockCanvas::ClipRect(const SkRect& rect, ClipOp op, bool is_aa) { state_stack_.back().clipRect(rect, op, is_aa); } +void MockCanvas::ClipOval(const SkRect& bounds, ClipOp op, bool is_aa) { + ClipEdgeStyle style = is_aa ? kSoftClipEdgeStyle : kHardClipEdgeStyle; + draw_calls_.emplace_back( + DrawCall{current_layer_, ClipOvalData{bounds, op, style}}); + state_stack_.back().clipOval(bounds, op, is_aa); +} + void MockCanvas::ClipRRect(const SkRRect& rrect, ClipOp op, bool is_aa) { ClipEdgeStyle style = is_aa ? kSoftClipEdgeStyle : kHardClipEdgeStyle; draw_calls_.emplace_back( @@ -520,6 +527,16 @@ std::ostream& operator<<(std::ostream& os, return os << data.rect << " " << data.clip_op << " " << data.style; } +bool operator==(const MockCanvas::ClipOvalData& a, + const MockCanvas::ClipOvalData& b) { + return a.bounds == b.bounds && a.clip_op == b.clip_op && a.style == b.style; +} + +std::ostream& operator<<(std::ostream& os, + const MockCanvas::ClipOvalData& data) { + return os << data.bounds << " " << data.clip_op << " " << data.style; +} + bool operator==(const MockCanvas::ClipRRectData& a, const MockCanvas::ClipRRectData& b) { return a.rrect == b.rrect && a.clip_op == b.clip_op && a.style == b.style; diff --git a/testing/mock_canvas.h b/testing/mock_canvas.h index f15e0ec54adf2..7384d5ecf578a 100644 --- a/testing/mock_canvas.h +++ b/testing/mock_canvas.h @@ -114,6 +114,12 @@ class MockCanvas final : public DlCanvas { ClipEdgeStyle style; }; + struct ClipOvalData { + SkRect bounds; + ClipOp clip_op; + ClipEdgeStyle style; + }; + struct ClipRRectData { SkRRect rrect; ClipOp clip_op; @@ -145,6 +151,7 @@ class MockCanvas final : public DlCanvas { DrawDisplayListData, DrawShadowData, ClipRectData, + ClipOvalData, ClipRRectData, ClipPathData, DrawPaintData>; @@ -206,6 +213,7 @@ class MockCanvas final : public DlCanvas { SkMatrix GetTransform() const override; void ClipRect(const SkRect& rect, ClipOp clip_op, bool is_aa) override; + void ClipOval(const SkRect& bounds, ClipOp clip_op, bool is_aa) override; void ClipRRect(const SkRRect& rrect, ClipOp clip_op, bool is_aa) override; void ClipPath(const SkPath& path, ClipOp clip_op, bool is_aa) override;