[Impeller] Specialize RRect geometry (#40337)
[Impeller] Specialize RRect geometry
diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc
index a1dbd35..ec41a4c 100644
--- a/impeller/aiks/canvas.cc
+++ b/impeller/aiks/canvas.cc
@@ -237,7 +237,20 @@
if (AttemptDrawBlurredRRect(rect, corner_radius, paint)) {
return;
}
- DrawPath(PathBuilder{}.AddRoundedRect(rect, corner_radius).TakePath(), paint);
+ if (paint.style == Paint::Style::kFill) {
+ Entity entity;
+ entity.SetTransformation(GetCurrentTransformation());
+ entity.SetStencilDepth(GetStencilDepth());
+ entity.SetBlendMode(paint.blend_mode);
+ entity.SetContents(paint.WithFilters(paint.CreateContentsForGeometry(
+ Geometry::MakeRRect(rect, corner_radius))));
+
+ GetCurrentPass().AddEntity(entity);
+ return;
+ } else {
+ DrawPath(PathBuilder{}.AddRoundedRect(rect, corner_radius).TakePath(),
+ paint);
+ }
}
void Canvas::DrawCircle(Point center, Scalar radius, const Paint& paint) {
@@ -250,8 +263,23 @@
}
void Canvas::ClipPath(const Path& path, Entity::ClipOperation clip_op) {
+ ClipGeometry(Geometry::MakeFillPath(path), clip_op);
+}
+
+void Canvas::ClipRect(const Rect& rect, Entity::ClipOperation clip_op) {
+ ClipGeometry(Geometry::MakeRect(rect), clip_op);
+}
+
+void Canvas::ClipRRect(const Rect& rect,
+ Scalar corner_radius,
+ Entity::ClipOperation clip_op) {
+ ClipGeometry(Geometry::MakeRRect(rect, corner_radius), clip_op);
+}
+
+void Canvas::ClipGeometry(std::unique_ptr<Geometry> geometry,
+ Entity::ClipOperation clip_op) {
auto contents = std::make_shared<ClipContents>();
- contents->SetGeometry(Geometry::MakeFillPath(path));
+ contents->SetGeometry(std::move(geometry));
contents->SetClipOperation(clip_op);
Entity entity;
@@ -379,7 +407,7 @@
// the size of the render target that would have been allocated will be
// absent. Explicitly add back a clip to reproduce that behavior. Since
// clips never require a render target switch, this is a cheap operation.
- ClipPath(PathBuilder{}.AddRect(bounds.value()).TakePath());
+ ClipRect(bounds.value());
}
}
diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h
index 17f2d1b..80b9c74 100644
--- a/impeller/aiks/canvas.h
+++ b/impeller/aiks/canvas.h
@@ -91,6 +91,15 @@
const Path& path,
Entity::ClipOperation clip_op = Entity::ClipOperation::kIntersect);
+ void ClipRect(
+ const Rect& rect,
+ Entity::ClipOperation clip_op = Entity::ClipOperation::kIntersect);
+
+ void ClipRRect(
+ const Rect& rect,
+ Scalar corner_radius,
+ Entity::ClipOperation clip_op = Entity::ClipOperation::kIntersect);
+
void DrawPicture(Picture picture);
void DrawTextFrame(const TextFrame& text_frame,
@@ -126,6 +135,9 @@
size_t GetStencilDepth() const;
+ void ClipGeometry(std::unique_ptr<Geometry> geometry,
+ Entity::ClipOperation clip_op);
+
void Save(bool create_subpass,
BlendMode = BlendMode::kSourceOver,
std::optional<EntityPass::BackdropFilterProc> backdrop_filter =
diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc
index e7ee036..06ee1ee 100644
--- a/impeller/display_list/display_list_dispatcher.cc
+++ b/impeller/display_list/display_list_dispatcher.cc
@@ -938,8 +938,7 @@
void DisplayListDispatcher::clipRect(const SkRect& rect,
ClipOp clip_op,
bool is_aa) {
- auto path = PathBuilder{}.AddRect(ToRect(rect)).TakePath();
- canvas_.ClipPath(path, ToClipOperation(clip_op));
+ canvas_.ClipRect(ToRect(rect), ToClipOperation(clip_op));
}
static PathBuilder::RoundingRadii ToRoundingRadii(const SkRRect& rrect) {
@@ -1038,7 +1037,12 @@
void DisplayListDispatcher::clipRRect(const SkRRect& rrect,
ClipOp clip_op,
bool is_aa) {
- canvas_.ClipPath(ToPath(rrect), ToClipOperation(clip_op));
+ if (rrect.isSimple()) {
+ canvas_.ClipRRect(ToRect(rrect.rect()), rrect.getSimpleRadii().fX,
+ ToClipOperation(clip_op));
+ } else {
+ canvas_.ClipPath(ToPath(rrect), ToClipOperation(clip_op));
+ }
}
// |flutter::DlOpReceiver|
diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc
index 9afede2..8f0ea29 100644
--- a/impeller/entity/entity_unittests.cc
+++ b/impeller/entity/entity_unittests.cc
@@ -219,6 +219,18 @@
ASSERT_TRUE(OpenPlaygroundHere(entity));
}
+TEST_P(EntityTest, CanDrawRRect) {
+ auto contents = std::make_shared<SolidColorContents>();
+ contents->SetGeometry(Geometry::MakeRRect({100, 100, 100, 100}, 10.0));
+ contents->SetColor(Color::Red());
+
+ Entity entity;
+ entity.SetTransformation(Matrix::MakeScale(GetContentScale()));
+ entity.SetContents(contents);
+
+ ASSERT_TRUE(OpenPlaygroundHere(entity));
+}
+
TEST_P(EntityTest, GeometryBoundsAreTransformed) {
auto geometry = Geometry::MakeRect({100, 100, 100, 100});
auto transform = Matrix::MakeScale({2.0, 2.0, 2.0});
diff --git a/impeller/entity/geometry.cc b/impeller/entity/geometry.cc
index 0c2c0d2..80bf058 100644
--- a/impeller/entity/geometry.cc
+++ b/impeller/entity/geometry.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "impeller/entity/geometry.h"
+
#include "impeller/entity/contents/content_context.h"
#include "impeller/entity/entity.h"
#include "impeller/entity/position_color.vert.h"
@@ -32,6 +33,11 @@
return std::make_unique<FillPathGeometry>(path);
}
+// static
+std::unique_ptr<Geometry> Geometry::MakeRRect(Rect rect, Scalar corner_radius) {
+ return std::make_unique<RRectGeometry>(rect, corner_radius);
+}
+
std::unique_ptr<Geometry> Geometry::MakeStrokePath(const Path& path,
Scalar stroke_width,
Scalar miter_limit,
@@ -706,4 +712,163 @@
return rect_.TransformBounds(transform);
}
+/////// RRect Geometry ///////
+
+RRectGeometry::RRectGeometry(Rect rect, Scalar corner_radius)
+ : rect_(rect), corner_radius_(corner_radius) {}
+
+RRectGeometry::~RRectGeometry() = default;
+
+static void AppendRRectCorner(Path::Polyline polyline,
+ Point corner,
+ VertexBufferBuilder<Point>& vtx_builder) {
+ for (auto i = 1u; i < polyline.points.size(); i++) {
+ vtx_builder.AddVertices({
+ polyline.points[i - 1],
+ polyline.points[i],
+ corner,
+ });
+ }
+}
+
+VertexBufferBuilder<Point> RRectGeometry::CreatePositionBuffer(
+ const Entity& entity) const {
+ VertexBufferBuilder<Point> vtx_builder;
+
+ // The rounded rectangle is split into parts:
+ // * four corner sections defined by an arc
+ // * An interior shape composed of three rectangles.
+
+ auto left = rect_.GetLeft();
+ auto right = rect_.GetRight();
+ auto bottom = rect_.GetBottom();
+ auto top = rect_.GetTop();
+ auto radii = PathBuilder::RoundingRadii(corner_radius_, corner_radius_,
+ corner_radius_, corner_radius_);
+
+ auto topLeft =
+ PathBuilder{}
+ .MoveTo({rect_.origin.x, rect_.origin.y + corner_radius_})
+ .AddRoundedRectTopLeft(rect_, radii)
+ .TakePath()
+ .CreatePolyline(entity.GetTransformation().GetMaxBasisLength());
+ auto topRight =
+ PathBuilder{}
+ .MoveTo({right - radii.top_right.x, rect_.origin.y})
+ .AddRoundedRectTopRight(rect_, radii)
+ .TakePath()
+ .CreatePolyline(entity.GetTransformation().GetMaxBasisLength());
+ auto bottomLeft =
+ PathBuilder{}
+ .MoveTo({left + corner_radius_, bottom})
+ .AddRoundedRectBottomLeft(rect_, radii)
+ .TakePath()
+ .CreatePolyline(entity.GetTransformation().GetMaxBasisLength());
+ auto bottomRight =
+ PathBuilder{}
+ .MoveTo({right, bottom - corner_radius_})
+ .AddRoundedRectBottomRight(rect_, radii)
+ .TakePath()
+ .CreatePolyline(entity.GetTransformation().GetMaxBasisLength());
+
+ vtx_builder.Reserve(12 * (topLeft.points.size() - 1) + 18);
+
+ AppendRRectCorner(topLeft, Point(left + corner_radius_, top + corner_radius_),
+ vtx_builder);
+
+ AppendRRectCorner(topRight,
+ Point(right - corner_radius_, top + corner_radius_),
+ vtx_builder);
+
+ AppendRRectCorner(bottomLeft,
+ Point(left + corner_radius_, bottom - corner_radius_),
+ vtx_builder);
+
+ AppendRRectCorner(bottomRight,
+ Point(right - corner_radius_, bottom - corner_radius_),
+ vtx_builder);
+ vtx_builder.AddVertices({
+ // Top Component.
+ Point(left + corner_radius_, top + corner_radius_),
+ Point(left + corner_radius_, top),
+ Point(right - corner_radius_, top + corner_radius_),
+
+ Point(left + corner_radius_, top),
+ Point(right - corner_radius_, top + corner_radius_),
+ Point(right - corner_radius_, top),
+
+ // Bottom Component.
+ Point(left + corner_radius_, bottom - corner_radius_),
+ Point(left + corner_radius_, bottom),
+ Point(right - corner_radius_, bottom - corner_radius_),
+
+ Point(left + corner_radius_, bottom),
+ Point(right - corner_radius_, bottom - corner_radius_),
+ Point(right - corner_radius_, bottom),
+
+ // // Center Component.
+ Point(left, top + corner_radius_),
+ Point(right, top + corner_radius_),
+ Point(right, bottom - corner_radius_),
+
+ Point(left, top + corner_radius_),
+ Point(left, bottom - corner_radius_),
+ Point(right, bottom - corner_radius_),
+ });
+
+ return vtx_builder;
+}
+
+GeometryResult RRectGeometry::GetPositionBuffer(const ContentContext& renderer,
+ const Entity& entity,
+ RenderPass& pass) {
+ auto vtx_builder = CreatePositionBuffer(entity);
+
+ return GeometryResult{
+ .type = PrimitiveType::kTriangle,
+ .vertex_buffer =
+ vtx_builder.CreateVertexBuffer(pass.GetTransientsBuffer()),
+ .transform = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
+ entity.GetTransformation(),
+ .prevent_overdraw = false,
+ };
+}
+
+GeometryResult RRectGeometry::GetPositionUVBuffer(
+ Rect texture_coverage,
+ Matrix effect_transform,
+ const ContentContext& renderer,
+ const Entity& entity,
+ RenderPass& pass) {
+ auto vtx_builder = CreatePositionBuffer(entity);
+
+ VertexBufferBuilder<TextureFillVertexShader::PerVertexData> vertex_builder;
+ vtx_builder.IterateVertices(
+ [&vertex_builder, &texture_coverage, &effect_transform](Point position) {
+ TextureFillVertexShader::PerVertexData data;
+ data.position = position;
+ auto coverage_coords =
+ (position - texture_coverage.origin) / texture_coverage.size;
+ data.texture_coords = effect_transform * coverage_coords;
+ vertex_builder.AppendVertex(data);
+ });
+
+ return GeometryResult{
+ .type = PrimitiveType::kTriangle,
+ .vertex_buffer =
+ vertex_builder.CreateVertexBuffer(pass.GetTransientsBuffer()),
+ .transform = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
+ entity.GetTransformation(),
+ .prevent_overdraw = true,
+ };
+}
+
+GeometryVertexType RRectGeometry::GetVertexType() const {
+ return GeometryVertexType::kPosition;
+}
+
+std::optional<Rect> RRectGeometry::GetCoverage(const Matrix& transform) const {
+ return rect_.TransformBounds(transform);
+}
+
} // namespace impeller
diff --git a/impeller/entity/geometry.h b/impeller/entity/geometry.h
index bdf34d6..4bb5180 100644
--- a/impeller/entity/geometry.h
+++ b/impeller/entity/geometry.h
@@ -50,6 +50,8 @@
static std::unique_ptr<Geometry> MakeFillPath(const Path& path);
+ static std::unique_ptr<Geometry> MakeRRect(Rect rect, Scalar corner_radius);
+
static std::unique_ptr<Geometry> MakeStrokePath(
const Path& path,
Scalar stroke_width = 0.0,
@@ -264,4 +266,37 @@
FML_DISALLOW_COPY_AND_ASSIGN(RectGeometry);
};
+class RRectGeometry : public Geometry {
+ public:
+ explicit RRectGeometry(Rect rect, Scalar corner_radius);
+
+ ~RRectGeometry();
+
+ private:
+ // |Geometry|
+ GeometryResult GetPositionBuffer(const ContentContext& renderer,
+ const Entity& entity,
+ RenderPass& pass) override;
+
+ // |Geometry|
+ GeometryResult GetPositionUVBuffer(Rect texture_coverage,
+ Matrix effect_transform,
+ const ContentContext& renderer,
+ const Entity& entity,
+ RenderPass& pass) override;
+
+ // |Geometry|
+ GeometryVertexType GetVertexType() const override;
+
+ // |Geometry|
+ std::optional<Rect> GetCoverage(const Matrix& transform) const override;
+
+ VertexBufferBuilder<Point> CreatePositionBuffer(const Entity& entity) const;
+
+ Rect rect_;
+ Scalar corner_radius_;
+
+ FML_DISALLOW_COPY_AND_ASSIGN(RRectGeometry);
+};
+
} // namespace impeller
diff --git a/impeller/geometry/path_builder.cc b/impeller/geometry/path_builder.cc
index f7f68cf..ef34248 100644
--- a/impeller/geometry/path_builder.cc
+++ b/impeller/geometry/path_builder.cc
@@ -201,11 +201,6 @@
current_ = rect.origin + Point{radii.top_left.x, 0.0};
- 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;
-
MoveTo({rect.origin.x + radii.top_left.x, rect.origin.y});
//----------------------------------------------------------------------------
@@ -218,13 +213,7 @@
//----------------------------------------------------------------------------
// Top right arc.
//
- prototype_.AddCubicComponent(
- {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.top_right.y - magic_top_right.y},
- {rect.origin.x + rect.size.width, rect.origin.y + radii.top_right.y});
+ AddRoundedRectTopRight(rect, radii);
//----------------------------------------------------------------------------
// Right line.
@@ -237,17 +226,7 @@
//----------------------------------------------------------------------------
// Bottom right arc.
//
- prototype_.AddCubicComponent(
- {rect.origin.x + rect.size.width,
- rect.origin.y + rect.size.height - radii.bottom_right.y},
- {rect.origin.x + rect.size.width, rect.origin.y + rect.size.height -
- 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.bottom_right.x,
- rect.origin.y + rect.size.height});
+ AddRoundedRectBottomRight(rect, radii);
//----------------------------------------------------------------------------
// Bottom line.
@@ -260,13 +239,7 @@
//----------------------------------------------------------------------------
// Bottom left arc.
//
- prototype_.AddCubicComponent(
- {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.bottom_left.y +
- magic_bottom_left.y},
- {rect.origin.x, rect.origin.y + rect.size.height - radii.bottom_left.y});
+ AddRoundedRectBottomLeft(rect, radii);
//----------------------------------------------------------------------------
// Left line.
@@ -278,14 +251,64 @@
//----------------------------------------------------------------------------
// Top left arc.
//
+ AddRoundedRectTopLeft(rect, radii);
+
+ Close();
+
+ return *this;
+}
+
+PathBuilder& PathBuilder::AddRoundedRectTopLeft(Rect rect,
+ RoundingRadii radii) {
+ const auto magic_top_left = radii.top_left * kArcApproximationMagic;
prototype_.AddCubicComponent(
{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;
+}
- Close();
+PathBuilder& PathBuilder::AddRoundedRectTopRight(Rect rect,
+ RoundingRadii radii) {
+ const auto magic_top_right = radii.top_right * kArcApproximationMagic;
+ prototype_.AddCubicComponent(
+ {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.top_right.y - magic_top_right.y},
+ {rect.origin.x + rect.size.width, rect.origin.y + radii.top_right.y});
+ return *this;
+}
+PathBuilder& PathBuilder::AddRoundedRectBottomRight(Rect rect,
+ RoundingRadii radii) {
+ const auto magic_bottom_right = radii.bottom_right * kArcApproximationMagic;
+ prototype_.AddCubicComponent(
+ {rect.origin.x + rect.size.width,
+ rect.origin.y + rect.size.height - radii.bottom_right.y},
+ {rect.origin.x + rect.size.width, rect.origin.y + rect.size.height -
+ 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.bottom_right.x,
+ rect.origin.y + rect.size.height});
+ return *this;
+}
+
+PathBuilder& PathBuilder::AddRoundedRectBottomLeft(Rect rect,
+ RoundingRadii radii) {
+ const auto magic_bottom_left = radii.bottom_left * kArcApproximationMagic;
+ prototype_.AddCubicComponent(
+ {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.bottom_left.y +
+ magic_bottom_left.y},
+ {rect.origin.x, rect.origin.y + rect.size.height - radii.bottom_left.y});
return *this;
}
diff --git a/impeller/geometry/path_builder.h b/impeller/geometry/path_builder.h
index 13e9240..3c4dede 100644
--- a/impeller/geometry/path_builder.h
+++ b/impeller/geometry/path_builder.h
@@ -102,6 +102,14 @@
PathBuilder& AddRoundedRect(Rect rect, Scalar radius);
+ PathBuilder& AddRoundedRectTopLeft(Rect rect, RoundingRadii radii);
+
+ PathBuilder& AddRoundedRectTopRight(Rect rect, RoundingRadii radii);
+
+ PathBuilder& AddRoundedRectBottomRight(Rect rect, RoundingRadii radii);
+
+ PathBuilder& AddRoundedRectBottomLeft(Rect rect, RoundingRadii radii);
+
PathBuilder& AddPath(const Path& path);
private: