diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 43ce3a337b997..3fdfec01ae0df 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -318,6 +318,74 @@ TEST_P(AiksTest, CanRenderClips) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } +TEST_P(AiksTest, CanRenderSimpleClips) { + Canvas canvas; + canvas.Scale(GetContentScale()); + Paint paint; + + paint.color = Color::White(); + canvas.DrawPaint(paint); + + auto draw = [&canvas](const Paint& paint, Scalar x, Scalar y) { + canvas.Save(); + canvas.Translate({x, y}); + { + canvas.Save(); + canvas.ClipRect(Rect::MakeLTRB(50, 50, 150, 150)); + canvas.DrawPaint(paint); + canvas.Restore(); + } + { + canvas.Save(); + canvas.ClipOval(Rect::MakeLTRB(200, 50, 300, 150)); + canvas.DrawPaint(paint); + canvas.Restore(); + } + { + canvas.Save(); + canvas.ClipRRect(Rect::MakeLTRB(50, 200, 150, 300), {20, 20}); + canvas.DrawPaint(paint); + canvas.Restore(); + } + canvas.Restore(); + }; + + paint.color = Color::Blue(); + draw(paint, 0, 0); + + std::vector gradient_colors = { + Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0}, + Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0}, + Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0}, + Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0}, + Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0}, + Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0}, + Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}}; + std::vector stops = { + 0.0, + (1.0 / 6.0) * 1, + (1.0 / 6.0) * 2, + (1.0 / 6.0) * 3, + (1.0 / 6.0) * 4, + (1.0 / 6.0) * 5, + 1.0, + }; + auto texture = CreateTextureForFixture("airplane.jpg", + /*enable_mipmapping=*/true); + + paint.color_source = ColorSource::MakeRadialGradient( + {500, 600}, 75, std::move(gradient_colors), std::move(stops), + Entity::TileMode::kMirror, {}); + draw(paint, 0, 300); + + paint.color_source = ColorSource::MakeImage( + texture, Entity::TileMode::kRepeat, Entity::TileMode::kRepeat, {}, + Matrix::MakeTranslation({0, 0})); + draw(paint, 300, 0); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + TEST_P(AiksTest, CanRenderNestedClips) { Canvas canvas; Paint paint; diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index 6e51001b87b78..a7b290fbb5d45 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -448,23 +448,34 @@ void Canvas::ClipRect(const Rect& rect, Entity::ClipOperation clip_op) { } } +void Canvas::ClipOval(const Rect& bounds, Entity::ClipOperation clip_op) { + auto geometry = Geometry::MakeOval(bounds); + auto& cull_rect = transform_stack_.back().cull_rect; + if (clip_op == Entity::ClipOperation::kIntersect && // + cull_rect.has_value() && // + geometry->CoversArea(transform_stack_.back().transform, *cull_rect) // + ) { + return; // This clip will do nothing, so skip it. + } + + ClipGeometry(geometry, clip_op); + switch (clip_op) { + case Entity::ClipOperation::kIntersect: + IntersectCulling(bounds); + break; + case Entity::ClipOperation::kDifference: + break; + } +} + void Canvas::ClipRRect(const Rect& rect, const Size& corner_radii, Entity::ClipOperation clip_op) { - auto path = PathBuilder{} - .SetConvexity(Convexity::kConvex) - .AddRoundedRect(rect, corner_radii) - .SetBounds(rect) - .TakePath(); - auto size = rect.GetSize(); // Does the rounded rect have a flat part on the top/bottom or left/right? bool flat_on_TB = corner_radii.width * 2 < size.width; bool flat_on_LR = corner_radii.height * 2 < size.height; - std::optional inner_rect = (flat_on_LR && flat_on_TB) - ? rect.Expand(-corner_radii) - : std::make_optional(); - auto geometry = Geometry::MakeFillPath(std::move(path), inner_rect); + auto geometry = Geometry::MakeRoundRect(rect, corner_radii); auto& cull_rect = transform_stack_.back().cull_rect; if (clip_op == Entity::ClipOperation::kIntersect && // cull_rect.has_value() && // diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h index b1c6bab07340c..7258202302e80 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -136,6 +136,10 @@ class Canvas { const Rect& rect, Entity::ClipOperation clip_op = Entity::ClipOperation::kIntersect); + void ClipOval( + const Rect& bounds, + Entity::ClipOperation clip_op = Entity::ClipOperation::kIntersect); + void ClipRRect( const Rect& rect, const Size& corner_radii, diff --git a/impeller/display_list/dl_dispatcher.cc b/impeller/display_list/dl_dispatcher.cc index 12681579fbb91..dca79aba9ee85 100644 --- a/impeller/display_list/dl_dispatcher.cc +++ b/impeller/display_list/dl_dispatcher.cc @@ -725,22 +725,40 @@ void DlDispatcher::clipRect(const SkRect& rect, ClipOp clip_op, bool is_aa) { } // |flutter::DlOpReceiver| -void DlDispatcher::clipRRect(const SkRRect& rrect, ClipOp clip_op, bool is_aa) { +void DlDispatcher::clipRRect(const SkRRect& rrect, ClipOp sk_op, bool is_aa) { + auto clip_op = ToClipOperation(sk_op); if (rrect.isRect()) { - canvas_.ClipRect(skia_conversions::ToRect(rrect.rect()), - ToClipOperation(clip_op)); + canvas_.ClipRect(skia_conversions::ToRect(rrect.rect()), clip_op); + } else if (rrect.isOval()) { + canvas_.ClipOval(skia_conversions::ToRect(rrect.rect()), clip_op); } else if (rrect.isSimple()) { canvas_.ClipRRect(skia_conversions::ToRect(rrect.rect()), skia_conversions::ToSize(rrect.getSimpleRadii()), - ToClipOperation(clip_op)); + clip_op); } else { - canvas_.ClipPath(skia_conversions::ToPath(rrect), ToClipOperation(clip_op)); + canvas_.ClipPath(skia_conversions::ToPath(rrect), clip_op); } } // |flutter::DlOpReceiver| -void DlDispatcher::clipPath(const SkPath& path, ClipOp clip_op, bool is_aa) { - canvas_.ClipPath(skia_conversions::ToPath(path), ToClipOperation(clip_op)); +void DlDispatcher::clipPath(const SkPath& path, ClipOp sk_op, bool is_aa) { + auto clip_op = ToClipOperation(sk_op); + + SkRect rect; + if (path.isRect(&rect)) { + canvas_.ClipRect(skia_conversions::ToRect(rect), clip_op); + } else if (path.isOval(&rect)) { + canvas_.ClipOval(skia_conversions::ToRect(rect), clip_op); + } else { + SkRRect rrect; + if (path.isRRect(&rrect) && rrect.isSimple()) { + canvas_.ClipRRect(skia_conversions::ToRect(rrect.rect()), + skia_conversions::ToSize(rrect.getSimpleRadii()), + clip_op); + } else { + canvas_.ClipPath(skia_conversions::ToPath(path), clip_op); + } + } } // |flutter::DlOpReceiver|