| Flutter Impeller |

path_unittests.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "gtest/gtest.h"
6
7 #include "flutter/testing/testing.h"
8 #include "impeller/geometry/geometry_asserts.h"
9 #include "impeller/geometry/path.h"
10 #include "impeller/geometry/path_builder.h"
11 #include "impeller/geometry/path_component.h"
12 #include "impeller/geometry/round_rect.h"
13
14 namespace impeller {
15 namespace testing {
16
17 TEST(PathTest, CubicPathComponentPolylineDoesNotIncludePointOne) {
18 CubicPathComponent component({10, 10}, {20, 35}, {35, 20}, {40, 40});
19 std::vector<Point> polyline;
20 component.AppendPolylinePoints(1.0f, polyline);
21 ASSERT_NE(polyline.front().x, 10);
22 ASSERT_NE(polyline.front().y, 10);
23 ASSERT_EQ(polyline.back().x, 40);
24 ASSERT_EQ(polyline.back().y, 40);
25 }
26
27 TEST(PathTest, EmptyPathWithContour) {
28 PathBuilder builder;
29 auto path = builder.TakePath();
30
31 EXPECT_TRUE(path.IsEmpty());
32 }
33
34 TEST(PathTest, PathCreatePolyLineDoesNotDuplicatePoints) {
35 PathBuilder builder;
36 builder.MoveTo({10, 10});
37 builder.LineTo({20, 20});
38 builder.LineTo({30, 30});
39 builder.MoveTo({40, 40});
40 builder.LineTo({50, 50});
41
42 auto polyline = builder.TakePath().CreatePolyline(1.0f);
43
44 ASSERT_EQ(polyline.contours.size(), 2u);
45 ASSERT_EQ(polyline.points->size(), 5u);
46 ASSERT_EQ(polyline.GetPoint(0).x, 10);
47 ASSERT_EQ(polyline.GetPoint(1).x, 20);
48 ASSERT_EQ(polyline.GetPoint(2).x, 30);
49 ASSERT_EQ(polyline.GetPoint(3).x, 40);
50 ASSERT_EQ(polyline.GetPoint(4).x, 50);
51 }
52
53 TEST(PathTest, PathSingleContour) {
54 // Closed shapes.
55 {
56 Path path = PathBuilder{}.AddCircle({100, 100}, 50).TakePath();
57 EXPECT_TRUE(path.IsSingleContour());
58 }
59
60 {
61 Path path =
62 PathBuilder{}.AddOval(Rect::MakeXYWH(100, 100, 100, 100)).TakePath();
63
64 EXPECT_TRUE(path.IsSingleContour());
65 }
66
67 {
68 Path path =
69 PathBuilder{}.AddRect(Rect::MakeXYWH(100, 100, 100, 100)).TakePath();
70
71 EXPECT_TRUE(path.IsSingleContour());
72 }
73
74 {
75 Path path = PathBuilder{}
76 .AddRoundRect(RoundRect::MakeRectRadius(
77 Rect::MakeXYWH(100, 100, 100, 100), 10))
78 .TakePath();
79
80 EXPECT_TRUE(path.IsSingleContour());
81 }
82
83 {
84 Path path = PathBuilder{}
85 .AddRoundSuperellipse(RoundSuperellipse::MakeRectRadius(
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{}
156 .AddRoundRect(RoundRect::MakeRectRadius(
157 Rect::MakeXYWH(100, 100, 100, 100), 10))
158 .AddRoundRect(RoundRect::MakeRectRadius(
159 Rect::MakeXYWH(100, 100, 100, 100), 10))
160 .TakePath();
161
162 EXPECT_FALSE(path.IsSingleContour());
163 }
164
165 {
166 Path path = PathBuilder{}
167 .AddRoundRect(RoundRect::MakeRectXY(
168 Rect::MakeXYWH(100, 100, 100, 100), Size(10, 20)))
169 .AddRoundRect(RoundRect::MakeRectXY(
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{}
178 .AddRoundSuperellipse(RoundSuperellipse::MakeRectRadius(
179 Rect::MakeXYWH(100, 100, 100, 100), 10))
180 .AddRoundSuperellipse(RoundSuperellipse::MakeRectRadius(
181 Rect::MakeXYWH(100, 100, 100, 100), 10))
182 .TakePath();
183
184 EXPECT_FALSE(path.IsSingleContour());
185 }
186
187 {
188 Path path = PathBuilder{}
189 .AddRoundSuperellipse(RoundSuperellipse::MakeRectXY(
190 Rect::MakeXYWH(100, 100, 100, 100), Size(10, 20)))
191 .AddRoundSuperellipse(RoundSuperellipse::MakeRectXY(
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{}
274 .AddRoundRect(RoundRect::MakeRectRadius(
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{}
287 .AddRoundRect(RoundRect::MakeRectXY(
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{}
300 .AddRoundSuperellipse(RoundSuperellipse::MakeRectRadius(
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{}
313 .AddRoundSuperellipse(RoundSuperellipse::MakeRectXY(
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) {
375 Path::Polyline polyline = PathBuilder{}
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) {
392 Path::Polyline polyline = PathBuilder{}
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) {
410 Path::Polyline polyline = PathBuilder{}
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) {
426 Path::Polyline polyline =
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());
457 Path::Polyline polyline =
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();
711 ASSERT_POINT_NEAR(c->destination, Point());
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));
890 builder.SetConvexity(Convexity::kConvex);
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{}
919 .AddRoundRect(RoundRect::MakeRectRadius(
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{}
1005 .AddRoundRect(RoundRect::MakeRectRadius(
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) { //
1140 builder.SetConvexity(Convexity::kConvex);
1141 },
1142 false, {}, "SetConvex");
1143
1144 test_isolation(
1145 [](PathBuilder& builder) { //
1146 builder.SetConvexity(Convexity::kUnknown);
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
impeller::FanVertexWriter
A vertex writer that generates a triangle fan and requires primitive restart.
Definition: path_component.h:30
impeller::FanVertexWriter::GetIndexCount
size_t GetIndexCount() const
Definition: path_component.cc:23
impeller::Path::ComponentIterator::contour
const ContourComponent * contour() const
Definition: path.cc:63
impeller::Path::ComponentIterator::type
ComponentType type() const
Definition: path.cc:22
impeller::PathBuilder
Definition: path_builder.h:16
impeller::PathBuilder::TakePath
Path TakePath(FillType fill=FillType::kNonZero)
Definition: path_builder.cc:30
impeller::PathBuilder::AddArc
PathBuilder & AddArc(const Rect &oval_bounds, Radians start, Radians sweep, bool use_center=false)
Definition: path_builder.cc:397
impeller::PathBuilder::AddRoundRect
PathBuilder & AddRoundRect(RoundRect rect)
Definition: path_builder.cc:175
impeller::PathBuilder::LineTo
PathBuilder & LineTo(Point point, bool relative=false)
Insert a line from the current position to point.
Definition: path_builder.cc:66
impeller::PathBuilder::MoveTo
PathBuilder & MoveTo(Point point, bool relative=false)
Definition: path_builder.cc:47
impeller::PathBuilder::AddRect
PathBuilder & AddRect(const Rect &rect)
Definition: path_builder.cc:153
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:576
impeller::PathBuilder::AddCubicCurve
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.
Definition: path_builder.cc:144
impeller::PathBuilder::AddOval
PathBuilder & AddOval(const Rect &rect)
Definition: path_builder.cc:455
impeller::PathBuilder::AddCircle
PathBuilder & AddCircle(const Point &center, Scalar radius)
Definition: path_builder.cc:171
impeller::PathBuilder::Close
PathBuilder & Close()
Definition: path_builder.cc:54
impeller::PathBuilder::AddPath
PathBuilder & AddPath(const Path &path)
Definition: path_builder.cc:509
impeller::PathBuilder::AddConicCurve
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...
Definition: path_builder.cc:135
impeller::PathBuilder::CopyPath
Path CopyPath(FillType fill=FillType::kNonZero)
Definition: path_builder.cc:21
impeller::PathBuilder::VerticalLineTo
PathBuilder & VerticalLineTo(Scalar y, bool relative=false)
Definition: path_builder.cc:81
impeller::PathBuilder::Shift
PathBuilder & Shift(Point offset)
Transform the existing path segments and contours by the given offset.
Definition: path_builder.cc:529
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:503
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:115
impeller::PathBuilder::ConicCurveTo
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
impeller::PathBuilder::AddQuadraticCurve
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.
Definition: path_builder.cc:127
impeller::PathBuilder::HorizontalLineTo
PathBuilder & HorizontalLineTo(Scalar x, bool relative=false)
Definition: path_builder.cc:73
impeller::PathBuilder::AddRoundSuperellipse
PathBuilder & AddRoundSuperellipse(RoundSuperellipse rse)
Definition: path_builder.cc:246
impeller::PathBuilder::SetConvexity
PathBuilder & SetConvexity(Convexity value)
Definition: path_builder.cc:110
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:89
impeller::Path
Paths are lightweight objects that describe a collection of linear, quadratic, or cubic segments....
Definition: path.h:54
impeller::Path::GetComponentCount
size_t GetComponentCount(std::optional< ComponentType > type={}) const
Definition: path.cc:92
impeller::Path::begin
ComponentIterator begin() const
Definition: path.cc:253
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:355
impeller::Path::ComponentType::kCubic
@ kCubic
impeller::Path::ComponentType::kLinear
@ kLinear
impeller::Path::ComponentType::kConic
@ kConic
impeller::Path::ComponentType::kContour
@ kContour
impeller::Path::ComponentType::kQuadratic
@ kQuadratic
impeller::Path::IsSingleContour
bool IsSingleContour() const
Whether the line contains a single contour.
Definition: path.cc:124
impeller::Path::end
ComponentIterator end() const
Definition: path.cc:257
impeller::Path::WritePolyline
void WritePolyline(Scalar scale, VertexWriter &writer) const
Definition: path.cc:174
impeller::Path::GetBoundingBox
std::optional< Rect > GetBoundingBox() const
Definition: path.cc:466
impeller::Path::CountStorage
std::pair< size_t, size_t > CountStorage(Scalar scale) const
Determine required storage for points and number of contours.
Definition: path.cc:129
impeller::StripVertexWriter
A vertex writer that generates a triangle strip and requires primitive restart.
Definition: path_component.h:51
impeller::StripVertexWriter::GetIndexCount
size_t GetIndexCount() const
Definition: path_component.cc:47
path.h
path_builder.h
geometry_asserts.h
ASSERT_RECT_NEAR
#define ASSERT_RECT_NEAR(a, b)
Definition: geometry_asserts.h:191
ASSERT_POINT_NEAR
#define ASSERT_POINT_NEAR(a, b)
Definition: geometry_asserts.h:193
EXPECT_POINT_NEAR
#define EXPECT_POINT_NEAR(a, b)
Definition: geometry_asserts.h:205
impeller::testing::TEST
TEST(AllocationSizeTest, CanCreateTypedAllocations)
Definition: allocation_size_unittests.cc:10
impeller
Definition: allocation.cc:12
impeller::FillType::kOdd
@ kOdd
impeller::Point
TPoint< Scalar > Point
Definition: point.h:327
impeller::Convexity::kUnknown
@ kUnknown
impeller::Convexity::kConvex
@ kConvex
impeller::Size
TSize< Scalar > Size
Definition: size.h:159
impeller::MoveTo
void MoveTo(PathBuilder *builder, Scalar x, Scalar y)
Definition: tessellator.cc:20
impeller::LineTo
void LineTo(PathBuilder *builder, Scalar x, Scalar y)
Definition: tessellator.cc:24
impeller::Close
void Close(PathBuilder *builder)
Definition: tessellator.cc:38
path_component.h
round_rect.h
polyline
const Path::Polyline & polyline
Definition: stroke_path_geometry.cc:314
offset
SeparatedVector2 offset
Definition: stroke_path_geometry.cc:322
impeller::ConicPathComponent
Definition: path_component.h:226
impeller::ConicPathComponent::cp
Point cp
Definition: path_component.h:230
impeller::ConicPathComponent::weight
Point weight
Definition: path_component.h:243
impeller::ConicPathComponent::p1
Point p1
Definition: path_component.h:228
impeller::ConicPathComponent::p2
Point p2
Definition: path_component.h:232
impeller::ContourComponent
Definition: path_component.h:332
impeller::ContourComponent::IsClosed
constexpr bool IsClosed() const
Definition: path_component.h:340
impeller::ContourComponent::destination
Point destination
Definition: path_component.h:333
impeller::CubicPathComponent
Definition: path_component.h:280
impeller::CubicPathComponent::p1
Point p1
Definition: path_component.h:282
impeller::CubicPathComponent::cp1
Point cp1
Definition: path_component.h:284
impeller::CubicPathComponent::cp2
Point cp2
Definition: path_component.h:286
impeller::CubicPathComponent::Extrema
std::vector< Point > Extrema() const
Definition: path_component.cc:566
impeller::CubicPathComponent::p2
Point p2
Definition: path_component.h:288
impeller::Degrees
Definition: scalar.h:51
impeller::LinearPathComponent
Definition: path_component.h:111
impeller::LinearPathComponent::p2
Point p2
Definition: path_component.h:113
impeller::LinearPathComponent::p1
Point p1
Definition: path_component.h:112
impeller::Path::Polyline
Definition: path.h:146
impeller::Path::Polyline::PointBufferPtr
std::unique_ptr< std::vector< Point > > PointBufferPtr
Definition: path.h:149
impeller::QuadraticPathComponent
Definition: path_component.h:135
impeller::QuadraticPathComponent::p2
Point p2
Definition: path_component.h:141
impeller::QuadraticPathComponent::cp
Point cp
Definition: path_component.h:139
impeller::QuadraticPathComponent::p1
Point p1
Definition: path_component.h:137
impeller::RoundRect::MakeRectRadius
constexpr static RoundRect MakeRectRadius(const Rect &rect, Scalar radius)
Definition: round_rect.h:26
impeller::RoundRect::MakeRectXY
constexpr static RoundRect MakeRectXY(const Rect &rect, Scalar x_radius, Scalar y_radius)
Definition: round_rect.h:30
impeller::RoundSuperellipse::MakeRectRadius
constexpr static RoundSuperellipse MakeRectRadius(const Rect &rect, Scalar radius)
Definition: round_superellipse.h:28
impeller::RoundSuperellipse::MakeRectXY
constexpr static RoundSuperellipse MakeRectXY(const Rect &rect, Scalar x_radius, Scalar y_radius)
Definition: round_superellipse.h:33
impeller::TPoint< Scalar >
impeller::TRect< Scalar >
impeller::TRect< Scalar >::MakeXYWH
constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition: rect.h:136
impeller::TRect< Scalar >::MakeLTRB
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129
impeller::TRect< Scalar >::MakeMaximum
constexpr static TRect MakeMaximum()
Definition: rect.h:188