| Flutter Impeller |

tessellator_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 "flutter/testing/testing.h"
6 #include "gtest/gtest.h"
7
8 #include "flutter/display_list/geometry/dl_path_builder.h"
9 #include "impeller/geometry/constants.h"
10 #include "impeller/geometry/geometry_asserts.h"
11 #include "impeller/tessellator/tessellator.h"
12 #include "impeller/tessellator/tessellator_libtess.h"
13
14 namespace impeller {
15 namespace testing {
16
17 TEST(TessellatorTest, TessellatorBuilderReturnsCorrectResultStatus) {
18 // Zero points.
19 {
20 TessellatorLibtess t;
21 auto path = flutter::DlPathBuilder{} //
22 .SetFillType(FillType::kOdd)
23 .TakePath();
24 TessellatorLibtess::Result result = t.Tessellate(
25 path, 1.0f,
26 [](const float* vertices, size_t vertices_count,
27 const uint16_t* indices, size_t indices_count) { return true; });
28
29 ASSERT_EQ(result, TessellatorLibtess::Result::kInputError);
30 }
31
32 // One point.
33 {
34 TessellatorLibtess t;
35 auto path = flutter::DlPathBuilder{}
36 .LineTo({0, 0})
37 .SetFillType(FillType::kOdd)
38 .TakePath();
39 TessellatorLibtess::Result result = t.Tessellate(
40 path, 1.0f,
41 [](const float* vertices, size_t vertices_count,
42 const uint16_t* indices, size_t indices_count) { return true; });
43
44 ASSERT_EQ(result, TessellatorLibtess::Result::kSuccess);
45 }
46
47 // Two points.
48 {
49 TessellatorLibtess t;
50 auto path = flutter::DlPathBuilder{}
51 .MoveTo({0, 0})
52 .LineTo({0, 1})
53 .SetFillType(FillType::kOdd)
54 .TakePath();
55 TessellatorLibtess::Result result = t.Tessellate(
56 path, 1.0f,
57 [](const float* vertices, size_t vertices_count,
58 const uint16_t* indices, size_t indices_count) { return true; });
59
60 ASSERT_EQ(result, TessellatorLibtess::Result::kSuccess);
61 }
62
63 // Many points.
64 {
65 TessellatorLibtess t;
66 flutter::DlPathBuilder builder;
67 for (int i = 0; i < 1000; i++) {
68 auto coord = i * 1.0f;
69 builder.MoveTo({coord, coord}).LineTo({coord + 1, coord + 1});
70 }
71 auto path = builder.SetFillType(FillType::kOdd).TakePath();
72 TessellatorLibtess::Result result = t.Tessellate(
73 path, 1.0f,
74 [](const float* vertices, size_t vertices_count,
75 const uint16_t* indices, size_t indices_count) { return true; });
76
77 ASSERT_EQ(result, TessellatorLibtess::Result::kSuccess);
78 }
79
80 // Closure fails.
81 {
82 TessellatorLibtess t;
83 auto path = flutter::DlPathBuilder{}
84 .MoveTo({0, 0})
85 .LineTo({0, 1})
86 .SetFillType(FillType::kOdd)
87 .TakePath();
88 TessellatorLibtess::Result result = t.Tessellate(
89 path, 1.0f,
90 [](const float* vertices, size_t vertices_count,
91 const uint16_t* indices, size_t indices_count) { return false; });
92
93 ASSERT_EQ(result, TessellatorLibtess::Result::kInputError);
94 }
95 }
96
97 TEST(TessellatorTest, TessellateConvex) {
98 {
99 std::vector<Point> points;
100 std::vector<uint16_t> indices;
101 // Sanity check simple rectangle.
102 Tessellator::TessellateConvexInternal(
103 flutter::DlPath::MakeRect(Rect::MakeLTRB(0, 0, 10, 10)), points,
104 indices, 1.0);
105
106 // Note: the origin point is repeated but not referenced in the indices
107 // below
108 std::vector<Point> expected = {{0, 0}, {10, 0}, {10, 10}, {0, 10}, {0, 0}};
109 std::vector<uint16_t> expected_indices = {0, 1, 3, 2};
110 EXPECT_EQ(points, expected);
111 EXPECT_EQ(indices, expected_indices);
112 }
113
114 {
115 std::vector<Point> points;
116 std::vector<uint16_t> indices;
117 Tessellator::TessellateConvexInternal(
118 flutter::DlPath(flutter::DlPathBuilder{}
119 .AddRect(Rect::MakeLTRB(0, 0, 10, 10))
120 .AddRect(Rect::MakeLTRB(20, 20, 30, 30))
121 .TakePath()),
122 points, indices, 1.0);
123
124 std::vector<Point> expected = {{0, 0}, {10, 0}, {10, 10}, {0, 10},
125 {0, 0}, {20, 20}, {30, 20}, {30, 30},
126 {20, 30}, {20, 20}};
127 std::vector<uint16_t> expected_indices = {0, 1, 3, 2, 2, 5, 5, 6, 8, 7};
128 EXPECT_EQ(points, expected);
129 EXPECT_EQ(indices, expected_indices);
130 }
131 }
132
133 // Filled Paths without an explicit close should still be closed implicitly
134 TEST(TessellatorTest, TessellateConvexUnclosedPath) {
135 std::vector<Point> points;
136 std::vector<uint16_t> indices;
137
138 // Create a rectangle that lacks an explicit close.
139 flutter::DlPath path = flutter::DlPathBuilder{}
140 .LineTo({100, 0})
141 .LineTo({100, 100})
142 .LineTo({0, 100})
143 .TakePath();
144 Tessellator::TessellateConvexInternal(flutter::DlPath(path), points, indices,
145 1.0);
146
147 std::vector<Point> expected = {
148 {0, 0}, {100, 0}, {100, 100}, {0, 100}, {0, 0}};
149 std::vector<uint16_t> expected_indices = {0, 1, 3, 2};
150 EXPECT_EQ(points, expected);
151 EXPECT_EQ(indices, expected_indices);
152 }
153
154 TEST(TessellatorTest, CircleVertexCounts) {
155 Tessellator tessellator;
156
157 auto test = [&tessellator](const Matrix& transform, Scalar radius) {
158 auto generator = tessellator.FilledCircle(transform, {}, radius);
159 size_t quadrant_divisions = generator.GetVertexCount() / 4;
160
161 // Confirm the approximation error is within the currently accepted
162 // |kCircleTolerance| value advertised by |CircleTessellator|.
163 // (With an additional 1% tolerance for floating point rounding.)
164 double angle = kPiOver2 / quadrant_divisions;
165 Point first = {radius, 0};
166 Point next = {static_cast<Scalar>(cos(angle) * radius),
167 static_cast<Scalar>(sin(angle) * radius)};
168 Point midpoint = (first + next) * 0.5;
169 EXPECT_GE(midpoint.GetLength(),
170 radius - Tessellator::kCircleTolerance * 1.01)
171 << ", transform = " << transform << ", radius = " << radius
172 << ", divisions = " << quadrant_divisions;
173 };
174
175 test({}, 0.0);
176 test({}, 0.9);
177 test({}, 1.0);
178 test({}, 1.9);
179 test(Matrix::MakeScale(Vector2(2.0, 2.0)), 0.95);
180 test({}, 2.0);
181 test(Matrix::MakeScale(Vector2(2.0, 2.0)), 1.0);
182 test({}, 11.9);
183 test({}, 12.0);
184 test({}, 35.9);
185 for (int i = 36; i < 10000; i += 4) {
186 test({}, i);
187 }
188 }
189
190 TEST(TessellatorTest, FilledCircleTessellationVertices) {
191 Tessellator tessellator;
192
193 auto test = [&tessellator](const Matrix& transform, const Point& center,
194 Scalar radius) {
195 auto generator = tessellator.FilledCircle(transform, center, radius);
196 EXPECT_EQ(generator.GetTriangleType(), PrimitiveType::kTriangleStrip);
197
198 auto vertex_count = generator.GetVertexCount();
199 auto vertices = std::vector<Point>();
200 generator.GenerateVertices([&vertices](const Point& p) { //
201 vertices.push_back(p);
202 });
203 EXPECT_EQ(vertices.size(), vertex_count);
204 ASSERT_EQ(vertex_count % 4, 0u);
205
206 auto quadrant_count = vertex_count / 4;
207 for (size_t i = 0; i < quadrant_count; i++) {
208 double angle = kPiOver2 * i / (quadrant_count - 1);
209 double degrees = angle * 180.0 / kPi;
210 double rsin = sin(angle) * radius;
211 // Note that cos(radians(90 degrees)) isn't exactly 0.0 like it should be
212 double rcos = (i == quadrant_count - 1) ? 0.0f : cos(angle) * radius;
213 EXPECT_POINT_NEAR(vertices[i * 2],
214 Point(center.x - rcos, center.y + rsin))
215 << "vertex " << i << ", angle = " << degrees << std::endl;
216 EXPECT_POINT_NEAR(vertices[i * 2 + 1],
217 Point(center.x - rcos, center.y - rsin))
218 << "vertex " << i << ", angle = " << degrees << std::endl;
219 EXPECT_POINT_NEAR(vertices[vertex_count - i * 2 - 1],
220 Point(center.x + rcos, center.y - rsin))
221 << "vertex " << i << ", angle = " << degrees << std::endl;
222 EXPECT_POINT_NEAR(vertices[vertex_count - i * 2 - 2],
223 Point(center.x + rcos, center.y + rsin))
224 << "vertex " << i << ", angle = " << degrees << std::endl;
225 }
226 };
227
228 test({}, {}, 2.0);
229 test({}, {10, 10}, 2.0);
230 test(Matrix::MakeScale({500.0, 500.0, 0.0}), {}, 2.0);
231 test(Matrix::MakeScale({0.002, 0.002, 0.0}), {}, 1000.0);
232 }
233
234 TEST(TessellatorTest, StrokedCircleTessellationVertices) {
235 Tessellator tessellator;
236
237 auto test = [&tessellator](const Matrix& transform, const Point& center,
238 Scalar radius, Scalar half_width) {
239 ASSERT_GT(radius, half_width);
240 auto generator =
241 tessellator.StrokedCircle(transform, center, radius, half_width);
242 EXPECT_EQ(generator.GetTriangleType(), PrimitiveType::kTriangleStrip);
243
244 auto vertex_count = generator.GetVertexCount();
245 auto vertices = std::vector<Point>();
246 generator.GenerateVertices([&vertices](const Point& p) { //
247 vertices.push_back(p);
248 });
249 EXPECT_EQ(vertices.size(), vertex_count);
250 ASSERT_EQ(vertex_count % 4, 0u);
251
252 auto quadrant_count = vertex_count / 8;
253
254 // Test outer points first
255 for (size_t i = 0; i < quadrant_count; i++) {
256 double angle = kPiOver2 * i / (quadrant_count - 1);
257 double degrees = angle * 180.0 / kPi;
258 double rsin = sin(angle) * (radius + half_width);
259 // Note that cos(radians(90 degrees)) isn't exactly 0.0 like it should be
260 double rcos =
261 (i == quadrant_count - 1) ? 0.0f : cos(angle) * (radius + half_width);
262 EXPECT_POINT_NEAR(vertices[i * 2],
263 Point(center.x - rcos, center.y - rsin))
264 << "vertex " << i << ", angle = " << degrees << std::endl;
265 EXPECT_POINT_NEAR(vertices[quadrant_count * 2 + i * 2],
266 Point(center.x + rsin, center.y - rcos))
267 << "vertex " << i << ", angle = " << degrees << std::endl;
268 EXPECT_POINT_NEAR(vertices[quadrant_count * 4 + i * 2],
269 Point(center.x + rcos, center.y + rsin))
270 << "vertex " << i << ", angle = " << degrees << std::endl;
271 EXPECT_POINT_NEAR(vertices[quadrant_count * 6 + i * 2],
272 Point(center.x - rsin, center.y + rcos))
273 << "vertex " << i << ", angle = " << degrees << std::endl;
274 }
275
276 // Then test innerer points
277 for (size_t i = 0; i < quadrant_count; i++) {
278 double angle = kPiOver2 * i / (quadrant_count - 1);
279 double degrees = angle * 180.0 / kPi;
280 double rsin = sin(angle) * (radius - half_width);
281 // Note that cos(radians(90 degrees)) isn't exactly 0.0 like it should be
282 double rcos =
283 (i == quadrant_count - 1) ? 0.0f : cos(angle) * (radius - half_width);
284 EXPECT_POINT_NEAR(vertices[i * 2 + 1],
285 Point(center.x - rcos, center.y - rsin))
286 << "vertex " << i << ", angle = " << degrees << std::endl;
287 EXPECT_POINT_NEAR(vertices[quadrant_count * 2 + i * 2 + 1],
288 Point(center.x + rsin, center.y - rcos))
289 << "vertex " << i << ", angle = " << degrees << std::endl;
290 EXPECT_POINT_NEAR(vertices[quadrant_count * 4 + i * 2 + 1],
291 Point(center.x + rcos, center.y + rsin))
292 << "vertex " << i << ", angle = " << degrees << std::endl;
293 EXPECT_POINT_NEAR(vertices[quadrant_count * 6 + i * 2 + 1],
294 Point(center.x - rsin, center.y + rcos))
295 << "vertex " << i << ", angle = " << degrees << std::endl;
296 }
297 };
298
299 test({}, {}, 2.0, 1.0);
300 test({}, {}, 2.0, 0.5);
301 test({}, {10, 10}, 2.0, 1.0);
302 test(Matrix::MakeScale({500.0, 500.0, 0.0}), {}, 2.0, 1.0);
303 test(Matrix::MakeScale({0.002, 0.002, 0.0}), {}, 1000.0, 10.0);
304 }
305
306 TEST(TessellatorTest, FilledArcStripTessellationVertices) {
307 Tessellator tessellator;
308
309 auto test = [&tessellator](const Matrix& transform, const Arc& arc) {
310 auto generator = tessellator.FilledArc(transform, arc,
311 /*supports_triangle_fans=*/false);
312 EXPECT_EQ(generator.GetTriangleType(), PrimitiveType::kTriangleStrip);
313
314 auto vertex_count = generator.GetVertexCount();
315 auto vertices = std::vector<Point>();
316 generator.GenerateVertices([&vertices](const Point& p) { //
317 vertices.push_back(p);
318 });
319 EXPECT_EQ(vertices.size(), vertex_count);
320
321 auto center = arc.GetOvalBounds().GetCenter();
322 auto radius = arc.GetOvalSize().width * 0.5;
323
324 // Test position of first point
325 EXPECT_POINT_NEAR(
326 vertices[0],
327 Point(center.x + cos(Radians(arc.GetStart()).radians) * radius,
328 center.y + sin(Radians(arc.GetStart()).radians) * radius));
329
330 // Test position of last point
331 auto last_angle = arc.GetStart() + arc.GetSweep();
332 EXPECT_POINT_NEAR(
333 vertices[vertex_count - 1],
334 Point(center.x + cos(Radians(last_angle).radians) * radius,
335 center.y + sin(Radians(last_angle).radians) * radius));
336
337 // Test odd-indexed points. These are all the origin.
338 Point origin = arc.IncludeCenter()
339 ? center
340 : (vertices[0] + vertices[vertex_count - 1]) * 0.5f;
341 for (size_t i = 1; i < vertex_count; i += 2) {
342 EXPECT_POINT_NEAR(vertices[i], origin);
343 }
344
345 // Test even-indexed points. These are points on the outer edge of the arc.
346 auto previous_outer_point = vertices[0];
347 auto outer_increment_distance = (vertices[4] - vertices[2]).GetLength();
348 for (size_t i = 2; i < vertex_count; i += 2) {
349 // Each is |radius| from the center.
350 EXPECT_NEAR((vertices[i] - center).GetLength(), radius, kEhCloseEnough);
351
352 // Each is within |outer_increment_distance| from the previous
353 if (i == 2 || i == vertex_count - 1) {
354 // The very first and last points may be closer than
355 // |outer_increment_distance| to their adjacent outer points
356 EXPECT_LE((vertices[i] - previous_outer_point).GetLength(),
357 outer_increment_distance + kEhCloseEnough);
358 } else {
359 // Other outer points are |outer_increment_distance| apart
360 EXPECT_NEAR((vertices[i] - previous_outer_point).GetLength(),
361 outer_increment_distance, kEhCloseEnough);
362 }
363
364 previous_outer_point = vertices[i];
365 }
366 };
367
368 test({}, Arc(Rect::MakeXYWH(0, 0, 100, 100), Degrees(0), Degrees(90), false));
369 test({}, Arc(Rect::MakeXYWH(0, 0, 100, 100), Degrees(0), Degrees(90), true));
370
371 test({},
372 Arc(Rect::MakeXYWH(0, 0, 100, 100), Degrees(0), Degrees(-270), false));
373 test({},
374 Arc(Rect::MakeXYWH(0, 0, 100, 100), Degrees(0), Degrees(-270), true));
375
376 test({},
377 Arc(Rect::MakeXYWH(0, 0, 100, 100), Degrees(94), Degrees(322), false));
378 test({},
379 Arc(Rect::MakeXYWH(0, 0, 100, 100), Degrees(94), Degrees(322), true));
380 }
381
382 TEST(TessellatorTest, RoundCapLineTessellationVertices) {
383 Tessellator tessellator;
384
385 auto test = [&tessellator](const Matrix& transform, const Point& p0,
386 const Point& p1, Scalar radius) {
387 auto generator = tessellator.RoundCapLine(transform, p0, p1, radius);
388 EXPECT_EQ(generator.GetTriangleType(), PrimitiveType::kTriangleStrip);
389
390 auto vertex_count = generator.GetVertexCount();
391 auto vertices = std::vector<Point>();
392 generator.GenerateVertices([&vertices](const Point& p) { //
393 vertices.push_back(p);
394 });
395 EXPECT_EQ(vertices.size(), vertex_count);
396 ASSERT_EQ(vertex_count % 4, 0u);
397
398 Point along = p1 - p0;
399 Scalar length = along.GetLength();
400 if (length > 0) {
401 along *= radius / length;
402 } else {
403 along = {radius, 0};
404 }
405 Point across = {-along.y, along.x};
406
407 auto quadrant_count = vertex_count / 4;
408 for (size_t i = 0; i < quadrant_count; i++) {
409 double angle = kPiOver2 * i / (quadrant_count - 1);
410 double degrees = angle * 180.0 / kPi;
411 // Note that cos(radians(90 degrees)) isn't exactly 0.0 like it should be
412 Point relative_along =
413 along * ((i == quadrant_count - 1) ? 0.0f : cos(angle));
414 Point relative_across = across * sin(angle);
415 EXPECT_POINT_NEAR(vertices[i * 2], //
416 p0 - relative_along + relative_across)
417 << "vertex " << i << ", angle = " << degrees << ", " //
418 << "line = " << p0 << " => " << p1 << ", " //
419 << "radius = " << radius << std::endl;
420 EXPECT_POINT_NEAR(vertices[i * 2 + 1], //
421 p0 - relative_along - relative_across)
422 << "vertex " << i << ", angle = " << degrees << ", " //
423 << "line = " << p0 << " => " << p1 << ", " //
424 << "radius = " << radius << std::endl;
425 EXPECT_POINT_NEAR(vertices[vertex_count - i * 2 - 1], //
426 p1 + relative_along - relative_across)
427 << "vertex " << i << ", angle = " << degrees << ", " //
428 << "line = " << p0 << " => " << p1 << ", " //
429 << "radius = " << radius << std::endl;
430 EXPECT_POINT_NEAR(vertices[vertex_count - i * 2 - 2], //
431 p1 + relative_along + relative_across)
432 << "vertex " << i << ", angle = " << degrees << ", " //
433 << "line = " << p0 << " => " << p1 << ", " //
434 << "radius = " << radius << std::endl;
435 }
436 };
437
438 // Empty line should actually use the circle generator, but its
439 // results should match the same math as the round cap generator.
440 test({}, {0, 0}, {0, 0}, 10);
441
442 test({}, {0, 0}, {10, 0}, 2);
443 test({}, {10, 0}, {0, 0}, 2);
444 test({}, {0, 0}, {10, 10}, 2);
445
446 test(Matrix::MakeScale({500.0, 500.0, 0.0}), {0, 0}, {10, 0}, 2);
447 test(Matrix::MakeScale({500.0, 500.0, 0.0}), {10, 0}, {0, 0}, 2);
448 test(Matrix::MakeScale({500.0, 500.0, 0.0}), {0, 0}, {10, 10}, 2);
449
450 test(Matrix::MakeScale({0.002, 0.002, 0.0}), {0, 0}, {10, 0}, 2);
451 test(Matrix::MakeScale({0.002, 0.002, 0.0}), {10, 0}, {0, 0}, 2);
452 test(Matrix::MakeScale({0.002, 0.002, 0.0}), {0, 0}, {10, 10}, 2);
453 }
454
455 TEST(TessellatorTest, FilledEllipseTessellationVertices) {
456 Tessellator tessellator;
457
458 auto test = [&tessellator](const Matrix& transform, const Rect& bounds) {
459 auto center = bounds.GetCenter();
460 auto half_size = bounds.GetSize() * 0.5f;
461
462 auto generator = tessellator.FilledEllipse(transform, bounds);
463 EXPECT_EQ(generator.GetTriangleType(), PrimitiveType::kTriangleStrip);
464
465 auto vertex_count = generator.GetVertexCount();
466 auto vertices = std::vector<Point>();
467 generator.GenerateVertices([&vertices](const Point& p) { //
468 vertices.push_back(p);
469 });
470 EXPECT_EQ(vertices.size(), vertex_count);
471 ASSERT_EQ(vertex_count % 4, 0u);
472
473 auto quadrant_count = vertex_count / 4;
474 for (size_t i = 0; i < quadrant_count; i++) {
475 double angle = kPiOver2 * i / (quadrant_count - 1);
476 double degrees = angle * 180.0 / kPi;
477 // Note that cos(radians(90 degrees)) isn't exactly 0.0 like it should be
478 double rcos =
479 (i == quadrant_count - 1) ? 0.0f : cos(angle) * half_size.width;
480 double rsin = sin(angle) * half_size.height;
481 EXPECT_POINT_NEAR(vertices[i * 2],
482 Point(center.x - rcos, center.y + rsin))
483 << "vertex " << i << ", angle = " << degrees << ", " //
484 << "bounds = " << bounds << std::endl;
485 EXPECT_POINT_NEAR(vertices[i * 2 + 1],
486 Point(center.x - rcos, center.y - rsin))
487 << "vertex " << i << ", angle = " << degrees << ", " //
488 << "bounds = " << bounds << std::endl;
489 EXPECT_POINT_NEAR(vertices[vertex_count - i * 2 - 1],
490 Point(center.x + rcos, center.y - rsin))
491 << "vertex " << i << ", angle = " << degrees << ", " //
492 << "bounds = " << bounds << std::endl;
493 EXPECT_POINT_NEAR(vertices[vertex_count - i * 2 - 2],
494 Point(center.x + rcos, center.y + rsin))
495 << "vertex " << i << ", angle = " << degrees << ", " //
496 << "bounds = " << bounds << std::endl;
497 }
498 };
499
500 // Square bounds should actually use the circle generator, but its
501 // results should match the same math as the ellipse generator.
502 test({}, Rect::MakeXYWH(0, 0, 2, 2));
503
504 test({}, Rect::MakeXYWH(0, 0, 2, 3));
505 test({}, Rect::MakeXYWH(0, 0, 3, 2));
506 test({}, Rect::MakeXYWH(5, 10, 2, 3));
507 test({}, Rect::MakeXYWH(16, 7, 3, 2));
508 test(Matrix::MakeScale({500.0, 500.0, 0.0}), Rect::MakeXYWH(5, 10, 3, 2));
509 test(Matrix::MakeScale({500.0, 500.0, 0.0}), Rect::MakeXYWH(5, 10, 2, 3));
510 test(Matrix::MakeScale({0.002, 0.002, 0.0}),
511 Rect::MakeXYWH(5000, 10000, 3000, 2000));
512 test(Matrix::MakeScale({0.002, 0.002, 0.0}),
513 Rect::MakeXYWH(5000, 10000, 2000, 3000));
514 }
515
516 TEST(TessellatorTest, FilledRoundRectTessellationVertices) {
517 Tessellator tessellator;
518
519 auto test = [&tessellator](const Matrix& transform, const Rect& bounds,
520 const Size& radii) {
521 FML_DCHECK(radii.width * 2 <= bounds.GetWidth()) << radii << bounds;
522 FML_DCHECK(radii.height * 2 <= bounds.GetHeight()) << radii << bounds;
523
524 Scalar middle_left = bounds.GetX() + radii.width;
525 Scalar middle_top = bounds.GetY() + radii.height;
526 Scalar middle_right = bounds.GetX() + bounds.GetWidth() - radii.width;
527 Scalar middle_bottom = bounds.GetY() + bounds.GetHeight() - radii.height;
528
529 auto generator = tessellator.FilledRoundRect(transform, bounds, radii);
530 EXPECT_EQ(generator.GetTriangleType(), PrimitiveType::kTriangleStrip);
531
532 auto vertex_count = generator.GetVertexCount();
533 auto vertices = std::vector<Point>();
534 generator.GenerateVertices([&vertices](const Point& p) { //
535 vertices.push_back(p);
536 });
537 EXPECT_EQ(vertices.size(), vertex_count);
538 ASSERT_EQ(vertex_count % 4, 0u);
539
540 auto quadrant_count = vertex_count / 4;
541 for (size_t i = 0; i < quadrant_count; i++) {
542 double angle = kPiOver2 * i / (quadrant_count - 1);
543 double degrees = angle * 180.0 / kPi;
544 // Note that cos(radians(90 degrees)) isn't exactly 0.0 like it should be
545 double rcos = (i == quadrant_count - 1) ? 0.0f : cos(angle) * radii.width;
546 double rsin = sin(angle) * radii.height;
547 EXPECT_POINT_NEAR(vertices[i * 2],
548 Point(middle_left - rcos, middle_bottom + rsin))
549 << "vertex " << i << ", angle = " << degrees << ", " //
550 << "bounds = " << bounds << std::endl;
551 EXPECT_POINT_NEAR(vertices[i * 2 + 1],
552 Point(middle_left - rcos, middle_top - rsin))
553 << "vertex " << i << ", angle = " << degrees << ", " //
554 << "bounds = " << bounds << std::endl;
555 EXPECT_POINT_NEAR(vertices[vertex_count - i * 2 - 1],
556 Point(middle_right + rcos, middle_top - rsin))
557 << "vertex " << i << ", angle = " << degrees << ", " //
558 << "bounds = " << bounds << std::endl;
559 EXPECT_POINT_NEAR(vertices[vertex_count - i * 2 - 2],
560 Point(middle_right + rcos, middle_bottom + rsin))
561 << "vertex " << i << ", angle = " << degrees << ", " //
562 << "bounds = " << bounds << std::endl;
563 }
564 };
565
566 // Both radii spanning the bounds should actually use the circle/ellipse
567 // generator, but their results should match the same math as the round
568 // rect generator.
569 test({}, Rect::MakeXYWH(0, 0, 20, 20), {10, 10});
570
571 // One radius spanning the bounds, but not the other will not match the
572 // round rect math if the generator transfers to circle/ellipse
573 test({}, Rect::MakeXYWH(0, 0, 20, 20), {10, 5});
574 test({}, Rect::MakeXYWH(0, 0, 20, 20), {5, 10});
575
576 test({}, Rect::MakeXYWH(0, 0, 20, 30), {2, 2});
577 test({}, Rect::MakeXYWH(0, 0, 30, 20), {2, 2});
578 test({}, Rect::MakeXYWH(5, 10, 20, 30), {2, 3});
579 test({}, Rect::MakeXYWH(16, 7, 30, 20), {2, 3});
580 test(Matrix::MakeScale({500.0, 500.0, 0.0}), Rect::MakeXYWH(5, 10, 30, 20),
581 {2, 3});
582 test(Matrix::MakeScale({500.0, 500.0, 0.0}), Rect::MakeXYWH(5, 10, 20, 30),
583 {2, 3});
584 test(Matrix::MakeScale({0.002, 0.002, 0.0}),
585 Rect::MakeXYWH(5000, 10000, 3000, 2000), {50, 70});
586 test(Matrix::MakeScale({0.002, 0.002, 0.0}),
587 Rect::MakeXYWH(5000, 10000, 2000, 3000), {50, 70});
588 }
589
590 TEST(TessellatorTest, EarlyReturnEmptyConvexShape) {
591 // This path is not technically empty (it has a size in one dimension), but
592 // it contains only move commands and no actual path segment definitions.
593 flutter::DlPathBuilder builder;
594 builder.MoveTo({0, 0});
595 builder.MoveTo({10, 10});
596
597 std::vector<Point> points;
598 std::vector<uint16_t> indices;
599 Tessellator::TessellateConvexInternal(builder.TakePath(), points, indices,
600 3.0f);
601
602 EXPECT_TRUE(points.empty());
603 EXPECT_TRUE(indices.empty());
604 }
605
606 } // namespace testing
607 } // namespace impeller
impeller::Tessellator::EllipticalVertexGenerator::GetVertexCount
size_t GetVertexCount() const override
|VertexGenerator|
Definition: tessellator.h:148
impeller::Tessellator
A utility that generates triangles of the specified fill type given a polyline. This happens on the C...
Definition: tessellator.h:37
impeller::Tessellator::RoundCapLine
EllipticalVertexGenerator RoundCapLine(const Matrix &view_transform, const Point &p0, const Point &p1, Scalar radius)
Create a |VertexGenerator| that can produce vertices for a line with round end caps of the given radi...
Definition: tessellator.cc:645
impeller::Tessellator::FilledRoundRect
EllipticalVertexGenerator FilledRoundRect(const Matrix &view_transform, const Rect &bounds, const Size &radii)
Create a |VertexGenerator| that can produce vertices for a filled round rect within the given bounds ...
Definition: tessellator.cc:689
impeller::Tessellator::kCircleTolerance
static constexpr Scalar kCircleTolerance
The pixel tolerance used by the algorighm to determine how many divisions to create for a circle.
Definition: tessellator.h:280
impeller::Tessellator::FilledCircle
EllipticalVertexGenerator FilledCircle(const Matrix &view_transform, const Point &center, Scalar radius)
Create a |VertexGenerator| that can produce vertices for a filled circle of the given radius around t...
Definition: tessellator.cc:488
impeller::Tessellator::TessellateConvexInternal
static void TessellateConvexInternal(const PathSource &path, std::vector< Point > &point_buffer, std::vector< uint16_t > &index_buffer, Scalar tolerance)
Definition: tessellator.cc:440
impeller::Tessellator::StrokedCircle
EllipticalVertexGenerator StrokedCircle(const Matrix &view_transform, const Point &center, Scalar radius, Scalar half_width)
Create a |VertexGenerator| that can produce vertices for a stroked circle of the given radius and hal...
Definition: tessellator.cc:504
impeller::Tessellator::FilledEllipse
EllipticalVertexGenerator FilledEllipse(const Matrix &view_transform, const Rect &bounds)
Create a |VertexGenerator| that can produce vertices for a filled ellipse inscribed within the given ...
Definition: tessellator.cc:668
impeller::Tessellator::FilledArc
ArcVertexGenerator FilledArc(const Matrix &view_transform, const Arc &arc, bool supports_triangle_fans)
Create a |VertexGenerator| that can produce vertices for a stroked arc inscribed within the given ova...
Definition: tessellator.cc:612
impeller::TessellatorLibtess
An extended tessellator that offers arbitrary/concave tessellation via the libtess2 library.
Definition: tessellator_libtess.h:29
impeller::TessellatorLibtess::Result
Result
Definition: tessellator_libtess.h:35
impeller::TessellatorLibtess::Result::kSuccess
@ kSuccess
impeller::TessellatorLibtess::Result::kInputError
@ kInputError
impeller::TessellatorLibtess::Tessellate
TessellatorLibtess::Result Tessellate(const PathSource &source, Scalar tolerance, const BuilderCallback &callback)
Generates filled triangles from the path. A callback is invoked once for the entire tessellation.
Definition: tessellator_libtess.cc:85
transform
Matrix transform
Definition: gaussian_blur_filter_contents.cc:312
constants.h
geometry_asserts.h
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::PrimitiveType::kTriangleStrip
@ kTriangleStrip
impeller::Vector2
Point Vector2
Definition: point.h:429
impeller::kPi
constexpr float kPi
Definition: constants.h:26
impeller::Scalar
float Scalar
Definition: scalar.h:19
impeller::kEhCloseEnough
constexpr float kEhCloseEnough
Definition: constants.h:57
impeller::FillType::kOdd
@ kOdd
impeller::Point
TPoint< Scalar > Point
Definition: point.h:425
impeller::kPiOver2
constexpr float kPiOver2
Definition: constants.h:32
impeller::DlPath
flutter::DlPath DlPath
Definition: dl_dispatcher.h:29
impeller::LineTo
void LineTo(PathBuilder *builder, Scalar x, Scalar y)
Definition: tessellator.cc:24
p1
Point p1
Definition: round_superellipse_param.cc:313
impeller::Arc
Definition: arc.h:13
impeller::Degrees
Definition: scalar.h:66
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
impeller::Matrix::MakeScale
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104
impeller::Radians
Definition: scalar.h:44
impeller::Radians::radians
Scalar radians
Definition: scalar.h:45
impeller::TPoint< Scalar >
impeller::TPoint::GetLength
constexpr Type GetLength() const
Definition: point.h:209
impeller::TPoint::y
Type y
Definition: point.h:32
impeller::TPoint::x
Type x
Definition: point.h:31
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::TSize< Scalar >
tessellator.h
points
std::vector< Point > points
Definition: tessellator_libtess.cc:76
tessellator_libtess.h