| 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
12 namespace impeller {
13 namespace testing {
14
15 TEST(PathTest, CubicPathComponentPolylineDoesNotIncludePointOne) {
16 CubicPathComponent component({10, 10}, {20, 35}, {35, 20}, {40, 40});
17 std::vector<Point> polyline;
18 component.AppendPolylinePoints(1.0f, polyline);
19 ASSERT_NE(polyline.front().x, 10);
20 ASSERT_NE(polyline.front().y, 10);
21 ASSERT_EQ(polyline.back().x, 40);
22 ASSERT_EQ(polyline.back().y, 40);
23 }
24
25 TEST(PathTest, PathCreatePolyLineDoesNotDuplicatePoints) {
26 PathBuilder builder;
27 builder.MoveTo({10, 10});
28 builder.LineTo({20, 20});
29 builder.LineTo({30, 30});
30 builder.MoveTo({40, 40});
31 builder.LineTo({50, 50});
32
33 auto polyline = builder.TakePath().CreatePolyline(1.0f);
34
35 ASSERT_EQ(polyline.contours.size(), 2u);
36 ASSERT_EQ(polyline.points->size(), 5u);
37 ASSERT_EQ(polyline.GetPoint(0).x, 10);
38 ASSERT_EQ(polyline.GetPoint(1).x, 20);
39 ASSERT_EQ(polyline.GetPoint(2).x, 30);
40 ASSERT_EQ(polyline.GetPoint(3).x, 40);
41 ASSERT_EQ(polyline.GetPoint(4).x, 50);
42 }
43
44 TEST(PathTest, PathBuilderSetsCorrectContourPropertiesForAddCommands) {
45 // Closed shapes.
46 {
47 Path path = PathBuilder{}.AddCircle({100, 100}, 50).TakePath();
48 ContourComponent contour;
49 path.GetContourComponentAtIndex(0, contour);
50 EXPECT_POINT_NEAR(contour.destination, Point(100, 50));
51 EXPECT_TRUE(contour.is_closed);
52 }
53
54 {
55 Path path =
56 PathBuilder{}.AddOval(Rect::MakeXYWH(100, 100, 100, 100)).TakePath();
57 ContourComponent contour;
58 path.GetContourComponentAtIndex(0, contour);
59 EXPECT_POINT_NEAR(contour.destination, Point(150, 100));
60 EXPECT_TRUE(contour.is_closed);
61 }
62
63 {
64 Path path =
65 PathBuilder{}.AddRect(Rect::MakeXYWH(100, 100, 100, 100)).TakePath();
66 ContourComponent contour;
67 path.GetContourComponentAtIndex(0, contour);
68 EXPECT_POINT_NEAR(contour.destination, Point(100, 100));
69 EXPECT_TRUE(contour.is_closed);
70 }
71
72 {
73 Path path = PathBuilder{}
74 .AddRoundedRect(Rect::MakeXYWH(100, 100, 100, 100), 10)
75 .TakePath();
76 ContourComponent contour;
77 path.GetContourComponentAtIndex(0, contour);
78 EXPECT_POINT_NEAR(contour.destination, Point(110, 100));
79 EXPECT_TRUE(contour.is_closed);
80 }
81
82 {
83 Path path =
84 PathBuilder{}
85 .AddRoundedRect(Rect::MakeXYWH(100, 100, 100, 100), Size(10, 20))
86 .TakePath();
87 ContourComponent contour;
88 path.GetContourComponentAtIndex(0, contour);
89 EXPECT_POINT_NEAR(contour.destination, Point(110, 100));
90 EXPECT_TRUE(contour.is_closed);
91 }
92
93 // Open shapes.
94 {
95 Point p(100, 100);
96 Path path = PathBuilder{}.AddLine(p, {200, 100}).TakePath();
97 ContourComponent contour;
98 path.GetContourComponentAtIndex(0, contour);
99 ASSERT_POINT_NEAR(contour.destination, p);
100 ASSERT_FALSE(contour.is_closed);
101 }
102
103 {
104 Path path =
105 PathBuilder{}
106 .AddCubicCurve({100, 100}, {100, 50}, {100, 150}, {200, 100})
107 .TakePath();
108 ContourComponent contour;
109 path.GetContourComponentAtIndex(0, contour);
110 ASSERT_POINT_NEAR(contour.destination, Point(100, 100));
111 ASSERT_FALSE(contour.is_closed);
112 }
113
114 {
115 Path path = PathBuilder{}
116 .AddQuadraticCurve({100, 100}, {100, 50}, {200, 100})
117 .TakePath();
118 ContourComponent contour;
119 path.GetContourComponentAtIndex(0, contour);
120 ASSERT_POINT_NEAR(contour.destination, Point(100, 100));
121 ASSERT_FALSE(contour.is_closed);
122 }
123 }
124
125 TEST(PathTest, PathCreatePolylineGeneratesCorrectContourData) {
126 Path::Polyline polyline = PathBuilder{}
127 .AddLine({100, 100}, {200, 100})
128 .MoveTo({100, 200})
129 .LineTo({150, 250})
130 .LineTo({200, 200})
131 .Close()
132 .TakePath()
133 .CreatePolyline(1.0f);
134 ASSERT_EQ(polyline.points->size(), 6u);
135 ASSERT_EQ(polyline.contours.size(), 2u);
136 ASSERT_EQ(polyline.contours[0].is_closed, false);
137 ASSERT_EQ(polyline.contours[0].start_index, 0u);
138 ASSERT_EQ(polyline.contours[1].is_closed, true);
139 ASSERT_EQ(polyline.contours[1].start_index, 2u);
140 }
141
142 TEST(PathTest, PolylineGetContourPointBoundsReturnsCorrectRanges) {
143 Path::Polyline polyline = PathBuilder{}
144 .AddLine({100, 100}, {200, 100})
145 .MoveTo({100, 200})
146 .LineTo({150, 250})
147 .LineTo({200, 200})
148 .Close()
149 .TakePath()
150 .CreatePolyline(1.0f);
151 size_t a1, a2, b1, b2;
152 std::tie(a1, a2) = polyline.GetContourPointBounds(0);
153 std::tie(b1, b2) = polyline.GetContourPointBounds(1);
154 ASSERT_EQ(a1, 0u);
155 ASSERT_EQ(a2, 2u);
156 ASSERT_EQ(b1, 2u);
157 ASSERT_EQ(b2, 6u);
158 }
159
160 TEST(PathTest, PathAddRectPolylineHasCorrectContourData) {
161 Path::Polyline polyline = PathBuilder{}
162 .AddRect(Rect::MakeLTRB(50, 60, 70, 80))
163 .TakePath()
164 .CreatePolyline(1.0f);
165 ASSERT_EQ(polyline.contours.size(), 1u);
166 ASSERT_TRUE(polyline.contours[0].is_closed);
167 ASSERT_EQ(polyline.contours[0].start_index, 0u);
168 ASSERT_EQ(polyline.points->size(), 5u);
169 ASSERT_EQ(polyline.GetPoint(0), Point(50, 60));
170 ASSERT_EQ(polyline.GetPoint(1), Point(70, 60));
171 ASSERT_EQ(polyline.GetPoint(2), Point(70, 80));
172 ASSERT_EQ(polyline.GetPoint(3), Point(50, 80));
173 ASSERT_EQ(polyline.GetPoint(4), Point(50, 60));
174 }
175
176 TEST(PathTest, PathPolylineDuplicatesAreRemovedForSameContour) {
177 Path::Polyline polyline =
178 PathBuilder{}
179 .MoveTo({50, 50})
180 .LineTo({50, 50}) // Insert duplicate at beginning of contour.
181 .LineTo({100, 50})
182 .LineTo({100, 50}) // Insert duplicate at contour join.
183 .LineTo({100, 100})
184 .Close() // Implicitly insert duplicate {50, 50} across contours.
185 .LineTo({0, 50})
186 .LineTo({0, 100})
187 .LineTo({0, 100}) // Insert duplicate at end of contour.
188 .TakePath()
189 .CreatePolyline(1.0f);
190 ASSERT_EQ(polyline.contours.size(), 2u);
191 ASSERT_EQ(polyline.contours[0].start_index, 0u);
192 ASSERT_TRUE(polyline.contours[0].is_closed);
193 ASSERT_EQ(polyline.contours[1].start_index, 4u);
194 ASSERT_FALSE(polyline.contours[1].is_closed);
195 ASSERT_EQ(polyline.points->size(), 7u);
196 ASSERT_EQ(polyline.GetPoint(0), Point(50, 50));
197 ASSERT_EQ(polyline.GetPoint(1), Point(100, 50));
198 ASSERT_EQ(polyline.GetPoint(2), Point(100, 100));
199 ASSERT_EQ(polyline.GetPoint(3), Point(50, 50));
200 ASSERT_EQ(polyline.GetPoint(4), Point(50, 50));
201 ASSERT_EQ(polyline.GetPoint(5), Point(0, 50));
202 ASSERT_EQ(polyline.GetPoint(6), Point(0, 100));
203 }
204
205 TEST(PathTest, PolylineBufferReuse) {
206 auto point_buffer = std::make_unique<std::vector<Point>>();
207 auto point_buffer_address = reinterpret_cast<uintptr_t>(point_buffer.get());
208 Path::Polyline polyline =
209 PathBuilder{}
210 .MoveTo({50, 50})
211 .LineTo({100, 100})
212 .TakePath()
213 .CreatePolyline(
214 1.0f, std::move(point_buffer),
215 [point_buffer_address](
216 Path::Polyline::PointBufferPtr point_buffer) {
217 ASSERT_EQ(point_buffer->size(), 0u);
218 ASSERT_EQ(point_buffer_address,
219 reinterpret_cast<uintptr_t>(point_buffer.get()));
220 });
221 }
222
223 TEST(PathTest, PolylineFailsWithNullptrBuffer) {
224 EXPECT_DEATH_IF_SUPPORTED(PathBuilder{}
225 .MoveTo({50, 50})
226 .LineTo({100, 100})
227 .TakePath()
228 .CreatePolyline(1.0f, nullptr),
229 "");
230 }
231
232 TEST(PathTest, PathShifting) {
233 PathBuilder builder{};
234 auto path =
235 builder.AddLine(Point(0, 0), Point(10, 10))
236 .AddQuadraticCurve(Point(10, 10), Point(15, 15), Point(20, 20))
237 .AddCubicCurve(Point(20, 20), Point(25, 25), Point(-5, -5),
238 Point(30, 30))
239 .Close()
240 .Shift(Point(1, 1))
241 .TakePath();
242
243 ContourComponent contour;
244 LinearPathComponent linear;
245 QuadraticPathComponent quad;
246 CubicPathComponent cubic;
247
248 ASSERT_TRUE(path.GetContourComponentAtIndex(0, contour));
249 ASSERT_TRUE(path.GetLinearComponentAtIndex(1, linear));
250 ASSERT_TRUE(path.GetQuadraticComponentAtIndex(3, quad));
251 ASSERT_TRUE(path.GetCubicComponentAtIndex(5, cubic));
252
253 EXPECT_EQ(contour.destination, Point(1, 1));
254
255 EXPECT_EQ(linear.p1, Point(1, 1));
256 EXPECT_EQ(linear.p2, Point(11, 11));
257
258 EXPECT_EQ(quad.cp, Point(16, 16));
259 EXPECT_EQ(quad.p1, Point(11, 11));
260 EXPECT_EQ(quad.p2, Point(21, 21));
261
262 EXPECT_EQ(cubic.cp1, Point(26, 26));
263 EXPECT_EQ(cubic.cp2, Point(-4, -4));
264 EXPECT_EQ(cubic.p1, Point(21, 21));
265 EXPECT_EQ(cubic.p2, Point(31, 31));
266 }
267
268 TEST(PathTest, PathBuilderWillComputeBounds) {
269 PathBuilder builder;
270 auto path_1 = builder.AddLine({0, 0}, {1, 1}).TakePath();
271
272 ASSERT_EQ(path_1.GetBoundingBox().value_or(Rect::MakeMaximum()),
273 Rect::MakeLTRB(0, 0, 1, 1));
274
275 auto path_2 = builder.AddLine({-1, -1}, {1, 1}).TakePath();
276
277 // Verify that PathBuilder recomputes the bounds.
278 ASSERT_EQ(path_2.GetBoundingBox().value_or(Rect::MakeMaximum()),
279 Rect::MakeLTRB(-1, -1, 1, 1));
280
281 // PathBuilder can set the bounds to whatever it wants
282 auto path_3 = builder.AddLine({0, 0}, {1, 1})
283 .SetBounds(Rect::MakeLTRB(0, 0, 100, 100))
284 .TakePath();
285
286 ASSERT_EQ(path_3.GetBoundingBox().value_or(Rect::MakeMaximum()),
287 Rect::MakeLTRB(0, 0, 100, 100));
288 }
289
290 TEST(PathTest, PathHorizontalLine) {
291 PathBuilder builder;
292 auto path = builder.HorizontalLineTo(10).TakePath();
293
294 LinearPathComponent linear;
295 path.GetLinearComponentAtIndex(1, linear);
296
297 EXPECT_EQ(linear.p1, Point(0, 0));
298 EXPECT_EQ(linear.p2, Point(10, 0));
299 }
300
301 TEST(PathTest, PathVerticalLine) {
302 PathBuilder builder;
303 auto path = builder.VerticalLineTo(10).TakePath();
304
305 LinearPathComponent linear;
306 path.GetLinearComponentAtIndex(1, linear);
307
308 EXPECT_EQ(linear.p1, Point(0, 0));
309 EXPECT_EQ(linear.p2, Point(0, 10));
310 }
311
312 TEST(PathTest, QuadradicPath) {
313 PathBuilder builder;
314 auto path = builder.QuadraticCurveTo(Point(10, 10), Point(20, 20)).TakePath();
315
316 QuadraticPathComponent quad;
317 path.GetQuadraticComponentAtIndex(1, quad);
318
319 EXPECT_EQ(quad.p1, Point(0, 0));
320 EXPECT_EQ(quad.cp, Point(10, 10));
321 EXPECT_EQ(quad.p2, Point(20, 20));
322 }
323
324 TEST(PathTest, CubicPath) {
325 PathBuilder builder;
326 auto path =
327 builder.CubicCurveTo(Point(10, 10), Point(-10, -10), Point(20, 20))
328 .TakePath();
329
330 CubicPathComponent cubic;
331 path.GetCubicComponentAtIndex(1, cubic);
332
333 EXPECT_EQ(cubic.p1, Point(0, 0));
334 EXPECT_EQ(cubic.cp1, Point(10, 10));
335 EXPECT_EQ(cubic.cp2, Point(-10, -10));
336 EXPECT_EQ(cubic.p2, Point(20, 20));
337 }
338
339 TEST(PathTest, BoundingBoxCubic) {
340 PathBuilder builder;
341 auto path =
342 builder.AddCubicCurve({120, 160}, {25, 200}, {220, 260}, {220, 40})
343 .TakePath();
344 auto box = path.GetBoundingBox();
345 Rect expected = Rect::MakeXYWH(93.9101, 40, 126.09, 158.862);
346 ASSERT_TRUE(box.has_value());
347 ASSERT_RECT_NEAR(box.value_or(Rect::MakeMaximum()), expected);
348 }
349
350 TEST(PathTest, BoundingBoxOfCompositePathIsCorrect) {
351 PathBuilder builder;
352 builder.AddRoundedRect(Rect::MakeXYWH(10, 10, 300, 300), {50, 50, 50, 50});
353 auto path = builder.TakePath();
354 auto actual = path.GetBoundingBox();
355 Rect expected = Rect::MakeXYWH(10, 10, 300, 300);
356
357 ASSERT_TRUE(actual.has_value());
358 ASSERT_RECT_NEAR(actual.value_or(Rect::MakeMaximum()), expected);
359 }
360
361 TEST(PathTest, ExtremaOfCubicPathComponentIsCorrect) {
362 CubicPathComponent cubic{{11.769268, 252.883148},
363 {-6.2857933, 204.356461},
364 {-4.53997231, 156.552902},
365 {17.0067291, 109.472488}};
366 auto points = cubic.Extrema();
367
368 ASSERT_EQ(points.size(), static_cast<size_t>(3));
369 ASSERT_POINT_NEAR(points[2], cubic.Solve(0.455916));
370 }
371
372 TEST(PathTest, PathGetBoundingBoxForCubicWithNoDerivativeRootsIsCorrect) {
373 PathBuilder builder;
374 // Straight diagonal line.
375 builder.AddCubicCurve({0, 1}, {2, 3}, {4, 5}, {6, 7});
376 auto path = builder.TakePath();
377 auto actual = path.GetBoundingBox();
378 auto expected = Rect::MakeLTRB(0, 1, 6, 7);
379
380 ASSERT_TRUE(actual.has_value());
381 ASSERT_RECT_NEAR(actual.value_or(Rect::MakeMaximum()), expected);
382 }
383
384 TEST(PathTest, EmptyPath) {
385 auto path = PathBuilder{}.TakePath();
386 ASSERT_EQ(path.GetComponentCount(), 1u);
387
388 ContourComponent c;
389 path.GetContourComponentAtIndex(0, c);
390 ASSERT_POINT_NEAR(c.destination, Point());
391
392 Path::Polyline polyline = path.CreatePolyline(1.0f);
393 ASSERT_TRUE(polyline.points->empty());
394 ASSERT_TRUE(polyline.contours.empty());
395 }
396
397 TEST(PathTest, SimplePath) {
398 PathBuilder builder;
399
400 auto path = builder.AddLine({0, 0}, {100, 100})
401 .AddQuadraticCurve({100, 100}, {200, 200}, {300, 300})
402 .AddCubicCurve({300, 300}, {400, 400}, {500, 500}, {600, 600})
403 .TakePath();
404
405 ASSERT_EQ(path.GetComponentCount(), 6u);
406 ASSERT_EQ(path.GetComponentCount(Path::ComponentType::kLinear), 1u);
407 ASSERT_EQ(path.GetComponentCount(Path::ComponentType::kQuadratic), 1u);
408 ASSERT_EQ(path.GetComponentCount(Path::ComponentType::kCubic), 1u);
409 ASSERT_EQ(path.GetComponentCount(Path::ComponentType::kContour), 3u);
410
411 path.EnumerateComponents(
412 [](size_t index, const LinearPathComponent& linear) {
413 Point p1(0, 0);
414 Point p2(100, 100);
415 ASSERT_EQ(index, 1u);
416 ASSERT_EQ(linear.p1, p1);
417 ASSERT_EQ(linear.p2, p2);
418 },
419 [](size_t index, const QuadraticPathComponent& quad) {
420 Point p1(100, 100);
421 Point cp(200, 200);
422 Point p2(300, 300);
423 ASSERT_EQ(index, 3u);
424 ASSERT_EQ(quad.p1, p1);
425 ASSERT_EQ(quad.cp, cp);
426 ASSERT_EQ(quad.p2, p2);
427 },
428 [](size_t index, const CubicPathComponent& cubic) {
429 Point p1(300, 300);
430 Point cp1(400, 400);
431 Point cp2(500, 500);
432 Point p2(600, 600);
433 ASSERT_EQ(index, 5u);
434 ASSERT_EQ(cubic.p1, p1);
435 ASSERT_EQ(cubic.cp1, cp1);
436 ASSERT_EQ(cubic.cp2, cp2);
437 ASSERT_EQ(cubic.p2, p2);
438 },
439 [](size_t index, const ContourComponent& contour) {
440 // There is an initial countour added for each curve.
441 if (index == 0u) {
442 Point p1(0, 0);
443 ASSERT_EQ(contour.destination, p1);
444 } else if (index == 2u) {
445 Point p1(100, 100);
446 ASSERT_EQ(contour.destination, p1);
447 } else if (index == 4u) {
448 Point p1(300, 300);
449 ASSERT_EQ(contour.destination, p1);
450 } else {
451 ASSERT_FALSE(true);
452 }
453 ASSERT_FALSE(contour.is_closed);
454 });
455 }
456
457 TEST(PathTest, RepeatCloseDoesNotAddNewLines) {
458 PathBuilder builder;
459 auto path = builder.LineTo({0, 10})
460 .LineTo({10, 10})
461 .Close() // Returns to (0, 0)
462 .Close() // No Op
463 .Close() // Still No op
464 .TakePath();
465
466 EXPECT_EQ(path.GetComponentCount(), 5u);
467 EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kLinear), 3u);
468 EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kContour), 2u);
469 }
470
471 TEST(PathTest, CloseAfterMoveDoesNotAddNewLines) {
472 PathBuilder builder;
473 auto path = builder.LineTo({0, 10})
474 .LineTo({10, 10})
475 .MoveTo({30, 30}) // Moves to (30, 30)
476 .Close() // No Op
477 .Close() // Still No op
478 .TakePath();
479
480 EXPECT_EQ(path.GetComponentCount(), 4u);
481 EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kLinear), 2u);
482 EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kContour), 2u);
483 }
484
485 TEST(PathTest, CloseAtOriginDoesNotAddNewLineSegment) {
486 PathBuilder builder;
487 // Create a path that has a current position at the origin when close is
488 // called. This should not insert a new line segment
489 auto path = builder.LineTo({10, 0})
490 .LineTo({10, 10})
491 .LineTo({0, 10})
492 .LineTo({0, 0})
493 .Close()
494 .TakePath();
495
496 EXPECT_EQ(path.GetComponentCount(), 6u);
497 EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kLinear), 4u);
498 EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kContour), 2u);
499 }
500
501 TEST(PathTest, CanBeCloned) {
502 PathBuilder builder;
503 builder.MoveTo({10, 10});
504 builder.LineTo({20, 20});
505 builder.SetBounds(Rect::MakeLTRB(0, 0, 100, 100));
506 builder.SetConvexity(Convexity::kConvex);
507
508 auto path_a = builder.TakePath(FillType::kOdd);
509 // NOLINTNEXTLINE(performance-unnecessary-copy-initialization)
510 auto path_b = path_a;
511
512 EXPECT_EQ(path_a.GetBoundingBox(), path_b.GetBoundingBox());
513 EXPECT_EQ(path_a.GetFillType(), path_b.GetFillType());
514 EXPECT_EQ(path_a.IsConvex(), path_b.IsConvex());
515
516 auto poly_a = path_a.CreatePolyline(1.0);
517 auto poly_b = path_b.CreatePolyline(1.0);
518
519 ASSERT_EQ(poly_a.points->size(), poly_b.points->size());
520 ASSERT_EQ(poly_a.contours.size(), poly_b.contours.size());
521
522 for (auto i = 0u; i < poly_a.points->size(); i++) {
523 EXPECT_EQ((*poly_a.points)[i], (*poly_b.points)[i]);
524 }
525
526 for (auto i = 0u; i < poly_a.contours.size(); i++) {
527 EXPECT_EQ(poly_a.contours[i].start_index, poly_b.contours[i].start_index);
528 EXPECT_EQ(poly_a.contours[i].start_direction,
529 poly_b.contours[i].start_direction);
530 }
531 }
532
533 TEST(PathTest, PathBuilderDoesNotMutateCopiedPaths) {
534 auto test_isolation =
535 [](const std::function<void(PathBuilder & builder)>& mutator,
536 bool will_close, Point mutation_offset, const std::string& label) {
537 PathBuilder builder;
538 builder.MoveTo({10, 10});
539 builder.LineTo({20, 20});
540 builder.LineTo({20, 10});
541
542 auto verify_path = [](const Path& path, bool is_mutated, bool is_closed,
543 Point offset, const std::string& label) {
544 if (is_mutated) {
545 // We can only test the initial state before the mutator did
546 // its work. We have >= 3 components and the first 3 components
547 // will match what we saw before the mutation.
548 EXPECT_GE(path.GetComponentCount(), 3u) << label;
549 } else {
550 EXPECT_EQ(path.GetComponentCount(), 3u) << label;
551 }
552 {
553 ContourComponent contour;
554 EXPECT_TRUE(path.GetContourComponentAtIndex(0, contour)) << label;
555 EXPECT_EQ(contour.destination, offset + Point(10, 10)) << label;
556 EXPECT_EQ(contour.is_closed, is_closed) << label;
557 }
558 {
559 LinearPathComponent line;
560 EXPECT_TRUE(path.GetLinearComponentAtIndex(1, line)) << label;
561 EXPECT_EQ(line.p1, offset + Point(10, 10)) << label;
562 EXPECT_EQ(line.p2, offset + Point(20, 20)) << label;
563 }
564 {
565 LinearPathComponent line;
566 EXPECT_TRUE(path.GetLinearComponentAtIndex(2, line)) << label;
567 EXPECT_EQ(line.p1, offset + Point(20, 20)) << label;
568 EXPECT_EQ(line.p2, offset + Point(20, 10)) << label;
569 }
570 };
571
572 auto path1 = builder.CopyPath();
573 verify_path(path1, false, false, {},
574 "Initial Path1 state before " + label);
575
576 for (int i = 0; i < 10; i++) {
577 auto path = builder.CopyPath();
578 verify_path(
579 path, false, false, {},
580 "Extra CopyPath #" + std::to_string(i + 1) + " for " + label);
581 }
582 mutator(builder);
583 verify_path(path1, false, false, {},
584 "Path1 state after subsequent " + label);
585
586 auto path2 = builder.CopyPath();
587 verify_path(path1, false, false, {},
588 "Path1 state after subsequent " + label + " and CopyPath");
589 verify_path(path2, true, will_close, mutation_offset,
590 "Initial Path2 state with subsequent " + label);
591 };
592
593 test_isolation(
594 [](PathBuilder& builder) { //
595 builder.SetConvexity(Convexity::kConvex);
596 },
597 false, {}, "SetConvex");
598
599 test_isolation(
600 [](PathBuilder& builder) { //
601 builder.SetConvexity(Convexity::kUnknown);
602 },
603 false, {}, "SetUnknownConvex");
604
605 test_isolation(
606 [](PathBuilder& builder) { //
607 builder.Close();
608 },
609 true, {}, "Close");
610
611 test_isolation(
612 [](PathBuilder& builder) {
613 builder.MoveTo({20, 30}, false);
614 },
615 false, {}, "Absolute MoveTo");
616
617 test_isolation(
618 [](PathBuilder& builder) {
619 builder.MoveTo({20, 30}, true);
620 },
621 false, {}, "Relative MoveTo");
622
623 test_isolation(
624 [](PathBuilder& builder) {
625 builder.LineTo({20, 30}, false);
626 },
627 false, {}, "Absolute LineTo");
628
629 test_isolation(
630 [](PathBuilder& builder) {
631 builder.LineTo({20, 30}, true);
632 },
633 false, {}, "Relative LineTo");
634
635 test_isolation(
636 [](PathBuilder& builder) { //
637 builder.HorizontalLineTo(100, false);
638 },
639 false, {}, "Absolute HorizontalLineTo");
640
641 test_isolation(
642 [](PathBuilder& builder) { //
643 builder.HorizontalLineTo(100, true);
644 },
645 false, {}, "Relative HorizontalLineTo");
646
647 test_isolation(
648 [](PathBuilder& builder) { //
649 builder.VerticalLineTo(100, false);
650 },
651 false, {}, "Absolute VerticalLineTo");
652
653 test_isolation(
654 [](PathBuilder& builder) { //
655 builder.VerticalLineTo(100, true);
656 },
657 false, {}, "Relative VerticalLineTo");
658
659 test_isolation(
660 [](PathBuilder& builder) {
661 builder.QuadraticCurveTo({20, 30}, {30, 20}, false);
662 },
663 false, {}, "Absolute QuadraticCurveTo");
664
665 test_isolation(
666 [](PathBuilder& builder) {
667 builder.QuadraticCurveTo({20, 30}, {30, 20}, true);
668 },
669 false, {}, "Relative QuadraticCurveTo");
670
671 test_isolation(
672 [](PathBuilder& builder) {
673 builder.CubicCurveTo({20, 30}, {30, 20}, {30, 30}, false);
674 },
675 false, {}, "Absolute CubicCurveTo");
676
677 test_isolation(
678 [](PathBuilder& builder) {
679 builder.CubicCurveTo({20, 30}, {30, 20}, {30, 30}, true);
680 },
681 false, {}, "Relative CubicCurveTo");
682
683 test_isolation(
684 [](PathBuilder& builder) {
685 builder.AddLine({100, 100}, {150, 100});
686 },
687 false, {}, "AddLine");
688
689 test_isolation(
690 [](PathBuilder& builder) {
691 builder.AddRect(Rect::MakeLTRB(100, 100, 120, 120));
692 },
693 false, {}, "AddRect");
694
695 test_isolation(
696 [](PathBuilder& builder) {
697 builder.AddOval(Rect::MakeLTRB(100, 100, 120, 120));
698 },
699 false, {}, "AddOval");
700
701 test_isolation(
702 [](PathBuilder& builder) {
703 builder.AddCircle({100, 100}, 20);
704 },
705 false, {}, "AddCircle");
706
707 test_isolation(
708 [](PathBuilder& builder) {
709 builder.AddArc(Rect::MakeLTRB(100, 100, 120, 120), Degrees(10),
710 Degrees(170));
711 },
712 false, {}, "AddArc");
713
714 test_isolation(
715 [](PathBuilder& builder) {
716 builder.AddQuadraticCurve({100, 100}, {150, 100}, {150, 150});
717 },
718 false, {}, "AddQuadraticCurve");
719
720 test_isolation(
721 [](PathBuilder& builder) {
722 builder.AddCubicCurve({100, 100}, {150, 100}, {100, 150}, {150, 150});
723 },
724 false, {}, "AddCubicCurve");
725
726 test_isolation(
727 [](PathBuilder& builder) {
728 builder.Shift({23, 42});
729 },
730 false, {23, 42}, "Shift");
731 }
732
733 } // namespace testing
734 } // namespace impeller
path.h
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:102
polyline
const Path::Polyline & polyline
Definition: stroke_path_geometry.cc:303
impeller::LinearPathComponent
Definition: path_component.h:39
geometry_asserts.h
impeller::CubicPathComponent::p1
Point p1
Definition: path_component.h:103
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:458
impeller::LinearPathComponent::p2
Point p2
Definition: path_component.h:41
impeller::QuadraticPathComponent::p1
Point p1
Definition: path_component.h:65
impeller::CubicPathComponent::cp2
Point cp2
Definition: path_component.h:107
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:90
impeller::Path::ComponentType::kCubic
@ kCubic
impeller::PathBuilder
Definition: path_builder.h:14
impeller::QuadraticPathComponent::cp
Point cp
Definition: path_component.h:67
impeller::testing::TEST
TEST(AiksCanvasTest, EmptyCullRect)
Definition: canvas_unittests.cc:18
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:405
impeller::Path::ComponentType::kQuadratic
@ kQuadratic
impeller::PathBuilder::AddRoundedRect
PathBuilder & AddRoundedRect(Rect rect, RoundingRadii radii)
Definition: path_builder.cc:150
impeller::PathBuilder::HorizontalLineTo
PathBuilder & HorizontalLineTo(Scalar x, bool relative=false)
Definition: path_builder.cc:59
impeller::Size
TSize< Scalar > Size
Definition: size.h:137
impeller::PathBuilder::SetConvexity
PathBuilder & SetConvexity(Convexity value)
Definition: path_builder.cc:85
ASSERT_POINT_NEAR
#define ASSERT_POINT_NEAR(a, b)
Definition: geometry_asserts.h:193
offset
SeparatedVector2 offset
Definition: stroke_path_geometry.cc:311
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:117
ASSERT_RECT_NEAR
#define ASSERT_RECT_NEAR(a, b)
Definition: geometry_asserts.h:191
path_builder.h
impeller::Path::Polyline
Definition: path.h:95
impeller::Convexity::kUnknown
@ kUnknown
impeller::CubicPathComponent::cp1
Point cp1
Definition: path_component.h:105
impeller::Point
TPoint< Scalar > Point
Definition: point.h:322
impeller::LinearPathComponent::p1
Point p1
Definition: path_component.h:40
impeller::PathBuilder::Shift
PathBuilder & Shift(Point offset)
Transform the existing path segments and contours by the given offset.
Definition: path_builder.cc:447
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:75
impeller::Path
Paths are lightweight objects that describe a collection of linear, quadratic, or cubic segments....
Definition: path.h:52
impeller::PathBuilder::CopyPath
Path CopyPath(FillType fill=FillType::kNonZero)
Definition: path_builder.cc:17
impeller::ContourComponent::is_closed
bool is_closed
Definition: path_component.h:153
impeller::PathBuilder::LineTo
PathBuilder & LineTo(Point point, bool relative=false)
Insert a line from the current position to point.
Definition: path_builder.cc:52
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:108
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:424
impeller::PathBuilder::TakePath
Path TakePath(FillType fill=FillType::kNonZero)
Definition: path_builder.cc:22
impeller::ContourComponent::destination
Point destination
Definition: path_component.h:152
impeller::Path::GetContourComponentAtIndex
bool GetContourComponentAtIndex(size_t index, ContourComponent &contour) const
Definition: path.cc:229
impeller::CubicPathComponent
Definition: path_component.h:101
impeller::Close
void Close(PathBuilder *builder)
Definition: tessellator.cc:38
impeller::PathBuilder::Close
PathBuilder & Close()
Definition: path_builder.cc:40
impeller::LineTo
void LineTo(PathBuilder *builder, Scalar x, Scalar y)
Definition: tessellator.cc:24
impeller::QuadraticPathComponent::p2
Point p2
Definition: path_component.h:69
impeller::TPoint< Scalar >
impeller::TRect< Scalar >::MakeMaximum
constexpr static TRect MakeMaximum()
Definition: rect.h:174
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:33
impeller::PathBuilder::VerticalLineTo
PathBuilder & VerticalLineTo(Scalar y, bool relative=false)
Definition: path_builder.cc:67
impeller::Degrees
Definition: scalar.h:46
impeller::Path::Polyline::PointBufferPtr
std::unique_ptr< std::vector< Point > > PointBufferPtr
Definition: path.h:98
impeller::CubicPathComponent::p2
Point p2
Definition: path_component.h:109
impeller::PathBuilder::AddCircle
PathBuilder & AddCircle(const Point &center, Scalar radius)
Definition: path_builder.cc:135
impeller::CubicPathComponent::Extrema
std::vector< Point > Extrema() const
Definition: path_component.cc:332
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:376
impeller::ContourComponent
Definition: path_component.h:151
impeller::Convexity::kConvex
@ kConvex
impeller
Definition: aiks_blend_unittests.cc:18
impeller::QuadraticPathComponent
Definition: path_component.h:63
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:264
impeller::Path::GetLinearComponentAtIndex
bool GetLinearComponentAtIndex(size_t index, LinearPathComponent &linear) const
Definition: path.cc:172
impeller::Path::ComponentType::kContour
@ kContour
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:318