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"
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{}
77  Rect::MakeXYWH(100, 100, 100, 100), 10))
78  .TakePath();
79 
80  EXPECT_TRUE(path.IsSingleContour());
81  }
82 
83  {
84  Path path = PathBuilder{}
86  Rect::MakeXYWH(100, 100, 100, 100), 10))
87  .TakePath();
88 
89  EXPECT_TRUE(path.IsSingleContour());
90  }
91 
92  // Open shapes.
93  {
94  Point p(100, 100);
95  Path path = PathBuilder{}.AddLine(p, {200, 100}).TakePath();
96 
97  EXPECT_TRUE(path.IsSingleContour());
98  }
99 
100  {
101  Path path =
102  PathBuilder{}
103  .AddCubicCurve({100, 100}, {100, 50}, {100, 150}, {200, 100})
104  .TakePath();
105 
106  EXPECT_TRUE(path.IsSingleContour());
107  }
108 
109  {
110  Path path = PathBuilder{}
111  .AddQuadraticCurve({100, 100}, {100, 50}, {200, 100})
112  .TakePath();
113 
114  EXPECT_TRUE(path.IsSingleContour());
115  }
116 
117  {
118  Path path = PathBuilder{}
119  .AddConicCurve({100, 100}, {100, 50}, {200, 100}, 0.75f)
120  .TakePath();
121 
122  EXPECT_TRUE(path.IsSingleContour());
123  }
124 }
125 
126 TEST(PathTest, PathSingleContourDoubleShapes) {
127  // Closed shapes.
128  {
129  Path path = PathBuilder{}
130  .AddCircle({100, 100}, 50)
131  .AddCircle({100, 100}, 50)
132  .TakePath();
133  EXPECT_FALSE(path.IsSingleContour());
134  }
135 
136  {
137  Path path = PathBuilder{}
138  .AddOval(Rect::MakeXYWH(100, 100, 100, 100))
139  .AddOval(Rect::MakeXYWH(100, 100, 100, 100))
140  .TakePath();
141 
142  EXPECT_FALSE(path.IsSingleContour());
143  }
144 
145  {
146  Path path = PathBuilder{}
147  .AddRect(Rect::MakeXYWH(100, 100, 100, 100))
148  .AddRect(Rect::MakeXYWH(100, 100, 100, 100))
149  .TakePath();
150 
151  EXPECT_FALSE(path.IsSingleContour());
152  }
153 
154  {
155  Path path = PathBuilder{}
157  Rect::MakeXYWH(100, 100, 100, 100), 10))
159  Rect::MakeXYWH(100, 100, 100, 100), 10))
160  .TakePath();
161 
162  EXPECT_FALSE(path.IsSingleContour());
163  }
164 
165  {
166  Path path = PathBuilder{}
168  Rect::MakeXYWH(100, 100, 100, 100), Size(10, 20)))
170  Rect::MakeXYWH(100, 100, 100, 100), Size(10, 20)))
171  .TakePath();
172 
173  EXPECT_FALSE(path.IsSingleContour());
174  }
175 
176  {
177  Path path = PathBuilder{}
179  Rect::MakeXYWH(100, 100, 100, 100), 10))
181  Rect::MakeXYWH(100, 100, 100, 100), 10))
182  .TakePath();
183 
184  EXPECT_FALSE(path.IsSingleContour());
185  }
186 
187  {
188  Path path = PathBuilder{}
190  Rect::MakeXYWH(100, 100, 100, 100), Size(10, 20)))
192  Rect::MakeXYWH(100, 100, 100, 100), Size(10, 20)))
193  .TakePath();
194 
195  EXPECT_FALSE(path.IsSingleContour());
196  }
197 
198  // Open shapes.
199  {
200  Point p(100, 100);
201  Path path =
202  PathBuilder{}.AddLine(p, {200, 100}).AddLine(p, {200, 100}).TakePath();
203 
204  EXPECT_FALSE(path.IsSingleContour());
205  }
206 
207  {
208  Path path =
209  PathBuilder{}
210  .AddCubicCurve({100, 100}, {100, 50}, {100, 150}, {200, 100})
211  .AddCubicCurve({100, 100}, {100, 50}, {100, 150}, {200, 100})
212  .TakePath();
213 
214  EXPECT_FALSE(path.IsSingleContour());
215  }
216 
217  {
218  Path path = PathBuilder{}
219  .AddQuadraticCurve({100, 100}, {100, 50}, {200, 100})
220  .Close()
221  .AddQuadraticCurve({100, 100}, {100, 50}, {200, 100})
222  .TakePath();
223 
224  EXPECT_FALSE(path.IsSingleContour());
225  }
226 
227  {
228  Path path = PathBuilder{}
229  .AddConicCurve({100, 100}, {100, 50}, {200, 100}, 0.75f)
230  .Close()
231  .AddConicCurve({100, 100}, {100, 50}, {200, 100}, 0.75f)
232  .TakePath();
233 
234  EXPECT_FALSE(path.IsSingleContour());
235  }
236 }
237 
238 TEST(PathTest, PathBuilderSetsCorrectContourPropertiesForAddCommands) {
239  // Closed shapes.
240  {
241  Path path = PathBuilder{}.AddCircle({100, 100}, 50).TakePath();
242  EXPECT_NE(path.begin(), path.end());
243  EXPECT_EQ(path.begin().type(), Path::ComponentType::kContour);
244  auto contour = path.begin().contour();
245  ASSERT_NE(contour, nullptr);
246  EXPECT_POINT_NEAR(contour->destination, Point(100, 50));
247  EXPECT_TRUE(contour->IsClosed());
248  }
249 
250  {
251  Path path =
252  PathBuilder{}.AddOval(Rect::MakeXYWH(100, 100, 100, 100)).TakePath();
253  EXPECT_NE(path.begin(), path.end());
254  EXPECT_EQ(path.begin().type(), Path::ComponentType::kContour);
255  auto contour = path.begin().contour();
256  ASSERT_NE(contour, nullptr);
257  EXPECT_POINT_NEAR(contour->destination, Point(150, 100));
258  EXPECT_TRUE(contour->IsClosed());
259  }
260 
261  {
262  Path path =
263  PathBuilder{}.AddRect(Rect::MakeXYWH(100, 100, 100, 100)).TakePath();
264  EXPECT_NE(path.begin(), path.end());
265  EXPECT_EQ(path.begin().type(), Path::ComponentType::kContour);
266  auto contour = path.begin().contour();
267  ASSERT_NE(contour, nullptr);
268  EXPECT_POINT_NEAR(contour->destination, Point(100, 100));
269  EXPECT_TRUE(contour->IsClosed());
270  }
271 
272  {
273  Path path = PathBuilder{}
275  Rect::MakeXYWH(100, 100, 100, 100), 10))
276  .TakePath();
277  EXPECT_NE(path.begin(), path.end());
278  EXPECT_EQ(path.begin().type(), Path::ComponentType::kContour);
279  auto contour = path.begin().contour();
280  ASSERT_NE(contour, nullptr);
281  EXPECT_POINT_NEAR(contour->destination, Point(110, 100));
282  EXPECT_TRUE(contour->IsClosed());
283  }
284 
285  {
286  Path path = PathBuilder{}
288  Rect::MakeXYWH(100, 100, 100, 100), Size(10, 20)))
289  .TakePath();
290  EXPECT_NE(path.begin(), path.end());
291  EXPECT_EQ(path.begin().type(), Path::ComponentType::kContour);
292  auto contour = path.begin().contour();
293  ASSERT_NE(contour, nullptr);
294  EXPECT_POINT_NEAR(contour->destination, Point(110, 100));
295  EXPECT_TRUE(contour->IsClosed());
296  }
297 
298  {
299  Path path = PathBuilder{}
301  Rect::MakeXYWH(100, 100, 100, 100), 10))
302  .TakePath();
303  EXPECT_NE(path.begin(), path.end());
304  EXPECT_EQ(path.begin().type(), Path::ComponentType::kContour);
305  auto contour = path.begin().contour();
306  ASSERT_NE(contour, nullptr);
307  EXPECT_POINT_NEAR(contour->destination, Point(150, 100));
308  EXPECT_TRUE(contour->IsClosed());
309  }
310 
311  {
312  Path path = PathBuilder{}
314  Rect::MakeXYWH(100, 100, 100, 100), Size(10, 20)))
315  .TakePath();
316  EXPECT_NE(path.begin(), path.end());
317  EXPECT_EQ(path.begin().type(), Path::ComponentType::kContour);
318  auto contour = path.begin().contour();
319  ASSERT_NE(contour, nullptr);
320  EXPECT_POINT_NEAR(contour->destination, Point(150, 100));
321  EXPECT_TRUE(contour->IsClosed());
322  }
323 
324  // Open shapes.
325  {
326  Point p(100, 100);
327  Path path = PathBuilder{}.AddLine(p, {200, 100}).TakePath();
328  EXPECT_NE(path.begin(), path.end());
329  EXPECT_EQ(path.begin().type(), Path::ComponentType::kContour);
330  auto contour = path.begin().contour();
331  ASSERT_NE(contour, nullptr);
332  EXPECT_POINT_NEAR(contour->destination, p);
333  EXPECT_FALSE(contour->IsClosed());
334  }
335 
336  {
337  Path path =
338  PathBuilder{}
339  .AddCubicCurve({100, 100}, {100, 50}, {100, 150}, {200, 100})
340  .TakePath();
341  EXPECT_NE(path.begin(), path.end());
342  EXPECT_EQ(path.begin().type(), Path::ComponentType::kContour);
343  auto contour = path.begin().contour();
344  ASSERT_NE(contour, nullptr);
345  EXPECT_POINT_NEAR(contour->destination, Point(100, 100));
346  EXPECT_FALSE(contour->IsClosed());
347  }
348 
349  {
350  Path path = PathBuilder{}
351  .AddQuadraticCurve({100, 100}, {100, 50}, {200, 100})
352  .TakePath();
353  EXPECT_NE(path.begin(), path.end());
354  EXPECT_EQ(path.begin().type(), Path::ComponentType::kContour);
355  auto contour = path.begin().contour();
356  ASSERT_NE(contour, nullptr);
357  EXPECT_POINT_NEAR(contour->destination, Point(100, 100));
358  EXPECT_FALSE(contour->IsClosed());
359  }
360 
361  {
362  Path path = PathBuilder{}
363  .AddConicCurve({100, 100}, {100, 50}, {200, 100}, 0.75f)
364  .TakePath();
365  EXPECT_NE(path.begin(), path.end());
366  EXPECT_EQ(path.begin().type(), Path::ComponentType::kContour);
367  auto contour = path.begin().contour();
368  ASSERT_NE(contour, nullptr);
369  EXPECT_POINT_NEAR(contour->destination, Point(100, 100));
370  EXPECT_FALSE(contour->IsClosed());
371  }
372 }
373 
374 TEST(PathTest, PathCreatePolylineGeneratesCorrectContourData) {
376  .AddLine({100, 100}, {200, 100})
377  .MoveTo({100, 200})
378  .LineTo({150, 250})
379  .LineTo({200, 200})
380  .Close()
381  .TakePath()
382  .CreatePolyline(1.0f);
383  ASSERT_EQ(polyline.points->size(), 6u);
384  ASSERT_EQ(polyline.contours.size(), 2u);
385  ASSERT_EQ(polyline.contours[0].is_closed, false);
386  ASSERT_EQ(polyline.contours[0].start_index, 0u);
387  ASSERT_EQ(polyline.contours[1].is_closed, true);
388  ASSERT_EQ(polyline.contours[1].start_index, 2u);
389 }
390 
391 TEST(PathTest, PolylineGetContourPointBoundsReturnsCorrectRanges) {
393  .AddLine({100, 100}, {200, 100})
394  .MoveTo({100, 200})
395  .LineTo({150, 250})
396  .LineTo({200, 200})
397  .Close()
398  .TakePath()
399  .CreatePolyline(1.0f);
400  size_t a1, a2, b1, b2;
401  std::tie(a1, a2) = polyline.GetContourPointBounds(0);
402  std::tie(b1, b2) = polyline.GetContourPointBounds(1);
403  ASSERT_EQ(a1, 0u);
404  ASSERT_EQ(a2, 2u);
405  ASSERT_EQ(b1, 2u);
406  ASSERT_EQ(b2, 6u);
407 }
408 
409 TEST(PathTest, PathAddRectPolylineHasCorrectContourData) {
411  .AddRect(Rect::MakeLTRB(50, 60, 70, 80))
412  .TakePath()
413  .CreatePolyline(1.0f);
414  ASSERT_EQ(polyline.contours.size(), 1u);
415  ASSERT_TRUE(polyline.contours[0].is_closed);
416  ASSERT_EQ(polyline.contours[0].start_index, 0u);
417  ASSERT_EQ(polyline.points->size(), 5u);
418  ASSERT_EQ(polyline.GetPoint(0), Point(50, 60));
419  ASSERT_EQ(polyline.GetPoint(1), Point(70, 60));
420  ASSERT_EQ(polyline.GetPoint(2), Point(70, 80));
421  ASSERT_EQ(polyline.GetPoint(3), Point(50, 80));
422  ASSERT_EQ(polyline.GetPoint(4), Point(50, 60));
423 }
424 
425 TEST(PathTest, PathPolylineDuplicatesAreRemovedForSameContour) {
427  PathBuilder{}
428  .MoveTo({50, 50})
429  .LineTo({50, 50}) // Insert duplicate at beginning of contour.
430  .LineTo({100, 50})
431  .LineTo({100, 50}) // Insert duplicate at contour join.
432  .LineTo({100, 100})
433  .Close() // Implicitly insert duplicate {50, 50} across contours.
434  .LineTo({0, 50})
435  .LineTo({0, 100})
436  .LineTo({0, 100}) // Insert duplicate at end of contour.
437  .TakePath()
438  .CreatePolyline(1.0f);
439  ASSERT_EQ(polyline.contours.size(), 2u);
440  ASSERT_EQ(polyline.contours[0].start_index, 0u);
441  ASSERT_TRUE(polyline.contours[0].is_closed);
442  ASSERT_EQ(polyline.contours[1].start_index, 4u);
443  ASSERT_FALSE(polyline.contours[1].is_closed);
444  ASSERT_EQ(polyline.points->size(), 7u);
445  ASSERT_EQ(polyline.GetPoint(0), Point(50, 50));
446  ASSERT_EQ(polyline.GetPoint(1), Point(100, 50));
447  ASSERT_EQ(polyline.GetPoint(2), Point(100, 100));
448  ASSERT_EQ(polyline.GetPoint(3), Point(50, 50));
449  ASSERT_EQ(polyline.GetPoint(4), Point(50, 50));
450  ASSERT_EQ(polyline.GetPoint(5), Point(0, 50));
451  ASSERT_EQ(polyline.GetPoint(6), Point(0, 100));
452 }
453 
454 TEST(PathTest, PolylineBufferReuse) {
455  auto point_buffer = std::make_unique<std::vector<Point>>();
456  auto point_buffer_address = reinterpret_cast<uintptr_t>(point_buffer.get());
458  PathBuilder{}
459  .MoveTo({50, 50})
460  .LineTo({100, 100})
461  .TakePath()
462  .CreatePolyline(
463  1.0f, std::move(point_buffer),
464  [point_buffer_address](
465  Path::Polyline::PointBufferPtr point_buffer) {
466  ASSERT_EQ(point_buffer->size(), 0u);
467  ASSERT_EQ(point_buffer_address,
468  reinterpret_cast<uintptr_t>(point_buffer.get()));
469  });
470 }
471 
472 TEST(PathTest, PolylineFailsWithNullptrBuffer) {
473  EXPECT_DEATH_IF_SUPPORTED(PathBuilder{}
474  .MoveTo({50, 50})
475  .LineTo({100, 100})
476  .TakePath()
477  .CreatePolyline(1.0f, nullptr),
478  "");
479 }
480 
481 TEST(PathTest, PathShifting) {
482  PathBuilder builder{};
483  auto path =
484  builder.AddLine(Point(0, 0), Point(10, 10))
485  .AddQuadraticCurve(Point(10, 10), Point(15, 15), Point(20, 20))
486  .AddConicCurve(Point(10, 10), Point(15, 10), Point(15, 15), 0.75f)
487  .AddCubicCurve(Point(20, 20), Point(25, 25), Point(-5, -5),
488  Point(30, 30))
489  .Close()
490  .Shift(Point(1, 1))
491  .TakePath();
492 
493  auto it = path.begin();
494 
495  ASSERT_EQ(it.type(), Path::ComponentType::kContour);
496  const ContourComponent* contour = it.contour();
497  ASSERT_NE(contour, nullptr);
498  ++it;
499 
500  ASSERT_EQ(it.type(), Path::ComponentType::kLinear);
501  const LinearPathComponent* linear = it.linear();
502  ASSERT_NE(linear, nullptr);
503  ++it;
504 
505  ASSERT_EQ(it.type(), Path::ComponentType::kContour);
506  ++it;
507 
508  ASSERT_EQ(it.type(), Path::ComponentType::kQuadratic);
509  const QuadraticPathComponent* quad = it.quadratic();
510  ASSERT_NE(quad, nullptr);
511  ++it;
512 
513  ASSERT_EQ(it.type(), Path::ComponentType::kContour);
514  ++it;
515 
516  ASSERT_EQ(it.type(), Path::ComponentType::kConic);
517  const ConicPathComponent* conic = it.conic();
518  ASSERT_NE(conic, nullptr);
519  ++it;
520 
521  ASSERT_EQ(it.type(), Path::ComponentType::kContour);
522  ++it;
523 
524  ASSERT_EQ(it.type(), Path::ComponentType::kCubic);
525  const CubicPathComponent* cubic = it.cubic();
526  ASSERT_NE(cubic, nullptr);
527  ++it;
528 
529  // Close always opens a new contour, even if it isn't needed
530  ASSERT_EQ(it.type(), Path::ComponentType::kContour);
531  ++it;
532 
533  EXPECT_EQ(it, path.end());
534 
535  EXPECT_EQ(contour->destination, Point(1, 1));
536 
537  EXPECT_EQ(linear->p1, Point(1, 1));
538  EXPECT_EQ(linear->p2, Point(11, 11));
539 
540  EXPECT_EQ(quad->cp, Point(16, 16));
541  EXPECT_EQ(quad->p1, Point(11, 11));
542  EXPECT_EQ(quad->p2, Point(21, 21));
543 
544  EXPECT_EQ(cubic->cp1, Point(26, 26));
545  EXPECT_EQ(cubic->cp2, Point(-4, -4));
546  EXPECT_EQ(cubic->p1, Point(21, 21));
547  EXPECT_EQ(cubic->p2, Point(31, 31));
548 }
549 
550 TEST(PathTest, PathBuilderWillComputeBounds) {
551  PathBuilder builder;
552  auto path_1 = builder.AddLine({0, 0}, {1, 1}).TakePath();
553 
554  ASSERT_EQ(path_1.GetBoundingBox().value_or(Rect::MakeMaximum()),
555  Rect::MakeLTRB(0, 0, 1, 1));
556 
557  auto path_2 = builder.AddLine({-1, -1}, {1, 1}).TakePath();
558 
559  // Verify that PathBuilder recomputes the bounds.
560  ASSERT_EQ(path_2.GetBoundingBox().value_or(Rect::MakeMaximum()),
561  Rect::MakeLTRB(-1, -1, 1, 1));
562 
563  // PathBuilder can set the bounds to whatever it wants
564  auto path_3 = builder.AddLine({0, 0}, {1, 1})
565  .SetBounds(Rect::MakeLTRB(0, 0, 100, 100))
566  .TakePath();
567 
568  ASSERT_EQ(path_3.GetBoundingBox().value_or(Rect::MakeMaximum()),
569  Rect::MakeLTRB(0, 0, 100, 100));
570 }
571 
572 TEST(PathTest, PathHorizontalLine) {
573  PathBuilder builder;
574  auto path = builder.HorizontalLineTo(10).TakePath();
575 
576  auto it = path.begin();
577  ASSERT_EQ(it.type(), Path::ComponentType::kContour);
578  ++it;
579 
580  ASSERT_EQ(it.type(), Path::ComponentType::kLinear);
581  const LinearPathComponent* linear = it.linear();
582  ASSERT_NE(linear, nullptr);
583 
584  EXPECT_EQ(linear->p1, Point(0, 0));
585  EXPECT_EQ(linear->p2, Point(10, 0));
586 }
587 
588 TEST(PathTest, PathVerticalLine) {
589  PathBuilder builder;
590  auto path = builder.VerticalLineTo(10).TakePath();
591 
592  auto it = path.begin();
593  ASSERT_EQ(it.type(), Path::ComponentType::kContour);
594  ++it;
595 
596  ASSERT_EQ(it.type(), Path::ComponentType::kLinear);
597  const LinearPathComponent* linear = it.linear();
598  ASSERT_NE(linear, nullptr);
599 
600  EXPECT_EQ(linear->p1, Point(0, 0));
601  EXPECT_EQ(linear->p2, Point(0, 10));
602 }
603 
604 TEST(PathTest, QuadradicPath) {
605  PathBuilder builder;
606  auto path = builder.QuadraticCurveTo(Point(10, 10), Point(20, 20)).TakePath();
607 
608  auto it = path.begin();
609  ASSERT_EQ(it.type(), Path::ComponentType::kContour);
610  ++it;
611 
612  ASSERT_EQ(it.type(), Path::ComponentType::kQuadratic);
613  const QuadraticPathComponent* quad = it.quadratic();
614  ASSERT_NE(quad, nullptr);
615 
616  EXPECT_EQ(quad->p1, Point(0, 0));
617  EXPECT_EQ(quad->cp, Point(10, 10));
618  EXPECT_EQ(quad->p2, Point(20, 20));
619 }
620 
621 TEST(PathTest, ConicPath) {
622  PathBuilder builder;
623  auto path =
624  builder.ConicCurveTo(Point(10, 10), Point(20, 20), 0.75f).TakePath();
625 
626  auto it = path.begin();
627  ASSERT_EQ(it.type(), Path::ComponentType::kContour);
628  ++it;
629 
630  ASSERT_EQ(it.type(), Path::ComponentType::kConic);
631  const ConicPathComponent* conic = it.conic();
632  ASSERT_NE(conic, nullptr);
633 
634  EXPECT_EQ(conic->p1, Point(0, 0));
635  EXPECT_EQ(conic->cp, Point(10, 10));
636  EXPECT_EQ(conic->p2, Point(20, 20));
637  EXPECT_EQ(conic->weight, Point(0.75f, 0.75f));
638 }
639 
640 TEST(PathTest, CubicPath) {
641  PathBuilder builder;
642  auto path =
643  builder.CubicCurveTo(Point(10, 10), Point(-10, -10), Point(20, 20))
644  .TakePath();
645 
646  auto it = path.begin();
647  ASSERT_EQ(it.type(), Path::ComponentType::kContour);
648  ++it;
649 
650  ASSERT_EQ(it.type(), Path::ComponentType::kCubic);
651  const CubicPathComponent* cubic = it.cubic();
652  ASSERT_NE(cubic, nullptr);
653 
654  EXPECT_EQ(cubic->p1, Point(0, 0));
655  EXPECT_EQ(cubic->cp1, Point(10, 10));
656  EXPECT_EQ(cubic->cp2, Point(-10, -10));
657  EXPECT_EQ(cubic->p2, Point(20, 20));
658 }
659 
660 TEST(PathTest, BoundingBoxCubic) {
661  PathBuilder builder;
662  auto path =
663  builder.AddCubicCurve({120, 160}, {25, 200}, {220, 260}, {220, 40})
664  .TakePath();
665  auto box = path.GetBoundingBox();
666  Rect expected = Rect::MakeXYWH(93.9101, 40, 126.09, 158.862);
667  ASSERT_TRUE(box.has_value());
668  ASSERT_RECT_NEAR(box.value_or(Rect::MakeMaximum()), expected);
669 }
670 
671 TEST(PathTest, BoundingBoxOfCompositePathIsCorrect) {
672  PathBuilder builder;
673  builder.AddRoundRect(
674  RoundRect::MakeRectRadius(Rect::MakeXYWH(10, 10, 300, 300), 50));
675  auto path = builder.TakePath();
676  auto actual = path.GetBoundingBox();
677  Rect expected = Rect::MakeXYWH(10, 10, 300, 300);
678 
679  ASSERT_TRUE(actual.has_value());
680  ASSERT_RECT_NEAR(actual.value_or(Rect::MakeMaximum()), expected);
681 }
682 
683 TEST(PathTest, ExtremaOfCubicPathComponentIsCorrect) {
684  CubicPathComponent cubic{{11.769268, 252.883148},
685  {-6.2857933, 204.356461},
686  {-4.53997231, 156.552902},
687  {17.0067291, 109.472488}};
688  auto points = cubic.Extrema();
689 
690  ASSERT_EQ(points.size(), static_cast<size_t>(3));
691  ASSERT_POINT_NEAR(points[2], cubic.Solve(0.455916));
692 }
693 
694 TEST(PathTest, PathGetBoundingBoxForCubicWithNoDerivativeRootsIsCorrect) {
695  PathBuilder builder;
696  // Straight diagonal line.
697  builder.AddCubicCurve({0, 1}, {2, 3}, {4, 5}, {6, 7});
698  auto path = builder.TakePath();
699  auto actual = path.GetBoundingBox();
700  auto expected = Rect::MakeLTRB(0, 1, 6, 7);
701 
702  ASSERT_TRUE(actual.has_value());
703  ASSERT_RECT_NEAR(actual.value_or(Rect::MakeMaximum()), expected);
704 }
705 
706 TEST(PathTest, EmptyPath) {
707  auto path = PathBuilder{}.TakePath();
708  ASSERT_EQ(path.GetComponentCount(), 1u);
709 
710  const ContourComponent* c = path.begin().contour();
712 
713  Path::Polyline polyline = path.CreatePolyline(1.0f);
714  ASSERT_TRUE(polyline.points->empty());
715  ASSERT_TRUE(polyline.contours.empty());
716 }
717 
718 TEST(PathTest, SimplePath) {
719  PathBuilder builder;
720 
721  auto path = builder.AddLine({0, 0}, {100, 100})
722  .AddQuadraticCurve({100, 100}, {200, 200}, {300, 300})
723  .AddConicCurve({100, 100}, {200, 200}, {300, 300}, 0.75f)
724  .AddCubicCurve({300, 300}, {400, 400}, {500, 500}, {600, 600})
725  .TakePath();
726 
727  EXPECT_EQ(path.GetComponentCount(), 8u);
728  EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kLinear), 1u);
729  EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kQuadratic), 1u);
730  EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kConic), 1u);
731  EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kCubic), 1u);
732  EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kContour), 4u);
733 
734  auto it = path.begin();
735 
736  {
737  ASSERT_EQ(it.type(), Path::ComponentType::kContour);
738  const ContourComponent* contour = it.contour();
739  ASSERT_NE(contour, nullptr);
740  ++it;
741 
742  Point p1(0, 0);
743  EXPECT_EQ(contour->destination, p1);
744  EXPECT_FALSE(contour->IsClosed());
745  }
746 
747  {
748  ASSERT_EQ(it.type(), Path::ComponentType::kLinear);
749  const LinearPathComponent* linear = it.linear();
750  ASSERT_NE(linear, nullptr);
751  ++it;
752 
753  Point p1(0, 0);
754  Point p2(100, 100);
755  EXPECT_EQ(linear->p1, p1);
756  EXPECT_EQ(linear->p2, p2);
757  }
758 
759  {
760  ASSERT_EQ(it.type(), Path::ComponentType::kContour);
761  const ContourComponent* contour = it.contour();
762  ASSERT_NE(contour, nullptr);
763  ++it;
764 
765  Point p1(100, 100);
766  EXPECT_EQ(contour->destination, p1);
767  EXPECT_FALSE(contour->IsClosed());
768  }
769 
770  {
771  ASSERT_EQ(it.type(), Path::ComponentType::kQuadratic);
772  const QuadraticPathComponent* quad = it.quadratic();
773  ASSERT_NE(quad, nullptr);
774  ++it;
775 
776  Point p1(100, 100);
777  Point cp(200, 200);
778  Point p2(300, 300);
779  EXPECT_EQ(quad->p1, p1);
780  EXPECT_EQ(quad->cp, cp);
781  EXPECT_EQ(quad->p2, p2);
782  }
783 
784  {
785  ASSERT_EQ(it.type(), Path::ComponentType::kContour);
786  const ContourComponent* contour = it.contour();
787  ASSERT_NE(contour, nullptr);
788  ++it;
789 
790  Point p1(100, 100);
791  EXPECT_EQ(contour->destination, p1);
792  EXPECT_FALSE(contour->IsClosed());
793  }
794 
795  {
796  ASSERT_EQ(it.type(), Path::ComponentType::kConic);
797  const ConicPathComponent* conic = it.conic();
798  ASSERT_NE(conic, nullptr);
799  ++it;
800 
801  Point p1(100, 100);
802  Point cp(200, 200);
803  Point p2(300, 300);
804  Point weight(0.75f, 0.75f);
805  EXPECT_EQ(conic->p1, p1);
806  EXPECT_EQ(conic->cp, cp);
807  EXPECT_EQ(conic->p2, p2);
808  EXPECT_EQ(conic->weight, weight);
809  }
810 
811  {
812  ASSERT_EQ(it.type(), Path::ComponentType::kContour);
813  const ContourComponent* contour = it.contour();
814  ASSERT_NE(contour, nullptr);
815  ++it;
816 
817  Point p1(300, 300);
818  EXPECT_EQ(contour->destination, p1);
819  EXPECT_FALSE(contour->IsClosed());
820  }
821 
822  {
823  ASSERT_EQ(it.type(), Path::ComponentType::kCubic);
824  const CubicPathComponent* cubic = it.cubic();
825  ASSERT_NE(cubic, nullptr);
826  ++it;
827 
828  Point p1(300, 300);
829  Point cp1(400, 400);
830  Point cp2(500, 500);
831  Point p2(600, 600);
832  EXPECT_EQ(cubic->p1, p1);
833  EXPECT_EQ(cubic->cp1, cp1);
834  EXPECT_EQ(cubic->cp2, cp2);
835  EXPECT_EQ(cubic->p2, p2);
836  }
837 
838  EXPECT_EQ(it, path.end());
839 }
840 
841 TEST(PathTest, RepeatCloseDoesNotAddNewLines) {
842  PathBuilder builder;
843  auto path = builder.LineTo({0, 10})
844  .LineTo({10, 10})
845  .Close() // Returns to (0, 0)
846  .Close() // No Op
847  .Close() // Still No op
848  .TakePath();
849 
850  EXPECT_EQ(path.GetComponentCount(), 5u);
851  EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kLinear), 3u);
852  EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kContour), 2u);
853 }
854 
855 TEST(PathTest, CloseAfterMoveDoesNotAddNewLines) {
856  PathBuilder builder;
857  auto path = builder.LineTo({0, 10})
858  .LineTo({10, 10})
859  .MoveTo({30, 30}) // Moves to (30, 30)
860  .Close() // No Op
861  .Close() // Still No op
862  .TakePath();
863 
864  EXPECT_EQ(path.GetComponentCount(), 4u);
865  EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kLinear), 2u);
866  EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kContour), 2u);
867 }
868 
869 TEST(PathTest, CloseAtOriginDoesNotAddNewLineSegment) {
870  PathBuilder builder;
871  // Create a path that has a current position at the origin when close is
872  // called. This should not insert a new line segment
873  auto path = builder.LineTo({10, 0})
874  .LineTo({10, 10})
875  .LineTo({0, 10})
876  .LineTo({0, 0})
877  .Close()
878  .TakePath();
879 
880  EXPECT_EQ(path.GetComponentCount(), 6u);
881  EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kLinear), 4u);
882  EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kContour), 2u);
883 }
884 
885 TEST(PathTest, CanBeCloned) {
886  PathBuilder builder;
887  builder.MoveTo({10, 10});
888  builder.LineTo({20, 20});
889  builder.SetBounds(Rect::MakeLTRB(0, 0, 100, 100));
891 
892  auto path_a = builder.TakePath(FillType::kOdd);
893  // NOLINTNEXTLINE(performance-unnecessary-copy-initialization)
894  auto path_b = path_a;
895 
896  EXPECT_EQ(path_a.GetBoundingBox(), path_b.GetBoundingBox());
897  EXPECT_EQ(path_a.GetFillType(), path_b.GetFillType());
898  EXPECT_EQ(path_a.IsConvex(), path_b.IsConvex());
899 
900  auto poly_a = path_a.CreatePolyline(1.0);
901  auto poly_b = path_b.CreatePolyline(1.0);
902 
903  ASSERT_EQ(poly_a.points->size(), poly_b.points->size());
904  ASSERT_EQ(poly_a.contours.size(), poly_b.contours.size());
905 
906  for (auto i = 0u; i < poly_a.points->size(); i++) {
907  EXPECT_EQ((*poly_a.points)[i], (*poly_b.points)[i]);
908  }
909 
910  for (auto i = 0u; i < poly_a.contours.size(); i++) {
911  EXPECT_EQ(poly_a.contours[i].start_index, poly_b.contours[i].start_index);
912  EXPECT_EQ(poly_a.contours[i].start_direction,
913  poly_b.contours[i].start_direction);
914  }
915 }
916 
917 TEST(PathTest, FanTessellation) {
918  Path path = PathBuilder{}
920  Rect::MakeLTRB(0, 0, 100, 100), 10))
921  .TakePath();
922  auto [points, contours] = path.CountStorage(1.0);
923 
924  std::vector<Point> point_storage(points);
925  std::vector<uint16_t> index_storage(points + (contours - 1));
926 
927  FanVertexWriter writer(point_storage.data(), index_storage.data());
928  path.WritePolyline(1.0, writer);
929 
930  EXPECT_LE(writer.GetIndexCount(), index_storage.size());
931  EXPECT_EQ(point_storage[0], Point(10, 0));
932 }
933 
934 // Filled Paths without an explicit close should still be closed
935 TEST(PathTest, FanTessellationUnclosedPath) {
936  // Create a rectangle that lacks an explicit close.
937  Path path = PathBuilder{}
938  .LineTo({100, 0})
939  .LineTo({100, 100})
940  .LineTo({0, 100})
941  .TakePath();
942 
943  std::vector<Point> expected = {{0, 0}, {100, 0}, {100, 100},
944  {0, 100}, {0, 0}, {0, 0}};
945  std::vector<uint16_t> expected_indices = {0, 1, 2, 3, 0xFFFF, 0};
946 
947  auto [points, contours] = path.CountStorage(1.0);
948 
949  std::vector<Point> point_storage(points);
950  std::vector<uint16_t> index_storage(points + (contours - 1));
951 
952  FanVertexWriter writer(point_storage.data(), index_storage.data());
953  path.WritePolyline(1.0, writer);
954 
955  EXPECT_LE(index_storage, expected_indices);
956  EXPECT_EQ(point_storage, expected);
957 }
958 
959 // Filled Paths without an explicit close should still be closed
960 TEST(PathTest, StripTessellationUnclosedPath) {
961  // Create a rectangle that lacks an explicit close.
962  Path path = PathBuilder{}
963  .LineTo({100, 0})
964  .LineTo({100, 100})
965  .LineTo({0, 100})
966  .TakePath();
967 
968  std::vector<Point> expected = {{0, 0}, {100, 0}, {100, 100},
969  {0, 100}, {0, 0}, {0, 0}};
970  std::vector<uint16_t> expected_indices = {0, 1, 3, 2, 0xFFFF, 0};
971 
972  auto [points, contours] = path.CountStorage(1.0);
973 
974  std::vector<Point> point_storage(points);
975  std::vector<uint16_t> index_storage(points + (contours - 1));
976 
977  StripVertexWriter writer(point_storage.data(), index_storage.data());
978  path.WritePolyline(1.0, writer);
979 
980  EXPECT_LE(index_storage, expected_indices);
981  EXPECT_EQ(point_storage, expected);
982 }
983 
984 TEST(PathTest, FanTessellationMultiContour) {
985  PathBuilder builder{};
986  for (auto i = 0; i < 10; i++) {
987  builder.AddRoundRect(
988  RoundRect::MakeRectRadius(Rect::MakeLTRB(0 + i, 0 + i, 100, 100), 10));
989  }
990  auto path = builder.TakePath();
991  auto [points, contours] = path.CountStorage(1.0);
992 
993  std::vector<Point> point_storage(points);
994  std::vector<uint16_t> index_storage(points + (contours - 1));
995 
996  FanVertexWriter writer(point_storage.data(), index_storage.data());
997  path.WritePolyline(1.0, writer);
998 
999  EXPECT_LE(writer.GetIndexCount(), index_storage.size());
1000  EXPECT_EQ(point_storage[0], Point(10, 0));
1001 }
1002 
1003 TEST(PathTest, StripTessellation) {
1004  Path path = PathBuilder{}
1006  Rect::MakeLTRB(0, 0, 100, 100), 10))
1007  .TakePath();
1008  auto [points, contours] = path.CountStorage(1.0);
1009 
1010  std::vector<Point> point_storage(points);
1011  std::vector<uint16_t> index_storage(points + (contours - 1));
1012 
1013  StripVertexWriter writer(point_storage.data(), index_storage.data());
1014  path.WritePolyline(1.0, writer);
1015 
1016  EXPECT_LE(writer.GetIndexCount(), index_storage.size());
1017  EXPECT_EQ(point_storage[0], Point(10, 0));
1018 }
1019 
1020 TEST(PathTest, StripTessellationMultiContour) {
1021  PathBuilder builder{};
1022  for (auto i = 0; i < 10; i++) {
1023  builder.AddRoundRect(
1024  RoundRect::MakeRectRadius(Rect::MakeLTRB(0 + i, 0 + i, 100, 100), 10));
1025  }
1026  auto path = builder.TakePath();
1027  auto [points, contours] = path.CountStorage(1.0);
1028 
1029  std::vector<Point> point_storage(points);
1030  std::vector<uint16_t> index_storage(points + (contours - 1));
1031 
1032  StripVertexWriter writer(point_storage.data(), index_storage.data());
1033  path.WritePolyline(1.0, writer);
1034 
1035  EXPECT_LE(writer.GetIndexCount(), index_storage.size());
1036  EXPECT_EQ(point_storage[0], Point(10, 0));
1037 }
1038 
1039 TEST(PathTest, PathBuilderAddPathBasher) {
1040  PathBuilder test_path_builder;
1041  test_path_builder.AddOval(Rect::MakeLTRB(10, 10, 50, 50));
1042  Path test_path = test_path_builder.TakePath();
1043  for (int i = 0; i < 2000; i++) {
1044  PathBuilder path_builder;
1045  for (int j = 0; j < 10; j++) {
1046  path_builder.AddCircle(Point(50, 50), 25);
1047  path_builder.AddOval(Rect::MakeLTRB(100, 100, 200, 200));
1048  path_builder.AddPath(test_path);
1049  path_builder.AddRect(Rect::MakeLTRB(50, 50, 75, 57));
1050  path_builder.AddLine(Point(80, 70), Point(110, 95));
1051  path_builder.AddArc(Rect::MakeLTRB(50, 50, 100, 100), Degrees(20),
1052  Degrees(100));
1053  path_builder.AddRoundRect(RoundRect::MakeRectXY(
1054  Rect::MakeLTRB(70, 70, 130, 130), Size(10, 10)));
1055  }
1056  Path test_path = path_builder.TakePath();
1057  auto bounds = test_path.GetBoundingBox();
1058  EXPECT_TRUE(bounds.has_value());
1059  if (bounds.has_value()) {
1060  EXPECT_EQ(bounds.value(), Rect::MakeLTRB(10, 10, 200, 200));
1061  }
1062  }
1063 }
1064 
1065 TEST(PathTest, PathBuilderDoesNotMutateCopiedPaths) {
1066  auto test_isolation =
1067  [](const std::function<void(PathBuilder & builder)>& mutator,
1068  bool will_close, Point mutation_offset, const std::string& label) {
1069  PathBuilder builder;
1070  builder.MoveTo({10, 10});
1071  builder.LineTo({20, 20});
1072  builder.LineTo({20, 10});
1073 
1074  auto verify_path = [](const Path& path, bool is_mutated, bool is_closed,
1075  Point offset, const std::string& label) {
1076  if (is_mutated) {
1077  // We can only test the initial state before the mutator did
1078  // its work. We have >= 3 components and the first 3 components
1079  // will match what we saw before the mutation.
1080  EXPECT_GE(path.GetComponentCount(), 3u) << label;
1081  } else {
1082  EXPECT_EQ(path.GetComponentCount(), 3u) << label;
1083  }
1084  auto it = path.begin();
1085  {
1086  ASSERT_EQ(it.type(), Path::ComponentType::kContour) << label;
1087  const ContourComponent* contour = it.contour();
1088  ASSERT_NE(contour, nullptr) << label;
1089  ++it;
1090 
1091  EXPECT_EQ(contour->destination, offset + Point(10, 10)) << label;
1092  EXPECT_EQ(contour->IsClosed(), is_closed) << label;
1093  }
1094  {
1095  ASSERT_EQ(it.type(), Path::ComponentType::kLinear) << label;
1096  const LinearPathComponent* line = it.linear();
1097  ASSERT_NE(line, nullptr) << label;
1098  ++it;
1099 
1100  EXPECT_EQ(line->p1, offset + Point(10, 10)) << label;
1101  EXPECT_EQ(line->p2, offset + Point(20, 20)) << label;
1102  }
1103  {
1104  ASSERT_EQ(it.type(), Path::ComponentType::kLinear) << label;
1105  const LinearPathComponent* line = it.linear();
1106  ASSERT_NE(line, nullptr) << label;
1107  ++it;
1108 
1109  EXPECT_EQ(line->p1, offset + Point(20, 20)) << label;
1110  EXPECT_EQ(line->p2, offset + Point(20, 10)) << label;
1111  }
1112  if (!is_mutated) {
1113  EXPECT_EQ(it, path.end()) << label;
1114  }
1115  };
1116 
1117  auto path1 = builder.CopyPath();
1118  verify_path(path1, false, false, {},
1119  "Initial Path1 state before " + label);
1120 
1121  for (int i = 0; i < 10; i++) {
1122  auto path = builder.CopyPath();
1123  verify_path(
1124  path, false, false, {},
1125  "Extra CopyPath #" + std::to_string(i + 1) + " for " + label);
1126  }
1127  mutator(builder);
1128  verify_path(path1, false, false, {},
1129  "Path1 state after subsequent " + label);
1130 
1131  auto path2 = builder.CopyPath();
1132  verify_path(path1, false, false, {},
1133  "Path1 state after subsequent " + label + " and CopyPath");
1134  verify_path(path2, true, will_close, mutation_offset,
1135  "Initial Path2 state with subsequent " + label);
1136  };
1137 
1138  test_isolation(
1139  [](PathBuilder& builder) { //
1141  },
1142  false, {}, "SetConvex");
1143 
1144  test_isolation(
1145  [](PathBuilder& builder) { //
1147  },
1148  false, {}, "SetUnknownConvex");
1149 
1150  test_isolation(
1151  [](PathBuilder& builder) { //
1152  builder.Close();
1153  },
1154  true, {}, "Close");
1155 
1156  test_isolation(
1157  [](PathBuilder& builder) {
1158  builder.MoveTo({20, 30}, false);
1159  },
1160  false, {}, "Absolute MoveTo");
1161 
1162  test_isolation(
1163  [](PathBuilder& builder) {
1164  builder.MoveTo({20, 30}, true);
1165  },
1166  false, {}, "Relative MoveTo");
1167 
1168  test_isolation(
1169  [](PathBuilder& builder) {
1170  builder.LineTo({20, 30}, false);
1171  },
1172  false, {}, "Absolute LineTo");
1173 
1174  test_isolation(
1175  [](PathBuilder& builder) {
1176  builder.LineTo({20, 30}, true);
1177  },
1178  false, {}, "Relative LineTo");
1179 
1180  test_isolation(
1181  [](PathBuilder& builder) { //
1182  builder.HorizontalLineTo(100, false);
1183  },
1184  false, {}, "Absolute HorizontalLineTo");
1185 
1186  test_isolation(
1187  [](PathBuilder& builder) { //
1188  builder.HorizontalLineTo(100, true);
1189  },
1190  false, {}, "Relative HorizontalLineTo");
1191 
1192  test_isolation(
1193  [](PathBuilder& builder) { //
1194  builder.VerticalLineTo(100, false);
1195  },
1196  false, {}, "Absolute VerticalLineTo");
1197 
1198  test_isolation(
1199  [](PathBuilder& builder) { //
1200  builder.VerticalLineTo(100, true);
1201  },
1202  false, {}, "Relative VerticalLineTo");
1203 
1204  test_isolation(
1205  [](PathBuilder& builder) {
1206  builder.QuadraticCurveTo({20, 30}, {30, 20}, false);
1207  },
1208  false, {}, "Absolute QuadraticCurveTo");
1209 
1210  test_isolation(
1211  [](PathBuilder& builder) {
1212  builder.QuadraticCurveTo({20, 30}, {30, 20}, true);
1213  },
1214  false, {}, "Relative QuadraticCurveTo");
1215 
1216  test_isolation(
1217  [](PathBuilder& builder) {
1218  builder.ConicCurveTo({20, 30}, {30, 20}, 0.75f, false);
1219  },
1220  false, {}, "Absolute ConicCurveTo");
1221 
1222  test_isolation(
1223  [](PathBuilder& builder) {
1224  builder.ConicCurveTo({20, 30}, {30, 20}, 0.75f, true);
1225  },
1226  false, {}, "Relative ConicCurveTo");
1227 
1228  test_isolation(
1229  [](PathBuilder& builder) {
1230  builder.CubicCurveTo({20, 30}, {30, 20}, {30, 30}, false);
1231  },
1232  false, {}, "Absolute CubicCurveTo");
1233 
1234  test_isolation(
1235  [](PathBuilder& builder) {
1236  builder.CubicCurveTo({20, 30}, {30, 20}, {30, 30}, true);
1237  },
1238  false, {}, "Relative CubicCurveTo");
1239 
1240  test_isolation(
1241  [](PathBuilder& builder) {
1242  builder.AddLine({100, 100}, {150, 100});
1243  },
1244  false, {}, "AddLine");
1245 
1246  test_isolation(
1247  [](PathBuilder& builder) {
1248  builder.AddRect(Rect::MakeLTRB(100, 100, 120, 120));
1249  },
1250  false, {}, "AddRect");
1251 
1252  test_isolation(
1253  [](PathBuilder& builder) {
1254  builder.AddOval(Rect::MakeLTRB(100, 100, 120, 120));
1255  },
1256  false, {}, "AddOval");
1257 
1258  test_isolation(
1259  [](PathBuilder& builder) {
1260  builder.AddCircle({100, 100}, 20);
1261  },
1262  false, {}, "AddCircle");
1263 
1264  test_isolation(
1265  [](PathBuilder& builder) {
1266  builder.AddArc(Rect::MakeLTRB(100, 100, 120, 120), Degrees(10),
1267  Degrees(170));
1268  },
1269  false, {}, "AddArc");
1270 
1271  test_isolation(
1272  [](PathBuilder& builder) {
1273  builder.AddQuadraticCurve({100, 100}, {150, 100}, {150, 150});
1274  },
1275  false, {}, "AddQuadraticCurve");
1276 
1277  test_isolation(
1278  [](PathBuilder& builder) {
1279  builder.AddConicCurve({100, 100}, {150, 100}, {150, 150}, 0.75f);
1280  },
1281  false, {}, "AddConicCurve");
1282 
1283  test_isolation(
1284  [](PathBuilder& builder) {
1285  builder.AddCubicCurve({100, 100}, {150, 100}, {100, 150}, {150, 150});
1286  },
1287  false, {}, "AddCubicCurve");
1288 
1289  test_isolation(
1290  [](PathBuilder& builder) {
1291  builder.Shift({23, 42});
1292  },
1293  false, {23, 42}, "Shift");
1294 }
1295 
1296 } // namespace testing
1297 } // namespace impeller
A vertex writer that generates a triangle fan and requires primitive restart.
size_t GetIndexCount() const
const ContourComponent * contour() const
Definition: path.cc:63
ComponentType type() const
Definition: path.cc:22
Path TakePath(FillType fill=FillType::kNonZero)
Definition: path_builder.cc:30
PathBuilder & AddArc(const Rect &oval_bounds, Radians start, Radians sweep, bool use_center=false)
PathBuilder & AddRoundRect(RoundRect rect)
PathBuilder & LineTo(Point point, bool relative=false)
Insert a line from the current position to point.
Definition: path_builder.cc:66
PathBuilder & MoveTo(Point point, bool relative=false)
Definition: path_builder.cc:47
PathBuilder & AddRect(const Rect &rect)
PathBuilder & SetBounds(Rect bounds)
Set the bounding box that will be used by Path.GetBoundingBox in place of performing the computation.
PathBuilder & AddCubicCurve(const Point &p1, const Point &cp1, const Point &cp2, const Point &p2)
Move to point p1, then insert a cubic curve from p1 to p2 with control points cp1 and cp2.
PathBuilder & AddOval(const Rect &rect)
PathBuilder & AddCircle(const Point &center, Scalar radius)
PathBuilder & Close()
Definition: path_builder.cc:54
PathBuilder & AddPath(const Path &path)
PathBuilder & AddConicCurve(const Point &p1, const Point &cp, const Point &p2, Scalar weight)
Move to point p1, then insert a conic curve from p1 to p2 with the control point cp and weight weight...
Path CopyPath(FillType fill=FillType::kNonZero)
Definition: path_builder.cc:21
PathBuilder & VerticalLineTo(Scalar y, bool relative=false)
Definition: path_builder.cc:81
PathBuilder & Shift(Point offset)
Transform the existing path segments and contours by the given offset.
PathBuilder & AddLine(const Point &p1, const Point &p2)
Move to point p1, then insert a line from p1 to p2.
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...
PathBuilder & ConicCurveTo(Point controlPoint, Point point, Scalar weight, bool relative=false)
Insert a conic curve from the current position to point using the control point controlPoint and the ...
Definition: path_builder.cc:99
PathBuilder & AddQuadraticCurve(const Point &p1, const Point &cp, const Point &p2)
Move to point p1, then insert a quadradic curve from p1 to p2 with the control point cp.
PathBuilder & HorizontalLineTo(Scalar x, bool relative=false)
Definition: path_builder.cc:73
PathBuilder & AddRoundSuperellipse(RoundSuperellipse rse)
PathBuilder & SetConvexity(Convexity value)
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:89
Paths are lightweight objects that describe a collection of linear, quadratic, or cubic segments....
Definition: path.h:54
size_t GetComponentCount(std::optional< ComponentType > type={}) const
Definition: path.cc:92
ComponentIterator begin() const
Definition: path.cc:253
Polyline CreatePolyline(Scalar scale, Polyline::PointBufferPtr point_buffer=std::make_unique< std::vector< Point >>(), Polyline::ReclaimPointBufferCallback reclaim=nullptr) const
Definition: path.cc:355
bool IsSingleContour() const
Whether the line contains a single contour.
Definition: path.cc:124
ComponentIterator end() const
Definition: path.cc:257
void WritePolyline(Scalar scale, VertexWriter &writer) const
Definition: path.cc:174
std::optional< Rect > GetBoundingBox() const
Definition: path.cc:466
std::pair< size_t, size_t > CountStorage(Scalar scale) const
Determine required storage for points and number of contours.
Definition: path.cc:129
A vertex writer that generates a triangle strip and requires primitive restart.
#define ASSERT_RECT_NEAR(a, b)
#define ASSERT_POINT_NEAR(a, b)
#define EXPECT_POINT_NEAR(a, b)
TEST(AllocationSizeTest, CanCreateTypedAllocations)
TPoint< Scalar > Point
Definition: point.h:327
TSize< Scalar > Size
Definition: size.h:159
void MoveTo(PathBuilder *builder, Scalar x, Scalar y)
Definition: tessellator.cc:20
void LineTo(PathBuilder *builder, Scalar x, Scalar y)
Definition: tessellator.cc:24
void Close(PathBuilder *builder)
Definition: tessellator.cc:38
const Path::Polyline & polyline
SeparatedVector2 offset
constexpr bool IsClosed() const
std::vector< Point > Extrema() const
std::unique_ptr< std::vector< Point > > PointBufferPtr
Definition: path.h:149
constexpr static RoundRect MakeRectRadius(const Rect &rect, Scalar radius)
Definition: round_rect.h:26
constexpr static RoundRect MakeRectXY(const Rect &rect, Scalar x_radius, Scalar y_radius)
Definition: round_rect.h:30
constexpr static RoundSuperellipse MakeRectRadius(const Rect &rect, Scalar radius)
constexpr static RoundSuperellipse MakeRectXY(const Rect &rect, Scalar x_radius, Scalar y_radius)
constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition: rect.h:136
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129
constexpr static TRect MakeMaximum()
Definition: rect.h:188