Skip to content

Commit 4810c61

Browse files
authored
Implement drawArc (flutter#109)
1 parent 6b6b3a4 commit 4810c61

File tree

7 files changed

+142
-15
lines changed

7 files changed

+142
-15
lines changed

impeller/display_list/display_list_dispatcher.cc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "impeller/entity/contents/solid_stroke_contents.h"
1212
#include "impeller/entity/entity.h"
1313
#include "impeller/geometry/path_builder.h"
14+
#include "impeller/geometry/scalar.h"
1415
#include "impeller/typographer/backends/skia/text_frame_skia.h"
1516
#include "third_party/skia/include/core/SkColor.h"
1617

@@ -546,7 +547,10 @@ void DisplayListDispatcher::drawArc(const SkRect& oval_bounds,
546547
SkScalar start_degrees,
547548
SkScalar sweep_degrees,
548549
bool use_center) {
549-
UNIMPLEMENTED;
550+
PathBuilder builder;
551+
builder.AddArc(ToRect(oval_bounds), Degrees(start_degrees),
552+
Degrees(sweep_degrees), use_center);
553+
canvas_.DrawPath(builder.TakePath(), paint_);
550554
}
551555

552556
// |flutter::Dispatcher|

impeller/display_list/display_list_playground.cc

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,27 @@ bool DisplayListPlayground::OpenPlaygroundHere(
2222

2323
bool DisplayListPlayground::OpenPlaygroundHere(
2424
sk_sp<flutter::DisplayList> list) {
25+
return OpenPlaygroundHere([&list]() { return list; });
26+
}
27+
28+
bool DisplayListPlayground::OpenPlaygroundHere(
29+
DisplayListPlaygroundCallback callback) {
2530
if (!Playground::is_enabled()) {
2631
return true;
2732
}
2833

29-
if (!list) {
30-
return false;
31-
}
32-
33-
DisplayListDispatcher dispatcher;
34-
list->Dispatch(dispatcher);
35-
auto picture = dispatcher.EndRecordingAsPicture();
36-
3734
AiksContext context(GetContext());
3835
if (!context.IsValid()) {
3936
return false;
4037
}
4138
return Playground::OpenPlaygroundHere(
42-
[&picture, &context](RenderPass& pass) -> bool {
39+
[&context, &callback](RenderPass& pass) -> bool {
40+
auto list = callback();
41+
42+
DisplayListDispatcher dispatcher;
43+
list->Dispatch(dispatcher);
44+
auto picture = dispatcher.EndRecordingAsPicture();
45+
4346
return context.Render(picture, pass);
4447
});
4548
}

impeller/display_list/display_list_playground.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ namespace impeller {
1414

1515
class DisplayListPlayground : public Playground {
1616
public:
17+
using DisplayListPlaygroundCallback =
18+
std::function<sk_sp<flutter::DisplayList>()>;
19+
1720
DisplayListPlayground();
1821

1922
~DisplayListPlayground();
@@ -22,6 +25,8 @@ class DisplayListPlayground : public Playground {
2225

2326
bool OpenPlaygroundHere(sk_sp<flutter::DisplayList> list);
2427

28+
bool OpenPlaygroundHere(DisplayListPlaygroundCallback callback);
29+
2530
SkFont CreateTestFontOfSize(SkScalar scalar);
2631

2732
SkFont CreateTestFont();

impeller/display_list/display_list_unittests.cc

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,17 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
#include "gtest/gtest.h"
6+
#include "third_party/imgui/imgui.h"
7+
#include "third_party/skia/include/core/SkColor.h"
8+
#include "third_party/skia/include/core/SkPathBuilder.h"
9+
510
#include "flutter/display_list/display_list_builder.h"
611
#include "flutter/testing/testing.h"
712
#include "impeller/display_list/display_list_image_impeller.h"
813
#include "impeller/display_list/display_list_playground.h"
9-
#include "third_party/skia/include/core/SkPathBuilder.h"
14+
#include "impeller/geometry/point.h"
15+
#include "impeller/playground/widgets.h"
1016

1117
namespace impeller {
1218
namespace testing {
@@ -36,7 +42,7 @@ TEST_F(DisplayListTest, CanDrawImage) {
3642
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
3743
}
3844

39-
TEST_F(DisplayListTest, CapsAndJoins) {
45+
TEST_F(DisplayListTest, CanDrawCapsAndJoins) {
4046
flutter::DisplayListBuilder builder;
4147

4248
builder.setStyle(SkPaint::Style::kStroke_Style);
@@ -81,5 +87,45 @@ TEST_F(DisplayListTest, CapsAndJoins) {
8187
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
8288
}
8389

90+
TEST_F(DisplayListTest, CanDrawArc) {
91+
bool first_frame = true;
92+
auto callback = [&]() {
93+
if (first_frame) {
94+
first_frame = false;
95+
ImGui::SetNextWindowSize({400, 100});
96+
ImGui::SetNextWindowPos({300, 550});
97+
}
98+
99+
static float start_angle = 45;
100+
static float sweep_angle = 270;
101+
static bool use_center = true;
102+
103+
ImGui::Begin("Controls");
104+
ImGui::SliderFloat("Start angle", &start_angle, -360, 360);
105+
ImGui::SliderFloat("Sweep angle", &sweep_angle, -360, 360);
106+
ImGui::Checkbox("Use center", &use_center);
107+
ImGui::End();
108+
109+
auto [p1, p2] = IMPELLER_PLAYGROUND_LINE(
110+
Point(200, 200), Point(400, 400), 20, Color::White(), Color::White());
111+
112+
flutter::DisplayListBuilder builder;
113+
builder.setStyle(SkPaint::Style::kStroke_Style);
114+
builder.setStrokeCap(SkPaint::Cap::kRound_Cap);
115+
builder.setStrokeJoin(SkPaint::Join::kMiter_Join);
116+
builder.setStrokeMiter(10);
117+
auto rect = SkRect::MakeLTRB(p1.x, p1.y, p2.x, p2.y);
118+
builder.setColor(SK_ColorGREEN);
119+
builder.setStrokeWidth(2);
120+
builder.drawRect(rect);
121+
builder.setColor(SK_ColorRED);
122+
builder.setStrokeWidth(10);
123+
builder.drawArc(rect, start_angle, sweep_angle, use_center);
124+
125+
return builder.Build();
126+
};
127+
ASSERT_TRUE(OpenPlaygroundHere(callback));
128+
}
129+
84130
} // namespace testing
85131
} // namespace impeller

impeller/geometry/constants.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,12 @@ constexpr float klogE_2 = 0.69314718055994530942;
1919
// log_e 10
2020
constexpr float klogE_10 = 2.30258509299404568402;
2121

22-
// pi */
22+
// pi
2323
constexpr float kPi = 3.14159265358979323846;
2424

25+
// pi*2
26+
constexpr float k2Pi = 6.28318530717958647693;
27+
2528
// pi/2
2629
constexpr float kPiOver2 = 1.57079632679489661923;
2730

impeller/geometry/path_builder.cc

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
#include "path_builder.h"
66

7+
#include <cmath>
8+
79
namespace impeller {
810

911
PathBuilder::PathBuilder() = default;
@@ -288,10 +290,69 @@ PathBuilder& PathBuilder::AddRoundedRect(Rect rect, RoundingRadii radii) {
288290
return *this;
289291
}
290292

293+
PathBuilder& PathBuilder::AddArc(const Rect& oval_bounds,
294+
Radians start,
295+
Radians sweep,
296+
bool use_center) {
297+
if (sweep.radians < 0) {
298+
start.radians += sweep.radians;
299+
sweep.radians *= -1;
300+
}
301+
sweep.radians = std::min(k2Pi, sweep.radians);
302+
start.radians = std::fmod(start.radians, k2Pi);
303+
304+
const Point radius = {oval_bounds.size.width * 0.5f,
305+
oval_bounds.size.height * 0.5f};
306+
const Point center = {oval_bounds.origin.x + radius.x,
307+
oval_bounds.origin.y + radius.y};
308+
309+
Vector2 p1_unit(std::cos(start.radians), std::sin(start.radians));
310+
311+
if (use_center) {
312+
MoveTo(center);
313+
LineTo(center + p1_unit * radius);
314+
} else {
315+
MoveTo(center + p1_unit * radius);
316+
}
317+
318+
while (sweep.radians > 0) {
319+
Vector2 p2_unit;
320+
Scalar quadrant_angle;
321+
if (sweep.radians < kPiOver2) {
322+
quadrant_angle = sweep.radians;
323+
p2_unit = Vector2(std::cos(start.radians + quadrant_angle),
324+
std::sin(start.radians + quadrant_angle));
325+
} else {
326+
quadrant_angle = kPiOver2;
327+
p2_unit = Vector2(-p1_unit.y, p1_unit.x);
328+
}
329+
330+
Vector2 arc_cp_lengths =
331+
(quadrant_angle / kPiOver2) * kArcApproximationMagic * radius;
332+
333+
Point p1 = center + p1_unit * radius;
334+
Point p2 = center + p2_unit * radius;
335+
Point cp1 = p1 + Vector2(-p1_unit.y, p1_unit.x) * arc_cp_lengths;
336+
Point cp2 = p2 + Vector2(p2_unit.y, -p2_unit.x) * arc_cp_lengths;
337+
338+
prototype_.AddCubicComponent(p1, cp1, cp2, p2);
339+
current_ = p2;
340+
341+
start.radians += quadrant_angle;
342+
sweep.radians -= quadrant_angle;
343+
p1_unit = p2_unit;
344+
}
345+
346+
if (use_center) {
347+
Close();
348+
}
349+
350+
return *this;
351+
}
352+
291353
PathBuilder& PathBuilder::AddOval(const Rect& container) {
292354
const Point r = {container.size.width * 0.5f, container.size.height * 0.5f};
293-
const Point c = {container.origin.x + (container.size.width * 0.5f),
294-
container.origin.y + (container.size.height * 0.5f)};
355+
const Point c = {container.origin.x + r.x, container.origin.y + r.y};
295356
const Point m = {kArcApproximationMagic * r.x, kArcApproximationMagic * r.y};
296357

297358
MoveTo({c.x, c.y - r.y});

impeller/geometry/path_builder.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ class PathBuilder {
6060

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

63+
PathBuilder& AddArc(const Rect& oval_bounds,
64+
Radians start,
65+
Radians sweep,
66+
bool use_center = false);
67+
6368
PathBuilder& AddOval(const Rect& rect);
6469

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

0 commit comments

Comments
 (0)