[Impeller] compute path bounds once, use Skia computed bounds where possible. (flutter/engine#45456)
Now that Paths are externally immutable, we can compute the bounds once at construction. This also allows the Skia conversions to set the already computed bounds.
This commit is contained in:
@@ -122,6 +122,8 @@ Path ToPath(const SkPath& path, Point shift) {
|
||||
builder.SetConvexity(path.isConvex() ? Convexity::kConvex
|
||||
: Convexity::kUnknown);
|
||||
builder.Shift(shift);
|
||||
auto sk_bounds = path.getBounds().makeOutset(shift.x, shift.y);
|
||||
builder.SetBounds(ToRect(sk_bounds));
|
||||
return builder.TakePath(fill_type);
|
||||
}
|
||||
|
||||
@@ -129,6 +131,7 @@ Path ToPath(const SkRRect& rrect) {
|
||||
return PathBuilder{}
|
||||
.AddRoundedRect(ToRect(rrect.getBounds()), ToRoundingRadii(rrect))
|
||||
.SetConvexity(Convexity::kConvex)
|
||||
.SetBounds(ToRect(rrect.getBounds()))
|
||||
.TakePath();
|
||||
}
|
||||
|
||||
|
||||
@@ -2422,6 +2422,25 @@ TEST(GeometryTest, PathShifting) {
|
||||
ASSERT_EQ(cubic.p2, Point(31, 31));
|
||||
}
|
||||
|
||||
TEST(GeometryTest, PathBuilderWillComputeBounds) {
|
||||
PathBuilder builder;
|
||||
auto path_1 = builder.AddLine({0, 0}, {1, 1}).TakePath();
|
||||
|
||||
ASSERT_EQ(path_1.GetBoundingBox().value(), Rect::MakeLTRB(0, 0, 1, 1));
|
||||
|
||||
auto path_2 = builder.AddLine({-1, -1}, {1, 1}).TakePath();
|
||||
|
||||
// Verify that PathBuilder recomputes the bounds.
|
||||
ASSERT_EQ(path_2.GetBoundingBox().value(), Rect::MakeLTRB(-1, -1, 1, 1));
|
||||
|
||||
// PathBuilder can set the bounds to whatever it wants
|
||||
auto path_3 = builder.AddLine({0, 0}, {1, 1})
|
||||
.SetBounds(Rect::MakeLTRB(0, 0, 100, 100))
|
||||
.TakePath();
|
||||
|
||||
ASSERT_EQ(path_3.GetBoundingBox().value(), Rect::MakeLTRB(0, 0, 100, 100));
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace impeller
|
||||
|
||||
|
||||
@@ -397,14 +397,19 @@ Path::Polyline Path::CreatePolyline(Scalar scale) const {
|
||||
}
|
||||
|
||||
std::optional<Rect> Path::GetBoundingBox() const {
|
||||
return computed_bounds_;
|
||||
}
|
||||
|
||||
void Path::ComputeBounds() {
|
||||
auto min_max = GetMinMaxCoveragePoints();
|
||||
if (!min_max.has_value()) {
|
||||
return std::nullopt;
|
||||
computed_bounds_ = std::nullopt;
|
||||
return;
|
||||
}
|
||||
auto min = min_max->first;
|
||||
auto max = min_max->second;
|
||||
const auto difference = max - min;
|
||||
return Rect{min.x, min.y, difference.x, difference.y};
|
||||
computed_bounds_ = Rect{min.x, min.y, difference.x, difference.y};
|
||||
}
|
||||
|
||||
std::optional<Rect> Path::GetTransformedBoundingBox(
|
||||
@@ -461,4 +466,8 @@ std::optional<std::pair<Point, Point>> Path::GetMinMaxCoveragePoints() const {
|
||||
return std::make_pair(min.value(), max.value());
|
||||
}
|
||||
|
||||
void Path::SetBounds(Rect rect) {
|
||||
computed_bounds_ = rect;
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
|
||||
@@ -138,6 +138,8 @@ class Path {
|
||||
|
||||
void SetFillType(FillType fill);
|
||||
|
||||
void SetBounds(Rect rect);
|
||||
|
||||
Path& AddLinearComponent(Point p1, Point p2);
|
||||
|
||||
Path& AddQuadraticComponent(Point p1, Point cp, Point p2);
|
||||
@@ -146,6 +148,12 @@ class Path {
|
||||
|
||||
Path& AddContourComponent(Point destination, bool is_closed = false);
|
||||
|
||||
/// @brief Called by `PathBuilder` to compute the bounds for certain paths.
|
||||
///
|
||||
/// `PathBuilder` may set the bounds directly, in case they come from a source
|
||||
/// with already computed bounds, such as an SkPath.
|
||||
void ComputeBounds();
|
||||
|
||||
void SetContourClosed(bool is_closed);
|
||||
|
||||
void Shift(Point shift);
|
||||
@@ -178,6 +186,8 @@ class Path {
|
||||
std::vector<QuadraticPathComponent> quads_;
|
||||
std::vector<CubicPathComponent> cubics_;
|
||||
std::vector<ContourComponent> contours_;
|
||||
|
||||
std::optional<Rect> computed_bounds_;
|
||||
};
|
||||
|
||||
} // namespace impeller
|
||||
|
||||
@@ -22,6 +22,10 @@ Path PathBuilder::TakePath(FillType fill) {
|
||||
auto path = prototype_;
|
||||
path.SetFillType(fill);
|
||||
path.SetConvexity(convexity_);
|
||||
if (!did_compute_bounds_) {
|
||||
path.ComputeBounds();
|
||||
}
|
||||
did_compute_bounds_ = false;
|
||||
return path;
|
||||
}
|
||||
|
||||
@@ -458,4 +462,10 @@ PathBuilder& PathBuilder::Shift(Point offset) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
PathBuilder& PathBuilder::SetBounds(Rect bounds) {
|
||||
prototype_.SetBounds(bounds);
|
||||
did_compute_bounds_ = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
|
||||
@@ -97,6 +97,14 @@ class PathBuilder {
|
||||
/// `offset`.
|
||||
PathBuilder& Shift(Point offset);
|
||||
|
||||
/// @brief Set the bounding box that will be used by `Path.GetBoundingBox` in
|
||||
/// place of performing the computation.
|
||||
///
|
||||
/// When Impeller recieves Skia Path objects, many of these already
|
||||
/// have computed bounds. This method is used to avoid needlessly
|
||||
/// recomputing these bounds.
|
||||
PathBuilder& SetBounds(Rect bounds);
|
||||
|
||||
struct RoundingRadii {
|
||||
Point top_left;
|
||||
Point bottom_left;
|
||||
@@ -133,6 +141,7 @@ class PathBuilder {
|
||||
Point current_;
|
||||
Path prototype_;
|
||||
Convexity convexity_;
|
||||
bool did_compute_bounds_ = false;
|
||||
|
||||
PathBuilder& AddRoundedRectTopLeft(Rect rect, RoundingRadii radii);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user