| Flutter Impeller |

path_unittests.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "gtest/gtest.h"
6
7 #include "flutter/testing/testing.h"
8 #include "impeller/geometry/geometry_asserts.h"
9 #include "impeller/geometry/path.h"
10 #include "impeller/geometry/path_builder.h"
11 #include "impeller/geometry/path_component.h"
12 #include "impeller/geometry/round_rect.h"
13
14 namespace impeller {
15 namespace testing {
16
17 TEST(PathTest, CubicPathComponentPolylineDoesNotIncludePointOne) {
18 CubicPathComponent component({10, 10}, {20, 35}, {35, 20}, {40, 40});
19 std::vector<Point> polyline;
20 component.AppendPolylinePoints(1.0f, polyline);
21 ASSERT_NE(polyline.front().x, 10);
22 ASSERT_NE(polyline.front().y, 10);
23 ASSERT_EQ(polyline.back().x, 40);
24 ASSERT_EQ(polyline.back().y, 40);
25 }
26
27 TEST(PathTest, EmptyPathWithContour) {
28 PathBuilder builder;
29 auto path = builder.TakePath();
30
31 EXPECT_TRUE(path.IsEmpty());
32 }
33
34 TEST(PathTest, PathCreatePolyLineDoesNotDuplicatePoints) {
35 PathBuilder builder;
36 builder.MoveTo({10, 10});
37 builder.LineTo({20, 20});
38 builder.LineTo({30, 30});
39 builder.MoveTo({40, 40});
40 builder.LineTo({50, 50});
41
42 auto polyline = builder.TakePath().CreatePolyline(1.0f);
43
44 ASSERT_EQ(polyline.contours.size(), 2u);
45 ASSERT_EQ(polyline.points->size(), 5u);
46 ASSERT_EQ(polyline.GetPoint(0).x, 10);
47 ASSERT_EQ(polyline.GetPoint(1).x, 20);
48 ASSERT_EQ(polyline.GetPoint(2).x, 30);
49 ASSERT_EQ(polyline.GetPoint(3).x, 40);
50 ASSERT_EQ(polyline.GetPoint(4).x, 50);
51 }
52
53 TEST(PathTest, PathSingleContour) {
54 // Closed shapes.
55 {
56 Path path = PathBuilder{}.AddCircle({100, 100}, 50).TakePath();
57 EXPECT_TRUE(path.IsSingleContour());
58 }
59
60 {
61 Path path =
62 PathBuilder{}.AddOval(Rect::MakeXYWH(100, 100, 100, 100)).TakePath();
63
64 EXPECT_TRUE(path.IsSingleContour());
65 }
66
67 {
68 Path path =
69 PathBuilder{}.AddRect(Rect::MakeXYWH(100, 100, 100, 100)).TakePath();
70
71 EXPECT_TRUE(path.IsSingleContour());
72 }
73
74 {
75 Path path = PathBuilder{}
76 .AddRoundRect(RoundRect::MakeRectRadius(
77 Rect::MakeXYWH(100, 100, 100, 100), 10))
78 .TakePath();
79
80 EXPECT_TRUE(path.IsSingleContour());
81 }
82
83 // Open shapes.
84 {
85 Point p(100, 100);
86 Path path = PathBuilder{}.AddLine(p, {200, 100}).TakePath();
87
88 EXPECT_TRUE(path.IsSingleContour());
89 }
90
91 {
92 Path path =
93 PathBuilder{}
94 .AddCubicCurve({100, 100}, {100, 50}, {100, 150}, {200, 100})
95 .TakePath();
96
97 EXPECT_TRUE(path.IsSingleContour());
98 }
99
100 {
101 Path path = PathBuilder{}
102 .AddQuadraticCurve({100, 100}, {100, 50}, {200, 100})
103 .TakePath();
104
105 EXPECT_TRUE(path.IsSingleContour());
106 }
107 }
108
109 TEST(PathTest, PathSingleContourDoubleShapes) {
110 // Closed shapes.
111 {
112 Path path = PathBuilder{}
113 .AddCircle({100, 100}, 50)
114 .AddCircle({100, 100}, 50)
115 .TakePath();
116 EXPECT_FALSE(path.IsSingleContour());
117 }
118
119 {
120 Path path = PathBuilder{}
121 .AddOval(Rect::MakeXYWH(100, 100, 100, 100))
122 .AddOval(Rect::MakeXYWH(100, 100, 100, 100))
123 .TakePath();
124
125 EXPECT_FALSE(path.IsSingleContour());
126 }
127
128 {
129 Path path = PathBuilder{}
130 .AddRect(Rect::MakeXYWH(100, 100, 100, 100))
131 .AddRect(Rect::MakeXYWH(100, 100, 100, 100))
132 .TakePath();
133
134 EXPECT_FALSE(path.IsSingleContour());
135 }
136
137 {
138 Path path = PathBuilder{}
139 .AddRoundRect(RoundRect::MakeRectRadius(
140 Rect::MakeXYWH(100, 100, 100, 100), 10))
141 .AddRoundRect(RoundRect::MakeRectRadius(
142 Rect::MakeXYWH(100, 100, 100, 100), 10))
143 .TakePath();
144
145 EXPECT_FALSE(path.IsSingleContour());
146 }
147
148 {
149 Path path = PathBuilder{}
150 .AddRoundRect(RoundRect::MakeRectXY(
151 Rect::MakeXYWH(100, 100, 100, 100), Size(10, 20)))
152 .AddRoundRect(RoundRect::MakeRectXY(
153 Rect::MakeXYWH(100, 100, 100, 100), Size(10, 20)))
154 .TakePath();
155
156 EXPECT_FALSE(path.IsSingleContour());
157 }
158
159 // Open shapes.
160 {
161 Point p(100, 100);
162 Path path =
163 PathBuilder{}.AddLine(p, {200, 100}).AddLine(p, {200, 100}).TakePath();
164
165 EXPECT_FALSE(path.IsSingleContour());
166 }
167
168 {
169 Path path =
170 PathBuilder{}
171 .AddCubicCurve({100, 100}, {100, 50}, {100, 150}, {200, 100})
172 .AddCubicCurve({100, 100}, {100, 50}, {100, 150}, {200, 100})
173 .TakePath();
174
175 EXPECT_FALSE(path.IsSingleContour());
176 }
177
178 {
179 Path path = PathBuilder{}
180 .AddQuadraticCurve({100, 100}, {100, 50}, {200, 100})
181 .Close()
182 .AddQuadraticCurve({100, 100}, {100, 50}, {200, 100})
183 .TakePath();
184
185 EXPECT_FALSE(path.IsSingleContour());
186 }
187 }
188
189 TEST(PathTest, PathBuilderSetsCorrectContourPropertiesForAddCommands) {
190 // Closed shapes.
191 {
192 Path path = PathBuilder{}.AddCircle({100, 100}, 50).TakePath();
193 ContourComponent contour;
194 path.GetContourComponentAtIndex(0, contour);
195 EXPECT_POINT_NEAR(contour.destination, Point(100, 50));
196 EXPECT_TRUE(contour.IsClosed());
197 }
198
199 {
200 Path path =
201 PathBuilder{}.AddOval(Rect::MakeXYWH(100, 100, 100, 100)).TakePath();
202 ContourComponent contour;
203 path.GetContourComponentAtIndex(0, contour);
204 EXPECT_POINT_NEAR(contour.destination, Point(150, 100));
205 EXPECT_TRUE(contour.IsClosed());
206 }
207
208 {
209 Path path =
210 PathBuilder{}.AddRect(Rect::MakeXYWH(100, 100, 100, 100)).TakePath();
211 ContourComponent contour;
212 path.GetContourComponentAtIndex(0, contour);
213 EXPECT_POINT_NEAR(contour.destination, Point(100, 100));
214 EXPECT_TRUE(contour.IsClosed());
215 }
216
217 {
218 Path path = PathBuilder{}
219 .AddRoundRect(RoundRect::MakeRectRadius(
220 Rect::MakeXYWH(100, 100, 100, 100), 10))
221 .TakePath();
222 ContourComponent contour;
223 path.GetContourComponentAtIndex(0, contour);
224 EXPECT_POINT_NEAR(contour.destination, Point(110, 100));
225 EXPECT_TRUE(contour.IsClosed());
226 }
227
228 {
229 Path path = PathBuilder{}
230 .AddRoundRect(RoundRect::MakeRectXY(
231 Rect::MakeXYWH(100, 100, 100, 100), Size(10, 20)))
232 .TakePath();
233 ContourComponent contour;
234 path.GetContourComponentAtIndex(0, contour);
235 EXPECT_POINT_NEAR(contour.destination, Point(110, 100));
236 EXPECT_TRUE(contour.IsClosed());
237 }
238
239 // Open shapes.
240 {
241 Point p(100, 100);
242 Path path = PathBuilder{}.AddLine(p, {200, 100}).TakePath();
243 ContourComponent contour;
244 path.GetContourComponentAtIndex(0, contour);
245 ASSERT_POINT_NEAR(contour.destination, p);
246 ASSERT_FALSE(contour.IsClosed());
247 }
248
249 {
250 Path path =
251 PathBuilder{}
252 .AddCubicCurve({100, 100}, {100, 50}, {100, 150}, {200, 100})
253 .TakePath();
254 ContourComponent contour;
255 path.GetContourComponentAtIndex(0, contour);
256 ASSERT_POINT_NEAR(contour.destination, Point(100, 100));
257 ASSERT_FALSE(contour.IsClosed());
258 }
259
260 {
261 Path path = PathBuilder{}
262 .AddQuadraticCurve({100, 100}, {100, 50}, {200, 100})
263 .TakePath();
264 ContourComponent contour;
265 path.GetContourComponentAtIndex(0, contour);
266 ASSERT_POINT_NEAR(contour.destination, Point(100, 100));
267 ASSERT_FALSE(contour.IsClosed());
268 }
269 }
270
271 TEST(PathTest, PathCreatePolylineGeneratesCorrectContourData) {
272 Path::Polyline polyline = PathBuilder{}
273 .AddLine({100, 100}, {200, 100})
274 .MoveTo({100, 200})
275 .LineTo({150, 250})
276 .LineTo({200, 200})
277 .Close()
278 .TakePath()
279 .CreatePolyline(1.0f);
280 ASSERT_EQ(polyline.points->size(), 6u);
281 ASSERT_EQ(polyline.contours.size(), 2u);
282 ASSERT_EQ(polyline.contours[0].is_closed, false);
283 ASSERT_EQ(polyline.contours[0].start_index, 0u);
284 ASSERT_EQ(polyline.contours[1].is_closed, true);
285 ASSERT_EQ(polyline.contours[1].start_index, 2u);
286 }
287
288 TEST(PathTest, PolylineGetContourPointBoundsReturnsCorrectRanges) {
289 Path::Polyline polyline = PathBuilder{}
290 .AddLine({100, 100}, {200, 100})
291 .MoveTo({100, 200})
292 .LineTo({150, 250})
293 .LineTo({200, 200})
294 .Close()
295 .TakePath()
296 .CreatePolyline(1.0f);
297 size_t a1, a2, b1, b2;
298 std::tie(a1, a2) = polyline.GetContourPointBounds(0);
299 std::tie(b1, b2) = polyline.GetContourPointBounds(1);
300 ASSERT_EQ(a1, 0u);
301 ASSERT_EQ(a2, 2u);
302 ASSERT_EQ(b1, 2u);
303 ASSERT_EQ(b2, 6u);
304 }
305
306 TEST(PathTest, PathAddRectPolylineHasCorrectContourData) {
307 Path::Polyline polyline = PathBuilder{}
308 .AddRect(Rect::MakeLTRB(50, 60, 70, 80))
309 .TakePath()
310 .CreatePolyline(1.0f);
311 ASSERT_EQ(polyline.contours.size(), 1u);
312 ASSERT_TRUE(polyline.contours[0].is_closed);
313 ASSERT_EQ(polyline.contours[0].start_index, 0u);
314 ASSERT_EQ(polyline.points->size(), 5u);
315 ASSERT_EQ(polyline.GetPoint(0), Point(50, 60));
316 ASSERT_EQ(polyline.GetPoint(1), Point(70, 60));
317 ASSERT_EQ(polyline.GetPoint(2), Point(70, 80));
318 ASSERT_EQ(polyline.GetPoint(3), Point(50, 80));
319 ASSERT_EQ(polyline.GetPoint(4), Point(50, 60));
320 }
321
322 TEST(PathTest, PathPolylineDuplicatesAreRemovedForSameContour) {
323 Path::Polyline polyline =
324 PathBuilder{}
325 .MoveTo({50, 50})
326 .LineTo({50, 50}) // Insert duplicate at beginning of contour.
327 .LineTo({100, 50})
328 .LineTo({100, 50}) // Insert duplicate at contour join.
329 .LineTo({100, 100})
330 .Close() // Implicitly insert duplicate {50, 50} across contours.
331 .LineTo({0, 50})
332 .LineTo({0, 100})
333 .LineTo({0, 100}) // Insert duplicate at end of contour.
334 .TakePath()
335 .CreatePolyline(1.0f);
336 ASSERT_EQ(polyline.contours.size(), 2u);
337 ASSERT_EQ(polyline.contours[0].start_index, 0u);
338 ASSERT_TRUE(polyline.contours[0].is_closed);
339 ASSERT_EQ(polyline.contours[1].start_index, 4u);
340 ASSERT_FALSE(polyline.contours[1].is_closed);
341 ASSERT_EQ(polyline.points->size(), 7u);
342 ASSERT_EQ(polyline.GetPoint(0), Point(50, 50));
343 ASSERT_EQ(polyline.GetPoint(1), Point(100, 50));
344 ASSERT_EQ(polyline.GetPoint(2), Point(100, 100));
345 ASSERT_EQ(polyline.GetPoint(3), Point(50, 50));
346 ASSERT_EQ(polyline.GetPoint(4), Point(50, 50));
347 ASSERT_EQ(polyline.GetPoint(5), Point(0, 50));
348 ASSERT_EQ(polyline.GetPoint(6), Point(0, 100));
349 }
350
351 TEST(PathTest, PolylineBufferReuse) {
352 auto point_buffer = std::make_unique<std::vector<Point>>();
353 auto point_buffer_address = reinterpret_cast<uintptr_t>(point_buffer.get());
354 Path::Polyline polyline =
355 PathBuilder{}
356 .MoveTo({50, 50})
357 .LineTo({100, 100})
358 .TakePath()
359 .CreatePolyline(
360 1.0f, std::move(point_buffer),
361 [point_buffer_address](
362 Path::Polyline::PointBufferPtr point_buffer) {
363 ASSERT_EQ(point_buffer->size(), 0u);
364 ASSERT_EQ(point_buffer_address,
365 reinterpret_cast<uintptr_t>(point_buffer.get()));
366 });
367 }
368
369 TEST(PathTest, PolylineFailsWithNullptrBuffer) {
370 EXPECT_DEATH_IF_SUPPORTED(PathBuilder{}
371 .MoveTo({50, 50})
372 .LineTo({100, 100})
373 .TakePath()
374 .CreatePolyline(1.0f, nullptr),
375 "");
376 }
377
378 TEST(PathTest, PathShifting) {
379 PathBuilder builder{};
380 auto path =
381 builder.AddLine(Point(0, 0), Point(10, 10))
382 .AddQuadraticCurve(Point(10, 10), Point(15, 15), Point(20, 20))
383 .AddCubicCurve(Point(20, 20), Point(25, 25), Point(-5, -5),
384 Point(30, 30))
385 .Close()
386 .Shift(Point(1, 1))
387 .TakePath();
388
389 ContourComponent contour;
390 LinearPathComponent linear;
391 QuadraticPathComponent quad;
392 CubicPathComponent cubic;
393
394 ASSERT_TRUE(path.GetContourComponentAtIndex(0, contour));
395 ASSERT_TRUE(path.GetLinearComponentAtIndex(1, linear));
396 ASSERT_TRUE(path.GetQuadraticComponentAtIndex(3, quad));
397 ASSERT_TRUE(path.GetCubicComponentAtIndex(5, cubic));
398
399 EXPECT_EQ(contour.destination, Point(1, 1));
400
401 EXPECT_EQ(linear.p1, Point(1, 1));
402 EXPECT_EQ(linear.p2, Point(11, 11));
403
404 EXPECT_EQ(quad.cp, Point(16, 16));
405 EXPECT_EQ(quad.p1, Point(11, 11));
406 EXPECT_EQ(quad.p2, Point(21, 21));
407
408 EXPECT_EQ(cubic.cp1, Point(26, 26));
409 EXPECT_EQ(cubic.cp2, Point(-4, -4));
410 EXPECT_EQ(cubic.p1, Point(21, 21));
411 EXPECT_EQ(cubic.p2, Point(31, 31));
412 }
413
414 TEST(PathTest, PathBuilderWillComputeBounds) {
415 PathBuilder builder;
416 auto path_1 = builder.AddLine({0, 0}, {1, 1}).TakePath();
417
418 ASSERT_EQ(path_1.GetBoundingBox().value_or(Rect::MakeMaximum()),
419 Rect::MakeLTRB(0, 0, 1, 1));
420
421 auto path_2 = builder.AddLine({-1, -1}, {1, 1}).TakePath();
422
423 // Verify that PathBuilder recomputes the bounds.
424 ASSERT_EQ(path_2.GetBoundingBox().value_or(Rect::MakeMaximum()),
425 Rect::MakeLTRB(-1, -1, 1, 1));
426
427 // PathBuilder can set the bounds to whatever it wants
428 auto path_3 = builder.AddLine({0, 0}, {1, 1})
429 .SetBounds(Rect::MakeLTRB(0, 0, 100, 100))
430 .TakePath();
431
432 ASSERT_EQ(path_3.GetBoundingBox().value_or(Rect::MakeMaximum()),
433 Rect::MakeLTRB(0, 0, 100, 100));
434 }
435
436 TEST(PathTest, PathHorizontalLine) {
437 PathBuilder builder;
438 auto path = builder.HorizontalLineTo(10).TakePath();
439
440 LinearPathComponent linear;
441 path.GetLinearComponentAtIndex(1, linear);
442
443 EXPECT_EQ(linear.p1, Point(0, 0));
444 EXPECT_EQ(linear.p2, Point(10, 0));
445 }
446
447 TEST(PathTest, PathVerticalLine) {
448 PathBuilder builder;
449 auto path = builder.VerticalLineTo(10).TakePath();
450
451 LinearPathComponent linear;
452 path.GetLinearComponentAtIndex(1, linear);
453
454 EXPECT_EQ(linear.p1, Point(0, 0));
455 EXPECT_EQ(linear.p2, Point(0, 10));
456 }
457
458 TEST(PathTest, QuadradicPath) {
459 PathBuilder builder;
460 auto path = builder.QuadraticCurveTo(Point(10, 10), Point(20, 20)).TakePath();
461
462 QuadraticPathComponent quad;
463 path.GetQuadraticComponentAtIndex(1, quad);
464
465 EXPECT_EQ(quad.p1, Point(0, 0));
466 EXPECT_EQ(quad.cp, Point(10, 10));
467 EXPECT_EQ(quad.p2, Point(20, 20));
468 }
469
470 TEST(PathTest, CubicPath) {
471 PathBuilder builder;
472 auto path =
473 builder.CubicCurveTo(Point(10, 10), Point(-10, -10), Point(20, 20))
474 .TakePath();
475
476 CubicPathComponent cubic;
477 path.GetCubicComponentAtIndex(1, cubic);
478
479 EXPECT_EQ(cubic.p1, Point(0, 0));
480 EXPECT_EQ(cubic.cp1, Point(10, 10));
481 EXPECT_EQ(cubic.cp2, Point(-10, -10));
482 EXPECT_EQ(cubic.p2, Point(20, 20));
483 }
484
485 TEST(PathTest, BoundingBoxCubic) {
486 PathBuilder builder;
487 auto path =
488 builder.AddCubicCurve({120, 160}, {25, 200}, {220, 260}, {220, 40})
489 .TakePath();
490 auto box = path.GetBoundingBox();
491 Rect expected = Rect::MakeXYWH(93.9101, 40, 126.09, 158.862);
492 ASSERT_TRUE(box.has_value());
493 ASSERT_RECT_NEAR(box.value_or(Rect::MakeMaximum()), expected);
494 }
495
496 TEST(PathTest, BoundingBoxOfCompositePathIsCorrect) {
497 PathBuilder builder;
498 builder.AddRoundRect(
499 RoundRect::MakeRectRadius(Rect::MakeXYWH(10, 10, 300, 300), 50));
500 auto path = builder.TakePath();
501 auto actual = path.GetBoundingBox();
502 Rect expected = Rect::MakeXYWH(10, 10, 300, 300);
503
504 ASSERT_TRUE(actual.has_value());
505 ASSERT_RECT_NEAR(actual.value_or(Rect::MakeMaximum()), expected);
506 }
507
508 TEST(PathTest, ExtremaOfCubicPathComponentIsCorrect) {
509 CubicPathComponent cubic{{11.769268, 252.883148},
510 {-6.2857933, 204.356461},
511 {-4.53997231, 156.552902},
512 {17.0067291, 109.472488}};
513 auto points = cubic.Extrema();
514
515 ASSERT_EQ(points.size(), static_cast<size_t>(3));
516 ASSERT_POINT_NEAR(points[2], cubic.Solve(0.455916));
517 }
518
519 TEST(PathTest, PathGetBoundingBoxForCubicWithNoDerivativeRootsIsCorrect) {
520 PathBuilder builder;
521 // Straight diagonal line.
522 builder.AddCubicCurve({0, 1}, {2, 3}, {4, 5}, {6, 7});
523 auto path = builder.TakePath();
524 auto actual = path.GetBoundingBox();
525 auto expected = Rect::MakeLTRB(0, 1, 6, 7);
526
527 ASSERT_TRUE(actual.has_value());
528 ASSERT_RECT_NEAR(actual.value_or(Rect::MakeMaximum()), expected);
529 }
530
531 TEST(PathTest, EmptyPath) {
532 auto path = PathBuilder{}.TakePath();
533 ASSERT_EQ(path.GetComponentCount(), 1u);
534
535 ContourComponent c;
536 path.GetContourComponentAtIndex(0, c);
537 ASSERT_POINT_NEAR(c.destination, Point());
538
539 Path::Polyline polyline = path.CreatePolyline(1.0f);
540 ASSERT_TRUE(polyline.points->empty());
541 ASSERT_TRUE(polyline.contours.empty());
542 }
543
544 TEST(PathTest, SimplePath) {
545 PathBuilder builder;
546
547 auto path = builder.AddLine({0, 0}, {100, 100})
548 .AddQuadraticCurve({100, 100}, {200, 200}, {300, 300})
549 .AddCubicCurve({300, 300}, {400, 400}, {500, 500}, {600, 600})
550 .TakePath();
551
552 EXPECT_EQ(path.GetComponentCount(), 6u);
553 EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kLinear), 1u);
554 EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kQuadratic), 1u);
555 EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kCubic), 1u);
556 EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kContour), 3u);
557
558 {
559 LinearPathComponent linear;
560 EXPECT_TRUE(path.GetLinearComponentAtIndex(1, linear));
561
562 Point p1(0, 0);
563 Point p2(100, 100);
564 EXPECT_EQ(linear.p1, p1);
565 EXPECT_EQ(linear.p2, p2);
566 }
567
568 {
569 QuadraticPathComponent quad;
570 EXPECT_TRUE(path.GetQuadraticComponentAtIndex(3, quad));
571
572 Point p1(100, 100);
573 Point cp(200, 200);
574 Point p2(300, 300);
575 EXPECT_EQ(quad.p1, p1);
576 EXPECT_EQ(quad.cp, cp);
577 EXPECT_EQ(quad.p2, p2);
578 }
579
580 {
581 CubicPathComponent cubic;
582 EXPECT_TRUE(path.GetCubicComponentAtIndex(5, cubic));
583
584 Point p1(300, 300);
585 Point cp1(400, 400);
586 Point cp2(500, 500);
587 Point p2(600, 600);
588 EXPECT_EQ(cubic.p1, p1);
589 EXPECT_EQ(cubic.cp1, cp1);
590 EXPECT_EQ(cubic.cp2, cp2);
591 EXPECT_EQ(cubic.p2, p2);
592 }
593
594 {
595 ContourComponent contour;
596 EXPECT_TRUE(path.GetContourComponentAtIndex(0, contour));
597
598 Point p1(0, 0);
599 EXPECT_EQ(contour.destination, p1);
600 EXPECT_FALSE(contour.IsClosed());
601 }
602
603 {
604 ContourComponent contour;
605 EXPECT_TRUE(path.GetContourComponentAtIndex(2, contour));
606
607 Point p1(100, 100);
608 EXPECT_EQ(contour.destination, p1);
609 EXPECT_FALSE(contour.IsClosed());
610 }
611
612 {
613 ContourComponent contour;
614 EXPECT_TRUE(path.GetContourComponentAtIndex(4, contour));
615
616 Point p1(300, 300);
617 EXPECT_EQ(contour.destination, p1);
618 EXPECT_FALSE(contour.IsClosed());
619 }
620 }
621
622 TEST(PathTest, RepeatCloseDoesNotAddNewLines) {
623 PathBuilder builder;
624 auto path = builder.LineTo({0, 10})
625 .LineTo({10, 10})
626 .Close() // Returns to (0, 0)
627 .Close() // No Op
628 .Close() // Still No op
629 .TakePath();
630
631 EXPECT_EQ(path.GetComponentCount(), 5u);
632 EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kLinear), 3u);
633 EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kContour), 2u);
634 }
635
636 TEST(PathTest, CloseAfterMoveDoesNotAddNewLines) {
637 PathBuilder builder;
638 auto path = builder.LineTo({0, 10})
639 .LineTo({10, 10})
640 .MoveTo({30, 30}) // Moves to (30, 30)
641 .Close() // No Op
642 .Close() // Still No op
643 .TakePath();
644
645 EXPECT_EQ(path.GetComponentCount(), 4u);
646 EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kLinear), 2u);
647 EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kContour), 2u);
648 }
649
650 TEST(PathTest, CloseAtOriginDoesNotAddNewLineSegment) {
651 PathBuilder builder;
652 // Create a path that has a current position at the origin when close is
653 // called. This should not insert a new line segment
654 auto path = builder.LineTo({10, 0})
655 .LineTo({10, 10})
656 .LineTo({0, 10})
657 .LineTo({0, 0})
658 .Close()
659 .TakePath();
660
661 EXPECT_EQ(path.GetComponentCount(), 6u);
662 EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kLinear), 4u);
663 EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kContour), 2u);
664 }
665
666 TEST(PathTest, CanBeCloned) {
667 PathBuilder builder;
668 builder.MoveTo({10, 10});
669 builder.LineTo({20, 20});
670 builder.SetBounds(Rect::MakeLTRB(0, 0, 100, 100));
671 builder.SetConvexity(Convexity::kConvex);
672
673 auto path_a = builder.TakePath(FillType::kOdd);
674 // NOLINTNEXTLINE(performance-unnecessary-copy-initialization)
675 auto path_b = path_a;
676
677 EXPECT_EQ(path_a.GetBoundingBox(), path_b.GetBoundingBox());
678 EXPECT_EQ(path_a.GetFillType(), path_b.GetFillType());
679 EXPECT_EQ(path_a.IsConvex(), path_b.IsConvex());
680
681 auto poly_a = path_a.CreatePolyline(1.0);
682 auto poly_b = path_b.CreatePolyline(1.0);
683
684 ASSERT_EQ(poly_a.points->size(), poly_b.points->size());
685 ASSERT_EQ(poly_a.contours.size(), poly_b.contours.size());
686
687 for (auto i = 0u; i < poly_a.points->size(); i++) {
688 EXPECT_EQ((*poly_a.points)[i], (*poly_b.points)[i]);
689 }
690
691 for (auto i = 0u; i < poly_a.contours.size(); i++) {
692 EXPECT_EQ(poly_a.contours[i].start_index, poly_b.contours[i].start_index);
693 EXPECT_EQ(poly_a.contours[i].start_direction,
694 poly_b.contours[i].start_direction);
695 }
696 }
697
698 TEST(PathTest, FanTessellation) {
699 Path path = PathBuilder{}
700 .AddRoundRect(RoundRect::MakeRectRadius(
701 Rect::MakeLTRB(0, 0, 100, 100), 10))
702 .TakePath();
703 auto [points, contours] = path.CountStorage(1.0);
704
705 std::vector<Point> point_storage(points);
706 std::vector<uint16_t> index_storage(points + (contours - 1));
707
708 FanVertexWriter writer(point_storage.data(), index_storage.data());
709 path.WritePolyline(1.0, writer);
710
711 EXPECT_LE(writer.GetIndexCount(), index_storage.size());
712 EXPECT_EQ(point_storage[0], Point(10, 0));
713 }
714
715 // Filled Paths without an explicit close should still be closed
716 TEST(PathTest, FanTessellationUnclosedPath) {
717 // Create a rectangle that lacks an explicit close.
718 Path path = PathBuilder{}
719 .LineTo({100, 0})
720 .LineTo({100, 100})
721 .LineTo({0, 100})
722 .TakePath();
723
724 std::vector<Point> expected = {{0, 0}, {100, 0}, {100, 100},
725 {0, 100}, {0, 0}, {0, 0}};
726 std::vector<uint16_t> expected_indices = {0, 1, 2, 3, 0xFFFF, 0};
727
728 auto [points, contours] = path.CountStorage(1.0);
729
730 std::vector<Point> point_storage(points);
731 std::vector<uint16_t> index_storage(points + (contours - 1));
732
733 FanVertexWriter writer(point_storage.data(), index_storage.data());
734 path.WritePolyline(1.0, writer);
735
736 EXPECT_LE(index_storage, expected_indices);
737 EXPECT_EQ(point_storage, expected);
738 }
739
740 // Filled Paths without an explicit close should still be closed
741 TEST(PathTest, StripTessellationUnclosedPath) {
742 // Create a rectangle that lacks an explicit close.
743 Path path = PathBuilder{}
744 .LineTo({100, 0})
745 .LineTo({100, 100})
746 .LineTo({0, 100})
747 .TakePath();
748
749 std::vector<Point> expected = {{0, 0}, {100, 0}, {100, 100},
750 {0, 100}, {0, 0}, {0, 0}};
751 std::vector<uint16_t> expected_indices = {0, 1, 3, 2, 0xFFFF, 0};
752
753 auto [points, contours] = path.CountStorage(1.0);
754
755 std::vector<Point> point_storage(points);
756 std::vector<uint16_t> index_storage(points + (contours - 1));
757
758 StripVertexWriter writer(point_storage.data(), index_storage.data());
759 path.WritePolyline(1.0, writer);
760
761 EXPECT_LE(index_storage, expected_indices);
762 EXPECT_EQ(point_storage, expected);
763 }
764
765 TEST(PathTest, FanTessellationMultiContour) {
766 PathBuilder builder{};
767 for (auto i = 0; i < 10; i++) {
768 builder.AddRoundRect(
769 RoundRect::MakeRectRadius(Rect::MakeLTRB(0 + i, 0 + i, 100, 100), 10));
770 }
771 auto path = builder.TakePath();
772 auto [points, contours] = path.CountStorage(1.0);
773
774 std::vector<Point> point_storage(points);
775 std::vector<uint16_t> index_storage(points + (contours - 1));
776
777 FanVertexWriter writer(point_storage.data(), index_storage.data());
778 path.WritePolyline(1.0, writer);
779
780 EXPECT_LE(writer.GetIndexCount(), index_storage.size());
781 EXPECT_EQ(point_storage[0], Point(10, 0));
782 }
783
784 TEST(PathTest, StripTessellation) {
785 Path path = PathBuilder{}
786 .AddRoundRect(RoundRect::MakeRectRadius(
787 Rect::MakeLTRB(0, 0, 100, 100), 10))
788 .TakePath();
789 auto [points, contours] = path.CountStorage(1.0);
790
791 std::vector<Point> point_storage(points);
792 std::vector<uint16_t> index_storage(points + (contours - 1));
793
794 StripVertexWriter writer(point_storage.data(), index_storage.data());
795 path.WritePolyline(1.0, writer);
796
797 EXPECT_LE(writer.GetIndexCount(), index_storage.size());
798 EXPECT_EQ(point_storage[0], Point(10, 0));
799 }
800
801 TEST(PathTest, StripTessellationMultiContour) {
802 PathBuilder builder{};
803 for (auto i = 0; i < 10; i++) {
804 builder.AddRoundRect(
805 RoundRect::MakeRectRadius(Rect::MakeLTRB(0 + i, 0 + i, 100, 100), 10));
806 }
807 auto path = builder.TakePath();
808 auto [points, contours] = path.CountStorage(1.0);
809
810 std::vector<Point> point_storage(points);
811 std::vector<uint16_t> index_storage(points + (contours - 1));
812
813 StripVertexWriter writer(point_storage.data(), index_storage.data());
814 path.WritePolyline(1.0, writer);
815
816 EXPECT_LE(writer.GetIndexCount(), index_storage.size());
817 EXPECT_EQ(point_storage[0], Point(10, 0));
818 }
819
820 TEST(PathTest, PathBuilderDoesNotMutateCopiedPaths) {
821 auto test_isolation =
822 [](const std::function<void(PathBuilder & builder)>& mutator,
823 bool will_close, Point mutation_offset, const std::string& label) {
824 PathBuilder builder;
825 builder.MoveTo({10, 10});
826 builder.LineTo({20, 20});
827 builder.LineTo({20, 10});
828
829 auto verify_path = [](const Path& path, bool is_mutated, bool is_closed,
830 Point offset, const std::string& label) {
831 if (is_mutated) {
832 // We can only test the initial state before the mutator did
833 // its work. We have >= 3 components and the first 3 components
834 // will match what we saw before the mutation.
835 EXPECT_GE(path.GetComponentCount(), 3u) << label;
836 } else {
837 EXPECT_EQ(path.GetComponentCount(), 3u) << label;
838 }
839 {
840 ContourComponent contour;
841 EXPECT_TRUE(path.GetContourComponentAtIndex(0, contour)) << label;
842 EXPECT_EQ(contour.destination, offset + Point(10, 10)) << label;
843 EXPECT_EQ(contour.IsClosed(), is_closed) << label;
844 }
845 {
846 LinearPathComponent line;
847 EXPECT_TRUE(path.GetLinearComponentAtIndex(1, line)) << label;
848 EXPECT_EQ(line.p1, offset + Point(10, 10)) << label;
849 EXPECT_EQ(line.p2, offset + Point(20, 20)) << label;
850 }
851 {
852 LinearPathComponent line;
853 EXPECT_TRUE(path.GetLinearComponentAtIndex(2, line)) << label;
854 EXPECT_EQ(line.p1, offset + Point(20, 20)) << label;
855 EXPECT_EQ(line.p2, offset + Point(20, 10)) << label;
856 }
857 };
858
859 auto path1 = builder.CopyPath();
860 verify_path(path1, false, false, {},
861 "Initial Path1 state before " + label);
862
863 for (int i = 0; i < 10; i++) {
864 auto path = builder.CopyPath();
865 verify_path(
866 path, false, false, {},
867 "Extra CopyPath #" + std::to_string(i + 1) + " for " + label);
868 }
869 mutator(builder);
870 verify_path(path1, false, false, {},
871 "Path1 state after subsequent " + label);
872
873 auto path2 = builder.CopyPath();
874 verify_path(path1, false, false, {},
875 "Path1 state after subsequent " + label + " and CopyPath");
876 verify_path(path2, true, will_close, mutation_offset,
877 "Initial Path2 state with subsequent " + label);
878 };
879
880 test_isolation(
881 [](PathBuilder& builder) { //
882 builder.SetConvexity(Convexity::kConvex);
883 },
884 false, {}, "SetConvex");
885
886 test_isolation(
887 [](PathBuilder& builder) { //
888 builder.SetConvexity(Convexity::kUnknown);
889 },
890 false, {}, "SetUnknownConvex");
891
892 test_isolation(
893 [](PathBuilder& builder) { //
894 builder.Close();
895 },
896 true, {}, "Close");
897
898 test_isolation(
899 [](PathBuilder& builder) {
900 builder.MoveTo({20, 30}, false);
901 },
902 false, {}, "Absolute MoveTo");
903
904 test_isolation(
905 [](PathBuilder& builder) {
906 builder.MoveTo({20, 30}, true);
907 },
908 false, {}, "Relative MoveTo");
909
910 test_isolation(
911 [](PathBuilder& builder) {
912 builder.LineTo({20, 30}, false);
913 },
914 false, {}, "Absolute LineTo");
915
916 test_isolation(
917 [](PathBuilder& builder) {
918 builder.LineTo({20, 30}, true);
919 },
920 false, {}, "Relative LineTo");
921
922 test_isolation(
923 [](PathBuilder& builder) { //
924 builder.HorizontalLineTo(100, false);
925 },
926 false, {}, "Absolute HorizontalLineTo");
927
928 test_isolation(
929 [](PathBuilder& builder) { //
930 builder.HorizontalLineTo(100, true);
931 },
932 false, {}, "Relative HorizontalLineTo");
933
934 test_isolation(
935 [](PathBuilder& builder) { //
936 builder.VerticalLineTo(100, false);
937 },
938 false, {}, "Absolute VerticalLineTo");
939
940 test_isolation(
941 [](PathBuilder& builder) { //
942 builder.VerticalLineTo(100, true);
943 },
944 false, {}, "Relative VerticalLineTo");
945
946 test_isolation(
947 [](PathBuilder& builder) {
948 builder.QuadraticCurveTo({20, 30}, {30, 20}, false);
949 },
950 false, {}, "Absolute QuadraticCurveTo");
951
952 test_isolation(
953 [](PathBuilder& builder) {
954 builder.QuadraticCurveTo({20, 30}, {30, 20}, true);
955 },
956 false, {}, "Relative QuadraticCurveTo");
957
958 test_isolation(
959 [](PathBuilder& builder) {
960 builder.CubicCurveTo({20, 30}, {30, 20}, {30, 30}, false);
961 },
962 false, {}, "Absolute CubicCurveTo");
963
964 test_isolation(
965 [](PathBuilder& builder) {
966 builder.CubicCurveTo({20, 30}, {30, 20}, {30, 30}, true);
967 },
968 false, {}, "Relative CubicCurveTo");
969
970 test_isolation(
971 [](PathBuilder& builder) {
972 builder.AddLine({100, 100}, {150, 100});
973 },
974 false, {}, "AddLine");
975
976 test_isolation(
977 [](PathBuilder& builder) {
978 builder.AddRect(Rect::MakeLTRB(100, 100, 120, 120));
979 },
980 false, {}, "AddRect");
981
982 test_isolation(
983 [](PathBuilder& builder) {
984 builder.AddOval(Rect::MakeLTRB(100, 100, 120, 120));
985 },
986 false, {}, "AddOval");
987
988 test_isolation(
989 [](PathBuilder& builder) {
990 builder.AddCircle({100, 100}, 20);
991 },
992 false, {}, "AddCircle");
993
994 test_isolation(
995 [](PathBuilder& builder) {
996 builder.AddArc(Rect::MakeLTRB(100, 100, 120, 120), Degrees(10),
997 Degrees(170));
998 },
999 false, {}, "AddArc");
1000
1001 test_isolation(
1002 [](PathBuilder& builder) {
1003 builder.AddQuadraticCurve({100, 100}, {150, 100}, {150, 150});
1004 },
1005 false, {}, "AddQuadraticCurve");
1006
1007 test_isolation(
1008 [](PathBuilder& builder) {
1009 builder.AddCubicCurve({100, 100}, {150, 100}, {100, 150}, {150, 150});
1010 },
1011 false, {}, "AddCubicCurve");
1012
1013 test_isolation(
1014 [](PathBuilder& builder) {
1015 builder.Shift({23, 42});
1016 },
1017 false, {23, 42}, "Shift");
1018 }
1019
1020 } // namespace testing
1021 } // namespace impeller
impeller::PathBuilder::AddQuadraticCurve
PathBuilder & AddQuadraticCurve(Point p1, Point cp, Point p2)
Move to point p1, then insert a quadradic curve from p1 to p2 with the control point cp.
Definition: path_builder.cc:114
polyline
const Path::Polyline & polyline
Definition: stroke_path_geometry.cc:311
impeller::LinearPathComponent
Definition: path_component.h:110
impeller::FanVertexWriter::GetIndexCount
size_t GetIndexCount() const
Definition: path_component.cc:22
geometry_asserts.h
impeller::CubicPathComponent::p1
Point p1
Definition: path_component.h:176
impeller::FillType::kOdd
@ kOdd
impeller::TRect< Scalar >::MakeXYWH
constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition: rect.h:136
impeller::Path::ComponentType::kLinear
@ kLinear
impeller::PathBuilder::SetBounds
PathBuilder & SetBounds(Rect bounds)
Set the bounding box that will be used by Path.GetBoundingBox in place of performing the computation.
Definition: path_builder.cc:510
impeller::LinearPathComponent::p2
Point p2
Definition: path_component.h:112
impeller::QuadraticPathComponent::p1
Point p1
Definition: path_component.h:136
impeller::RoundRect::MakeRectRadius
constexpr static RoundRect MakeRectRadius(const Rect &rect, Scalar radius)
Definition: round_rect.h:26
impeller::CubicPathComponent::cp2
Point cp2
Definition: path_component.h:180
impeller::PathBuilder::CubicCurveTo
PathBuilder & CubicCurveTo(Point controlPoint1, Point controlPoint2, Point point, bool relative=false)
Insert a cubic curve from the curren position to point using the control points controlPoint1 and con...
Definition: path_builder.cc:102
impeller::Path::ComponentType::kCubic
@ kCubic
impeller::PathBuilder
Definition: path_builder.h:15
impeller::QuadraticPathComponent::cp
Point cp
Definition: path_component.h:138
EXPECT_POINT_NEAR
#define EXPECT_POINT_NEAR(a, b)
Definition: geometry_asserts.h:205
impeller::Path::GetBoundingBox
std::optional< Rect > GetBoundingBox() const
Definition: path.cc:436
path_builder.h
impeller::Path::ComponentType::kQuadratic
@ kQuadratic
impeller::PathBuilder::HorizontalLineTo
PathBuilder & HorizontalLineTo(Scalar x, bool relative=false)
Definition: path_builder.cc:71
impeller::Size
TSize< Scalar > Size
Definition: size.h:171
impeller::PathBuilder::SetConvexity
PathBuilder & SetConvexity(Convexity value)
Definition: path_builder.cc:97
ASSERT_POINT_NEAR
#define ASSERT_POINT_NEAR(a, b)
Definition: geometry_asserts.h:193
offset
SeparatedVector2 offset
Definition: stroke_path_geometry.cc:319
impeller::MoveTo
void MoveTo(PathBuilder *builder, Scalar x, Scalar y)
Definition: tessellator.cc:20
impeller::PathBuilder::AddRect
PathBuilder & AddRect(Rect rect)
Definition: path_builder.cc:129
impeller::PathBuilder::AddRoundRect
PathBuilder & AddRoundRect(RoundRect rect)
Definition: path_builder.cc:151
ASSERT_RECT_NEAR
#define ASSERT_RECT_NEAR(a, b)
Definition: geometry_asserts.h:191
impeller::Path::Polyline
Definition: path.h:111
impeller::Convexity::kUnknown
@ kUnknown
impeller::Path::WritePolyline
void WritePolyline(Scalar scale, VertexWriter &writer) const
Definition: path.cc:105
impeller::CubicPathComponent::cp1
Point cp1
Definition: path_component.h:178
impeller::Point
TPoint< Scalar > Point
Definition: point.h:327
impeller::LinearPathComponent::p1
Point p1
Definition: path_component.h:111
impeller::PathBuilder::Shift
PathBuilder & Shift(Point offset)
Transform the existing path segments and contours by the given offset.
Definition: path_builder.cc:470
impeller::PathBuilder::QuadraticCurveTo
PathBuilder & QuadraticCurveTo(Point controlPoint, Point point, bool relative=false)
Insert a quadradic curve from the current position to point using the control point controlPoint.
Definition: path_builder.cc:87
impeller::Path
Paths are lightweight objects that describe a collection of linear, quadratic, or cubic segments....
Definition: path.h:53
impeller::FanVertexWriter
A vertex writer that generates a triangle fan and requires primitive restart.
Definition: path_component.h:29
impeller::PathBuilder::CopyPath
Path CopyPath(FillType fill=FillType::kNonZero)
Definition: path_builder.cc:19
impeller::PathBuilder::LineTo
PathBuilder & LineTo(Point point, bool relative=false)
Insert a line from the current position to point.
Definition: path_builder.cc:64
impeller::PathBuilder::AddCubicCurve
PathBuilder & AddCubicCurve(Point p1, Point cp1, Point cp2, Point p2)
Move to point p1, then insert a cubic curve from p1 to p2 with control points cp1 and cp2.
Definition: path_builder.cc:120
impeller::PathBuilder::AddLine
PathBuilder & AddLine(const Point &p1, const Point &p2)
Move to point p1, then insert a line from p1 to p2.
Definition: path_builder.cc:444
impeller::PathBuilder::TakePath
Path TakePath(FillType fill=FillType::kNonZero)
Definition: path_builder.cc:28
impeller::ContourComponent::destination
Point destination
Definition: path_component.h:227
impeller::Path::GetContourComponentAtIndex
bool GetContourComponentAtIndex(size_t index, ContourComponent &contour) const
Definition: path.cc:237
impeller::CubicPathComponent
Definition: path_component.h:174
impeller::Close
void Close(PathBuilder *builder)
Definition: tessellator.cc:38
impeller::Path::IsSingleContour
bool IsSingleContour() const
Whether the line contains a single contour.
Definition: path.cc:62
impeller::ContourComponent::IsClosed
constexpr bool IsClosed() const
Definition: path_component.h:234
impeller::PathBuilder::Close
PathBuilder & Close()
Definition: path_builder.cc:52
impeller::LineTo
void LineTo(PathBuilder *builder, Scalar x, Scalar y)
Definition: tessellator.cc:24
impeller::QuadraticPathComponent::p2
Point p2
Definition: path_component.h:140
impeller::TPoint< Scalar >
impeller::TRect< Scalar >::MakeMaximum
constexpr static TRect MakeMaximum()
Definition: rect.h:188
impeller::Path::GetComponentCount
size_t GetComponentCount(std::optional< ComponentType > type={}) const
Definition: path.cc:34
impeller::PathBuilder::MoveTo
PathBuilder & MoveTo(Point point, bool relative=false)
Definition: path_builder.cc:45
impeller::PathBuilder::VerticalLineTo
PathBuilder & VerticalLineTo(Scalar y, bool relative=false)
Definition: path_builder.cc:79
impeller::Degrees
Definition: scalar.h:51
impeller::Path::Polyline::PointBufferPtr
std::unique_ptr< std::vector< Point > > PointBufferPtr
Definition: path.h:114
round_rect.h
impeller::CubicPathComponent::p2
Point p2
Definition: path_component.h:182
impeller::PathBuilder::AddCircle
PathBuilder & AddCircle(const Point &center, Scalar radius)
Definition: path_builder.cc:147
impeller::CubicPathComponent::Extrema
std::vector< Point > Extrema() const
Definition: path_component.cc:433
impeller::TRect< Scalar >::MakeLTRB
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129
impeller::PathBuilder::AddOval
PathBuilder & AddOval(const Rect &rect)
Definition: path_builder.cc:396
impeller::StripVertexWriter::GetIndexCount
size_t GetIndexCount() const
Definition: path_component.cc:46
path_component.h
impeller::ContourComponent
Definition: path_component.h:226
path.h
impeller::Convexity::kConvex
@ kConvex
impeller
Definition: allocation.cc:12
impeller::QuadraticPathComponent
Definition: path_component.h:134
impeller::testing::TEST
TEST(AllocationSizeTest, CanCreateTypedAllocations)
Definition: allocation_size_unittests.cc:10
impeller::Path::CreatePolyline
Polyline CreatePolyline(Scalar scale, Polyline::PointBufferPtr point_buffer=std::make_unique< std::vector< Point >>(), Polyline::ReclaimPointBufferCallback reclaim=nullptr) const
Definition: path.cc:338
impeller::Path::GetLinearComponentAtIndex
bool GetLinearComponentAtIndex(size_t index, LinearPathComponent &linear) const
Definition: path.cc:178
impeller::Path::ComponentType::kContour
@ kContour
impeller::StripVertexWriter
A vertex writer that generates a triangle strip and requires primitive restart.
Definition: path_component.h:50
impeller::TRect
Definition: rect.h:122
impeller::PathBuilder::AddArc
PathBuilder & AddArc(const Rect &oval_bounds, Radians start, Radians sweep, bool use_center=false)
Definition: path_builder.cc:338
impeller::RoundRect::MakeRectXY
constexpr static RoundRect MakeRectXY(const Rect &rect, Scalar x_radius, Scalar y_radius)
Definition: round_rect.h:30
impeller::Path::CountStorage
std::pair< size_t, size_t > CountStorage(Scalar scale) const
Determine required storage for points and number of contours.
Definition: path.cc:67