From 69334fd7b901911e5026edcac2ee9ca7923262ae Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Wed, 30 Mar 2022 13:14:02 -0700 Subject: [PATCH] Implement drawArc (#109) --- .../display_list/display_list_dispatcher.cc | 6 +- .../display_list/display_list_playground.cc | 21 +++--- .../display_list/display_list_playground.h | 5 ++ .../display_list/display_list_unittests.cc | 50 +++++++++++++- impeller/geometry/constants.h | 5 +- impeller/geometry/path_builder.cc | 65 ++++++++++++++++++- impeller/geometry/path_builder.h | 5 ++ 7 files changed, 142 insertions(+), 15 deletions(-) diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index c4d61dafc8fb2..a1da89a19dd03 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -11,6 +11,7 @@ #include "impeller/entity/contents/solid_stroke_contents.h" #include "impeller/entity/entity.h" #include "impeller/geometry/path_builder.h" +#include "impeller/geometry/scalar.h" #include "impeller/typographer/backends/skia/text_frame_skia.h" #include "third_party/skia/include/core/SkColor.h" @@ -546,7 +547,10 @@ void DisplayListDispatcher::drawArc(const SkRect& oval_bounds, SkScalar start_degrees, SkScalar sweep_degrees, bool use_center) { - UNIMPLEMENTED; + PathBuilder builder; + builder.AddArc(ToRect(oval_bounds), Degrees(start_degrees), + Degrees(sweep_degrees), use_center); + canvas_.DrawPath(builder.TakePath(), paint_); } // |flutter::Dispatcher| diff --git a/impeller/display_list/display_list_playground.cc b/impeller/display_list/display_list_playground.cc index 4bbcb07fe410e..508af2fcb206c 100644 --- a/impeller/display_list/display_list_playground.cc +++ b/impeller/display_list/display_list_playground.cc @@ -22,24 +22,27 @@ bool DisplayListPlayground::OpenPlaygroundHere( bool DisplayListPlayground::OpenPlaygroundHere( sk_sp list) { + return OpenPlaygroundHere([&list]() { return list; }); +} + +bool DisplayListPlayground::OpenPlaygroundHere( + DisplayListPlaygroundCallback callback) { if (!Playground::is_enabled()) { return true; } - if (!list) { - return false; - } - - DisplayListDispatcher dispatcher; - list->Dispatch(dispatcher); - auto picture = dispatcher.EndRecordingAsPicture(); - AiksContext context(GetContext()); if (!context.IsValid()) { return false; } return Playground::OpenPlaygroundHere( - [&picture, &context](RenderPass& pass) -> bool { + [&context, &callback](RenderPass& pass) -> bool { + auto list = callback(); + + DisplayListDispatcher dispatcher; + list->Dispatch(dispatcher); + auto picture = dispatcher.EndRecordingAsPicture(); + return context.Render(picture, pass); }); } diff --git a/impeller/display_list/display_list_playground.h b/impeller/display_list/display_list_playground.h index f0028a50acf2b..fac3aef91795a 100644 --- a/impeller/display_list/display_list_playground.h +++ b/impeller/display_list/display_list_playground.h @@ -14,6 +14,9 @@ namespace impeller { class DisplayListPlayground : public Playground { public: + using DisplayListPlaygroundCallback = + std::function()>; + DisplayListPlayground(); ~DisplayListPlayground(); @@ -22,6 +25,8 @@ class DisplayListPlayground : public Playground { bool OpenPlaygroundHere(sk_sp list); + bool OpenPlaygroundHere(DisplayListPlaygroundCallback callback); + SkFont CreateTestFontOfSize(SkScalar scalar); SkFont CreateTestFont(); diff --git a/impeller/display_list/display_list_unittests.cc b/impeller/display_list/display_list_unittests.cc index d8487de313523..a0b975f05e6d2 100644 --- a/impeller/display_list/display_list_unittests.cc +++ b/impeller/display_list/display_list_unittests.cc @@ -2,11 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "gtest/gtest.h" +#include "third_party/imgui/imgui.h" +#include "third_party/skia/include/core/SkColor.h" +#include "third_party/skia/include/core/SkPathBuilder.h" + #include "flutter/display_list/display_list_builder.h" #include "flutter/testing/testing.h" #include "impeller/display_list/display_list_image_impeller.h" #include "impeller/display_list/display_list_playground.h" -#include "third_party/skia/include/core/SkPathBuilder.h" +#include "impeller/geometry/point.h" +#include "impeller/playground/widgets.h" namespace impeller { namespace testing { @@ -36,7 +42,7 @@ TEST_F(DisplayListTest, CanDrawImage) { ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); } -TEST_F(DisplayListTest, CapsAndJoins) { +TEST_F(DisplayListTest, CanDrawCapsAndJoins) { flutter::DisplayListBuilder builder; builder.setStyle(SkPaint::Style::kStroke_Style); @@ -81,5 +87,45 @@ TEST_F(DisplayListTest, CapsAndJoins) { ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); } +TEST_F(DisplayListTest, CanDrawArc) { + bool first_frame = true; + auto callback = [&]() { + if (first_frame) { + first_frame = false; + ImGui::SetNextWindowSize({400, 100}); + ImGui::SetNextWindowPos({300, 550}); + } + + static float start_angle = 45; + static float sweep_angle = 270; + static bool use_center = true; + + ImGui::Begin("Controls"); + ImGui::SliderFloat("Start angle", &start_angle, -360, 360); + ImGui::SliderFloat("Sweep angle", &sweep_angle, -360, 360); + ImGui::Checkbox("Use center", &use_center); + ImGui::End(); + + auto [p1, p2] = IMPELLER_PLAYGROUND_LINE( + Point(200, 200), Point(400, 400), 20, Color::White(), Color::White()); + + flutter::DisplayListBuilder builder; + builder.setStyle(SkPaint::Style::kStroke_Style); + builder.setStrokeCap(SkPaint::Cap::kRound_Cap); + builder.setStrokeJoin(SkPaint::Join::kMiter_Join); + builder.setStrokeMiter(10); + auto rect = SkRect::MakeLTRB(p1.x, p1.y, p2.x, p2.y); + builder.setColor(SK_ColorGREEN); + builder.setStrokeWidth(2); + builder.drawRect(rect); + builder.setColor(SK_ColorRED); + builder.setStrokeWidth(10); + builder.drawArc(rect, start_angle, sweep_angle, use_center); + + return builder.Build(); + }; + ASSERT_TRUE(OpenPlaygroundHere(callback)); +} + } // namespace testing } // namespace impeller diff --git a/impeller/geometry/constants.h b/impeller/geometry/constants.h index a0cadfffcc483..b297c575ceb4d 100644 --- a/impeller/geometry/constants.h +++ b/impeller/geometry/constants.h @@ -19,9 +19,12 @@ constexpr float klogE_2 = 0.69314718055994530942; // log_e 10 constexpr float klogE_10 = 2.30258509299404568402; -// pi */ +// pi constexpr float kPi = 3.14159265358979323846; +// pi*2 +constexpr float k2Pi = 6.28318530717958647693; + // pi/2 constexpr float kPiOver2 = 1.57079632679489661923; diff --git a/impeller/geometry/path_builder.cc b/impeller/geometry/path_builder.cc index 81e269806c234..9ddb29690060f 100644 --- a/impeller/geometry/path_builder.cc +++ b/impeller/geometry/path_builder.cc @@ -4,6 +4,8 @@ #include "path_builder.h" +#include + namespace impeller { PathBuilder::PathBuilder() = default; @@ -288,10 +290,69 @@ PathBuilder& PathBuilder::AddRoundedRect(Rect rect, RoundingRadii radii) { return *this; } +PathBuilder& PathBuilder::AddArc(const Rect& oval_bounds, + Radians start, + Radians sweep, + bool use_center) { + if (sweep.radians < 0) { + start.radians += sweep.radians; + sweep.radians *= -1; + } + sweep.radians = std::min(k2Pi, sweep.radians); + start.radians = std::fmod(start.radians, k2Pi); + + const Point radius = {oval_bounds.size.width * 0.5f, + oval_bounds.size.height * 0.5f}; + const Point center = {oval_bounds.origin.x + radius.x, + oval_bounds.origin.y + radius.y}; + + Vector2 p1_unit(std::cos(start.radians), std::sin(start.radians)); + + if (use_center) { + MoveTo(center); + LineTo(center + p1_unit * radius); + } else { + MoveTo(center + p1_unit * radius); + } + + while (sweep.radians > 0) { + Vector2 p2_unit; + Scalar quadrant_angle; + if (sweep.radians < kPiOver2) { + quadrant_angle = sweep.radians; + p2_unit = Vector2(std::cos(start.radians + quadrant_angle), + std::sin(start.radians + quadrant_angle)); + } else { + quadrant_angle = kPiOver2; + p2_unit = Vector2(-p1_unit.y, p1_unit.x); + } + + Vector2 arc_cp_lengths = + (quadrant_angle / kPiOver2) * kArcApproximationMagic * radius; + + Point p1 = center + p1_unit * radius; + Point p2 = center + p2_unit * radius; + Point cp1 = p1 + Vector2(-p1_unit.y, p1_unit.x) * arc_cp_lengths; + Point cp2 = p2 + Vector2(p2_unit.y, -p2_unit.x) * arc_cp_lengths; + + prototype_.AddCubicComponent(p1, cp1, cp2, p2); + current_ = p2; + + start.radians += quadrant_angle; + sweep.radians -= quadrant_angle; + p1_unit = p2_unit; + } + + if (use_center) { + Close(); + } + + return *this; +} + PathBuilder& PathBuilder::AddOval(const Rect& container) { const Point r = {container.size.width * 0.5f, container.size.height * 0.5f}; - const Point c = {container.origin.x + (container.size.width * 0.5f), - container.origin.y + (container.size.height * 0.5f)}; + const Point c = {container.origin.x + r.x, container.origin.y + r.y}; const Point m = {kArcApproximationMagic * r.x, kArcApproximationMagic * r.y}; MoveTo({c.x, c.y - r.y}); diff --git a/impeller/geometry/path_builder.h b/impeller/geometry/path_builder.h index 038a9f49f3219..13e9240d68600 100644 --- a/impeller/geometry/path_builder.h +++ b/impeller/geometry/path_builder.h @@ -60,6 +60,11 @@ class PathBuilder { PathBuilder& AddCircle(const Point& center, Scalar radius); + PathBuilder& AddArc(const Rect& oval_bounds, + Radians start, + Radians sweep, + bool use_center = false); + PathBuilder& AddOval(const Rect& rect); PathBuilder& AddLine(const Point& p1, const Point& p2);