Skip to content

Commit

Permalink
[Impeller] Use direct tessellation geometry for simple clip shapes (f…
Browse files Browse the repository at this point in the history
…lutter#48959)

This work leverages the recent work to directly tessellate the simple primitives for rendering and uses their geometry classes for equivalent clip primitives.
  • Loading branch information
flar authored Dec 13, 2023
1 parent 251efb1 commit 44fdca9
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 17 deletions.
68 changes: 68 additions & 0 deletions impeller/aiks/aiks_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<Color> 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<Scalar> 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;
Expand Down
31 changes: 21 additions & 10 deletions impeller/aiks/canvas.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<Rect> inner_rect = (flat_on_LR && flat_on_TB)
? rect.Expand(-corner_radii)
: std::make_optional<Rect>();
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() && //
Expand Down
4 changes: 4 additions & 0 deletions impeller/aiks/canvas.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,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,
Expand Down
32 changes: 25 additions & 7 deletions impeller/display_list/dl_dispatcher.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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|
Expand Down

0 comments on commit 44fdca9

Please sign in to comment.