From f83a0211af0f0421780fa0bcab289bfaea6d6767 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 8 Dec 2021 15:19:04 -0800 Subject: [PATCH] Support path fill types. --- impeller/aiks/aiks_unittests.cc | 41 +++++++++++++++---- impeller/aiks/canvas.cc | 8 ++-- .../display_list/display_list_impeller.cc | 14 +++---- impeller/entity/contents.cc | 33 +++++++-------- impeller/entity/entity_pass.cc | 3 +- impeller/entity/entity_unittests.cc | 2 +- impeller/geometry/geometry_unittests.cc | 2 +- impeller/geometry/path.cc | 8 ++++ impeller/geometry/path.h | 13 ++++++ impeller/geometry/path_builder.cc | 12 +++++- impeller/geometry/path_builder.h | 4 +- impeller/renderer/tessellator.cc | 24 ++++------- impeller/renderer/tessellator.h | 17 ++------ 13 files changed, 109 insertions(+), 72 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index bac170f9cdc71..62782b05f526c 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -45,7 +45,7 @@ TEST_F(AiksTest, CanRenderColoredRect) { paint.color = Color::Red(); canvas.DrawPath(PathBuilder{} .AddRect(Rect::MakeXYWH(100.0, 100.0, 100.0, 100.0)) - .CreatePath(), + .TakePath(), paint); // ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } @@ -81,7 +81,7 @@ TEST_F(AiksTest, CanRenderStrokes) { paint.color = Color::Red(); paint.stroke_width = 20.0; paint.style = Paint::Style::kStroke; - canvas.DrawPath(PathBuilder{}.AddLine({200, 100}, {800, 100}).CreatePath(), + canvas.DrawPath(PathBuilder{}.AddLine({200, 100}, {800, 100}).TakePath(), paint); // ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } @@ -92,7 +92,7 @@ TEST_F(AiksTest, CanRenderCurvedStrokes) { paint.color = Color::Red(); paint.stroke_width = 25.0; paint.style = Paint::Style::kStroke; - canvas.DrawPath(PathBuilder{}.AddCircle({500, 500}, 250).CreatePath(), paint); + canvas.DrawPath(PathBuilder{}.AddCircle({500, 500}, 250).TakePath(), paint); ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } @@ -101,8 +101,8 @@ TEST_F(AiksTest, CanRenderClips) { Paint paint; paint.color = Color::Fuchsia(); canvas.ClipPath( - PathBuilder{}.AddRect(Rect::MakeXYWH(0, 0, 500, 500)).CreatePath()); - canvas.DrawPath(PathBuilder{}.AddCircle({500, 500}, 250).CreatePath(), paint); + PathBuilder{}.AddRect(Rect::MakeXYWH(0, 0, 500, 500)).TakePath()); + canvas.DrawPath(PathBuilder{}.AddCircle({500, 500}, 250).TakePath(), paint); ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } @@ -236,14 +236,39 @@ TEST_F(AiksTest, CanRenderRoundedRectWithNonUniformRadii) { radii.bottom_right = {50, 25}; radii.bottom_left = {25, 50}; - auto path = PathBuilder{} - .AddRoundedRect(Rect{100, 100, 500, 500}, radii) - .CreatePath(); + auto path = + PathBuilder{}.AddRoundedRect(Rect{100, 100, 500, 500}, radii).TakePath(); canvas.DrawPath(path, paint); ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } +TEST_F(AiksTest, CanRenderDifferencePaths) { + Canvas canvas; + + Paint paint; + paint.color = Color::Red(); + + PathBuilder builder; + + PathBuilder::RoundingRadii radii; + radii.top_left = {50, 25}; + radii.top_right = {25, 50}; + radii.bottom_right = {50, 25}; + radii.bottom_left = {25, 50}; + + builder.AddRoundedRect({100, 100, 200, 200}, radii); + builder.AddCircle({200, 200}, 50); + auto path = builder.TakePath(FillType::kOdd); + + canvas.DrawImage( + std::make_shared(CreateTextureForFixture("boston.jpg")), {10, 10}, + Paint{}); + canvas.DrawPath(std::move(path), paint); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + } // namespace testing } // namespace impeller diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index 7b66187af37fe..99d7a47c58368 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -100,11 +100,11 @@ void Canvas::DrawPath(Path path, Paint paint) { } void Canvas::DrawRect(Rect rect, Paint paint) { - DrawPath(PathBuilder{}.AddRect(rect).CreatePath(), std::move(paint)); + DrawPath(PathBuilder{}.AddRect(rect).TakePath(), std::move(paint)); } void Canvas::DrawCircle(Point center, Scalar radius, Paint paint) { - DrawPath(PathBuilder{}.AddCircle(center, radius).CreatePath(), + DrawPath(PathBuilder{}.AddCircle(center, radius).TakePath(), std::move(paint)); } @@ -120,7 +120,7 @@ void Canvas::SaveLayer(Paint paint, std::optional bounds) { // 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()).CreatePath()); + ClipPath(PathBuilder{}.AddRect(bounds.value()).TakePath()); } } @@ -187,7 +187,7 @@ void Canvas::DrawImageRect(std::shared_ptr image, contents->SetSourceRect(source); Entity entity; - entity.SetPath(PathBuilder{}.AddRect(dest).CreatePath()); + entity.SetPath(PathBuilder{}.AddRect(dest).TakePath()); entity.SetContents(contents); entity.SetTransformation(GetCurrentTransformation()); diff --git a/impeller/display_list/display_list_impeller.cc b/impeller/display_list/display_list_impeller.cc index 68213eebba3f8..1258b7f6f2045 100644 --- a/impeller/display_list/display_list_impeller.cc +++ b/impeller/display_list/display_list_impeller.cc @@ -213,7 +213,7 @@ static Rect ToRect(const SkRect& rect) { void DisplayListImpeller::clipRect(const SkRect& rect, SkClipOp clip_op, bool is_aa) { - auto path = PathBuilder{}.AddRect(ToRect(rect)).CreatePath(); + auto path = PathBuilder{}.AddRect(ToRect(rect)).TakePath(); canvas_.ClipPath(std::move(path)); } @@ -238,7 +238,7 @@ void DisplayListImpeller::clipRRect(const SkRRect& rrect, auto path = PathBuilder{} .AddRoundedRect(ToRect(rrect.getBounds()), ToRoundingRadii(rrect)) - .CreatePath(); + .TakePath(); canvas_.ClipPath(std::move(path)); } @@ -266,25 +266,25 @@ void DisplayListImpeller::drawPaint() { // |flutter::Dispatcher| void DisplayListImpeller::drawLine(const SkPoint& p0, const SkPoint& p1) { - auto path = PathBuilder{}.AddLine(ToPoint(p0), ToPoint(p1)).CreatePath(); + auto path = PathBuilder{}.AddLine(ToPoint(p0), ToPoint(p1)).TakePath(); canvas_.DrawPath(std::move(path), paint_); } // |flutter::Dispatcher| void DisplayListImpeller::drawRect(const SkRect& rect) { - auto path = PathBuilder{}.AddRect(ToRect(rect)).CreatePath(); + auto path = PathBuilder{}.AddRect(ToRect(rect)).TakePath(); canvas_.DrawPath(std::move(path), paint_); } // |flutter::Dispatcher| void DisplayListImpeller::drawOval(const SkRect& bounds) { - auto path = PathBuilder{}.AddOval(ToRect(bounds)).CreatePath(); + auto path = PathBuilder{}.AddOval(ToRect(bounds)).TakePath(); canvas_.DrawPath(std::move(path), paint_); } // |flutter::Dispatcher| void DisplayListImpeller::drawCircle(const SkPoint& center, SkScalar radius) { - auto path = PathBuilder{}.AddCircle(ToPoint(center), radius).CreatePath(); + auto path = PathBuilder{}.AddCircle(ToPoint(center), radius).TakePath(); canvas_.DrawPath(std::move(path), paint_); } @@ -293,7 +293,7 @@ void DisplayListImpeller::drawRRect(const SkRRect& rrect) { auto path = PathBuilder{} .AddRoundedRect(ToRect(rrect.getBounds()), ToRoundingRadii(rrect)) - .CreatePath(); + .TakePath(); canvas_.DrawPath(std::move(path), paint_); } diff --git a/impeller/entity/contents.cc b/impeller/entity/contents.cc index f2e1d7f18d27f..71ae6c2b66e55 100644 --- a/impeller/entity/contents.cc +++ b/impeller/entity/contents.cc @@ -68,12 +68,12 @@ bool LinearGradientContents::Render(const ContentRenderer& renderer, auto vertices_builder = VertexBufferBuilder(); { - auto result = Tessellator{}.Tessellate(entity.GetPath().CreatePolyline(), - [&vertices_builder](Point point) { - VS::PerVertexData vtx; - vtx.vertices = point; - vertices_builder.AppendVertex(vtx); - }); + auto result = Tessellator{entity.GetPath().GetFillType()}.Tessellate( + entity.GetPath().CreatePolyline(), [&vertices_builder](Point point) { + VS::PerVertexData vtx; + vtx.vertices = point; + vertices_builder.AppendVertex(vtx); + }); if (!result) { return false; } @@ -124,7 +124,7 @@ static VertexBuffer CreateSolidFillVertices(const Path& path, VertexBufferBuilder vtx_builder; - auto tesselation_result = Tessellator{}.Tessellate( + auto tesselation_result = Tessellator{path.GetFillType()}.Tessellate( path.CreatePolyline(), [&vtx_builder](auto point) { VS::PerVertexData vtx; vtx.vertices = point; @@ -225,15 +225,16 @@ bool TextureContents::Render(const ContentRenderer& renderer, VertexBufferBuilder vertex_builder; { - const auto tess_result = Tessellator{}.Tessellate( - entity.GetPath().CreatePolyline(), - [&vertex_builder, &coverage_rect](Point vtx) { - VS::PerVertexData data; - data.vertices = vtx; - data.texture_coords = - ((vtx - coverage_rect->origin) / coverage_rect->size); - vertex_builder.AppendVertex(data); - }); + const auto tess_result = + Tessellator{entity.GetPath().GetFillType()}.Tessellate( + entity.GetPath().CreatePolyline(), + [&vertex_builder, &coverage_rect](Point vtx) { + VS::PerVertexData data; + data.vertices = vtx; + data.texture_coords = + ((vtx - coverage_rect->origin) / coverage_rect->size); + vertex_builder.AppendVertex(data); + }); if (!tess_result) { return false; } diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index fc8734a59a91e..e0a7d1f413390 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -184,8 +184,7 @@ bool EntityPass::Render(ContentRenderer& renderer, } Entity entity; - entity.SetPath( - PathBuilder{}.AddRect(subpass_coverage.value()).CreatePath()); + entity.SetPath(PathBuilder{}.AddRect(subpass_coverage.value()).TakePath()); entity.SetContents(std::move(offscreen_texture_contents)); entity.SetStencilDepth(stencil_depth_); entity.SetTransformation(xformation_); diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index c76fb2d249fcc..cf3cafb47cb70 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -20,7 +20,7 @@ TEST_F(EntityTest, CanCreateEntity) { TEST_F(EntityTest, CanDrawRect) { Entity entity; - entity.SetPath(PathBuilder{}.AddRect({100, 100, 100, 100}).CreatePath()); + entity.SetPath(PathBuilder{}.AddRect({100, 100, 100, 100}).TakePath()); entity.SetContents(SolidColorContents::Make(Color::Red())); // ASSERT_TRUE(OpenPlaygroundHere(entity)); } diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index 5aec38a236ae4..fdd36133691f5 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -192,7 +192,7 @@ TEST(GeometryTest, BoundingBoxCubic) { TEST(GeometryTest, BoundingBoxOfCompositePathIsCorrect) { PathBuilder builder; builder.AddRoundedRect({{10, 10}, {300, 300}}, {50, 50, 50, 50}); - auto path = builder.CreatePath(); + auto path = builder.TakePath(); auto actual = path.GetBoundingBox(); Rect expected(10, 10, 300, 300); ASSERT_TRUE(actual.has_value()); diff --git a/impeller/geometry/path.cc b/impeller/geometry/path.cc index 1c66461bea0c8..5bbf70b184a03 100644 --- a/impeller/geometry/path.cc +++ b/impeller/geometry/path.cc @@ -16,6 +16,14 @@ size_t Path::GetComponentCount() const { return components_.size(); } +void Path::SetFillType(FillType fill) { + fill_ = fill; +} + +FillType Path::GetFillType() const { + return fill_; +} + Path& Path::AddLinearComponent(Point p1, Point p2) { linears_.emplace_back(p1, p2); components_.emplace_back(ComponentType::kLinear, linears_.size() - 1); diff --git a/impeller/geometry/path.h b/impeller/geometry/path.h index 2abd72d08e5d3..e14f057e7f5d5 100644 --- a/impeller/geometry/path.h +++ b/impeller/geometry/path.h @@ -12,6 +12,14 @@ namespace impeller { +enum class FillType { + kNonZero, // The default winding order. + kOdd, + kPositive, + kNegative, + kAbsGeqTwo, +}; + //------------------------------------------------------------------------------ /// @brief Paths are lightweight objects that describe a collection of /// linear, quadratic, or cubic segments. @@ -36,6 +44,10 @@ class Path { size_t GetComponentCount() const; + void SetFillType(FillType fill); + + FillType GetFillType() const; + Path& AddLinearComponent(Point p1, Point p2); Path& AddQuadraticComponent(Point p1, Point cp, Point p2); @@ -82,6 +94,7 @@ class Path { : type(aType), index(aIndex) {} }; + FillType fill_ = FillType::kNonZero; std::vector components_; std::vector linears_; std::vector quads_; diff --git a/impeller/geometry/path_builder.cc b/impeller/geometry/path_builder.cc index 2524c4276291b..8cd4b29048f08 100644 --- a/impeller/geometry/path_builder.cc +++ b/impeller/geometry/path_builder.cc @@ -12,8 +12,16 @@ PathBuilder::PathBuilder() = default; PathBuilder::~PathBuilder() = default; -Path PathBuilder::CreatePath() const { - return prototype_; +Path PathBuilder::CopyPath(FillType fill) const { + auto path = prototype_; + path.SetFillType(fill); + return path; +} + +Path PathBuilder::TakePath(FillType fill) { + auto path = std::move(prototype_); + path.SetFillType(fill); + return path; } PathBuilder& PathBuilder::MoveTo(Point point, bool relative) { diff --git a/impeller/geometry/path_builder.h b/impeller/geometry/path_builder.h index 13ebf6ab13068..7687d889e8f57 100644 --- a/impeller/geometry/path_builder.h +++ b/impeller/geometry/path_builder.h @@ -17,7 +17,9 @@ class PathBuilder { ~PathBuilder(); - Path CreatePath() const; + Path CopyPath(FillType fill = FillType::kNonZero) const; + + Path TakePath(FillType fill = FillType::kNonZero); const Path& GetCurrentPath() const; diff --git a/impeller/renderer/tessellator.cc b/impeller/renderer/tessellator.cc index 3f7960f7f4ddf..9cc5d4ebba0cd 100644 --- a/impeller/renderer/tessellator.cc +++ b/impeller/renderer/tessellator.cc @@ -8,29 +8,21 @@ namespace impeller { -Tessellator::Tessellator() {} +Tessellator::Tessellator(FillType type) : fill_type_(type) {} -Tessellator::~Tessellator() {} +Tessellator::~Tessellator() = default; -void Tessellator::SetFillType(FillType winding) { - fill_type_ = winding; -} - -Tessellator::FillType Tessellator::GetFillType() const { - return fill_type_; -} - -static int ToTessWindingRule(Tessellator::FillType fill_type) { +static int ToTessWindingRule(FillType fill_type) { switch (fill_type) { - case Tessellator::FillType::kOdd: + case FillType::kOdd: return TESS_WINDING_ODD; - case Tessellator::FillType::kNonZero: + case FillType::kNonZero: return TESS_WINDING_NONZERO; - case Tessellator::FillType::kPositive: + case FillType::kPositive: return TESS_WINDING_POSITIVE; - case Tessellator::FillType::kNegative: + case FillType::kNegative: return TESS_WINDING_NEGATIVE; - case Tessellator::FillType::kAbsGeqTwo: + case FillType::kAbsGeqTwo: return TESS_WINDING_ABS_GEQ_TWO; } return TESS_WINDING_ODD; diff --git a/impeller/renderer/tessellator.h b/impeller/renderer/tessellator.h index 0ca17608b61f5..ee27d924748e2 100644 --- a/impeller/renderer/tessellator.h +++ b/impeller/renderer/tessellator.h @@ -8,6 +8,7 @@ #include #include "flutter/fml/macros.h" +#include "impeller/geometry/path.h" #include "impeller/geometry/point.h" #include "impeller/renderer/formats.h" @@ -21,22 +22,10 @@ namespace impeller { /// class Tessellator { public: - enum class FillType { - kNonZero, // The default winding order. - kOdd, - kPositive, - kNegative, - kAbsGeqTwo, - }; - - Tessellator(); + explicit Tessellator(FillType type); ~Tessellator(); - void SetFillType(FillType winding); - - FillType GetFillType() const; - WindingOrder GetFrontFaceWinding() const; using VertexCallback = std::function; @@ -53,7 +42,7 @@ class Tessellator { VertexCallback callback) const; private: - FillType fill_type_ = FillType::kNonZero; + const FillType fill_type_ = FillType::kNonZero; FML_DISALLOW_COPY_AND_ASSIGN(Tessellator); };