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