diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files index ad09cd8cad96d..98286f6347c78 100644 --- a/ci/licenses_golden/excluded_files +++ b/ci/licenses_golden/excluded_files @@ -178,6 +178,7 @@ ../../../flutter/impeller/geometry/matrix_unittests.cc ../../../flutter/impeller/geometry/path_unittests.cc ../../../flutter/impeller/geometry/rect_unittests.cc +../../../flutter/impeller/geometry/round_rect_unittests.cc ../../../flutter/impeller/geometry/saturated_math_unittests.cc ../../../flutter/impeller/geometry/size_unittests.cc ../../../flutter/impeller/geometry/trig_unittests.cc diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index f2873e025d53f..58c532962b341 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -42931,6 +42931,8 @@ ORIGIN: ../../../flutter/impeller/geometry/quaternion.cc + ../../../flutter/LICE ORIGIN: ../../../flutter/impeller/geometry/quaternion.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/geometry/rect.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/geometry/rect.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/geometry/round_rect.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/geometry/round_rect.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/geometry/saturated_math.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/geometry/scalar.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/geometry/separated_vector.cc + ../../../flutter/LICENSE @@ -45801,6 +45803,8 @@ FILE: ../../../flutter/impeller/geometry/quaternion.cc FILE: ../../../flutter/impeller/geometry/quaternion.h FILE: ../../../flutter/impeller/geometry/rect.cc FILE: ../../../flutter/impeller/geometry/rect.h +FILE: ../../../flutter/impeller/geometry/round_rect.cc +FILE: ../../../flutter/impeller/geometry/round_rect.h FILE: ../../../flutter/impeller/geometry/saturated_math.h FILE: ../../../flutter/impeller/geometry/scalar.h FILE: ../../../flutter/impeller/geometry/separated_vector.cc diff --git a/display_list/benchmarking/dl_complexity_gl.cc b/display_list/benchmarking/dl_complexity_gl.cc index 5da3703b7fbb2..438a31af7d092 100644 --- a/display_list/benchmarking/dl_complexity_gl.cc +++ b/display_list/benchmarking/dl_complexity_gl.cc @@ -237,8 +237,8 @@ void DisplayListGLComplexityCalculator::GLHelper::drawCircle( AccumulateComplexity(complexity); } -void DisplayListGLComplexityCalculator::GLHelper::drawRRect( - const SkRRect& rrect) { +void DisplayListGLComplexityCalculator::GLHelper::drawRoundRect( + const DlRoundRect& rrect) { if (IsComplex()) { return; } @@ -257,14 +257,15 @@ void DisplayListGLComplexityCalculator::GLHelper::drawRRect( // approximately matching the measured data, normalising the data so that // 0.0005ms resulted in a score of 100 then simplifying down the formula. if (DrawStyle() == DlDrawStyle::kFill || - ((rrect.getType() == SkRRect::Type::kSimple_Type) && IsAntiAliased())) { - unsigned int area = rrect.width() * rrect.height(); + ((rrect.GetRadii().AreAllCornersSame()) && IsAntiAliased())) { + unsigned int area = rrect.GetBounds().Area(); // m = 1/3200 // c = 0.5 complexity = (area + 1600) / 80; } else { // Take the average of the width and height. - unsigned int length = (rrect.width() + rrect.height()) / 2; + unsigned int length = + (rrect.GetBounds().GetWidth() + rrect.GetBounds().GetHeight()) / 2; // There is some difference between hairline and non-hairline performance // but the spread is relatively inconsistent and it's pretty much a wash. @@ -282,9 +283,9 @@ void DisplayListGLComplexityCalculator::GLHelper::drawRRect( AccumulateComplexity(complexity); } -void DisplayListGLComplexityCalculator::GLHelper::drawDRRect( - const SkRRect& outer, - const SkRRect& inner) { +void DisplayListGLComplexityCalculator::GLHelper::drawDiffRoundRect( + const DlRoundRect& outer, + const DlRoundRect& inner) { if (IsComplex()) { return; } @@ -307,8 +308,8 @@ void DisplayListGLComplexityCalculator::GLHelper::drawDRRect( // There is also a kStrokeAndFill_Style that Skia exposes, but we do not // currently use it anywhere in Flutter. if (DrawStyle() == DlDrawStyle::kFill) { - unsigned int area = outer.width() * outer.height(); - if (outer.getType() == SkRRect::Type::kComplex_Type) { + unsigned int area = outer.GetBounds().Area(); + if (!outer.GetRadii().AreAllCornersSame()) { // m = 1/500 // c = 0.5 complexity = (area + 250) / 5; @@ -318,7 +319,8 @@ void DisplayListGLComplexityCalculator::GLHelper::drawDRRect( complexity = (area + 3200) / 16; } } else { - unsigned int length = (outer.width() + outer.height()) / 2; + unsigned int length = + (outer.GetBounds().GetWidth() + outer.GetBounds().GetHeight()) / 2; if (IsAntiAliased()) { // m = 1/15 // c = 1 diff --git a/display_list/benchmarking/dl_complexity_gl.h b/display_list/benchmarking/dl_complexity_gl.h index 3041f9014102f..d9e7891de5007 100644 --- a/display_list/benchmarking/dl_complexity_gl.h +++ b/display_list/benchmarking/dl_complexity_gl.h @@ -48,8 +48,9 @@ class DisplayListGLComplexityCalculator void drawRect(const DlRect& rect) override; void drawOval(const DlRect& bounds) override; void drawCircle(const DlPoint& center, DlScalar radius) override; - void drawRRect(const SkRRect& rrect) override; - void drawDRRect(const SkRRect& outer, const SkRRect& inner) override; + void drawRoundRect(const DlRoundRect& rrect) override; + void drawDiffRoundRect(const DlRoundRect& outer, + const DlRoundRect& inner) override; void drawPath(const DlPath& path) override; void drawArc(const DlRect& oval_bounds, DlScalar start_degrees, diff --git a/display_list/benchmarking/dl_complexity_metal.cc b/display_list/benchmarking/dl_complexity_metal.cc index 25fa25a4912a2..eaa1bb99ea5b4 100644 --- a/display_list/benchmarking/dl_complexity_metal.cc +++ b/display_list/benchmarking/dl_complexity_metal.cc @@ -242,21 +242,20 @@ void DisplayListMetalComplexityCalculator::MetalHelper::drawCircle( AccumulateComplexity(complexity); } -void DisplayListMetalComplexityCalculator::MetalHelper::drawRRect( - const SkRRect& rrect) { +void DisplayListMetalComplexityCalculator::MetalHelper::drawRoundRect( + const DlRoundRect& rrect) { if (IsComplex()) { return; } // RRects scale linearly with the area of the bounding rect. - unsigned int area = rrect.width() * rrect.height(); + unsigned int area = rrect.GetBounds().Area(); // Drawing RRects is split into two performance tiers; an expensive // one and a less expensive one. Both scale linearly with area. // // Expensive: All filled style, symmetric w/AA. - bool expensive = - (DrawStyle() == DlDrawStyle::kFill) || - ((rrect.getType() == SkRRect::Type::kSimple_Type) && IsAntiAliased()); + bool expensive = (DrawStyle() == DlDrawStyle::kFill) || + (rrect.GetRadii().AreAllCornersSame() && IsAntiAliased()); unsigned int complexity; @@ -278,9 +277,9 @@ void DisplayListMetalComplexityCalculator::MetalHelper::drawRRect( AccumulateComplexity(complexity); } -void DisplayListMetalComplexityCalculator::MetalHelper::drawDRRect( - const SkRRect& outer, - const SkRRect& inner) { +void DisplayListMetalComplexityCalculator::MetalHelper::drawDiffRoundRect( + const DlRoundRect& outer, + const DlRoundRect& inner) { if (IsComplex()) { return; } @@ -293,7 +292,8 @@ void DisplayListMetalComplexityCalculator::MetalHelper::drawDRRect( // a) and c) scale linearly with the area, b) and d) scale linearly with // a single dimension (length). In all cases, the dimensions refer to // the outer RRect. - unsigned int length = (outer.width() + outer.height()) / 2; + unsigned int length = + (outer.GetBounds().GetWidth() + outer.GetBounds().GetHeight()) / 2; unsigned int complexity; @@ -304,8 +304,8 @@ void DisplayListMetalComplexityCalculator::MetalHelper::drawDRRect( // There is also a kStrokeAndFill_Style that Skia exposes, but we do not // currently use it anywhere in Flutter. if (DrawStyle() == DlDrawStyle::kFill) { - unsigned int area = outer.width() * outer.height(); - if (outer.getType() == SkRRect::Type::kComplex_Type) { + unsigned int area = outer.GetBounds().Area(); + if (!outer.GetRadii().AreAllCornersSame()) { // m = 1/1000 // c = 1 complexity = (area + 1000) / 10; diff --git a/display_list/benchmarking/dl_complexity_metal.h b/display_list/benchmarking/dl_complexity_metal.h index 79dd5c3155be3..4ecf3957832f3 100644 --- a/display_list/benchmarking/dl_complexity_metal.h +++ b/display_list/benchmarking/dl_complexity_metal.h @@ -48,8 +48,9 @@ class DisplayListMetalComplexityCalculator void drawRect(const DlRect& rect) override; void drawOval(const DlRect& bounds) override; void drawCircle(const DlPoint& center, DlScalar radius) override; - void drawRRect(const SkRRect& rrect) override; - void drawDRRect(const SkRRect& outer, const SkRRect& inner) override; + void drawRoundRect(const DlRoundRect& rrect) override; + void drawDiffRoundRect(const DlRoundRect& outer, + const DlRoundRect& inner) override; void drawPath(const DlPath& path) override; void drawArc(const DlRect& oval_bounds, DlScalar start_degrees, diff --git a/display_list/display_list.cc b/display_list/display_list.cc index 95f802001ebbe..1d30ebd2813a3 100644 --- a/display_list/display_list.cc +++ b/display_list/display_list.cc @@ -310,11 +310,11 @@ DisplayListOpCategory DisplayList::GetOpCategory(DisplayListOpType type) { case DisplayListOpType::kClipIntersectRect: case DisplayListOpType::kClipIntersectOval: - case DisplayListOpType::kClipIntersectRRect: + case DisplayListOpType::kClipIntersectRoundRect: case DisplayListOpType::kClipIntersectPath: case DisplayListOpType::kClipDifferenceRect: case DisplayListOpType::kClipDifferenceOval: - case DisplayListOpType::kClipDifferenceRRect: + case DisplayListOpType::kClipDifferenceRoundRect: case DisplayListOpType::kClipDifferencePath: return DisplayListOpCategory::kClip; @@ -325,8 +325,8 @@ DisplayListOpCategory DisplayList::GetOpCategory(DisplayListOpType type) { case DisplayListOpType::kDrawRect: case DisplayListOpType::kDrawOval: case DisplayListOpType::kDrawCircle: - case DisplayListOpType::kDrawRRect: - case DisplayListOpType::kDrawDRRect: + case DisplayListOpType::kDrawRoundRect: + case DisplayListOpType::kDrawDiffRoundRect: case DisplayListOpType::kDrawArc: case DisplayListOpType::kDrawPath: case DisplayListOpType::kDrawPoints: diff --git a/display_list/display_list.h b/display_list/display_list.h index c0356df100c6d..339c291c2f684 100644 --- a/display_list/display_list.h +++ b/display_list/display_list.h @@ -100,11 +100,11 @@ namespace flutter { \ V(ClipIntersectRect) \ V(ClipIntersectOval) \ - V(ClipIntersectRRect) \ + V(ClipIntersectRoundRect) \ V(ClipIntersectPath) \ V(ClipDifferenceRect) \ V(ClipDifferenceOval) \ - V(ClipDifferenceRRect) \ + V(ClipDifferenceRoundRect) \ V(ClipDifferencePath) \ \ V(DrawPaint) \ @@ -115,8 +115,8 @@ namespace flutter { V(DrawRect) \ V(DrawOval) \ V(DrawCircle) \ - V(DrawRRect) \ - V(DrawDRRect) \ + V(DrawRoundRect) \ + V(DrawDiffRoundRect) \ V(DrawArc) \ V(DrawPath) \ \ diff --git a/display_list/display_list_unittests.cc b/display_list/display_list_unittests.cc index 524a5aabf2d87..d43a2334dc928 100644 --- a/display_list/display_list_unittests.cc +++ b/display_list/display_list_unittests.cc @@ -2947,7 +2947,7 @@ TEST_F(DisplayListTest, ClipRRectTriggersDeferredSave) { { builder1.Save(); { - builder1.ClipRRect(kTestRRect, ClipOp::kIntersect, true); + builder1.ClipRRect(kTestSkRRect, ClipOp::kIntersect, true); builder1.DrawRect(SkRect{0, 0, 100, 100}, DlPaint()); } @@ -2963,7 +2963,7 @@ TEST_F(DisplayListTest, ClipRRectTriggersDeferredSave) { DisplayListBuilder builder2; builder2.Save(); - builder2.ClipRRect(kTestRRect, ClipOp::kIntersect, true); + builder2.ClipRRect(kTestSkRRect, ClipOp::kIntersect, true); builder2.DrawRect(SkRect{0, 0, 100, 100}, DlPaint()); builder2.Restore(); @@ -4538,7 +4538,7 @@ TEST_F(DisplayListTest, DrawDisplayListForwardsBackdropFlag) { #define CLIP_EXPECTOR(name) ClipExpector name(__FILE__, __LINE__) struct ClipExpectation { - std::variant shape; + std::variant shape; bool is_oval; ClipOp clip_op; bool is_aa; @@ -4548,7 +4548,7 @@ struct ClipExpectation { case 0: return is_oval ? "SkOval" : "SkRect"; case 1: - return "SkRRect"; + return "DlRoundRect"; case 2: return "DlPath"; default: @@ -4567,7 +4567,7 @@ ::std::ostream& operator<<(::std::ostream& os, const ClipExpectation& expect) { } break; case 1: - os << std::get(expect.shape); + os << std::get(expect.shape); break; case 2: os << std::get(expect.shape).GetSkPath(); @@ -4626,8 +4626,10 @@ class ClipExpector : public virtual DlOpReceiver, ClipExpector& addExpectation(const SkRRect& rrect, ClipOp clip_op = ClipOp::kIntersect, bool is_aa = false) { + auto dl_rrect = ToDlRoundRect(rrect); + EXPECT_EQ(ToSkRRect(dl_rrect), rrect); clip_expectations_.push_back({ - .shape = rrect, + .shape = dl_rrect, .is_oval = false, .clip_op = clip_op, .is_aa = is_aa, @@ -4663,9 +4665,9 @@ class ClipExpector : public virtual DlOpReceiver, bool is_aa) override { check(bounds, clip_op, is_aa, true); } - void clipRRect(const SkRRect& rrect, - DlCanvas::ClipOp clip_op, - bool is_aa) override { + void clipRoundRect(const DlRoundRect& rrect, + DlCanvas::ClipOp clip_op, + bool is_aa) override { check(rrect, clip_op, is_aa); } void clipPath(const DlPath& path, diff --git a/display_list/dl_builder.cc b/display_list/dl_builder.cc index 39953dfd3c54f..2d10ad33e44b5 100644 --- a/display_list/dl_builder.cc +++ b/display_list/dl_builder.cc @@ -995,15 +995,15 @@ void DisplayListBuilder::ClipOval(const DlRect& bounds, break; } } -void DisplayListBuilder::ClipRRect(const SkRRect& rrect, - ClipOp clip_op, - bool is_aa) { - if (rrect.isRect()) { - ClipRect(ToDlRect(rrect.rect()), clip_op, is_aa); +void DisplayListBuilder::ClipRoundRect(const DlRoundRect& rrect, + ClipOp clip_op, + bool is_aa) { + if (rrect.IsRect()) { + ClipRect(rrect.GetBounds(), clip_op, is_aa); return; } - if (rrect.isOval()) { - ClipOval(ToDlRect(rrect.rect()), clip_op, is_aa); + if (rrect.IsOval()) { + ClipOval(rrect.GetBounds(), clip_op, is_aa); return; } if (current_info().is_nop) { @@ -1025,10 +1025,10 @@ void DisplayListBuilder::ClipRRect(const SkRRect& rrect, checkForDeferredSave(); switch (clip_op) { case ClipOp::kIntersect: - Push(0, rrect, is_aa); + Push(0, rrect, is_aa); break; case ClipOp::kDifference: - Push(0, rrect, is_aa); + Push(0, rrect, is_aa); break; } } @@ -1185,42 +1185,43 @@ void DisplayListBuilder::DrawCircle(const DlPoint& center, SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawCircleFlags); drawCircle(center, radius); } -void DisplayListBuilder::drawRRect(const SkRRect& rrect) { - if (rrect.isRect()) { - drawRect(ToDlRect(rrect.rect())); - } else if (rrect.isOval()) { - drawOval(ToDlRect(rrect.rect())); +void DisplayListBuilder::drawRoundRect(const DlRoundRect& rrect) { + if (rrect.IsRect()) { + drawRect(rrect.GetBounds()); + } else if (rrect.IsOval()) { + drawOval(rrect.GetBounds()); } else { DisplayListAttributeFlags flags = kDrawRRectFlags; OpResult result = PaintResult(current_, flags); if (result != OpResult::kNoEffect && - AccumulateOpBounds(rrect.getBounds(), flags)) { - Push(0, rrect); + AccumulateOpBounds(ToSkRect(rrect.GetBounds()), flags)) { + Push(0, rrect); CheckLayerOpacityCompatibility(); UpdateLayerResult(result); } } } -void DisplayListBuilder::DrawRRect(const SkRRect& rrect, const DlPaint& paint) { +void DisplayListBuilder::DrawRoundRect(const DlRoundRect& rrect, + const DlPaint& paint) { SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawRRectFlags); - drawRRect(rrect); + drawRoundRect(rrect); } -void DisplayListBuilder::drawDRRect(const SkRRect& outer, - const SkRRect& inner) { +void DisplayListBuilder::drawDiffRoundRect(const DlRoundRect& outer, + const DlRoundRect& inner) { DisplayListAttributeFlags flags = kDrawDRRectFlags; OpResult result = PaintResult(current_, flags); if (result != OpResult::kNoEffect && - AccumulateOpBounds(outer.getBounds(), flags)) { - Push(0, outer, inner); + AccumulateOpBounds(ToSkRect(outer.GetBounds()), flags)) { + Push(0, outer, inner); CheckLayerOpacityCompatibility(); UpdateLayerResult(result); } } -void DisplayListBuilder::DrawDRRect(const SkRRect& outer, - const SkRRect& inner, - const DlPaint& paint) { +void DisplayListBuilder::DrawDiffRoundRect(const DlRoundRect& outer, + const DlRoundRect& inner, + const DlPaint& paint) { SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawDRRectFlags); - drawDRRect(outer, inner); + drawDiffRoundRect(outer, inner); } void DisplayListBuilder::drawPath(const DlPath& path) { DisplayListAttributeFlags flags = kDrawPathFlags; diff --git a/display_list/dl_builder.h b/display_list/dl_builder.h index 58d343d92ab6a..0dfb365c11993 100644 --- a/display_list/dl_builder.h +++ b/display_list/dl_builder.h @@ -110,9 +110,9 @@ class DisplayListBuilder final : public virtual DlCanvas, 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; + void ClipRoundRect(const DlRoundRect& rrect, + ClipOp clip_op = ClipOp::kIntersect, + bool is_aa = false) override; // |DlCanvas| void ClipPath(const DlPath& path, ClipOp clip_op = ClipOp::kIntersect, @@ -162,11 +162,11 @@ class DisplayListBuilder final : public virtual DlCanvas, DlScalar radius, const DlPaint& paint) override; // |DlCanvas| - void DrawRRect(const SkRRect& rrect, const DlPaint& paint) override; + void DrawRoundRect(const DlRoundRect& rrect, const DlPaint& paint) override; // |DlCanvas| - void DrawDRRect(const SkRRect& outer, - const SkRRect& inner, - const DlPaint& paint) override; + void DrawDiffRoundRect(const DlRoundRect& outer, + const DlRoundRect& inner, + const DlPaint& paint) override; // |DlCanvas| void DrawPath(const DlPath& path, const DlPaint& paint) override; // |DlCanvas| @@ -399,8 +399,10 @@ class DisplayListBuilder final : public virtual DlCanvas, 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); + void clipRoundRect(const DlRoundRect& rrect, + ClipOp clip_op, + bool is_aa) override { + ClipRoundRect(rrect, clip_op, is_aa); } // |DlOpReceiver| void clipPath(const DlPath& path, ClipOp clip_op, bool is_aa) override { @@ -427,9 +429,10 @@ class DisplayListBuilder final : public virtual DlCanvas, // |DlOpReceiver| void drawCircle(const DlPoint& center, DlScalar radius) override; // |DlOpReceiver| - void drawRRect(const SkRRect& rrect) override; + void drawRoundRect(const DlRoundRect& rrect) override; // |DlOpReceiver| - void drawDRRect(const SkRRect& outer, const SkRRect& inner) override; + void drawDiffRoundRect(const DlRoundRect& outer, + const DlRoundRect& inner) override; // |DlOpReceiver| void drawPath(const DlPath& path) override; // |DlOpReceiver| diff --git a/display_list/dl_canvas.h b/display_list/dl_canvas.h index 80076fc0012b3..e27e4e7c7a192 100644 --- a/display_list/dl_canvas.h +++ b/display_list/dl_canvas.h @@ -97,9 +97,9 @@ class DlCanvas { virtual void ClipOval(const DlRect& 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; + virtual void ClipRoundRect(const DlRoundRect& rrect, + ClipOp clip_op = ClipOp::kIntersect, + bool is_aa = false) = 0; virtual void ClipPath(const DlPath& path, ClipOp clip_op = ClipOp::kIntersect, bool is_aa = false) = 0; @@ -135,10 +135,11 @@ class DlCanvas { virtual void DrawCircle(const DlPoint& center, DlScalar radius, const DlPaint& paint) = 0; - virtual void DrawRRect(const SkRRect& rrect, const DlPaint& paint) = 0; - virtual void DrawDRRect(const SkRRect& outer, - const SkRRect& inner, - const DlPaint& paint) = 0; + virtual void DrawRoundRect(const DlRoundRect& rrect, + const DlPaint& paint) = 0; + virtual void DrawDiffRoundRect(const DlRoundRect& outer, + const DlRoundRect& inner, + const DlPaint& paint) = 0; virtual void DrawPath(const DlPath& path, const DlPaint& paint) = 0; virtual void DrawArc(const DlRect& bounds, DlScalar start, @@ -281,12 +282,16 @@ class DlCanvas { bool is_aa = false) { ClipRect(ToDlRect(rect), clip_op, is_aa); } - void ClipOval(const SkRect& bounds, ClipOp clip_op = ClipOp::kIntersect, bool is_aa = false) { ClipOval(ToDlRect(bounds), clip_op, is_aa); } + void ClipRRect(const SkRRect& rrect, + ClipOp clip_op = ClipOp::kIntersect, + bool is_aa = false) { + ClipRoundRect(ToDlRoundRect(rrect), clip_op, is_aa); + } void ClipPath(const SkPath& path, ClipOp clip_op = ClipOp::kIntersect, bool is_aa = false) { @@ -315,6 +320,14 @@ class DlCanvas { const DlPaint& paint) { DrawCircle(ToDlPoint(center), radius, paint); } + void DrawRRect(const SkRRect& rrect, const DlPaint& paint) { + DrawRoundRect(ToDlRoundRect(rrect), paint); + } + void DrawDRRect(const SkRRect& outer, + const SkRRect& inner, + const DlPaint& paint) { + DrawDiffRoundRect(ToDlRoundRect(outer), ToDlRoundRect(inner), paint); + } void DrawPath(const SkPath& path, const DlPaint& paint) { DrawPath(DlPath(path), paint); } diff --git a/display_list/dl_op_receiver.h b/display_list/dl_op_receiver.h index 79d5d28dcd5a4..081c44b5444f8 100644 --- a/display_list/dl_op_receiver.h +++ b/display_list/dl_op_receiver.h @@ -293,7 +293,9 @@ class DlOpReceiver { virtual void clipRect(const DlRect& rect, ClipOp clip_op, bool is_aa) = 0; virtual void clipOval(const DlRect& bounds, ClipOp clip_op, bool is_aa) = 0; - virtual void clipRRect(const SkRRect& rrect, ClipOp clip_op, bool is_aa) = 0; + virtual void clipRoundRect(const DlRoundRect& rrect, + ClipOp clip_op, + bool is_aa) = 0; virtual void clipPath(const DlPath& path, ClipOp clip_op, bool is_aa) = 0; // The following rendering methods all take their rendering attributes @@ -313,8 +315,9 @@ class DlOpReceiver { virtual void drawRect(const DlRect& rect) = 0; virtual void drawOval(const DlRect& bounds) = 0; virtual void drawCircle(const DlPoint& center, DlScalar radius) = 0; - virtual void drawRRect(const SkRRect& rrect) = 0; - virtual void drawDRRect(const SkRRect& outer, const SkRRect& inner) = 0; + virtual void drawRoundRect(const DlRoundRect& rrect) = 0; + virtual void drawDiffRoundRect(const DlRoundRect& outer, + const DlRoundRect& inner) = 0; virtual void drawPath(const DlPath& path) = 0; virtual void drawArc(const DlRect& oval_bounds, DlScalar start_degrees, diff --git a/display_list/dl_op_records.h b/display_list/dl_op_records.h index 189038b37a5fb..d08602994e876 100644 --- a/display_list/dl_op_records.h +++ b/display_list/dl_op_records.h @@ -465,8 +465,8 @@ struct TransformResetOp final : TransformClipOpBase { // 4 byte header + 4 byte common payload packs into minimum 8 bytes // DlRect is 16 more bytes, which packs efficiently into 24 bytes total -// SkRRect is 52 more bytes, which rounds up to 56 bytes (4 bytes unused) -// which packs into 64 bytes total +// DlRoundRect is 48 more bytes, which rounds up to 48 bytes +// which packs into 56 bytes total // CacheablePath is 128 more bytes, which packs efficiently into 136 bytes total // // We could pack the clip_op and the bool both into the free 4 bytes after @@ -489,10 +489,10 @@ struct TransformResetOp final : TransformClipOpBase { }; DEFINE_CLIP_SHAPE_OP(Rect, DlRect, Intersect) DEFINE_CLIP_SHAPE_OP(Oval, DlRect, Intersect) -DEFINE_CLIP_SHAPE_OP(RRect, SkRRect, Intersect) +DEFINE_CLIP_SHAPE_OP(RoundRect, DlRoundRect, Intersect) DEFINE_CLIP_SHAPE_OP(Rect, DlRect, Difference) DEFINE_CLIP_SHAPE_OP(Oval, DlRect, Difference) -DEFINE_CLIP_SHAPE_OP(RRect, SkRRect, Difference) +DEFINE_CLIP_SHAPE_OP(RoundRect, DlRoundRect, Difference) #undef DEFINE_CLIP_SHAPE_OP // 4 byte header + 20 byte payload packs evenly into 24 bytes @@ -554,7 +554,8 @@ struct DrawColorOp final : DrawOpBase { // DlRect is 16 more bytes, using 20 bytes which rounds up to 24 bytes total // (4 bytes unused) // SkOval is same as DlRect -// SkRRect is 52 more bytes, which packs efficiently into 56 bytes total +// DlRoundRect is 48 more bytes, using 52 bytes which rounds up to 56 bytes +// total (4 bytes unused) #define DEFINE_DRAW_1ARG_OP(op_name, arg_type, arg_name) \ struct Draw##op_name##Op final : DrawOpBase { \ static constexpr auto kType = DisplayListOpType::kDraw##op_name; \ @@ -569,7 +570,7 @@ struct DrawColorOp final : DrawOpBase { }; DEFINE_DRAW_1ARG_OP(Rect, DlRect, rect) DEFINE_DRAW_1ARG_OP(Oval, DlRect, oval) -DEFINE_DRAW_1ARG_OP(RRect, SkRRect, rrect) +DEFINE_DRAW_1ARG_OP(RoundRect, DlRoundRect, rrect) #undef DEFINE_DRAW_1ARG_OP // 4 byte header + 16 byte payload uses 20 bytes but is rounded @@ -595,8 +596,8 @@ struct DrawPathOp final : DrawOpBase { // 2 x DlPoint is 16 more bytes, using 20 bytes rounding up to 24 bytes total // (4 bytes unused) // DlPoint + DlScalar is 12 more bytes, packing efficiently into 16 bytes total -// 2 x SkRRect is 104 more bytes, using 108 and rounding up to 112 bytes total -// (4 bytes unused) +// 2 x DlRoundRect is 96 more bytes, using 100 and rounding up to 104 bytes +// total (4 bytes unused) #define DEFINE_DRAW_2ARG_OP(op_name, type1, name1, type2, name2) \ struct Draw##op_name##Op final : DrawOpBase { \ static constexpr auto kType = DisplayListOpType::kDraw##op_name; \ @@ -613,7 +614,7 @@ struct DrawPathOp final : DrawOpBase { }; DEFINE_DRAW_2ARG_OP(Line, DlPoint, p0, DlPoint, p1) DEFINE_DRAW_2ARG_OP(Circle, DlPoint, center, DlScalar, radius) -DEFINE_DRAW_2ARG_OP(DRRect, SkRRect, outer, SkRRect, inner) +DEFINE_DRAW_2ARG_OP(DiffRoundRect, DlRoundRect, outer, DlRoundRect, inner) #undef DEFINE_DRAW_2ARG_OP // 4 byte header + 24 byte payload packs into 32 bytes (4 bytes unused) diff --git a/display_list/geometry/dl_geometry_types.h b/display_list/geometry/dl_geometry_types.h index cf468e52b8fb4..66ef9a7428464 100644 --- a/display_list/geometry/dl_geometry_types.h +++ b/display_list/geometry/dl_geometry_types.h @@ -7,10 +7,12 @@ #include "flutter/impeller/geometry/matrix.h" #include "flutter/impeller/geometry/rect.h" +#include "flutter/impeller/geometry/round_rect.h" #include "flutter/impeller/geometry/scalar.h" #include "flutter/third_party/skia/include/core/SkM44.h" #include "flutter/third_party/skia/include/core/SkMatrix.h" +#include "flutter/third_party/skia/include/core/SkRRect.h" #include "flutter/third_party/skia/include/core/SkRect.h" #include "flutter/third_party/skia/include/core/SkSize.h" @@ -26,6 +28,7 @@ using DlSize = impeller::Size; using DlISize = impeller::ISize32; using DlRect = impeller::Rect; using DlIRect = impeller::IRect32; +using DlRoundRect = impeller::RoundRect; using DlMatrix = impeller::Matrix; static_assert(sizeof(SkPoint) == sizeof(DlPoint)); @@ -34,6 +37,7 @@ static_assert(sizeof(SkSize) == sizeof(DlSize)); static_assert(sizeof(SkISize) == sizeof(DlISize)); static_assert(sizeof(SkRect) == sizeof(DlRect)); static_assert(sizeof(SkIRect) == sizeof(DlIRect)); +static_assert(sizeof(SkVector) == sizeof(DlSize)); inline const DlPoint& ToDlPoint(const SkPoint& point) { return *reinterpret_cast(&point); @@ -75,6 +79,21 @@ inline const DlISize& ToDlISize(const SkISize& size) { return *reinterpret_cast(&size); } +inline const DlSize& ToDlSize(const SkVector& vector) { + return *reinterpret_cast(&vector); +} + +inline const DlRoundRect ToDlRoundRect(const SkRRect& rrect) { + return DlRoundRect::MakeRectRadii( + ToDlRect(rrect.rect()), + { + .top_left = ToDlSize(rrect.radii(SkRRect::kUpperLeft_Corner)), + .top_right = ToDlSize(rrect.radii(SkRRect::kUpperRight_Corner)), + .bottom_left = ToDlSize(rrect.radii(SkRRect::kLowerLeft_Corner)), + .bottom_right = ToDlSize(rrect.radii(SkRRect::kLowerRight_Corner)), + }); +} + inline constexpr DlMatrix ToDlMatrix(const SkMatrix& matrix) { // clang-format off return DlMatrix::MakeColumn( @@ -128,6 +147,25 @@ inline const SkISize& ToSkISize(const DlISize& size) { return *reinterpret_cast(&size); } +inline const SkVector& ToSkVector(const DlSize& size) { + return *reinterpret_cast(&size); +} + +inline const SkRRect ToSkRRect(const DlRoundRect& round_rect) { + SkVector radii[4]; + radii[SkRRect::kUpperLeft_Corner] = + ToSkVector(round_rect.GetRadii().top_left); + radii[SkRRect::kUpperRight_Corner] = + ToSkVector(round_rect.GetRadii().top_right); + radii[SkRRect::kLowerLeft_Corner] = + ToSkVector(round_rect.GetRadii().bottom_left); + radii[SkRRect::kLowerRight_Corner] = + ToSkVector(round_rect.GetRadii().bottom_right); + SkRRect rrect; + rrect.setRectRadii(ToSkRect(round_rect.GetBounds()), radii); + return rrect; +}; + inline constexpr SkMatrix ToSkMatrix(const DlMatrix& matrix) { return SkMatrix::MakeAll(matrix.m[0], matrix.m[4], matrix.m[12], // matrix.m[1], matrix.m[5], matrix.m[13], // diff --git a/display_list/geometry/dl_geometry_types_unittests.cc b/display_list/geometry/dl_geometry_types_unittests.cc index 739052017c684..192fdc5d1eb76 100644 --- a/display_list/geometry/dl_geometry_types_unittests.cc +++ b/display_list/geometry/dl_geometry_types_unittests.cc @@ -50,5 +50,23 @@ TEST(DisplayListGeometryTypes, ISizeConversion) { EXPECT_NE(ToDlISize(sk_s), dl_s); } +TEST(DisplayListGeometryTypes, VectorToSizeConversion) { + SkVector sk_v = SkVector::Make(1.0f, 2.0f); + DlSize dl_s = DlSize(1.0f, 2.0f); + + EXPECT_EQ(sk_v, ToSkVector(dl_s)); + EXPECT_EQ(ToDlSize(sk_v), dl_s); + + dl_s = DlSize(1.0f, 3.0f); + + EXPECT_NE(sk_v, ToSkVector(dl_s)); + EXPECT_NE(ToDlSize(sk_v), dl_s); + + dl_s = DlSize(3.0f, 2.0f); + + EXPECT_NE(sk_v, ToSkVector(dl_s)); + EXPECT_NE(ToDlSize(sk_v), dl_s); +} + } // namespace testing } // namespace flutter diff --git a/display_list/skia/dl_sk_canvas.cc b/display_list/skia/dl_sk_canvas.cc index a190519925af5..3746a88c3e038 100644 --- a/display_list/skia/dl_sk_canvas.cc +++ b/display_list/skia/dl_sk_canvas.cc @@ -150,10 +150,10 @@ void DlSkCanvasAdapter::ClipOval(const DlRect& bounds, is_aa); } -void DlSkCanvasAdapter::ClipRRect(const SkRRect& rrect, - ClipOp clip_op, - bool is_aa) { - delegate_->clipRRect(rrect, ToSk(clip_op), is_aa); +void DlSkCanvasAdapter::ClipRoundRect(const DlRoundRect& rrect, + ClipOp clip_op, + bool is_aa) { + delegate_->clipRRect(ToSkRRect(rrect), ToSk(clip_op), is_aa); } void DlSkCanvasAdapter::ClipPath(const DlPath& path, @@ -223,14 +223,15 @@ void DlSkCanvasAdapter::DrawCircle(const DlPoint& center, delegate_->drawCircle(ToSkPoint(center), radius, ToSk(paint)); } -void DlSkCanvasAdapter::DrawRRect(const SkRRect& rrect, const DlPaint& paint) { - delegate_->drawRRect(rrect, ToSk(paint)); +void DlSkCanvasAdapter::DrawRoundRect(const DlRoundRect& rrect, + const DlPaint& paint) { + delegate_->drawRRect(ToSkRRect(rrect), ToSk(paint)); } -void DlSkCanvasAdapter::DrawDRRect(const SkRRect& outer, - const SkRRect& inner, - const DlPaint& paint) { - delegate_->drawDRRect(outer, inner, ToSk(paint)); +void DlSkCanvasAdapter::DrawDiffRoundRect(const DlRoundRect& outer, + const DlRoundRect& inner, + const DlPaint& paint) { + delegate_->drawDRRect(ToSkRRect(outer), ToSkRRect(inner), ToSk(paint)); } void DlSkCanvasAdapter::DrawPath(const DlPath& path, const DlPaint& paint) { diff --git a/display_list/skia/dl_sk_canvas.h b/display_list/skia/dl_sk_canvas.h index 7047f41a37625..70b0ffa97b95b 100644 --- a/display_list/skia/dl_sk_canvas.h +++ b/display_list/skia/dl_sk_canvas.h @@ -66,7 +66,9 @@ class DlSkCanvasAdapter final : public virtual DlCanvas { void ClipRect(const DlRect& rect, ClipOp clip_op, bool is_aa) override; void ClipOval(const DlRect& bounds, ClipOp clip_op, bool is_aa) override; - void ClipRRect(const SkRRect& rrect, ClipOp clip_op, bool is_aa) override; + void ClipRoundRect(const DlRoundRect& rrect, + ClipOp clip_op, + bool is_aa) override; void ClipPath(const DlPath& path, ClipOp clip_op, bool is_aa) override; /// Conservative estimate of the bounds of all outstanding clip operations @@ -98,10 +100,10 @@ class DlSkCanvasAdapter final : public virtual DlCanvas { void DrawCircle(const DlPoint& center, DlScalar radius, const DlPaint& paint) override; - void DrawRRect(const SkRRect& rrect, const DlPaint& paint) override; - void DrawDRRect(const SkRRect& outer, - const SkRRect& inner, - const DlPaint& paint) override; + void DrawRoundRect(const DlRoundRect& rrect, const DlPaint& paint) override; + void DrawDiffRoundRect(const DlRoundRect& outer, + const DlRoundRect& inner, + const DlPaint& paint) override; void DrawPath(const DlPath& path, const DlPaint& paint) override; void DrawArc(const DlRect& bounds, DlScalar start, diff --git a/display_list/skia/dl_sk_dispatcher.cc b/display_list/skia/dl_sk_dispatcher.cc index 45066eb22305f..c79542d01e1f9 100644 --- a/display_list/skia/dl_sk_dispatcher.cc +++ b/display_list/skia/dl_sk_dispatcher.cc @@ -133,10 +133,10 @@ void DlSkCanvasDispatcher::clipOval(const DlRect& bounds, bool is_aa) { canvas_->clipRRect(SkRRect::MakeOval(ToSkRect(bounds)), ToSk(clip_op), is_aa); } -void DlSkCanvasDispatcher::clipRRect(const SkRRect& rrect, - ClipOp clip_op, - bool is_aa) { - canvas_->clipRRect(rrect, ToSk(clip_op), is_aa); +void DlSkCanvasDispatcher::clipRoundRect(const DlRoundRect& rrect, + ClipOp clip_op, + bool is_aa) { + canvas_->clipRRect(ToSkRRect(rrect), ToSk(clip_op), is_aa); } void DlSkCanvasDispatcher::clipPath(const DlPath& path, ClipOp clip_op, @@ -183,12 +183,12 @@ void DlSkCanvasDispatcher::drawOval(const DlRect& bounds) { void DlSkCanvasDispatcher::drawCircle(const DlPoint& center, DlScalar radius) { canvas_->drawCircle(ToSkPoint(center), radius, paint()); } -void DlSkCanvasDispatcher::drawRRect(const SkRRect& rrect) { - canvas_->drawRRect(rrect, paint()); +void DlSkCanvasDispatcher::drawRoundRect(const DlRoundRect& rrect) { + canvas_->drawRRect(ToSkRRect(rrect), paint()); } -void DlSkCanvasDispatcher::drawDRRect(const SkRRect& outer, - const SkRRect& inner) { - canvas_->drawDRRect(outer, inner, paint()); +void DlSkCanvasDispatcher::drawDiffRoundRect(const DlRoundRect& outer, + const DlRoundRect& inner) { + canvas_->drawDRRect(ToSkRRect(outer), ToSkRRect(inner), paint()); } void DlSkCanvasDispatcher::drawPath(const DlPath& path) { path.WillRenderSkPath(); diff --git a/display_list/skia/dl_sk_dispatcher.h b/display_list/skia/dl_sk_dispatcher.h index 1455479a8b22c..659506b6bff21 100644 --- a/display_list/skia/dl_sk_dispatcher.h +++ b/display_list/skia/dl_sk_dispatcher.h @@ -53,7 +53,9 @@ class DlSkCanvasDispatcher : public virtual DlOpReceiver, void clipRect(const DlRect& rect, ClipOp clip_op, bool is_aa) override; void clipOval(const DlRect& bounds, ClipOp clip_op, bool is_aa) override; - void clipRRect(const SkRRect& rrect, ClipOp clip_op, bool is_aa) override; + void clipRoundRect(const DlRoundRect& rrect, + ClipOp clip_op, + bool is_aa) override; void clipPath(const DlPath& path, ClipOp clip_op, bool is_aa) override; void drawPaint() override; @@ -66,8 +68,9 @@ class DlSkCanvasDispatcher : public virtual DlOpReceiver, void drawRect(const DlRect& rect) override; void drawOval(const DlRect& bounds) override; void drawCircle(const DlPoint& center, DlScalar radius) override; - void drawRRect(const SkRRect& rrect) override; - void drawDRRect(const SkRRect& outer, const SkRRect& inner) override; + void drawRoundRect(const DlRoundRect& rrect) override; + void drawDiffRoundRect(const DlRoundRect& outer, + const DlRoundRect& inner) override; void drawPath(const DlPath& path) override; void drawArc(const DlRect& bounds, DlScalar start, diff --git a/display_list/testing/dl_test_snippets.cc b/display_list/testing/dl_test_snippets.cc index 64ce2a0fef0a0..3ce6e7ce1b11b 100644 --- a/display_list/testing/dl_test_snippets.cc +++ b/display_list/testing/dl_test_snippets.cc @@ -484,26 +484,26 @@ std::vector CreateAllClipOps() { }}, {"ClipRRect", { - {1, 64, 0, + {1, 56, 0, [](DlOpReceiver& r) { - r.clipRRect(kTestRRect, DlCanvas::ClipOp::kIntersect, true); + r.clipRoundRect(kTestRRect, DlCanvas::ClipOp::kIntersect, true); }}, - {1, 64, 0, + {1, 56, 0, [](DlOpReceiver& r) { - r.clipRRect(kTestRRect.makeOffset(1, 1), - DlCanvas::ClipOp::kIntersect, true); + r.clipRoundRect(kTestRRect.Shift(1, 1), + DlCanvas::ClipOp::kIntersect, true); }}, - {1, 64, 0, + {1, 56, 0, [](DlOpReceiver& r) { - r.clipRRect(kTestRRect, DlCanvas::ClipOp::kIntersect, false); + r.clipRoundRect(kTestRRect, DlCanvas::ClipOp::kIntersect, false); }}, - {1, 64, 0, + {1, 56, 0, [](DlOpReceiver& r) { - r.clipRRect(kTestRRect, DlCanvas::ClipOp::kDifference, true); + r.clipRoundRect(kTestRRect, DlCanvas::ClipOp::kDifference, true); }}, - {1, 64, 0, + {1, 56, 0, [](DlOpReceiver& r) { - r.clipRRect(kTestRRect, DlCanvas::ClipOp::kDifference, false); + r.clipRoundRect(kTestRRect, DlCanvas::ClipOp::kDifference, false); }}, }}, {"ClipPath", @@ -543,7 +543,7 @@ std::vector CreateAllClipOps() { r.clipPath(kTestPathOval, DlCanvas::ClipOp::kIntersect, true); }}, // clipPath(rrect) becomes clipRRect - {1, 64, 0, + {1, 56, 0, [](DlOpReceiver& r) { r.clipPath(kTestPathRRect, DlCanvas::ClipOp::kIntersect, true); }}, @@ -681,18 +681,20 @@ std::vector CreateAllRenderingOps() { }}, {"DrawRRect", { - {1, 56, 1, [](DlOpReceiver& r) { r.drawRRect(kTestRRect); }}, + {1, 56, 1, [](DlOpReceiver& r) { r.drawRoundRect(kTestRRect); }}, {1, 56, 1, - [](DlOpReceiver& r) { r.drawRRect(kTestRRect.makeOffset(5, 5)); }}, + [](DlOpReceiver& r) { r.drawRoundRect(kTestRRect.Shift(5, 5)); }}, }}, {"DrawDRRect", { - {1, 112, 1, - [](DlOpReceiver& r) { r.drawDRRect(kTestRRect, kTestInnerRRect); }}, - {1, 112, 1, + {1, 104, 1, [](DlOpReceiver& r) { - r.drawDRRect(kTestRRect.makeOffset(5, 5), - kTestInnerRRect.makeOffset(4, 4)); + r.drawDiffRoundRect(kTestRRect, kTestInnerRRect); + }}, + {1, 104, 1, + [](DlOpReceiver& r) { + r.drawDiffRoundRect(kTestRRect.Shift(5, 5), + kTestInnerRRect.Shift(4, 4)); }}, }}, {"DrawPath", diff --git a/display_list/testing/dl_test_snippets.h b/display_list/testing/dl_test_snippets.h index 1057990baaa33..055d7725e3eb5 100644 --- a/display_list/testing/dl_test_snippets.h +++ b/display_list/testing/dl_test_snippets.h @@ -178,13 +178,17 @@ static const DlBlurMaskFilter kTestMaskFilter4(DlBlurStyle::kInner, 3.0); static const DlBlurMaskFilter kTestMaskFilter5(DlBlurStyle::kOuter, 3.0); constexpr DlRect kTestBounds = DlRect::MakeLTRB(10, 10, 50, 60); constexpr SkRect kTestSkBounds = SkRect::MakeLTRB(10, 10, 50, 60); -static const SkRRect kTestRRect = SkRRect::MakeRectXY(kTestSkBounds, 5, 5); +static const DlRoundRect kTestRRect = + DlRoundRect::MakeRectXY(kTestBounds, 5, 5); +static const SkRRect kTestSkRRect = SkRRect::MakeRectXY(kTestSkBounds, 5, 5); static const SkRRect kTestRRectRect = SkRRect::MakeRect(kTestSkBounds); -static const SkRRect kTestInnerRRect = +static const DlRoundRect kTestInnerRRect = + DlRoundRect::MakeRectXY(kTestBounds.Expand(-5, -5), 2, 2); +static const SkRRect kTestSkInnerRRect = SkRRect::MakeRectXY(kTestSkBounds.makeInset(5, 5), 2, 2); static const DlPath kTestPathRect = DlPath(SkPath::Rect(kTestSkBounds)); static const DlPath kTestPathOval = DlPath(SkPath::Oval(kTestSkBounds)); -static const DlPath kTestPathRRect = DlPath(SkPath::RRect(kTestRRect)); +static const DlPath kTestPathRRect = DlPath(SkPath::RRect(kTestSkRRect)); static const DlPath kTestPath1 = DlPath(SkPath::Polygon({{0, 0}, {10, 10}, {10, 0}, {0, 10}}, true)); static const DlPath kTestPath2 = diff --git a/display_list/utils/dl_matrix_clip_tracker.cc b/display_list/utils/dl_matrix_clip_tracker.cc index eda4ccdb30343..76d8fd4e64d95 100644 --- a/display_list/utils/dl_matrix_clip_tracker.cc +++ b/display_list/utils/dl_matrix_clip_tracker.cc @@ -92,11 +92,11 @@ void DisplayListMatrixClipState::clipOval(const DlRect& bounds, } } -void DisplayListMatrixClipState::clipRRect(const SkRRect& rrect, +void DisplayListMatrixClipState::clipRRect(const DlRoundRect& rrect, ClipOp op, bool is_aa) { - DlRect bounds = ToDlRect(rrect.getBounds()); - if (rrect.isRect()) { + DlRect bounds = rrect.GetBounds(); + if (rrect.IsRect()) { return clipRect(bounds, op, is_aa); } switch (op) { @@ -108,15 +108,14 @@ void DisplayListMatrixClipState::clipRRect(const SkRRect& rrect, cull_rect_ = DlRect(); return; } - auto upper_left = rrect.radii(SkRRect::kUpperLeft_Corner); - auto upper_right = rrect.radii(SkRRect::kUpperRight_Corner); - auto lower_left = rrect.radii(SkRRect::kLowerLeft_Corner); - auto lower_right = rrect.radii(SkRRect::kLowerRight_Corner); - DlRect safe = bounds.Expand(-std::max(upper_left.fX, lower_left.fX), 0, - -std::max(upper_right.fX, lower_right.fX), 0); + auto radii = rrect.GetRadii(); + DlRect safe = bounds.Expand( + -std::max(radii.top_left.width, radii.bottom_left.width), 0, + -std::max(radii.top_right.width, radii.bottom_right.width), 0); adjustCullRect(safe, op, is_aa); - safe = bounds.Expand(0, -std::max(upper_left.fY, upper_right.fY), // - 0, -std::max(lower_left.fY, lower_right.fY)); + safe = bounds.Expand( + 0, -std::max(radii.top_left.height, radii.top_right.height), // + 0, -std::max(radii.bottom_left.height, radii.bottom_right.height)); adjustCullRect(safe, op, is_aa); break; } @@ -300,46 +299,39 @@ bool DisplayListMatrixClipState::oval_covers_cull(const DlRect& bounds) const { } bool DisplayListMatrixClipState::rrect_covers_cull( - const SkRRect& content) const { - if (content.isEmpty()) { + const DlRoundRect& content) const { + if (content.IsEmpty()) { return false; } if (cull_rect_.IsEmpty()) { return true; } - if (content.isRect()) { - return rect_covers_cull(content.getBounds()); + if (content.IsRect()) { + return rect_covers_cull(content.GetBounds()); } - if (content.isOval()) { - return oval_covers_cull(content.getBounds()); + if (content.IsOval()) { + return oval_covers_cull(content.GetBounds()); } - if (!content.isSimple()) { + if (!content.GetRadii().AreAllCornersSame()) { return false; } DlPoint corners[4]; if (!getLocalCullCorners(corners)) { return false; } - auto outer = content.getBounds(); - DlScalar x_center = outer.centerX(); - DlScalar y_center = outer.centerY(); - auto radii = content.getSimpleRadii(); - DlScalar inner_x = outer.width() * 0.5f - radii.fX; - DlScalar inner_y = outer.height() * 0.5f - radii.fY; - DlScalar scale_x = 1.0 / radii.fX; - DlScalar scale_y = 1.0 / radii.fY; + auto outer = content.GetBounds(); + auto center = outer.GetCenter(); + auto radii = content.GetRadii().top_left; + auto inner = outer.GetSize() * 0.5 - radii; + auto scale = 1.0 / radii; for (auto corner : corners) { - if (!outer.contains(corner.x, corner.y)) { + if (!outer.Contains(corner)) { return false; } - DlScalar x_rel = std::abs(corner.x - x_center) - inner_x; - DlScalar y_rel = std::abs(corner.y - y_center) - inner_y; - if (x_rel > 0.0f && y_rel > 0.0f) { - x_rel *= scale_x; - y_rel *= scale_y; - if (x_rel * x_rel + y_rel * y_rel >= 1.0f) { - return false; - } + auto rel = (corner - center).Abs() - inner; + if (rel.x > 0.0f && rel.y > 0.0f && + (rel * scale).GetLengthSquared() >= 1.0f) { + return false; } } return true; diff --git a/display_list/utils/dl_matrix_clip_tracker.h b/display_list/utils/dl_matrix_clip_tracker.h index 6125cc0a68cd3..344a54bd57ff8 100644 --- a/display_list/utils/dl_matrix_clip_tracker.h +++ b/display_list/utils/dl_matrix_clip_tracker.h @@ -73,7 +73,10 @@ class DisplayListMatrixClipState { bool oval_covers_cull(const SkRect& content_bounds) const { return oval_covers_cull(ToDlRect(content_bounds)); } - bool rrect_covers_cull(const SkRRect& content) const; + bool rrect_covers_cull(const DlRoundRect& content) const; + bool rrect_covers_cull(const SkRRect& content) const { + return rrect_covers_cull(ToDlRoundRect(content)); + } bool content_culled(const DlRect& content_bounds) const; bool content_culled(const SkRect& content_bounds) const { @@ -155,7 +158,10 @@ class DisplayListMatrixClipState { 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 clipRRect(const DlRoundRect& rrect, ClipOp op, bool is_aa); + void clipRRect(const SkRRect& rrect, ClipOp op, bool is_aa) { + clipRRect(ToDlRoundRect(rrect), op, is_aa); + } void clipPath(const SkPath& path, ClipOp op, bool is_aa) { clipPath(DlPath(path), op, is_aa); } diff --git a/display_list/utils/dl_matrix_clip_tracker_unittests.cc b/display_list/utils/dl_matrix_clip_tracker_unittests.cc index 7a76a99a6213e..71f470a7a4f87 100644 --- a/display_list/utils/dl_matrix_clip_tracker_unittests.cc +++ b/display_list/utils/dl_matrix_clip_tracker_unittests.cc @@ -938,7 +938,7 @@ TEST(DisplayListMatrixClipState, RRectCoverage) { // Expanded by 2.0 and then with a corner of 2.0 obviously still covers EXPECT_TRUE(state.rrect_covers_cull(SkRRect::MakeRectXY(test, 2.0f, 2.0f))); // The corner point of the cull rect is at (c-2, c-2) relative to the - // corner of the rrect bounds so we compute its disance to the center + // corner of the rrect bounds so we compute its distance to the center // of the circular part and compare it to the radius of the corner (c) // to find the corner radius where it will start to leave the rounded // rectangle: diff --git a/display_list/utils/dl_receiver_utils.h b/display_list/utils/dl_receiver_utils.h index fccc04c6ffcee..40a7df5de4652 100644 --- a/display_list/utils/dl_receiver_utils.h +++ b/display_list/utils/dl_receiver_utils.h @@ -47,9 +47,9 @@ class IgnoreClipDispatchHelper : public virtual DlOpReceiver { void clipOval(const DlRect& bounds, DlCanvas::ClipOp clip_op, bool is_aa) override {} - void clipRRect(const SkRRect& rrect, - DlCanvas::ClipOp clip_op, - bool is_aa) override {} + void clipRoundRect(const DlRoundRect& rrect, + DlCanvas::ClipOp clip_op, + bool is_aa) override {} void clipPath(const DlPath& path, DlCanvas::ClipOp clip_op, bool is_aa) override {} @@ -95,8 +95,9 @@ class IgnoreDrawDispatchHelper : public virtual DlOpReceiver { void drawRect(const DlRect& rect) override {} void drawOval(const DlRect& bounds) override {} void drawCircle(const DlPoint& center, DlScalar radius) override {} - void drawRRect(const SkRRect& rrect) override {} - void drawDRRect(const SkRRect& outer, const SkRRect& inner) override {} + void drawRoundRect(const DlRoundRect& rrect) override {} + void drawDiffRoundRect(const DlRoundRect& outer, + const DlRoundRect& inner) override {} void drawPath(const DlPath& path) override {} void drawArc(const DlRect& oval_bounds, DlScalar start_degrees, diff --git a/impeller/display_list/canvas.cc b/impeller/display_list/canvas.cc index c582ff1eedba1..7c9a9ae1b6e3c 100644 --- a/impeller/display_list/canvas.cc +++ b/impeller/display_list/canvas.cc @@ -609,26 +609,28 @@ void Canvas::DrawOval(const Rect& rect, const Paint& paint) { AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint); } -void Canvas::DrawRRect(const Rect& rect, - const Size& corner_radii, - const Paint& paint) { - if (AttemptDrawBlurredRRect(rect, corner_radii, paint)) { - return; - } +void Canvas::DrawRoundRect(const RoundRect& round_rect, const Paint& paint) { + auto& rect = round_rect.GetBounds(); + auto& radii = round_rect.GetRadii(); + if (radii.AreAllCornersSame()) { + if (AttemptDrawBlurredRRect(rect, radii.top_left, paint)) { + return; + } - if (paint.style == Paint::Style::kFill) { - Entity entity; - entity.SetTransform(GetCurrentTransform()); - entity.SetBlendMode(paint.blend_mode); + if (paint.style == Paint::Style::kFill) { + Entity entity; + entity.SetTransform(GetCurrentTransform()); + entity.SetBlendMode(paint.blend_mode); - RoundRectGeometry geom(rect, corner_radii); - AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint); - return; + RoundRectGeometry geom(rect, radii.top_left); + AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint); + return; + } } auto path = PathBuilder{} .SetConvexity(Convexity::kConvex) - .AddRoundedRect(rect, corner_radii) + .AddRoundRect(round_rect) .SetBounds(rect) .TakePath(); DrawPath(path, paint); diff --git a/impeller/display_list/canvas.h b/impeller/display_list/canvas.h index 3f17646224720..0d72771bf3b1d 100644 --- a/impeller/display_list/canvas.h +++ b/impeller/display_list/canvas.h @@ -24,6 +24,7 @@ #include "impeller/geometry/matrix.h" #include "impeller/geometry/path.h" #include "impeller/geometry/point.h" +#include "impeller/geometry/round_rect.h" #include "impeller/geometry/vector.h" #include "impeller/renderer/snapshot.h" #include "impeller/typographer/text_frame.h" @@ -190,9 +191,7 @@ class Canvas { void DrawOval(const Rect& rect, const Paint& paint); - void DrawRRect(const Rect& rect, - const Size& corner_radii, - const Paint& paint); + void DrawRoundRect(const RoundRect& rect, const Paint& paint); void DrawCircle(const Point& center, Scalar radius, const Paint& paint); diff --git a/impeller/display_list/dl_dispatcher.cc b/impeller/display_list/dl_dispatcher.cc index 895a4eb103cad..804f344470237 100644 --- a/impeller/display_list/dl_dispatcher.cc +++ b/impeller/display_list/dl_dispatcher.cc @@ -448,27 +448,24 @@ void DlDispatcherBase::clipOval(const DlRect& bounds, } // |flutter::DlOpReceiver| -void DlDispatcherBase::clipRRect(const SkRRect& rrect, - ClipOp sk_op, - bool is_aa) { +void DlDispatcherBase::clipRoundRect(const DlRoundRect& rrect, + ClipOp sk_op, + bool is_aa) { AUTO_DEPTH_WATCHER(0u); auto clip_op = ToClipOperation(sk_op); - if (rrect.isRect()) { + if (rrect.IsRect()) { + GetCanvas().ClipGeometry(Geometry::MakeRect(rrect.GetBounds()), clip_op); + } else if (rrect.IsOval()) { + GetCanvas().ClipGeometry(Geometry::MakeOval(rrect.GetBounds()), clip_op); + } else if (rrect.GetRadii().AreAllCornersSame()) { GetCanvas().ClipGeometry( - Geometry::MakeRect(skia_conversions::ToRect(rrect.rect())), clip_op); - } else if (rrect.isOval()) { - GetCanvas().ClipGeometry( - Geometry::MakeOval(skia_conversions::ToRect(rrect.rect())), clip_op); - } else if (rrect.isSimple()) { - GetCanvas().ClipGeometry( - Geometry::MakeRoundRect( - skia_conversions::ToRect(rrect.rect()), - skia_conversions::ToSize(rrect.getSimpleRadii())), + Geometry::MakeRoundRect(rrect.GetBounds(), rrect.GetRadii().top_left), clip_op); } else { GetCanvas().ClipGeometry( - Geometry::MakeFillPath(skia_conversions::ToPath(rrect)), clip_op); + Geometry::MakeFillPath(PathBuilder{}.AddRoundRect(rrect).TakePath()), + clip_op); } } @@ -585,25 +582,20 @@ void DlDispatcherBase::drawCircle(const DlPoint& center, DlScalar radius) { } // |flutter::DlOpReceiver| -void DlDispatcherBase::drawRRect(const SkRRect& rrect) { +void DlDispatcherBase::drawRoundRect(const DlRoundRect& rrect) { AUTO_DEPTH_WATCHER(1u); - if (skia_conversions::IsNearlySimpleRRect(rrect)) { - GetCanvas().DrawRRect(skia_conversions::ToRect(rrect.rect()), - skia_conversions::ToSize(rrect.getSimpleRadii()), - paint_); - } else { - GetCanvas().DrawPath(skia_conversions::ToPath(rrect), paint_); - } + GetCanvas().DrawRoundRect(rrect, paint_); } // |flutter::DlOpReceiver| -void DlDispatcherBase::drawDRRect(const SkRRect& outer, const SkRRect& inner) { +void DlDispatcherBase::drawDiffRoundRect(const DlRoundRect& outer, + const DlRoundRect& inner) { AUTO_DEPTH_WATCHER(1u); PathBuilder builder; - builder.AddPath(skia_conversions::ToPath(outer)); - builder.AddPath(skia_conversions::ToPath(inner)); + builder.AddRoundRect(outer); + builder.AddRoundRect(inner); GetCanvas().DrawPath(builder.TakePath(FillType::kOdd), paint_); } @@ -628,8 +620,7 @@ void DlDispatcherBase::SimplifyOrDrawPath(Canvas& canvas, SkRRect rrect; if (path.IsSkRRect(&rrect) && rrect.isSimple()) { - canvas.DrawRRect(skia_conversions::ToRect(rrect.rect()), - skia_conversions::ToSize(rrect.getSimpleRadii()), paint); + canvas.DrawRoundRect(flutter::ToDlRoundRect(rrect), paint); return; } diff --git a/impeller/display_list/dl_dispatcher.h b/impeller/display_list/dl_dispatcher.h index 8302a1f6b436b..b8ee05f2fa8a6 100644 --- a/impeller/display_list/dl_dispatcher.h +++ b/impeller/display_list/dl_dispatcher.h @@ -24,6 +24,7 @@ using DlScalar = flutter::DlScalar; using DlPoint = flutter::DlPoint; using DlRect = flutter::DlRect; using DlIRect = flutter::DlIRect; +using DlRoundRect = flutter::DlRoundRect; using DlPath = flutter::DlPath; class DlDispatcherBase : public flutter::DlOpReceiver { @@ -129,7 +130,9 @@ class DlDispatcherBase : public flutter::DlOpReceiver { void clipOval(const DlRect& bounds, ClipOp clip_op, bool is_aa) override; // |flutter::DlOpReceiver| - void clipRRect(const SkRRect& rrect, ClipOp clip_op, bool is_aa) override; + void clipRoundRect(const DlRoundRect& rrect, + ClipOp clip_op, + bool is_aa) override; // |flutter::DlOpReceiver| void clipPath(const DlPath& path, ClipOp clip_op, bool is_aa) override; @@ -159,10 +162,11 @@ class DlDispatcherBase : public flutter::DlOpReceiver { void drawCircle(const DlPoint& center, DlScalar radius) override; // |flutter::DlOpReceiver| - void drawRRect(const SkRRect& rrect) override; + void drawRoundRect(const DlRoundRect& rrect) override; // |flutter::DlOpReceiver| - void drawDRRect(const SkRRect& outer, const SkRRect& inner) override; + void drawDiffRoundRect(const DlRoundRect& outer, + const DlRoundRect& inner) override; // |flutter::DlOpReceiver| void drawPath(const DlPath& path) override; diff --git a/impeller/display_list/skia_conversions.cc b/impeller/display_list/skia_conversions.cc index 0171165ba765d..9d15c3ffb123a 100644 --- a/impeller/display_list/skia_conversions.cc +++ b/impeller/display_list/skia_conversions.cc @@ -77,24 +77,6 @@ std::vector ToPoints(const flutter::DlPoint points[], int count) { return result; } -PathBuilder::RoundingRadii ToRoundingRadii(const SkRRect& rrect) { - using Corner = SkRRect::Corner; - PathBuilder::RoundingRadii radii; - radii.bottom_left = ToPoint(rrect.radii(Corner::kLowerLeft_Corner)); - radii.bottom_right = ToPoint(rrect.radii(Corner::kLowerRight_Corner)); - radii.top_left = ToPoint(rrect.radii(Corner::kUpperLeft_Corner)); - radii.top_right = ToPoint(rrect.radii(Corner::kUpperRight_Corner)); - return radii; -} - -Path ToPath(const SkRRect& rrect) { - return PathBuilder{} - .AddRoundedRect(ToRect(rrect.getBounds()), ToRoundingRadii(rrect)) - .SetConvexity(Convexity::kConvex) - .SetBounds(ToRect(rrect.getBounds())) - .TakePath(); -} - Point ToPoint(const SkPoint& point) { return Point::MakeXY(point.fX, point.fY); } diff --git a/impeller/display_list/skia_conversions.h b/impeller/display_list/skia_conversions.h index e29f1146fab97..dce1a98f4ebfd 100644 --- a/impeller/display_list/skia_conversions.h +++ b/impeller/display_list/skia_conversions.h @@ -50,10 +50,6 @@ Color ToColor(const flutter::DlColor& color); Matrix ToRSXForm(const SkRSXform& form); -PathBuilder::RoundingRadii ToRoundingRadii(const SkRRect& rrect); - -Path ToPath(const SkRRect& rrect); - std::optional ToPixelFormat(SkColorType type); impeller::SamplerDescriptor ToSamplerDescriptor( diff --git a/impeller/geometry/BUILD.gn b/impeller/geometry/BUILD.gn index e9bfdad69d5f5..5c1d71f4e0005 100644 --- a/impeller/geometry/BUILD.gn +++ b/impeller/geometry/BUILD.gn @@ -29,6 +29,8 @@ impeller_component("geometry") { "quaternion.h", "rect.cc", "rect.h", + "round_rect.cc", + "round_rect.h", "saturated_math.h", "scalar.h", "separated_vector.cc", @@ -71,6 +73,7 @@ impeller_component("geometry_unittests") { "matrix_unittests.cc", "path_unittests.cc", "rect_unittests.cc", + "round_rect_unittests.cc", "saturated_math_unittests.cc", "size_unittests.cc", "trig_unittests.cc", diff --git a/impeller/geometry/geometry_benchmarks.cc b/impeller/geometry/geometry_benchmarks.cc index de14f0b1fe3c6..1df17cffd69ba 100644 --- a/impeller/geometry/geometry_benchmarks.cc +++ b/impeller/geometry/geometry_benchmarks.cc @@ -147,7 +147,8 @@ namespace { Path CreateRRect() { return PathBuilder{} - .AddRoundedRect(Rect::MakeLTRB(0, 0, 400, 400), 16) + .AddRoundRect( + RoundRect::MakeRectXY(Rect::MakeLTRB(0, 0, 400, 400), 16, 16)) .TakePath(); } diff --git a/impeller/geometry/path_builder.cc b/impeller/geometry/path_builder.cc index 91303550511d7..6d49fca61d4b0 100644 --- a/impeller/geometry/path_builder.cc +++ b/impeller/geometry/path_builder.cc @@ -139,35 +139,26 @@ PathBuilder& PathBuilder::AddCircle(const Point& c, Scalar r) { return AddOval(Rect::MakeXYWH(c.x - r, c.y - r, 2.0f * r, 2.0f * r)); } -PathBuilder& PathBuilder::AddRoundedRect(Rect rect, Scalar radius) { - return radius <= 0.0 ? AddRect(rect) - : AddRoundedRect(rect, RoundingRadii(radius)); -} - -PathBuilder& PathBuilder::AddRoundedRect(Rect rect, Size radii) { - return radii.width <= 0 || radii.height <= 0 - ? AddRect(rect) - : AddRoundedRect(rect, RoundingRadii(radii)); -} - -PathBuilder& PathBuilder::AddRoundedRect(Rect rect, RoundingRadii radii) { - if (radii.AreAllZero()) { +PathBuilder& PathBuilder::AddRoundRect(RoundRect round_rect) { + auto rect = round_rect.GetBounds(); + auto radii = round_rect.GetRadii(); + if (radii.AreAllCornersEmpty()) { return AddRect(rect); } auto rect_origin = rect.GetOrigin(); auto rect_size = rect.GetSize(); - current_ = rect_origin + Point{radii.top_left.x, 0.0}; + current_ = rect_origin + Point{radii.top_left.width, 0.0}; - MoveTo({rect_origin.x + radii.top_left.x, rect_origin.y}); + MoveTo({rect_origin.x + radii.top_left.width, rect_origin.y}); //---------------------------------------------------------------------------- // Top line. // AddLinearComponentIfNeeded( - {rect_origin.x + radii.top_left.x, rect_origin.y}, - {rect_origin.x + rect_size.width - radii.top_right.x, rect_origin.y}); + {rect_origin.x + radii.top_left.width, rect_origin.y}, + {rect_origin.x + rect_size.width - radii.top_right.width, rect_origin.y}); //---------------------------------------------------------------------------- // Top right arc. @@ -178,9 +169,9 @@ PathBuilder& PathBuilder::AddRoundedRect(Rect rect, RoundingRadii radii) { // Right line. // AddLinearComponentIfNeeded( - {rect_origin.x + rect_size.width, rect_origin.y + radii.top_right.y}, + {rect_origin.x + rect_size.width, rect_origin.y + radii.top_right.height}, {rect_origin.x + rect_size.width, - rect_origin.y + rect_size.height - radii.bottom_right.y}); + rect_origin.y + rect_size.height - radii.bottom_right.height}); //---------------------------------------------------------------------------- // Bottom right arc. @@ -191,9 +182,10 @@ PathBuilder& PathBuilder::AddRoundedRect(Rect rect, RoundingRadii radii) { // Bottom line. // AddLinearComponentIfNeeded( - {rect_origin.x + rect_size.width - radii.bottom_right.x, + {rect_origin.x + rect_size.width - radii.bottom_right.width, rect_origin.y + rect_size.height}, - {rect_origin.x + radii.bottom_left.x, rect_origin.y + rect_size.height}); + {rect_origin.x + radii.bottom_left.width, + rect_origin.y + rect_size.height}); //---------------------------------------------------------------------------- // Bottom left arc. @@ -204,8 +196,9 @@ PathBuilder& PathBuilder::AddRoundedRect(Rect rect, RoundingRadii radii) { // Left line. // AddLinearComponentIfNeeded( - {rect_origin.x, rect_origin.y + rect_size.height - radii.bottom_left.y}, - {rect_origin.x, rect_origin.y + radii.top_left.y}); + {rect_origin.x, + rect_origin.y + rect_size.height - radii.bottom_left.height}, + {rect_origin.x, rect_origin.y + radii.top_left.height}); //---------------------------------------------------------------------------- // Top left arc. @@ -221,10 +214,11 @@ PathBuilder& PathBuilder::AddRoundedRectTopLeft(Rect rect, RoundingRadii radii) { const auto magic_top_left = radii.top_left * kArcApproximationMagic; const auto corner = rect.GetOrigin(); - AddCubicComponent({corner.x, corner.y + radii.top_left.y}, - {corner.x, corner.y + radii.top_left.y - magic_top_left.y}, - {corner.x + radii.top_left.x - magic_top_left.x, corner.y}, - {corner.x + radii.top_left.x, corner.y}); + AddCubicComponent( + {corner.x, corner.y + radii.top_left.height}, + {corner.x, corner.y + radii.top_left.height - magic_top_left.height}, + {corner.x + radii.top_left.width - magic_top_left.width, corner.y}, + {corner.x + radii.top_left.width, corner.y}); return *this; } @@ -233,10 +227,10 @@ PathBuilder& PathBuilder::AddRoundedRectTopRight(Rect rect, const auto magic_top_right = radii.top_right * kArcApproximationMagic; const auto corner = rect.GetOrigin() + Point{rect.GetWidth(), 0}; AddCubicComponent( - {corner.x - radii.top_right.x, corner.y}, - {corner.x - radii.top_right.x + magic_top_right.x, corner.y}, - {corner.x, corner.y + radii.top_right.y - magic_top_right.y}, - {corner.x, corner.y + radii.top_right.y}); + {corner.x - radii.top_right.width, corner.y}, + {corner.x - radii.top_right.width + magic_top_right.width, corner.y}, + {corner.x, corner.y + radii.top_right.height - magic_top_right.height}, + {corner.x, corner.y + radii.top_right.height}); return *this; } @@ -245,10 +239,12 @@ PathBuilder& PathBuilder::AddRoundedRectBottomRight(Rect rect, const auto magic_bottom_right = radii.bottom_right * kArcApproximationMagic; const auto corner = rect.GetOrigin() + rect.GetSize(); AddCubicComponent( - {corner.x, corner.y - radii.bottom_right.y}, - {corner.x, corner.y - radii.bottom_right.y + magic_bottom_right.y}, - {corner.x - radii.bottom_right.x + magic_bottom_right.x, corner.y}, - {corner.x - radii.bottom_right.x, corner.y}); + {corner.x, corner.y - radii.bottom_right.height}, + {corner.x, + corner.y - radii.bottom_right.height + magic_bottom_right.height}, + {corner.x - radii.bottom_right.width + magic_bottom_right.width, + corner.y}, + {corner.x - radii.bottom_right.width, corner.y}); return *this; } @@ -257,10 +253,11 @@ PathBuilder& PathBuilder::AddRoundedRectBottomLeft(Rect rect, const auto magic_bottom_left = radii.bottom_left * kArcApproximationMagic; const auto corner = rect.GetOrigin() + Point{0, rect.GetHeight()}; AddCubicComponent( - {corner.x + radii.bottom_left.x, corner.y}, - {corner.x + radii.bottom_left.x - magic_bottom_left.x, corner.y}, - {corner.x, corner.y - radii.bottom_left.y + magic_bottom_left.y}, - {corner.x, corner.y - radii.bottom_left.y}); + {corner.x + radii.bottom_left.width, corner.y}, + {corner.x + radii.bottom_left.width - magic_bottom_left.width, corner.y}, + {corner.x, + corner.y - radii.bottom_left.height + magic_bottom_left.height}, + {corner.x, corner.y - radii.bottom_left.height}); return *this; } diff --git a/impeller/geometry/path_builder.h b/impeller/geometry/path_builder.h index 4e57e1d7d8700..abd899d2368c4 100644 --- a/impeller/geometry/path_builder.h +++ b/impeller/geometry/path_builder.h @@ -7,6 +7,7 @@ #include "impeller/geometry/path.h" #include "impeller/geometry/rect.h" +#include "impeller/geometry/round_rect.h" #include "impeller/geometry/scalar.h" namespace impeller { @@ -102,54 +103,7 @@ class PathBuilder { /// recomputing these bounds. PathBuilder& SetBounds(Rect bounds); - struct RoundingRadii { - Point top_left; - Point bottom_left; - Point top_right; - Point bottom_right; - - RoundingRadii() = default; - - RoundingRadii(Scalar p_top_left, - Scalar p_bottom_left, - Scalar p_top_right, - Scalar p_bottom_right) - : top_left(p_top_left, p_top_left), - bottom_left(p_bottom_left, p_bottom_left), - top_right(p_top_right, p_top_right), - bottom_right(p_bottom_right, p_bottom_right) {} - - explicit RoundingRadii(Scalar radius) - : top_left(radius, radius), - bottom_left(radius, radius), - top_right(radius, radius), - bottom_right(radius, radius) {} - - explicit RoundingRadii(Point radii) - : top_left(radii), - bottom_left(radii), - top_right(radii), - bottom_right(radii) {} - - explicit RoundingRadii(Size radii) - : top_left(radii), - bottom_left(radii), - top_right(radii), - bottom_right(radii) {} - - bool AreAllZero() const { - return top_left.IsZero() && // - bottom_left.IsZero() && // - top_right.IsZero() && // - bottom_right.IsZero(); - } - }; - - PathBuilder& AddRoundedRect(Rect rect, RoundingRadii radii); - - PathBuilder& AddRoundedRect(Rect rect, Size radii); - - PathBuilder& AddRoundedRect(Rect rect, Scalar radius); + PathBuilder& AddRoundRect(RoundRect rect); PathBuilder& AddPath(const Path& path); diff --git a/impeller/geometry/path_unittests.cc b/impeller/geometry/path_unittests.cc index f37689f5e8cf3..2f31c4bdc14fb 100644 --- a/impeller/geometry/path_unittests.cc +++ b/impeller/geometry/path_unittests.cc @@ -79,7 +79,8 @@ TEST(PathTest, PathBuilderSetsCorrectContourPropertiesForAddCommands) { { Path path = PathBuilder{} - .AddRoundedRect(Rect::MakeXYWH(100, 100, 100, 100), 10) + .AddRoundRect(RoundRect::MakeRectRadius( + Rect::MakeXYWH(100, 100, 100, 100), 10)) .TakePath(); ContourComponent contour; path.GetContourComponentAtIndex(0, contour); @@ -88,10 +89,10 @@ TEST(PathTest, PathBuilderSetsCorrectContourPropertiesForAddCommands) { } { - Path path = - PathBuilder{} - .AddRoundedRect(Rect::MakeXYWH(100, 100, 100, 100), Size(10, 20)) - .TakePath(); + Path path = PathBuilder{} + .AddRoundRect(RoundRect::MakeRectXY( + Rect::MakeXYWH(100, 100, 100, 100), Size(10, 20))) + .TakePath(); ContourComponent contour; path.GetContourComponentAtIndex(0, contour); EXPECT_POINT_NEAR(contour.destination, Point(110, 100)); @@ -357,7 +358,8 @@ TEST(PathTest, BoundingBoxCubic) { TEST(PathTest, BoundingBoxOfCompositePathIsCorrect) { PathBuilder builder; - builder.AddRoundedRect(Rect::MakeXYWH(10, 10, 300, 300), {50, 50, 50, 50}); + builder.AddRoundRect( + RoundRect::MakeRectRadius(Rect::MakeXYWH(10, 10, 300, 300), 50)); auto path = builder.TakePath(); auto actual = path.GetBoundingBox(); Rect expected = Rect::MakeXYWH(10, 10, 300, 300); diff --git a/impeller/geometry/round_rect.cc b/impeller/geometry/round_rect.cc new file mode 100644 index 0000000000000..6b33d376d83ce --- /dev/null +++ b/impeller/geometry/round_rect.cc @@ -0,0 +1,136 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/impeller/geometry/round_rect.h" + +namespace impeller { + +static inline void NormalizeEmptyToZero(Size& radii) { + if (radii.IsEmpty()) { + radii = Size(); + } +} + +static inline void AdjustScale(Scalar& radius1, + Scalar& radius2, + Scalar dimension, + Scalar& scale) { + FML_DCHECK(radius1 >= 0.0f && radius2 >= 0.0f); + FML_DCHECK(dimension > 0.0f); + if (radius1 + radius2 > dimension) { + scale = std::min(scale, dimension / (radius1 + radius2)); + } +} + +RoundRect RoundRect::MakeRectRadii(const Rect& bounds, + const RoundingRadii& in_radii) { + if (bounds.IsEmpty() || !bounds.IsFinite() || // + in_radii.AreAllCornersEmpty() || !in_radii.IsFinite()) { + // preserve the empty bounds as they might be strokable + return RoundRect(bounds, RoundingRadii()); + } + + // Copy the incoming radii so that we can work on normalizing them to the + // particular rectangle they are paired with without disturbing the caller. + RoundingRadii radii = in_radii; + + // If any corner is flat or has a negative value, normalize it to zeros + // We do this first so that the unnecessary non-flat part of that radius + // does not contribute to the global scaling below. + NormalizeEmptyToZero(radii.top_left); + NormalizeEmptyToZero(radii.top_right); + NormalizeEmptyToZero(radii.bottom_left); + NormalizeEmptyToZero(radii.bottom_right); + + // Now determine a global scale to apply to all of the radii to ensure + // that none of the adjacent pairs of radius values sum to larger than + // the corresponding dimension of the rectangle. + Size size = bounds.GetSize(); + Scalar scale = 1.0f; + // clang-format off + AdjustScale(radii.top_left.width, radii.top_right.width, size.width, + scale); + AdjustScale(radii.bottom_left.width, radii.bottom_right.width, size.width, + scale); + AdjustScale(radii.top_left.height, radii.bottom_left.height, size.height, + scale); + AdjustScale(radii.top_right.height, radii.bottom_right.height, size.height, + scale); + // clang-format on + if (scale < 1.0f) { + radii = radii * scale; + } + + return RoundRect(bounds, radii); +} + +// Determine if p is inside the elliptical corner curve defined by the +// indicated corner point and the indicated radii. +// p - is the test point in absolute coordinates +// corner - is the location of the associated corner in absolute coordinates +// direction - is the sign of (corner - center), or the sign of coordinates +// as they move in the direction of the corner from inside the +// rect ((-1,-1) for the upper left corner for instance) +// radii - the non-negative X and Y size of the corner's radii. +static bool CornerContains(const Point& p, + const Point& corner, + const Point& direction, + const Size& radii) { + FML_DCHECK(radii.width >= 0.0f && radii.height >= 0.0f); + if (radii.IsEmpty()) { + // This corner is not curved, therefore the containment is the same as + // the previously checked bounds containment. + return true; + } + + // The positive X,Y distance between the corner and the point. + Point corner_relative = (corner - p) * direction; + + // The distance from the "center" of the corner's elliptical curve. + // If both numbers are positive then we need to do an elliptical distance + // check to determine if it is inside the curve. + // If either number is negative, then the point is outside this quadrant + // and is governed by inclusion in the bounds and inclusion within other + // corners of this round rect. In that case, we return true here to allow + // further evaluation within other quadrants. + Point quadrant_relative = radii - corner_relative; + if (quadrant_relative.x <= 0.0f || quadrant_relative.y <= 0.0f) { + // Not within the curved quadrant of this corner, therefore "inside" + // relative to this one corner. + return true; + } + + // Dividing the quadrant_relative point by the radii gives a corresponding + // location within a unit circle which can be more easily tested for + // containment. We can use x^2 + y^2 and compare it against the radius + // squared (1.0) to avoid the sqrt. + Point quadrant_unit_circle_point = quadrant_relative / radii; + return quadrant_unit_circle_point.GetLengthSquared() <= 1.0; +} + +// The sign of the direction that points move as they approach the indicated +// corner from within the rectangle. +static constexpr Point kUpperLeftDirection(-1.0f, -1.0f); +static constexpr Point kUpperRightDirection(1.0f, -1.0f); +static constexpr Point kLowerLeftDirection(-1.0f, 1.0f); +static constexpr Point kLowerRightDirection(1.0f, 1.0f); + +[[nodiscard]] bool RoundRect::Contains(const Point& p) const { + if (!bounds_.Contains(p)) { + return false; + } + if (!CornerContains(p, bounds_.GetLeftTop(), kUpperLeftDirection, + radii_.top_left) || + !CornerContains(p, bounds_.GetRightTop(), kUpperRightDirection, + radii_.top_right) || + !CornerContains(p, bounds_.GetLeftBottom(), kLowerLeftDirection, + radii_.bottom_left) || + !CornerContains(p, bounds_.GetRightBottom(), kLowerRightDirection, + radii_.bottom_right)) { + return false; + } + return true; +} + +} // namespace impeller diff --git a/impeller/geometry/round_rect.h b/impeller/geometry/round_rect.h new file mode 100644 index 0000000000000..3e4e7742d0bb1 --- /dev/null +++ b/impeller/geometry/round_rect.h @@ -0,0 +1,215 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_IMPELLER_GEOMETRY_ROUND_RECT_H_ +#define FLUTTER_IMPELLER_GEOMETRY_ROUND_RECT_H_ + +#include "flutter/impeller/geometry/point.h" +#include "flutter/impeller/geometry/rect.h" +#include "flutter/impeller/geometry/size.h" + +namespace impeller { + +struct RoundingRadii { + Size top_left; + Size top_right; + Size bottom_left; + Size bottom_right; + + constexpr static RoundingRadii MakeRadius(Scalar radius) { + return {Size(radius), Size(radius), Size(radius), Size(radius)}; + } + + constexpr static RoundingRadii MakeRadii(Size radii) { + return {radii, radii, radii, radii}; + } + + constexpr bool IsFinite() const { + return top_left.IsFinite() && // + top_right.IsFinite() && // + bottom_left.IsFinite() && // + bottom_right.IsFinite(); + } + + constexpr bool AreAllCornersEmpty() const { + return top_left.IsEmpty() && // + top_right.IsEmpty() && // + bottom_left.IsEmpty() && // + bottom_right.IsEmpty(); + } + + constexpr bool AreAllCornersSame(Scalar tolerance = kEhCloseEnough) const { + return ScalarNearlyEqual(top_left.width, top_right.width, tolerance) && + ScalarNearlyEqual(top_left.width, bottom_right.width, tolerance) && + ScalarNearlyEqual(top_left.width, bottom_left.width, tolerance) && + ScalarNearlyEqual(top_left.height, top_right.height, tolerance) && + ScalarNearlyEqual(top_left.height, bottom_right.height, tolerance) && + ScalarNearlyEqual(top_left.height, bottom_left.height, tolerance); + } + + constexpr inline RoundingRadii operator*(Scalar scale) { + return { + .top_left = top_left * scale, + .top_right = top_right * scale, + .bottom_left = bottom_left * scale, + .bottom_right = bottom_right * scale, + }; + } + + [[nodiscard]] constexpr bool operator==(const RoundingRadii& rr) const { + return top_left == rr.top_left && // + top_right == rr.top_right && // + bottom_left == rr.bottom_left && // + bottom_right == rr.bottom_right; + } + + [[nodiscard]] constexpr bool operator!=(const RoundingRadii& rr) const { + return !(*this == rr); + } +}; + +struct RoundRect { + RoundRect() = default; + + constexpr static RoundRect MakeRect(const Rect& rect) { + return MakeRectRadii(rect, RoundingRadii()); + } + + constexpr static RoundRect MakeOval(const Rect& rect) { + return MakeRectRadii(rect, RoundingRadii::MakeRadii(rect.GetSize() * 0.5f)); + } + + constexpr static RoundRect MakeRectRadius(const Rect& rect, Scalar radius) { + return MakeRectRadii(rect, RoundingRadii::MakeRadius(radius)); + } + + constexpr static RoundRect MakeRectXY(const Rect& rect, + Scalar x_radius, + Scalar y_radius) { + return MakeRectRadii(rect, + RoundingRadii::MakeRadii(Size(x_radius, y_radius))); + } + + constexpr static RoundRect MakeRectXY(const Rect& rect, Size corner_radii) { + return MakeRectRadii(rect, RoundingRadii::MakeRadii(corner_radii)); + } + + static RoundRect MakeRectRadii(const Rect& rect, const RoundingRadii& radii); + + constexpr const Rect& GetBounds() const { return bounds_; } + constexpr const RoundingRadii& GetRadii() const { return radii_; } + + [[nodiscard]] constexpr bool IsFinite() const { + return bounds_.IsFinite() && // + radii_.top_left.IsFinite() && // + radii_.top_right.IsFinite() && // + radii_.bottom_left.IsFinite() && // + radii_.bottom_right.IsFinite(); + } + + [[nodiscard]] constexpr bool IsEmpty() const { return bounds_.IsEmpty(); } + + [[nodiscard]] constexpr bool IsRect() const { + return !bounds_.IsEmpty() && radii_.AreAllCornersEmpty(); + } + + [[nodiscard]] constexpr bool IsOval() const { + return !bounds_.IsEmpty() && radii_.AreAllCornersSame() && + ScalarNearlyEqual(radii_.top_left.width, + bounds_.GetWidth() * 0.5f) && + ScalarNearlyEqual(radii_.top_left.height, + bounds_.GetHeight() * 0.5f); + } + + /// @brief Returns true iff the provided point |p| is inside the + /// half-open interior of this rectangle. + /// + /// For purposes of containment, a rectangle contains points + /// along the top and left edges but not points along the + /// right and bottom edges so that a point is only ever + /// considered inside one of two abutting rectangles. + [[nodiscard]] bool Contains(const Point& p) const; + + /// @brief Returns a new round rectangle translated by the given offset. + [[nodiscard]] constexpr RoundRect Shift(Scalar dx, Scalar dy) const { + // Just in case, use the factory rather than the internal constructor + // as shifting the rectangle may increase/decrease its bit precision + // so we should re-validate the radii to the newly located rectangle. + return MakeRectRadii(bounds_.Shift(dx, dy), radii_); + } + + /// @brief Returns a round rectangle with expanded edges. Negative expansion + /// results in shrinking. + [[nodiscard]] constexpr RoundRect Expand(Scalar left, + Scalar top, + Scalar right, + Scalar bottom) const { + // Use the factory rather than the internal constructor as the changing + // size of the rectangle requires that we re-validate the radii to the + // newly sized rectangle. + return MakeRectRadii(bounds_.Expand(left, top, right, bottom), radii_); + } + + /// @brief Returns a round rectangle with expanded edges. Negative expansion + /// results in shrinking. + [[nodiscard]] constexpr RoundRect Expand(Scalar horizontal, + Scalar vertical) const { + // Use the factory rather than the internal constructor as the changing + // size of the rectangle requires that we re-validate the radii to the + // newly sized rectangle. + return MakeRectRadii(bounds_.Expand(horizontal, vertical), radii_); + } + + /// @brief Returns a round rectangle with expanded edges. Negative expansion + /// results in shrinking. + [[nodiscard]] constexpr RoundRect Expand(Scalar amount) const { + // Use the factory rather than the internal constructor as the changing + // size of the rectangle requires that we re-validate the radii to the + // newly sized rectangle. + return MakeRectRadii(bounds_.Expand(amount), radii_); + } + + [[nodiscard]] constexpr bool operator==(const RoundRect& rr) const { + return bounds_ == rr.bounds_ && radii_ == rr.radii_; + } + + [[nodiscard]] constexpr bool operator!=(const RoundRect& r) const { + return !(*this == r); + } + + private: + constexpr RoundRect(const Rect& bounds, const RoundingRadii& radii) + : bounds_(bounds), radii_(radii) {} + + const Rect bounds_; + const RoundingRadii radii_; +}; + +} // namespace impeller + +namespace std { + +inline std::ostream& operator<<(std::ostream& out, + const impeller::RoundingRadii& rr) { + out << "(" // + << "ul: " << rr.top_left << ", " // + << "ur: " << rr.top_right << ", " // + << "ll: " << rr.bottom_left << ", " // + << "lr: " << rr.bottom_right // + << ")"; + return out; +} + +inline std::ostream& operator<<(std::ostream& out, + const impeller::RoundRect& rr) { + out << "(" // + << "rect: " << rr.GetBounds() << ", " // + << "radii: " << rr.GetRadii() // + << ")"; + return out; +} + +} // namespace std + +#endif // FLUTTER_IMPELLER_GEOMETRY_ROUND_RECT_H_ diff --git a/impeller/geometry/round_rect_unittests.cc b/impeller/geometry/round_rect_unittests.cc new file mode 100644 index 0000000000000..ca6d736063ee6 --- /dev/null +++ b/impeller/geometry/round_rect_unittests.cc @@ -0,0 +1,954 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "gtest/gtest.h" + +#include "flutter/impeller/geometry/round_rect.h" + +#include "flutter/impeller/geometry/geometry_asserts.h" + +namespace impeller { +namespace testing { + +TEST(RoundRectTest, RoundingRadiiEmptyDeclaration) { + RoundingRadii radii; + + EXPECT_TRUE(radii.AreAllCornersEmpty()); + EXPECT_TRUE(radii.AreAllCornersSame()); + EXPECT_TRUE(radii.IsFinite()); + EXPECT_EQ(radii.top_left, Size()); + EXPECT_EQ(radii.top_right, Size()); + EXPECT_EQ(radii.bottom_left, Size()); + EXPECT_EQ(radii.bottom_right, Size()); + EXPECT_EQ(radii.top_left.width, 0.0f); + EXPECT_EQ(radii.top_left.height, 0.0f); + EXPECT_EQ(radii.top_right.width, 0.0f); + EXPECT_EQ(radii.top_right.height, 0.0f); + EXPECT_EQ(radii.bottom_left.width, 0.0f); + EXPECT_EQ(radii.bottom_left.height, 0.0f); + EXPECT_EQ(radii.bottom_right.width, 0.0f); + EXPECT_EQ(radii.bottom_right.height, 0.0f); +} + +TEST(RoundRectTest, RoundingRadiiDefaultConstructor) { + RoundingRadii radii = RoundingRadii(); + + EXPECT_TRUE(radii.AreAllCornersEmpty()); + EXPECT_TRUE(radii.AreAllCornersSame()); + EXPECT_TRUE(radii.IsFinite()); + EXPECT_EQ(radii.top_left, Size()); + EXPECT_EQ(radii.top_right, Size()); + EXPECT_EQ(radii.bottom_left, Size()); + EXPECT_EQ(radii.bottom_right, Size()); +} + +TEST(RoundRectTest, RoundingRadiiScalarConstructor) { + RoundingRadii radii = RoundingRadii::MakeRadius(5.0f); + + EXPECT_FALSE(radii.AreAllCornersEmpty()); + EXPECT_TRUE(radii.AreAllCornersSame()); + EXPECT_TRUE(radii.IsFinite()); + EXPECT_EQ(radii.top_left, Size(5.0f, 5.0f)); + EXPECT_EQ(radii.top_right, Size(5.0f, 5.0f)); + EXPECT_EQ(radii.bottom_left, Size(5.0f, 5.0f)); + EXPECT_EQ(radii.bottom_right, Size(5.0f, 5.0f)); +} + +TEST(RoundRectTest, RoundingRadiiEmptyScalarConstructor) { + RoundingRadii radii = RoundingRadii::MakeRadius(-5.0f); + + EXPECT_TRUE(radii.AreAllCornersEmpty()); + EXPECT_TRUE(radii.AreAllCornersSame()); + EXPECT_TRUE(radii.IsFinite()); + EXPECT_EQ(radii.top_left, Size(-5.0f, -5.0f)); + EXPECT_EQ(radii.top_right, Size(-5.0f, -5.0f)); + EXPECT_EQ(radii.bottom_left, Size(-5.0f, -5.0f)); + EXPECT_EQ(radii.bottom_right, Size(-5.0f, -5.0f)); +} + +TEST(RoundRectTest, RoundingRadiiSizeConstructor) { + RoundingRadii radii = RoundingRadii::MakeRadii(Size(5.0f, 6.0f)); + + EXPECT_FALSE(radii.AreAllCornersEmpty()); + EXPECT_TRUE(radii.AreAllCornersSame()); + EXPECT_TRUE(radii.IsFinite()); + EXPECT_EQ(radii.top_left, Size(5.0f, 6.0f)); + EXPECT_EQ(radii.top_right, Size(5.0f, 6.0f)); + EXPECT_EQ(radii.bottom_left, Size(5.0f, 6.0f)); + EXPECT_EQ(radii.bottom_right, Size(5.0f, 6.0f)); +} + +TEST(RoundRectTest, RoundingRadiiEmptySizeConstructor) { + { + RoundingRadii radii = RoundingRadii::MakeRadii(Size(-5.0f, 6.0f)); + + EXPECT_TRUE(radii.AreAllCornersEmpty()); + EXPECT_TRUE(radii.AreAllCornersSame()); + EXPECT_TRUE(radii.IsFinite()); + EXPECT_EQ(radii.top_left, Size(-5.0f, 6.0f)); + EXPECT_EQ(radii.top_right, Size(-5.0f, 6.0f)); + EXPECT_EQ(radii.bottom_left, Size(-5.0f, 6.0f)); + EXPECT_EQ(radii.bottom_right, Size(-5.0f, 6.0f)); + } + + { + RoundingRadii radii = RoundingRadii::MakeRadii(Size(5.0f, -6.0f)); + + EXPECT_TRUE(radii.AreAllCornersEmpty()); + EXPECT_TRUE(radii.AreAllCornersSame()); + EXPECT_TRUE(radii.IsFinite()); + EXPECT_EQ(radii.top_left, Size(5.0f, -6.0f)); + EXPECT_EQ(radii.top_right, Size(5.0f, -6.0f)); + EXPECT_EQ(radii.bottom_left, Size(5.0f, -6.0f)); + EXPECT_EQ(radii.bottom_right, Size(5.0f, -6.0f)); + } +} + +TEST(RoundRectTest, RoundingRadiiNamedSizesConstructor) { + RoundingRadii radii = { + .top_left = Size(5.0f, 5.5f), + .top_right = Size(6.0f, 6.5f), + .bottom_left = Size(7.0f, 7.5f), + .bottom_right = Size(8.0f, 8.5f), + }; + + EXPECT_FALSE(radii.AreAllCornersEmpty()); + EXPECT_FALSE(radii.AreAllCornersSame()); + EXPECT_TRUE(radii.IsFinite()); + EXPECT_EQ(radii.top_left, Size(5.0f, 5.5f)); + EXPECT_EQ(radii.top_right, Size(6.0f, 6.5f)); + EXPECT_EQ(radii.bottom_left, Size(7.0f, 7.5f)); + EXPECT_EQ(radii.bottom_right, Size(8.0f, 8.5f)); +} + +TEST(RoundRectTest, RoundingRadiiPartialNamedSizesConstructor) { + { + RoundingRadii radii = { + .top_left = Size(5.0f, 5.5f), + }; + + EXPECT_FALSE(radii.AreAllCornersEmpty()); + EXPECT_FALSE(radii.AreAllCornersSame()); + EXPECT_TRUE(radii.IsFinite()); + EXPECT_EQ(radii.top_left, Size(5.0f, 5.5f)); + EXPECT_EQ(radii.top_right, Size()); + EXPECT_EQ(radii.bottom_left, Size()); + EXPECT_EQ(radii.bottom_right, Size()); + } + + { + RoundingRadii radii = { + .top_right = Size(6.0f, 6.5f), + }; + + EXPECT_FALSE(radii.AreAllCornersEmpty()); + EXPECT_FALSE(radii.AreAllCornersSame()); + EXPECT_TRUE(radii.IsFinite()); + EXPECT_EQ(radii.top_left, Size()); + EXPECT_EQ(radii.top_right, Size(6.0f, 6.5f)); + EXPECT_EQ(radii.bottom_left, Size()); + EXPECT_EQ(radii.bottom_right, Size()); + } + + { + RoundingRadii radii = { + .bottom_left = Size(7.0f, 7.5f), + }; + + EXPECT_FALSE(radii.AreAllCornersEmpty()); + EXPECT_FALSE(radii.AreAllCornersSame()); + EXPECT_TRUE(radii.IsFinite()); + EXPECT_EQ(radii.top_left, Size()); + EXPECT_EQ(radii.top_right, Size()); + EXPECT_EQ(radii.bottom_left, Size(7.0f, 7.5f)); + EXPECT_EQ(radii.bottom_right, Size()); + } + + { + RoundingRadii radii = { + .bottom_right = Size(8.0f, 8.5f), + }; + + EXPECT_FALSE(radii.AreAllCornersEmpty()); + EXPECT_FALSE(radii.AreAllCornersSame()); + EXPECT_TRUE(radii.IsFinite()); + EXPECT_EQ(radii.top_left, Size()); + EXPECT_EQ(radii.top_right, Size()); + EXPECT_EQ(radii.bottom_left, Size()); + EXPECT_EQ(radii.bottom_right, Size(8.0f, 8.5f)); + } +} + +TEST(RoundRectTest, RoundingRadiiMultiply) { + RoundingRadii radii = { + .top_left = Size(5.0f, 5.5f), + .top_right = Size(6.0f, 6.5f), + .bottom_left = Size(7.0f, 7.5f), + .bottom_right = Size(8.0f, 8.5f), + }; + RoundingRadii doubled = radii * 2.0f; + + EXPECT_FALSE(doubled.AreAllCornersEmpty()); + EXPECT_FALSE(doubled.AreAllCornersSame()); + EXPECT_TRUE(doubled.IsFinite()); + EXPECT_EQ(doubled.top_left, Size(10.0f, 11.0f)); + EXPECT_EQ(doubled.top_right, Size(12.0f, 13.0f)); + EXPECT_EQ(doubled.bottom_left, Size(14.0f, 15.0f)); + EXPECT_EQ(doubled.bottom_right, Size(16.0f, 17.0f)); +} + +TEST(RoundRectTest, RoundingRadiiEquals) { + RoundingRadii radii = { + .top_left = Size(5.0f, 5.5f), + .top_right = Size(6.0f, 6.5f), + .bottom_left = Size(7.0f, 7.5f), + .bottom_right = Size(8.0f, 8.5f), + }; + RoundingRadii other = { + .top_left = Size(5.0f, 5.5f), + .top_right = Size(6.0f, 6.5f), + .bottom_left = Size(7.0f, 7.5f), + .bottom_right = Size(8.0f, 8.5f), + }; + + EXPECT_EQ(radii, other); +} + +TEST(RoundRectTest, RoundingRadiiNotEquals) { + const RoundingRadii radii = { + .top_left = Size(5.0f, 5.5f), + .top_right = Size(6.0f, 6.5f), + .bottom_left = Size(7.0f, 7.5f), + .bottom_right = Size(8.0f, 8.5f), + }; + + { + RoundingRadii different = radii; + different.top_left.width = 100.0f; + EXPECT_NE(different, radii); + } + { + RoundingRadii different = radii; + different.top_left.height = 100.0f; + EXPECT_NE(different, radii); + } + { + RoundingRadii different = radii; + different.top_right.width = 100.0f; + EXPECT_NE(different, radii); + } + { + RoundingRadii different = radii; + different.top_right.height = 100.0f; + EXPECT_NE(different, radii); + } + { + RoundingRadii different = radii; + different.bottom_left.width = 100.0f; + EXPECT_NE(different, radii); + } + { + RoundingRadii different = radii; + different.bottom_left.height = 100.0f; + EXPECT_NE(different, radii); + } + { + RoundingRadii different = radii; + different.bottom_right.width = 100.0f; + EXPECT_NE(different, radii); + } + { + RoundingRadii different = radii; + different.bottom_right.height = 100.0f; + EXPECT_NE(different, radii); + } +} + +TEST(RoundRectTest, RoundingRadiiCornersSameTolerance) { + RoundingRadii radii{ + .top_left = {10, 20}, + .top_right = {10.01, 20.01}, + .bottom_left = {9.99, 19.99}, + .bottom_right = {9.99, 20.01}, + }; + + EXPECT_TRUE(radii.AreAllCornersSame(.02)); + + { + RoundingRadii different = radii; + different.top_left.width = 10.03; + EXPECT_FALSE(different.AreAllCornersSame(.02)); + } + { + RoundingRadii different = radii; + different.top_left.height = 20.03; + EXPECT_FALSE(different.AreAllCornersSame(.02)); + } + { + RoundingRadii different = radii; + different.top_right.width = 10.03; + EXPECT_FALSE(different.AreAllCornersSame(.02)); + } + { + RoundingRadii different = radii; + different.top_right.height = 20.03; + EXPECT_FALSE(different.AreAllCornersSame(.02)); + } + { + RoundingRadii different = radii; + different.bottom_left.width = 9.97; + EXPECT_FALSE(different.AreAllCornersSame(.02)); + } + { + RoundingRadii different = radii; + different.bottom_left.height = 19.97; + EXPECT_FALSE(different.AreAllCornersSame(.02)); + } + { + RoundingRadii different = radii; + different.bottom_right.width = 9.97; + EXPECT_FALSE(different.AreAllCornersSame(.02)); + } + { + RoundingRadii different = radii; + different.bottom_right.height = 20.03; + EXPECT_FALSE(different.AreAllCornersSame(.02)); + } +} + +TEST(RoundRectTest, EmptyDeclaration) { + RoundRect round_rect; + + EXPECT_TRUE(round_rect.IsEmpty()); + EXPECT_FALSE(round_rect.IsRect()); + EXPECT_FALSE(round_rect.IsOval()); + EXPECT_TRUE(round_rect.IsFinite()); + EXPECT_TRUE(round_rect.GetBounds().IsEmpty()); + EXPECT_EQ(round_rect.GetBounds(), Rect()); + EXPECT_EQ(round_rect.GetBounds().GetLeft(), 0.0f); + EXPECT_EQ(round_rect.GetBounds().GetTop(), 0.0f); + EXPECT_EQ(round_rect.GetBounds().GetRight(), 0.0f); + EXPECT_EQ(round_rect.GetBounds().GetBottom(), 0.0f); + EXPECT_EQ(round_rect.GetRadii().top_left, Size()); + EXPECT_EQ(round_rect.GetRadii().top_right, Size()); + EXPECT_EQ(round_rect.GetRadii().bottom_left, Size()); + EXPECT_EQ(round_rect.GetRadii().bottom_right, Size()); + EXPECT_EQ(round_rect.GetRadii().top_left.width, 0.0f); + EXPECT_EQ(round_rect.GetRadii().top_left.height, 0.0f); + EXPECT_EQ(round_rect.GetRadii().top_right.width, 0.0f); + EXPECT_EQ(round_rect.GetRadii().top_right.height, 0.0f); + EXPECT_EQ(round_rect.GetRadii().bottom_left.width, 0.0f); + EXPECT_EQ(round_rect.GetRadii().bottom_left.height, 0.0f); + EXPECT_EQ(round_rect.GetRadii().bottom_right.width, 0.0f); + EXPECT_EQ(round_rect.GetRadii().bottom_right.height, 0.0f); +} + +TEST(RoundRectTest, DefaultConstructor) { + RoundRect round_rect = RoundRect(); + + EXPECT_TRUE(round_rect.IsEmpty()); + EXPECT_FALSE(round_rect.IsRect()); + EXPECT_FALSE(round_rect.IsOval()); + EXPECT_TRUE(round_rect.IsFinite()); + EXPECT_TRUE(round_rect.GetBounds().IsEmpty()); + EXPECT_EQ(round_rect.GetBounds(), Rect()); + EXPECT_EQ(round_rect.GetRadii().top_left, Size()); + EXPECT_EQ(round_rect.GetRadii().top_right, Size()); + EXPECT_EQ(round_rect.GetRadii().bottom_left, Size()); + EXPECT_EQ(round_rect.GetRadii().bottom_right, Size()); +} + +TEST(RoundRectTest, EmptyRectConstruction) { + RoundRect round_rect = RoundRect::MakeRectXY( + Rect::MakeLTRB(20.0f, 20.0f, 10.0f, 10.0f), 10.0f, 10.0f); + + EXPECT_TRUE(round_rect.IsEmpty()); + EXPECT_FALSE(round_rect.IsRect()); + EXPECT_FALSE(round_rect.IsOval()); + EXPECT_TRUE(round_rect.IsFinite()); + EXPECT_TRUE(round_rect.GetBounds().IsEmpty()); + EXPECT_EQ(round_rect.GetBounds(), Rect::MakeLTRB(20.0f, 20.0f, 10.0f, 10.0f)); + EXPECT_EQ(round_rect.GetRadii().top_left, Size()); + EXPECT_EQ(round_rect.GetRadii().top_right, Size()); + EXPECT_EQ(round_rect.GetRadii().bottom_left, Size()); + EXPECT_EQ(round_rect.GetRadii().bottom_right, Size()); +} + +TEST(RoundRectTest, RectConstructor) { + RoundRect round_rect = + RoundRect::MakeRect(Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f)); + + EXPECT_FALSE(round_rect.IsEmpty()); + EXPECT_TRUE(round_rect.IsRect()); + EXPECT_FALSE(round_rect.IsOval()); + EXPECT_TRUE(round_rect.IsFinite()); + EXPECT_FALSE(round_rect.GetBounds().IsEmpty()); + EXPECT_EQ(round_rect.GetBounds(), Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f)); + EXPECT_EQ(round_rect.GetRadii().top_left, Size()); + EXPECT_EQ(round_rect.GetRadii().top_right, Size()); + EXPECT_EQ(round_rect.GetRadii().bottom_left, Size()); + EXPECT_EQ(round_rect.GetRadii().bottom_right, Size()); +} + +TEST(RoundRectTest, OvalConstructor) { + RoundRect round_rect = + RoundRect::MakeOval(Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f)); + + EXPECT_FALSE(round_rect.IsEmpty()); + EXPECT_FALSE(round_rect.IsRect()); + EXPECT_TRUE(round_rect.IsOval()); + EXPECT_TRUE(round_rect.IsFinite()); + EXPECT_FALSE(round_rect.GetBounds().IsEmpty()); + EXPECT_EQ(round_rect.GetBounds(), Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f)); + EXPECT_EQ(round_rect.GetRadii().top_left, Size(5.0f, 5.0f)); + EXPECT_EQ(round_rect.GetRadii().top_right, Size(5.0f, 5.0f)); + EXPECT_EQ(round_rect.GetRadii().bottom_left, Size(5.0f, 5.0f)); + EXPECT_EQ(round_rect.GetRadii().bottom_right, Size(5.0f, 5.0f)); +} + +TEST(RoundRectTest, RectRadiusConstructor) { + RoundRect round_rect = RoundRect::MakeRectRadius( + Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f), 2.0f); + + EXPECT_FALSE(round_rect.IsEmpty()); + EXPECT_FALSE(round_rect.IsRect()); + EXPECT_FALSE(round_rect.IsOval()); + EXPECT_TRUE(round_rect.IsFinite()); + EXPECT_FALSE(round_rect.GetBounds().IsEmpty()); + EXPECT_EQ(round_rect.GetBounds(), Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f)); + EXPECT_EQ(round_rect.GetRadii().top_left, Size(2.0f, 2.0f)); + EXPECT_EQ(round_rect.GetRadii().top_right, Size(2.0f, 2.0f)); + EXPECT_EQ(round_rect.GetRadii().bottom_left, Size(2.0f, 2.0f)); + EXPECT_EQ(round_rect.GetRadii().bottom_right, Size(2.0f, 2.0f)); +} + +TEST(RoundRectTest, RectXYConstructor) { + RoundRect round_rect = RoundRect::MakeRectXY( + Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f), 2.0f, 3.0f); + + EXPECT_FALSE(round_rect.IsEmpty()); + EXPECT_FALSE(round_rect.IsRect()); + EXPECT_FALSE(round_rect.IsOval()); + EXPECT_TRUE(round_rect.IsFinite()); + EXPECT_FALSE(round_rect.GetBounds().IsEmpty()); + EXPECT_EQ(round_rect.GetBounds(), Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f)); + EXPECT_EQ(round_rect.GetRadii().top_left, Size(2.0f, 3.0f)); + EXPECT_EQ(round_rect.GetRadii().top_right, Size(2.0f, 3.0f)); + EXPECT_EQ(round_rect.GetRadii().bottom_left, Size(2.0f, 3.0f)); + EXPECT_EQ(round_rect.GetRadii().bottom_right, Size(2.0f, 3.0f)); +} + +TEST(RoundRectTest, RectSizeConstructor) { + RoundRect round_rect = RoundRect::MakeRectXY( + Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f), Size(2.0f, 3.0f)); + + EXPECT_FALSE(round_rect.IsEmpty()); + EXPECT_FALSE(round_rect.IsRect()); + EXPECT_FALSE(round_rect.IsOval()); + EXPECT_TRUE(round_rect.IsFinite()); + EXPECT_FALSE(round_rect.GetBounds().IsEmpty()); + EXPECT_EQ(round_rect.GetBounds(), Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f)); + EXPECT_EQ(round_rect.GetRadii().top_left, Size(2.0f, 3.0f)); + EXPECT_EQ(round_rect.GetRadii().top_right, Size(2.0f, 3.0f)); + EXPECT_EQ(round_rect.GetRadii().bottom_left, Size(2.0f, 3.0f)); + EXPECT_EQ(round_rect.GetRadii().bottom_right, Size(2.0f, 3.0f)); +} + +TEST(RoundRectTest, RectRadiiConstructor) { + RoundRect round_rect = + RoundRect::MakeRectRadii(Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f), + { + .top_left = Size(1.0, 1.5), + .top_right = Size(2.0, 2.5f), + .bottom_left = Size(3.0, 3.5f), + .bottom_right = Size(4.0, 4.5f), + }); + + EXPECT_FALSE(round_rect.IsEmpty()); + EXPECT_FALSE(round_rect.IsRect()); + EXPECT_FALSE(round_rect.IsOval()); + EXPECT_TRUE(round_rect.IsFinite()); + EXPECT_FALSE(round_rect.GetBounds().IsEmpty()); + EXPECT_EQ(round_rect.GetBounds(), Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f)); + EXPECT_EQ(round_rect.GetRadii().top_left, Size(1.0f, 1.5f)); + EXPECT_EQ(round_rect.GetRadii().top_right, Size(2.0f, 2.5f)); + EXPECT_EQ(round_rect.GetRadii().bottom_left, Size(3.0f, 3.5f)); + EXPECT_EQ(round_rect.GetRadii().bottom_right, Size(4.0f, 4.5f)); +} + +TEST(RoundRectTest, RectRadiiOverflowWidthConstructor) { + RoundRect round_rect = + RoundRect::MakeRectRadii(Rect::MakeXYWH(10.0f, 10.0f, 6.0f, 30.0f), + { + .top_left = Size(1.0f, 2.0f), + .top_right = Size(3.0f, 4.0f), + .bottom_left = Size(5.0f, 6.0f), + .bottom_right = Size(7.0f, 8.0f), + }); + // Largest sum of paired radii widths is the bottom edge which sums to 12 + // Rect is only 6 wide so all radii are scaled by half + // Rect is 30 tall so no scaling should happen due to radii heights + + EXPECT_FALSE(round_rect.IsEmpty()); + EXPECT_FALSE(round_rect.IsRect()); + EXPECT_FALSE(round_rect.IsOval()); + EXPECT_TRUE(round_rect.IsFinite()); + EXPECT_FALSE(round_rect.GetBounds().IsEmpty()); + EXPECT_EQ(round_rect.GetBounds(), Rect::MakeLTRB(10.0f, 10.0f, 16.0f, 40.0f)); + EXPECT_EQ(round_rect.GetRadii().top_left, Size(0.5f, 1.0f)); + EXPECT_EQ(round_rect.GetRadii().top_right, Size(1.5f, 2.0f)); + EXPECT_EQ(round_rect.GetRadii().bottom_left, Size(2.5f, 3.0f)); + EXPECT_EQ(round_rect.GetRadii().bottom_right, Size(3.5f, 4.0f)); +} + +TEST(RoundRectTest, RectRadiiOverflowHeightConstructor) { + RoundRect round_rect = + RoundRect::MakeRectRadii(Rect::MakeXYWH(10.0f, 10.0f, 30.0f, 6.0f), + { + .top_left = Size(1.0f, 2.0f), + .top_right = Size(3.0f, 4.0f), + .bottom_left = Size(5.0f, 6.0f), + .bottom_right = Size(7.0f, 8.0f), + }); + // Largest sum of paired radii heights is the right edge which sums to 12 + // Rect is only 6 tall so all radii are scaled by half + // Rect is 30 wide so no scaling should happen due to radii widths + + EXPECT_FALSE(round_rect.IsEmpty()); + EXPECT_FALSE(round_rect.IsRect()); + EXPECT_FALSE(round_rect.IsOval()); + EXPECT_TRUE(round_rect.IsFinite()); + EXPECT_FALSE(round_rect.GetBounds().IsEmpty()); + EXPECT_EQ(round_rect.GetBounds(), Rect::MakeLTRB(10.0f, 10.0f, 40.0f, 16.0f)); + EXPECT_EQ(round_rect.GetRadii().top_left, Size(0.5f, 1.0f)); + EXPECT_EQ(round_rect.GetRadii().top_right, Size(1.5f, 2.0f)); + EXPECT_EQ(round_rect.GetRadii().bottom_left, Size(2.5f, 3.0f)); + EXPECT_EQ(round_rect.GetRadii().bottom_right, Size(3.5f, 4.0f)); +} + +TEST(RoundRectTest, Shift) { + RoundRect round_rect = + RoundRect::MakeRectRadii(Rect::MakeXYWH(10.0f, 10.0f, 30.0f, 30.0f), + { + .top_left = Size(1.0f, 2.0f), + .top_right = Size(3.0f, 4.0f), + .bottom_left = Size(5.0f, 6.0f), + .bottom_right = Size(7.0f, 8.0f), + }); + RoundRect shifted = round_rect.Shift(5.0, 6.0); + + EXPECT_FALSE(shifted.IsEmpty()); + EXPECT_FALSE(shifted.IsRect()); + EXPECT_FALSE(shifted.IsOval()); + EXPECT_TRUE(shifted.IsFinite()); + EXPECT_FALSE(shifted.GetBounds().IsEmpty()); + EXPECT_EQ(shifted.GetBounds(), Rect::MakeLTRB(15.0f, 16.0f, 45.0f, 46.0f)); + EXPECT_EQ(shifted.GetRadii().top_left, Size(1.0f, 2.0f)); + EXPECT_EQ(shifted.GetRadii().top_right, Size(3.0f, 4.0f)); + EXPECT_EQ(shifted.GetRadii().bottom_left, Size(5.0f, 6.0f)); + EXPECT_EQ(shifted.GetRadii().bottom_right, Size(7.0f, 8.0f)); + + EXPECT_EQ(shifted, + RoundRect::MakeRectRadii(Rect::MakeXYWH(15.0f, 16.0f, 30.0f, 30.0f), + { + .top_left = Size(1.0f, 2.0f), + .top_right = Size(3.0f, 4.0f), + .bottom_left = Size(5.0f, 6.0f), + .bottom_right = Size(7.0f, 8.0f), + })); +} + +TEST(RoundRectTest, ExpandScalar) { + RoundRect round_rect = + RoundRect::MakeRectRadii(Rect::MakeXYWH(10.0f, 10.0f, 30.0f, 30.0f), + { + .top_left = Size(1.0f, 2.0f), + .top_right = Size(3.0f, 4.0f), + .bottom_left = Size(5.0f, 6.0f), + .bottom_right = Size(7.0f, 8.0f), + }); + RoundRect expanded = round_rect.Expand(5.0); + + EXPECT_FALSE(expanded.IsEmpty()); + EXPECT_FALSE(expanded.IsRect()); + EXPECT_FALSE(expanded.IsOval()); + EXPECT_TRUE(expanded.IsFinite()); + EXPECT_FALSE(expanded.GetBounds().IsEmpty()); + EXPECT_EQ(expanded.GetBounds(), Rect::MakeLTRB(5.0f, 5.0f, 45.0f, 45.0f)); + EXPECT_EQ(expanded.GetRadii().top_left, Size(1.0f, 2.0f)); + EXPECT_EQ(expanded.GetRadii().top_right, Size(3.0f, 4.0f)); + EXPECT_EQ(expanded.GetRadii().bottom_left, Size(5.0f, 6.0f)); + EXPECT_EQ(expanded.GetRadii().bottom_right, Size(7.0f, 8.0f)); + + EXPECT_EQ(expanded, + RoundRect::MakeRectRadii(Rect::MakeXYWH(5.0f, 5.0f, 40.0f, 40.0f), + { + .top_left = Size(1.0f, 2.0f), + .top_right = Size(3.0f, 4.0f), + .bottom_left = Size(5.0f, 6.0f), + .bottom_right = Size(7.0f, 8.0f), + })); +} + +TEST(RoundRectTest, ExpandTwoScalars) { + RoundRect round_rect = + RoundRect::MakeRectRadii(Rect::MakeXYWH(10.0f, 10.0f, 30.0f, 30.0f), + { + .top_left = Size(1.0f, 2.0f), + .top_right = Size(3.0f, 4.0f), + .bottom_left = Size(5.0f, 6.0f), + .bottom_right = Size(7.0f, 8.0f), + }); + RoundRect expanded = round_rect.Expand(5.0, 6.0); + + EXPECT_FALSE(expanded.IsEmpty()); + EXPECT_FALSE(expanded.IsRect()); + EXPECT_FALSE(expanded.IsOval()); + EXPECT_TRUE(expanded.IsFinite()); + EXPECT_FALSE(expanded.GetBounds().IsEmpty()); + EXPECT_EQ(expanded.GetBounds(), Rect::MakeLTRB(5.0f, 4.0f, 45.0f, 46.0f)); + EXPECT_EQ(expanded.GetRadii().top_left, Size(1.0f, 2.0f)); + EXPECT_EQ(expanded.GetRadii().top_right, Size(3.0f, 4.0f)); + EXPECT_EQ(expanded.GetRadii().bottom_left, Size(5.0f, 6.0f)); + EXPECT_EQ(expanded.GetRadii().bottom_right, Size(7.0f, 8.0f)); + + EXPECT_EQ(expanded, + RoundRect::MakeRectRadii(Rect::MakeXYWH(5.0f, 4.0f, 40.0f, 42.0f), + { + .top_left = Size(1.0f, 2.0f), + .top_right = Size(3.0f, 4.0f), + .bottom_left = Size(5.0f, 6.0f), + .bottom_right = Size(7.0f, 8.0f), + })); +} + +TEST(RoundRectTest, ExpandFourScalars) { + RoundRect round_rect = + RoundRect::MakeRectRadii(Rect::MakeXYWH(10.0f, 10.0f, 30.0f, 30.0f), + { + .top_left = Size(1.0f, 2.0f), + .top_right = Size(3.0f, 4.0f), + .bottom_left = Size(5.0f, 6.0f), + .bottom_right = Size(7.0f, 8.0f), + }); + RoundRect expanded = round_rect.Expand(5.0, 6.0, 7.0, 8.0); + + EXPECT_FALSE(expanded.IsEmpty()); + EXPECT_FALSE(expanded.IsRect()); + EXPECT_FALSE(expanded.IsOval()); + EXPECT_TRUE(expanded.IsFinite()); + EXPECT_FALSE(expanded.GetBounds().IsEmpty()); + EXPECT_EQ(expanded.GetBounds(), Rect::MakeLTRB(5.0f, 4.0f, 47.0f, 48.0f)); + EXPECT_EQ(expanded.GetRadii().top_left, Size(1.0f, 2.0f)); + EXPECT_EQ(expanded.GetRadii().top_right, Size(3.0f, 4.0f)); + EXPECT_EQ(expanded.GetRadii().bottom_left, Size(5.0f, 6.0f)); + EXPECT_EQ(expanded.GetRadii().bottom_right, Size(7.0f, 8.0f)); + + EXPECT_EQ(expanded, + RoundRect::MakeRectRadii(Rect::MakeXYWH(5.0f, 4.0f, 42.0f, 44.0f), + { + .top_left = Size(1.0f, 2.0f), + .top_right = Size(3.0f, 4.0f), + .bottom_left = Size(5.0f, 6.0f), + .bottom_right = Size(7.0f, 8.0f), + })); +} + +TEST(RoundRectTest, ContractScalar) { + RoundRect round_rect = + RoundRect::MakeRectRadii(Rect::MakeXYWH(10.0f, 10.0f, 30.0f, 30.0f), + { + .top_left = Size(1.0f, 2.0f), + .top_right = Size(3.0f, 4.0f), + .bottom_left = Size(5.0f, 6.0f), + .bottom_right = Size(7.0f, 8.0f), + }); + RoundRect expanded = round_rect.Expand(-2.0); + + EXPECT_FALSE(expanded.IsEmpty()); + EXPECT_FALSE(expanded.IsRect()); + EXPECT_FALSE(expanded.IsOval()); + EXPECT_TRUE(expanded.IsFinite()); + EXPECT_FALSE(expanded.GetBounds().IsEmpty()); + EXPECT_EQ(expanded.GetBounds(), Rect::MakeLTRB(12.0f, 12.0f, 38.0f, 38.0f)); + EXPECT_EQ(expanded.GetRadii().top_left, Size(1.0f, 2.0f)); + EXPECT_EQ(expanded.GetRadii().top_right, Size(3.0f, 4.0f)); + EXPECT_EQ(expanded.GetRadii().bottom_left, Size(5.0f, 6.0f)); + EXPECT_EQ(expanded.GetRadii().bottom_right, Size(7.0f, 8.0f)); + + EXPECT_EQ(expanded, + RoundRect::MakeRectRadii(Rect::MakeXYWH(12.0f, 12.0f, 26.0f, 26.0f), + { + .top_left = Size(1.0f, 2.0f), + .top_right = Size(3.0f, 4.0f), + .bottom_left = Size(5.0f, 6.0f), + .bottom_right = Size(7.0f, 8.0f), + })); +} + +TEST(RoundRectTest, ContractTwoScalars) { + RoundRect round_rect = + RoundRect::MakeRectRadii(Rect::MakeXYWH(10.0f, 10.0f, 30.0f, 30.0f), + { + .top_left = Size(1.0f, 2.0f), + .top_right = Size(3.0f, 4.0f), + .bottom_left = Size(5.0f, 6.0f), + .bottom_right = Size(7.0f, 8.0f), + }); + RoundRect expanded = round_rect.Expand(-1.0, -2.0); + + EXPECT_FALSE(expanded.IsEmpty()); + EXPECT_FALSE(expanded.IsRect()); + EXPECT_FALSE(expanded.IsOval()); + EXPECT_TRUE(expanded.IsFinite()); + EXPECT_FALSE(expanded.GetBounds().IsEmpty()); + EXPECT_EQ(expanded.GetBounds(), Rect::MakeLTRB(11.0f, 12.0f, 39.0f, 38.0f)); + EXPECT_EQ(expanded.GetRadii().top_left, Size(1.0f, 2.0f)); + EXPECT_EQ(expanded.GetRadii().top_right, Size(3.0f, 4.0f)); + EXPECT_EQ(expanded.GetRadii().bottom_left, Size(5.0f, 6.0f)); + EXPECT_EQ(expanded.GetRadii().bottom_right, Size(7.0f, 8.0f)); + + EXPECT_EQ(expanded, + RoundRect::MakeRectRadii(Rect::MakeXYWH(11.0f, 12.0f, 28.0f, 26.0f), + { + .top_left = Size(1.0f, 2.0f), + .top_right = Size(3.0f, 4.0f), + .bottom_left = Size(5.0f, 6.0f), + .bottom_right = Size(7.0f, 8.0f), + })); +} + +TEST(RoundRectTest, ContractFourScalars) { + RoundRect round_rect = + RoundRect::MakeRectRadii(Rect::MakeXYWH(10.0f, 10.0f, 30.0f, 30.0f), + { + .top_left = Size(1.0f, 2.0f), + .top_right = Size(3.0f, 4.0f), + .bottom_left = Size(5.0f, 6.0f), + .bottom_right = Size(7.0f, 8.0f), + }); + RoundRect expanded = round_rect.Expand(-1.0, -1.5, -2.0, -2.5); + + EXPECT_FALSE(expanded.IsEmpty()); + EXPECT_FALSE(expanded.IsRect()); + EXPECT_FALSE(expanded.IsOval()); + EXPECT_TRUE(expanded.IsFinite()); + EXPECT_FALSE(expanded.GetBounds().IsEmpty()); + EXPECT_EQ(expanded.GetBounds(), Rect::MakeLTRB(11.0f, 11.5f, 38.0f, 37.5f)); + EXPECT_EQ(expanded.GetRadii().top_left, Size(1.0f, 2.0f)); + EXPECT_EQ(expanded.GetRadii().top_right, Size(3.0f, 4.0f)); + EXPECT_EQ(expanded.GetRadii().bottom_left, Size(5.0f, 6.0f)); + EXPECT_EQ(expanded.GetRadii().bottom_right, Size(7.0f, 8.0f)); + + EXPECT_EQ(expanded, + RoundRect::MakeRectRadii(Rect::MakeXYWH(11.0f, 11.5f, 27.0f, 26.0f), + { + .top_left = Size(1.0f, 2.0f), + .top_right = Size(3.0f, 4.0f), + .bottom_left = Size(5.0f, 6.0f), + .bottom_right = Size(7.0f, 8.0f), + })); +} + +TEST(RoundRectTest, ContractAndRequireRadiiAdjustment) { + RoundRect round_rect = + RoundRect::MakeRectRadii(Rect::MakeXYWH(10.0f, 10.0f, 30.0f, 30.0f), + { + .top_left = Size(1.0f, 2.0f), + .top_right = Size(3.0f, 4.0f), + .bottom_left = Size(5.0f, 6.0f), + .bottom_right = Size(7.0f, 8.0f), + }); + RoundRect expanded = round_rect.Expand(-12.0); + // Largest sum of paired radii sizes are the bottom and right edges + // both of which sum to 12 + // Rect was 30x30 reduced by 12 on all sides leaving only 6x6, so all + // radii are scaled by half to avoid overflowing the contracted rect + + EXPECT_FALSE(expanded.IsEmpty()); + EXPECT_FALSE(expanded.IsRect()); + EXPECT_FALSE(expanded.IsOval()); + EXPECT_TRUE(expanded.IsFinite()); + EXPECT_FALSE(expanded.GetBounds().IsEmpty()); + EXPECT_EQ(expanded.GetBounds(), Rect::MakeLTRB(22.0f, 22.0f, 28.0f, 28.0f)); + EXPECT_EQ(expanded.GetRadii().top_left, Size(0.5f, 1.0f)); + EXPECT_EQ(expanded.GetRadii().top_right, Size(1.5f, 2.0f)); + EXPECT_EQ(expanded.GetRadii().bottom_left, Size(2.5f, 3.0f)); + EXPECT_EQ(expanded.GetRadii().bottom_right, Size(3.5f, 4.0f)); + + // In this test, the MakeRectRadii constructor will make the same + // adjustment to the radii that the Expand method applied. + EXPECT_EQ(expanded, + RoundRect::MakeRectRadii(Rect::MakeXYWH(22.0f, 22.0f, 6.0f, 6.0f), + { + .top_left = Size(1.0f, 2.0f), + .top_right = Size(3.0f, 4.0f), + .bottom_left = Size(5.0f, 6.0f), + .bottom_right = Size(7.0f, 8.0f), + })); + + // In this test, the arguments to the constructor supply the correctly + // adjusted radii (though there is no real way to tell other than + // the result is the same). + EXPECT_EQ(expanded, + RoundRect::MakeRectRadii(Rect::MakeXYWH(22.0f, 22.0f, 6.0f, 6.0f), + { + .top_left = Size(0.5f, 1.0f), + .top_right = Size(1.5f, 2.0f), + .bottom_left = Size(2.5f, 3.0f), + .bottom_right = Size(3.5f, 4.0f), + })); +} + +TEST(RoundRectTest, NoCornerRoundRectContains) { + Rect bounds = Rect::MakeLTRB(-50.0f, -50.0f, 50.0f, 50.0f); + // RRect of bounds with no corners contains corners just barely + auto no_corners = RoundRect::MakeRectXY(bounds, 0.0f, 0.0f); + + EXPECT_TRUE(no_corners.Contains({-50, -50})); + // Rectangles have half-in, half-out containment so we need + // to be careful about testing containment of right/bottom corners. + EXPECT_TRUE(no_corners.Contains({-50, 49.99})); + EXPECT_TRUE(no_corners.Contains({49.99, -50})); + EXPECT_TRUE(no_corners.Contains({49.99, 49.99})); + EXPECT_FALSE(no_corners.Contains({-50.01, -50})); + EXPECT_FALSE(no_corners.Contains({-50, -50.01})); + EXPECT_FALSE(no_corners.Contains({-50.01, 50})); + EXPECT_FALSE(no_corners.Contains({-50, 50.01})); + EXPECT_FALSE(no_corners.Contains({50.01, -50})); + EXPECT_FALSE(no_corners.Contains({50, -50.01})); + EXPECT_FALSE(no_corners.Contains({50.01, 50})); + EXPECT_FALSE(no_corners.Contains({50, 50.01})); +} + +TEST(RoundRectTest, TinyCornerRoundRectContains) { + Rect bounds = Rect::MakeLTRB(-50.0f, -50.0f, 50.0f, 50.0f); + // RRect of bounds with even the tiniest corners does not contain corners + auto tiny_corners = RoundRect::MakeRectXY(bounds, 0.01f, 0.01f); + + EXPECT_FALSE(tiny_corners.Contains({-50, -50})); + EXPECT_FALSE(tiny_corners.Contains({-50, 50})); + EXPECT_FALSE(tiny_corners.Contains({50, -50})); + EXPECT_FALSE(tiny_corners.Contains({50, 50})); +} + +TEST(RoundRectTest, UniformCircularRoundRectContains) { + Rect bounds = Rect::MakeLTRB(-50.0f, -50.0f, 50.0f, 50.0f); + auto expanded_2_r_2 = RoundRect::MakeRectXY(bounds.Expand(2.0), 2.0f, 2.0f); + + // Expanded by 2.0 and then with a corner of 2.0 obviously still + // contains the corners + EXPECT_TRUE(expanded_2_r_2.Contains({-50, -50})); + EXPECT_TRUE(expanded_2_r_2.Contains({-50, 50})); + EXPECT_TRUE(expanded_2_r_2.Contains({50, -50})); + EXPECT_TRUE(expanded_2_r_2.Contains({50, 50})); + + // Now we try to box in the corner containment to exactly where the + // rounded corner of the expanded round rect with radii of 2.0 lies. + // The 45-degree diagonal point of a circle of radius 2.0 lies at: + // + // (2 * sqrt(2) / 2, 2 * sqrt(2) / 2) + // (sqrt(2), sqrt(2)) + // + // So we test +/- (50 + sqrt(2) +/- epsilon) + const auto coord_out = 50 + kSqrt2 + kEhCloseEnough; + const auto coord_in = 50 + kSqrt2 - kEhCloseEnough; + // Upper left corner + EXPECT_TRUE(expanded_2_r_2.Contains({-coord_in, -coord_in})); + EXPECT_FALSE(expanded_2_r_2.Contains({-coord_out, -coord_out})); + // Upper right corner + EXPECT_TRUE(expanded_2_r_2.Contains({coord_in, -coord_in})); + EXPECT_FALSE(expanded_2_r_2.Contains({coord_out, -coord_out})); + // Lower left corner + EXPECT_TRUE(expanded_2_r_2.Contains({-coord_in, coord_in})); + EXPECT_FALSE(expanded_2_r_2.Contains({-coord_out, coord_out})); + // Lower right corner + EXPECT_TRUE(expanded_2_r_2.Contains({coord_in, coord_in})); + EXPECT_FALSE(expanded_2_r_2.Contains({coord_out, coord_out})); +} + +TEST(RoundRectTest, UniformEllipticalRoundRectContains) { + Rect bounds = Rect::MakeLTRB(-50.0f, -50.0f, 50.0f, 50.0f); + auto expanded_2_r_2 = RoundRect::MakeRectXY(bounds.Expand(2.0), 2.0f, 3.0f); + + // Expanded by 2.0 and then with a corner of 2x3 should still + // contain the corners + EXPECT_TRUE(expanded_2_r_2.Contains({-50, -50})); + EXPECT_TRUE(expanded_2_r_2.Contains({-50, 50})); + EXPECT_TRUE(expanded_2_r_2.Contains({50, -50})); + EXPECT_TRUE(expanded_2_r_2.Contains({50, 50})); + + // Now we try to box in the corner containment to exactly where the + // rounded corner of the expanded round rect with radii of 2x3 lies. + // The "45-degree diagonal point" of an ellipse of radii 2x3 lies at: + // + // (2 * sqrt(2) / 2, 3 * sqrt(2) / 2) + // (sqrt(2), 3 * sqrt(2) / 2) + // + // And the center(s) of these corners are at: + // (+/-(50 + 2 - 2), +/-(50 + 2 - 3)) + // = (+/-50, +/-49) + const auto x_coord_out = 50 + kSqrt2 + kEhCloseEnough; + const auto x_coord_in = 50 + kSqrt2 - kEhCloseEnough; + const auto y_coord_out = 49 + 3 * kSqrt2 / 2 + kEhCloseEnough; + const auto y_coord_in = 49 + 3 * kSqrt2 / 2 - kEhCloseEnough; + // Upper left corner + EXPECT_TRUE(expanded_2_r_2.Contains({-x_coord_in, -y_coord_in})); + EXPECT_FALSE(expanded_2_r_2.Contains({-x_coord_out, -y_coord_out})); + // Upper right corner + EXPECT_TRUE(expanded_2_r_2.Contains({x_coord_in, -y_coord_in})); + EXPECT_FALSE(expanded_2_r_2.Contains({x_coord_out, -y_coord_out})); + // Lower left corner + EXPECT_TRUE(expanded_2_r_2.Contains({-x_coord_in, y_coord_in})); + EXPECT_FALSE(expanded_2_r_2.Contains({-x_coord_out, y_coord_out})); + // Lower right corner + EXPECT_TRUE(expanded_2_r_2.Contains({x_coord_in, y_coord_in})); + EXPECT_FALSE(expanded_2_r_2.Contains({x_coord_out, y_coord_out})); +} + +TEST(RoundRectTest, DifferingCornersRoundRectContains) { + Rect bounds = Rect::MakeLTRB(-50.0f, -50.0f, 50.0f, 50.0f); + auto round_rect = + RoundRect::MakeRectRadii(bounds, { + .top_left = Size(2.0, 3.0), + .top_right = Size(4.0, 5.0), + .bottom_left = Size(6.0, 7.0), + .bottom_right = Size(8.0, 9.0), + }); + + // For a corner with radii {A, B}, the "45 degree point" on the + // corner curve will be at an offset of: + // + // (A * sqrt(2) / 2, B * sqrt(2) / 2) + // + // And the center(s) of these corners are at: + // + // (+/-(50 - A), +/-(50 - B)) + auto coord = [](Scalar radius) { + return 50 - radius + radius * kSqrt2 / 2.0f - kEhCloseEnough; + }; + auto coord_in = [&coord](Scalar radius) { + return coord(radius) - kEhCloseEnough; + }; + auto coord_out = [&coord](Scalar radius) { + // For some reason 1 kEhCloseEnough is not enough to put us outside + // in some of the cases, so we use 2x the epsilon. + return coord(radius) + 2 * kEhCloseEnough; + }; + // Upper left corner (radii = {2.0, 3.0}) + EXPECT_TRUE(round_rect.Contains({-coord_in(2.0), -coord_in(3.0)})); + EXPECT_FALSE(round_rect.Contains({-coord_out(2.0), -coord_out(3.0)})); + // Upper right corner (radii = {4.0, 5.0}) + EXPECT_TRUE(round_rect.Contains({coord_in(4.0), -coord_in(5.0)})); + EXPECT_FALSE(round_rect.Contains({coord_out(4.0), -coord_out(5.0)})); + // Lower left corner (radii = {6.0, 7.0}) + EXPECT_TRUE(round_rect.Contains({-coord_in(6.0), coord_in(7.0)})); + EXPECT_FALSE(round_rect.Contains({-coord_out(6.0), coord_out(7.0)})); + // Lower right corner (radii = {8.0, 9.0}) + EXPECT_TRUE(round_rect.Contains({coord_in(8.0), coord_in(9.0)})); + EXPECT_FALSE(round_rect.Contains({coord_out(8.0), coord_out(9.0)})); +} + +} // namespace testing +} // namespace impeller diff --git a/impeller/geometry/size.h b/impeller/geometry/size.h index 45554734a15f4..b31970f1ddc2a 100644 --- a/impeller/geometry/size.h +++ b/impeller/geometry/size.h @@ -15,6 +15,11 @@ namespace impeller { +#define ONLY_ON_FLOAT_M(Modifiers, Return) \ + template \ + Modifiers std::enable_if_t, Return> +#define ONLY_ON_FLOAT(Return) DL_ONLY_ON_FLOAT_M(, Return) + template struct TSize { using Type = T; @@ -26,6 +31,9 @@ struct TSize { constexpr TSize(Type width, Type height) : width(width), height(height) {} + constexpr explicit TSize(Type dimension) + : width(dimension), height(dimension) {} + template explicit constexpr TSize(const TSize& other) : TSize(static_cast(other.width), static_cast(other.height)) { @@ -44,6 +52,13 @@ struct TSize { return {width * scale, height * scale}; } + template >> + inline TSize operator*=(U scale) { + width *= static_cast(scale); + height *= static_cast(scale); + return *this; + } + constexpr TSize operator/(Scalar scale) const { return {static_cast(width) / scale, static_cast(height) / scale}; @@ -104,6 +119,9 @@ struct TSize { /// Returns true if either of the width or height are 0, negative, or NaN. constexpr bool IsEmpty() const { return !(width > 0 && height > 0); } + ONLY_ON_FLOAT_M(constexpr, bool) + IsFinite() const { return std::isfinite(width) && std::isfinite(height); } + constexpr bool IsSquare() const { return width == height; } template diff --git a/impeller/toolkit/interop/dl_builder.cc b/impeller/toolkit/interop/dl_builder.cc index 5299c2a04cf17..c0c5885354e12 100644 --- a/impeller/toolkit/interop/dl_builder.cc +++ b/impeller/toolkit/interop/dl_builder.cc @@ -77,10 +77,9 @@ void DisplayListBuilder::ClipOval(const Rect& rect, builder_.ClipOval(ToSkiaType(rect), op); } -void DisplayListBuilder::ClipRoundedRect( - const Rect& rect, - const impeller::PathBuilder::RoundingRadii& radii, - flutter::DlCanvas::ClipOp op) { +void DisplayListBuilder::ClipRoundedRect(const Rect& rect, + const RoundingRadii& radii, + flutter::DlCanvas::ClipOp op) { builder_.ClipRRect(ToSkiaType(rect, radii), op); } @@ -97,18 +96,17 @@ void DisplayListBuilder::DrawOval(const Rect& oval_bounds, const Paint& paint) { builder_.DrawOval(ToSkiaType(oval_bounds), paint.GetPaint()); } -void DisplayListBuilder::DrawRoundedRect( - const Rect& rect, - const impeller::PathBuilder::RoundingRadii& radii, - const Paint& paint) { +void DisplayListBuilder::DrawRoundedRect(const Rect& rect, + const RoundingRadii& radii, + const Paint& paint) { builder_.DrawRRect(ToSkiaType(rect, radii), paint.GetPaint()); } void DisplayListBuilder::DrawRoundedRectDifference( const Rect& outer_rect, - const impeller::PathBuilder::RoundingRadii& outer_radii, + const RoundingRadii& outer_radii, const Rect& inner_rect, - const impeller::PathBuilder::RoundingRadii& inner_radii, + const RoundingRadii& inner_radii, const Paint& paint) { builder_.DrawDRRect(ToSkiaType(outer_rect, outer_radii), // ToSkiaType(inner_rect, inner_radii), // diff --git a/impeller/toolkit/interop/dl_builder.h b/impeller/toolkit/interop/dl_builder.h index f2e07d1803367..59468d37e4864 100644 --- a/impeller/toolkit/interop/dl_builder.h +++ b/impeller/toolkit/interop/dl_builder.h @@ -62,7 +62,7 @@ class DisplayListBuilder final void ClipOval(const Rect& rect, flutter::DlCanvas::ClipOp op); void ClipRoundedRect(const Rect& rect, - const impeller::PathBuilder::RoundingRadii& radii, + const RoundingRadii& radii, flutter::DlCanvas::ClipOp op); void ClipPath(const Path& path, flutter::DlCanvas::ClipOp op); @@ -82,15 +82,14 @@ class DisplayListBuilder final void DrawOval(const Rect& oval_bounds, const Paint& paint); void DrawRoundedRect(const Rect& rect, - const impeller::PathBuilder::RoundingRadii& radii, + const RoundingRadii& radii, const Paint& paint); - void DrawRoundedRectDifference( - const Rect& outer_rect, - const impeller::PathBuilder::RoundingRadii& outer_radii, - const Rect& inner_rect, - const impeller::PathBuilder::RoundingRadii& inner_radii, - const Paint& paint); + void DrawRoundedRectDifference(const Rect& outer_rect, + const RoundingRadii& outer_radii, + const Rect& inner_rect, + const RoundingRadii& inner_radii, + const Paint& paint); void DrawPath(const Path& path, const Paint& paint); diff --git a/impeller/toolkit/interop/formats.h b/impeller/toolkit/interop/formats.h index 89fa141b12b9d..c3b718ffe39c4 100644 --- a/impeller/toolkit/interop/formats.h +++ b/impeller/toolkit/interop/formats.h @@ -34,6 +34,10 @@ constexpr SkPoint ToSkiaType(const Point& point) { return SkPoint::Make(point.x, point.y); } +constexpr SkVector ToSkiaVector(const Size& point) { + return SkVector::Make(point.width, point.height); +} + constexpr SkRect ToSkiaType(const Rect& rect) { return SkRect::MakeXYWH(rect.GetX(), // rect.GetY(), // @@ -182,14 +186,13 @@ constexpr flutter::DlBlendMode ToDisplayListType(BlendMode mode) { return Mode::kSrcOver; } -inline SkRRect ToSkiaType(const Rect& rect, - const impeller::PathBuilder::RoundingRadii& radii) { +inline SkRRect ToSkiaType(const Rect& rect, const RoundingRadii& radii) { using Corner = SkRRect::Corner; SkVector sk_radii[4]; - sk_radii[Corner::kUpperLeft_Corner] = ToSkiaType(radii.top_left); - sk_radii[Corner::kUpperRight_Corner] = ToSkiaType(radii.top_right); - sk_radii[Corner::kLowerRight_Corner] = ToSkiaType(radii.bottom_right); - sk_radii[Corner::kLowerLeft_Corner] = ToSkiaType(radii.bottom_left); + sk_radii[Corner::kUpperLeft_Corner] = ToSkiaVector(radii.top_left); + sk_radii[Corner::kUpperRight_Corner] = ToSkiaVector(radii.top_right); + sk_radii[Corner::kLowerRight_Corner] = ToSkiaVector(radii.bottom_right); + sk_radii[Corner::kLowerLeft_Corner] = ToSkiaVector(radii.bottom_left); SkRRect result; result.setRectRadii(ToSkiaType(rect), sk_radii); return result; @@ -230,6 +233,10 @@ constexpr Point ToImpellerType(const ImpellerPoint& point) { return Point{point.x, point.y}; } +constexpr Size ToImpellerSize(const ImpellerPoint& point) { + return Size{point.x, point.y}; +} + constexpr Rect ToImpellerType(const ImpellerRect& rect) { return Rect::MakeXYWH(rect.x, rect.y, rect.width, rect.height); } @@ -248,13 +255,12 @@ constexpr flutter::DlTileMode ToDisplayListType(ImpellerTileMode mode) { return flutter::DlTileMode::kClamp; } -constexpr impeller::PathBuilder::RoundingRadii ToImpellerType( - const ImpellerRoundingRadii& radii) { - auto result = impeller::PathBuilder::RoundingRadii{}; - result.top_left = ToImpellerType(radii.top_left); - result.bottom_left = ToImpellerType(radii.bottom_left); - result.top_right = ToImpellerType(radii.top_right); - result.bottom_right = ToImpellerType(radii.bottom_right); +constexpr RoundingRadii ToImpellerType(const ImpellerRoundingRadii& radii) { + auto result = RoundingRadii{}; + result.top_left = ToImpellerSize(radii.top_left); + result.bottom_left = ToImpellerSize(radii.bottom_left); + result.top_right = ToImpellerSize(radii.top_right); + result.bottom_right = ToImpellerSize(radii.bottom_right); return result; } diff --git a/impeller/toolkit/interop/path_builder.cc b/impeller/toolkit/interop/path_builder.cc index a1620185d7375..978b27112fee3 100644 --- a/impeller/toolkit/interop/path_builder.cc +++ b/impeller/toolkit/interop/path_builder.cc @@ -51,9 +51,7 @@ void PathBuilder::AddOval(const Rect& oval_bounds) { builder_.addOval(ToSkiaType(oval_bounds)); } -void PathBuilder::AddRoundedRect( - const Rect& rect, - const impeller::PathBuilder::RoundingRadii& radii) { +void PathBuilder::AddRoundedRect(const Rect& rect, const RoundingRadii& radii) { builder_.addRRect(ToSkiaType(rect, radii)); } diff --git a/impeller/toolkit/interop/path_builder.h b/impeller/toolkit/interop/path_builder.h index 1c9aa532b9078..bb77bbe08abcd 100644 --- a/impeller/toolkit/interop/path_builder.h +++ b/impeller/toolkit/interop/path_builder.h @@ -42,8 +42,7 @@ class PathBuilder final void AddOval(const Rect& oval_bounds); - void AddRoundedRect(const Rect& rect, - const impeller::PathBuilder::RoundingRadii& radii); + void AddRoundedRect(const Rect& rect, const RoundingRadii& radii); void Close(); diff --git a/shell/common/dl_op_spy.cc b/shell/common/dl_op_spy.cc index 77fb2092a549a..34746ae4698b3 100644 --- a/shell/common/dl_op_spy.cc +++ b/shell/common/dl_op_spy.cc @@ -60,10 +60,11 @@ void DlOpSpy::drawOval(const DlRect& bounds) { void DlOpSpy::drawCircle(const DlPoint& center, DlScalar radius) { did_draw_ |= will_draw_; } -void DlOpSpy::drawRRect(const SkRRect& rrect) { +void DlOpSpy::drawRoundRect(const DlRoundRect& rrect) { did_draw_ |= will_draw_; } -void DlOpSpy::drawDRRect(const SkRRect& outer, const SkRRect& inner) { +void DlOpSpy::drawDiffRoundRect(const DlRoundRect& outer, + const DlRoundRect& inner) { did_draw_ |= will_draw_; } void DlOpSpy::drawPath(const DlPath& path) { diff --git a/shell/common/dl_op_spy.h b/shell/common/dl_op_spy.h index 82c2dfc55684b..3c2c63d75f8c9 100644 --- a/shell/common/dl_op_spy.h +++ b/shell/common/dl_op_spy.h @@ -54,8 +54,9 @@ class DlOpSpy final : public virtual DlOpReceiver, void drawRect(const DlRect& rect) override; void drawOval(const DlRect& bounds) override; void drawCircle(const DlPoint& center, DlScalar radius) override; - void drawRRect(const SkRRect& rrect) override; - void drawDRRect(const SkRRect& outer, const SkRRect& inner) override; + void drawRoundRect(const DlRoundRect& rrect) override; + void drawDiffRoundRect(const DlRoundRect& outer, + const DlRoundRect& inner) override; void drawPath(const DlPath& path) override; void drawArc(const DlRect& oval_bounds, DlScalar start_degrees, diff --git a/testing/display_list_testing.cc b/testing/display_list_testing.cc index 4f9065178cbf3..c1815e741af11 100644 --- a/testing/display_list_testing.cc +++ b/testing/display_list_testing.cc @@ -201,20 +201,6 @@ static std::ostream& operator<<(std::ostream& os, const SkRect& rect) { << ")"; } -static std::ostream& operator<<(std::ostream& os, const SkRRect& rrect) { - return os << "SkRRect(" - << rrect.rect() << ", " - << "ul: (" << rrect.radii(SkRRect::kUpperLeft_Corner).fX << ", " - << rrect.radii(SkRRect::kUpperLeft_Corner).fY << "), " - << "ur: (" << rrect.radii(SkRRect::kUpperRight_Corner).fX << ", " - << rrect.radii(SkRRect::kUpperRight_Corner).fY << "), " - << "lr: (" << rrect.radii(SkRRect::kLowerRight_Corner).fX << ", " - << rrect.radii(SkRRect::kLowerRight_Corner).fY << "), " - << "ll: (" << rrect.radii(SkRRect::kLowerLeft_Corner).fX << ", " - << rrect.radii(SkRRect::kLowerLeft_Corner).fY << ")" - << ")"; -} - extern std::ostream& operator<<(std::ostream& os, const DlPath& path) { return os << "DlPath(" << "bounds: " << path.GetSkBounds() @@ -800,9 +786,9 @@ void DisplayListStreamDispatcher::clipOval(const DlRect& bounds, ClipOp clip_op, << "isaa: " << is_aa << ");" << std::endl; } -void DisplayListStreamDispatcher::clipRRect(const SkRRect& rrect, - ClipOp clip_op, - bool is_aa) { +void DisplayListStreamDispatcher::clipRoundRect(const DlRoundRect& rrect, + ClipOp clip_op, + bool is_aa) { startl() << "clipRRect(" << rrect << ", " << clip_op << ", " @@ -852,11 +838,11 @@ void DisplayListStreamDispatcher::drawCircle(const DlPoint& center, DlScalar radius) { startl() << "drawCircle(" << center << ", " << radius << ");" << std::endl; } -void DisplayListStreamDispatcher::drawRRect(const SkRRect& rrect) { +void DisplayListStreamDispatcher::drawRoundRect(const DlRoundRect& rrect) { startl() << "drawRRect(" << rrect << ");" << std::endl; } -void DisplayListStreamDispatcher::drawDRRect(const SkRRect& outer, - const SkRRect& inner) { +void DisplayListStreamDispatcher::drawDiffRoundRect(const DlRoundRect& outer, + const DlRoundRect& inner) { startl() << "drawDRRect(outer: " << outer << ", " << std::endl; startl() << " inner: " << inner << ");" << std::endl; } diff --git a/testing/display_list_testing.h b/testing/display_list_testing.h index 33fcd6b039dc3..96b15e67ccd4d 100644 --- a/testing/display_list_testing.h +++ b/testing/display_list_testing.h @@ -131,7 +131,9 @@ class DisplayListStreamDispatcher final : public DlOpReceiver { void clipRect(const DlRect& rect, ClipOp clip_op, bool is_aa) override; void clipOval(const DlRect& bounds, ClipOp clip_op, bool is_aa) override; - void clipRRect(const SkRRect& rrect, ClipOp clip_op, bool is_aa) override; + void clipRoundRect(const DlRoundRect& rrect, + ClipOp clip_op, + bool is_aa) override; void clipPath(const DlPath& path, ClipOp clip_op, bool is_aa) override; void drawColor(DlColor color, DlBlendMode mode) override; @@ -144,8 +146,9 @@ class DisplayListStreamDispatcher final : public DlOpReceiver { void drawRect(const DlRect& rect) override; void drawOval(const DlRect& bounds) override; void drawCircle(const DlPoint& center, DlScalar radius) override; - void drawRRect(const SkRRect& rrect) override; - void drawDRRect(const SkRRect& outer, const SkRRect& inner) override; + void drawRoundRect(const DlRoundRect& rrect) override; + void drawDiffRoundRect(const DlRoundRect& outer, + const DlRoundRect& inner) override; void drawPath(const DlPath& path) override; void drawArc(const DlRect& oval_bounds, DlScalar start_degrees, @@ -370,15 +373,15 @@ class DisplayListGeneralReceiver : public DlOpReceiver { break; } } - void clipRRect(const SkRRect& rrect, - DlCanvas::ClipOp clip_op, - bool is_aa) override { + void clipRoundRect(const DlRoundRect& rrect, + DlCanvas::ClipOp clip_op, + bool is_aa) override { switch (clip_op) { case DlCanvas::ClipOp::kIntersect: - RecordByType(DisplayListOpType::kClipIntersectRRect); + RecordByType(DisplayListOpType::kClipIntersectRoundRect); break; case DlCanvas::ClipOp::kDifference: - RecordByType(DisplayListOpType::kClipDifferenceRRect); + RecordByType(DisplayListOpType::kClipDifferenceRoundRect); break; } } @@ -430,11 +433,12 @@ class DisplayListGeneralReceiver : public DlOpReceiver { void drawCircle(const DlPoint& center, DlScalar radius) override { RecordByType(DisplayListOpType::kDrawCircle); } - void drawRRect(const SkRRect& rrect) override { - RecordByType(DisplayListOpType::kDrawRRect); + void drawRoundRect(const DlRoundRect& rrect) override { + RecordByType(DisplayListOpType::kDrawRoundRect); } - void drawDRRect(const SkRRect& outer, const SkRRect& inner) override { - RecordByType(DisplayListOpType::kDrawDRRect); + void drawDiffRoundRect(const DlRoundRect& outer, + const DlRoundRect& inner) override { + RecordByType(DisplayListOpType::kDrawDiffRoundRect); } void drawPath(const DlPath& path) override { RecordByType(DisplayListOpType::kDrawPath);