Skip to content

Commit

Permalink
Support non-uniform rounding radii.
Browse files Browse the repository at this point in the history
  • Loading branch information
chinmaygarde authored and dnfield committed Apr 27, 2022
1 parent cb54794 commit e3d4357
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 75 deletions.
21 changes: 21 additions & 0 deletions impeller/aiks/aiks_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -224,5 +224,26 @@ TEST_F(AiksTest,
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}

TEST_F(AiksTest, CanRenderRoundedRectWithNonUniformRadii) {
Canvas canvas;

Paint paint;
paint.color = Color::Red();

PathBuilder::RoundingRadii radii;
radii.top_left = {50, 25};
radii.top_right = {25, 50};
radii.bottom_right = {50, 25};
radii.bottom_left = {25, 50};

auto path = PathBuilder{}
.AddRoundedRect(Rect{100, 100, 500, 500}, radii)
.CreatePath();

canvas.DrawPath(path, paint);

ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}

} // namespace testing
} // namespace impeller
117 changes: 61 additions & 56 deletions impeller/geometry/path_builder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -163,92 +163,97 @@ PathBuilder& PathBuilder::AddCircle(const Point& c, Scalar r) {
}

PathBuilder& PathBuilder::AddRoundedRect(Rect rect, Scalar radius) {
return radius == 0.0 ? AddRect(rect)
return radius <= 0.0 ? AddRect(rect)
: AddRoundedRect(rect, {radius, radius, radius, radius});
}

PathBuilder& PathBuilder::AddRoundedRect(Rect rect, RoundingRadii radii) {
current_ = rect.origin + Point{radii.topLeft, 0.0};
if (radii.AreAllZero()) {
return AddRect(rect);
}

current_ = rect.origin + Point{radii.top_left.x, 0.0};

const Scalar magic_top_right = kArcApproximationMagic * radii.topRight;
const Scalar magic_bottom_right = kArcApproximationMagic * radii.bottomRight;
const Scalar magic_bottom_left = kArcApproximationMagic * radii.bottomLeft;
const Scalar magic_top_left = kArcApproximationMagic * radii.topLeft;
const auto magic_top_right = radii.top_right * kArcApproximationMagic;
const auto magic_bottom_right = radii.bottom_right * kArcApproximationMagic;
const auto magic_bottom_left = radii.bottom_left * kArcApproximationMagic;
const auto magic_top_left = radii.top_left * kArcApproximationMagic;

//----------------------------------------------------------------------------
/// Top line.
///
// Top line.
//
prototype_.AddLinearComponent(
{rect.origin.x + radii.topLeft, rect.origin.y},
{rect.origin.x + rect.size.width - radii.topRight, rect.origin.y});
{rect.origin.x + radii.top_left.x, rect.origin.y},
{rect.origin.x + rect.size.width - radii.top_right.x, rect.origin.y});

//----------------------------------------------------------------------------
/// Top right arc.
///
// Top right arc.
//
prototype_.AddCubicComponent(
{rect.origin.x + rect.size.width - radii.topRight, rect.origin.y},
{rect.origin.x + rect.size.width - radii.topRight + magic_top_right,
{rect.origin.x + rect.size.width - radii.top_right.x, rect.origin.y},
{rect.origin.x + rect.size.width - radii.top_right.x + magic_top_right.x,
rect.origin.y},
{rect.origin.x + rect.size.width,
rect.origin.y + radii.topRight - magic_top_right},
{rect.origin.x + rect.size.width, rect.origin.y + radii.topRight});
rect.origin.y + radii.top_right.y - magic_top_right.y},
{rect.origin.x + rect.size.width, rect.origin.y + radii.top_right.y});

//----------------------------------------------------------------------------
/// Right line.
///
// Right line.
//
prototype_.AddLinearComponent(
{rect.origin.x + rect.size.width, rect.origin.y + radii.topRight},
{rect.origin.x + rect.size.width, rect.origin.y + radii.top_right.y},
{rect.origin.x + rect.size.width,
rect.origin.y + rect.size.height - radii.bottomRight});
rect.origin.y + rect.size.height - radii.bottom_right.y});

//----------------------------------------------------------------------------
/// Bottom right arc.
///
// Bottom right arc.
//
prototype_.AddCubicComponent(
{rect.origin.x + rect.size.width,
rect.origin.y + rect.size.height - radii.bottomRight},
rect.origin.y + rect.size.height - radii.bottom_right.y},
{rect.origin.x + rect.size.width, rect.origin.y + rect.size.height -
radii.bottomRight +
magic_bottom_right},
{rect.origin.x + rect.size.width - radii.bottomRight + magic_bottom_right,
radii.bottom_right.y +
magic_bottom_right.y},
{rect.origin.x + rect.size.width - radii.bottom_right.x +
magic_bottom_right.x,
rect.origin.y + rect.size.height},
{rect.origin.x + rect.size.width - radii.bottomRight,
{rect.origin.x + rect.size.width - radii.bottom_right.x,
rect.origin.y + rect.size.height});

//----------------------------------------------------------------------------
/// Bottom line.
///
// Bottom line.
//
prototype_.AddLinearComponent(
{rect.origin.x + rect.size.width - radii.bottomRight,
{rect.origin.x + rect.size.width - radii.bottom_right.x,
rect.origin.y + rect.size.height},
{rect.origin.x + radii.bottomLeft, rect.origin.y + rect.size.height});
{rect.origin.x + radii.bottom_left.x, rect.origin.y + rect.size.height});

//----------------------------------------------------------------------------
/// Bottom left arc.
///
// Bottom left arc.
//
prototype_.AddCubicComponent(
{rect.origin.x + radii.bottomLeft, rect.origin.y + rect.size.height},
{rect.origin.x + radii.bottomLeft - magic_bottom_left,
{rect.origin.x + radii.bottom_left.x, rect.origin.y + rect.size.height},
{rect.origin.x + radii.bottom_left.x - magic_bottom_left.x,
rect.origin.y + rect.size.height},
{rect.origin.x,
rect.origin.y + rect.size.height - radii.bottomLeft + magic_bottom_left},
{rect.origin.x, rect.origin.y + rect.size.height - radii.bottomLeft});
{rect.origin.x, rect.origin.y + rect.size.height - radii.bottom_left.y +
magic_bottom_left.y},
{rect.origin.x, rect.origin.y + rect.size.height - radii.bottom_left.y});

//----------------------------------------------------------------------------
/// Left line.
///
// Left line.
//
prototype_.AddLinearComponent(
{rect.origin.x, rect.origin.y + rect.size.height - radii.bottomLeft},
{rect.origin.x, rect.origin.y + radii.topLeft});
{rect.origin.x, rect.origin.y + rect.size.height - radii.bottom_left.y},
{rect.origin.x, rect.origin.y + radii.top_left.y});

//----------------------------------------------------------------------------
/// Top left arc.
///
// Top left arc.
//
prototype_.AddCubicComponent(
{rect.origin.x, rect.origin.y + radii.topLeft},
{rect.origin.x, rect.origin.y + radii.topLeft - magic_top_left},
{rect.origin.x + radii.topLeft - magic_top_left, rect.origin.y},
{rect.origin.x + radii.topLeft, rect.origin.y});
{rect.origin.x, rect.origin.y + radii.top_left.y},
{rect.origin.x, rect.origin.y + radii.top_left.y - magic_top_left.y},
{rect.origin.x + radii.top_left.x - magic_top_left.x, rect.origin.y},
{rect.origin.x + radii.top_left.x, rect.origin.y});

return *this;
}
Expand All @@ -260,35 +265,35 @@ PathBuilder& PathBuilder::AddOval(const Rect& container) {
const Point m = {kArcApproximationMagic * r.x, kArcApproximationMagic * r.y};

//----------------------------------------------------------------------------
/// Top right arc.
///
// Top right arc.
//
prototype_.AddCubicComponent({c.x, c.y - r.y}, // p1
{c.x + m.x, c.y - r.y}, // cp1
{c.x + r.x, c.y - m.y}, // cp2
{c.x + r.x, c.y} // p2
);

//----------------------------------------------------------------------------
/// Bottom right arc.
///
// Bottom right arc.
//
prototype_.AddCubicComponent({c.x + r.x, c.y}, // p1
{c.x + r.x, c.y + m.y}, // cp1
{c.x + m.x, c.y + r.y}, // cp2
{c.x, c.y + r.y} // p2
);

//----------------------------------------------------------------------------
/// Bottom left arc.
///
// Bottom left arc.
//
prototype_.AddCubicComponent({c.x, c.y + r.y}, // p1
{c.x - m.x, c.y + r.y}, // cp1
{c.x - r.x, c.y + m.y}, // cp2
{c.x - r.x, c.y} // p2
);

//----------------------------------------------------------------------------
/// Top left arc.
///
// Top left arc.
//
prototype_.AddCubicComponent({c.x - r.x, c.y}, // p1
{c.x - r.x, c.y - m.y}, // cp1
{c.x - m.x, c.y - r.y}, // cp2
Expand Down
41 changes: 24 additions & 17 deletions impeller/geometry/path_builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,34 +48,41 @@ class PathBuilder {

PathBuilder& AddRect(Rect rect);

PathBuilder& AddRoundedRect(Rect rect, Scalar radius);

PathBuilder& AddCircle(const Point& center, Scalar radius);

PathBuilder& AddOval(const Rect& rect);

PathBuilder& AddLine(const Point& p1, const Point& p2);

struct RoundingRadii {
Scalar topLeft = 0.0;
Scalar bottomLeft = 0.0;
Scalar topRight = 0.0;
Scalar bottomRight = 0.0;

RoundingRadii() {}

RoundingRadii(Scalar pTopLeft,
Scalar pBottomLeft,
Scalar pTopRight,
Scalar pBottomRight)
: topLeft(pTopLeft),
bottomLeft(pBottomLeft),
topRight(pTopRight),
bottomRight(pBottomRight) {}
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) {}

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, Scalar radius);

private:
Point subpath_start_;
Point current_;
Expand Down
6 changes: 4 additions & 2 deletions impeller/geometry/point.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ struct TPoint {
return {x - s.width, y - s.height};
}

constexpr TPoint operator*(Type scale) const {
constexpr TPoint operator*(Scalar scale) const {
return {x * scale, y * scale};
}

Expand All @@ -69,7 +69,7 @@ struct TPoint {
return {x * s.width, y * s.height};
}

constexpr TPoint operator/(Type d) const { return {x / d, y / d}; }
constexpr TPoint operator/(Scalar d) const { return {x / d, y / d}; }

constexpr TPoint operator/(const TPoint& p) const {
return {x / p.x, y / p.y};
Expand Down Expand Up @@ -108,6 +108,8 @@ struct TPoint {
}
return {x / length, y / length};
}

constexpr bool IsZero() const { return x == 0 && y == 0; }
};

using Point = TPoint<Scalar>;
Expand Down

0 comments on commit e3d4357

Please sign in to comment.