Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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
12
13namespace impeller {
14namespace testing {
15
16TEST(TessellatorTest, TessellatorBuilderReturnsCorrectResultStatus) {
17 // Zero points.
18 {
20 auto path = PathBuilder{}.TakePath(FillType::kOdd);
22 path, 1.0f,
23 [](const float* vertices, size_t vertices_count,
24 const uint16_t* indices, size_t indices_count) { return true; });
25
27 }
28
29 // One point.
30 {
32 auto path = PathBuilder{}.LineTo({0, 0}).TakePath(FillType::kOdd);
34 path, 1.0f,
35 [](const float* vertices, size_t vertices_count,
36 const uint16_t* indices, size_t indices_count) { return true; });
37
39 }
40
41 // Two points.
42 {
44 auto path = PathBuilder{}.AddLine({0, 0}, {0, 1}).TakePath(FillType::kOdd);
46 path, 1.0f,
47 [](const float* vertices, size_t vertices_count,
48 const uint16_t* indices, size_t indices_count) { return true; });
49
51 }
52
53 // Many points.
54 {
56 PathBuilder builder;
57 for (int i = 0; i < 1000; i++) {
58 auto coord = i * 1.0f;
59 builder.AddLine({coord, coord}, {coord + 1, coord + 1});
60 }
61 auto path = builder.TakePath(FillType::kOdd);
63 path, 1.0f,
64 [](const float* vertices, size_t vertices_count,
65 const uint16_t* indices, size_t indices_count) { return true; });
66
68 }
69
70 // Closure fails.
71 {
73 auto path = PathBuilder{}.AddLine({0, 0}, {0, 1}).TakePath(FillType::kOdd);
75 path, 1.0f,
76 [](const float* vertices, size_t vertices_count,
77 const uint16_t* indices, size_t indices_count) { return false; });
78
80 }
81}
82
83TEST(TessellatorTest, TessellateConvex) {
84 {
86 // Sanity check simple rectangle.
87 auto pts = t.TessellateConvex(
88 PathBuilder{}.AddRect(Rect::MakeLTRB(0, 0, 10, 10)).TakePath(), 1.0);
89
90 std::vector<Point> expected = {{0, 0}, {10, 0}, {0, 10}, {10, 10}};
91 EXPECT_EQ(pts, expected);
92 }
93
94 {
96 auto pts = t.TessellateConvex(PathBuilder{}
97 .AddRect(Rect::MakeLTRB(0, 0, 10, 10))
98 .AddRect(Rect::MakeLTRB(20, 20, 30, 30))
99 .TakePath(),
100 1.0);
101
102 std::vector<Point> expected = {{0, 0}, {10, 0}, {0, 10}, {10, 10},
103 {10, 10}, {20, 20}, {20, 20}, {30, 20},
104 {20, 30}, {30, 30}};
105 EXPECT_EQ(pts, expected);
106 }
107}
108
109TEST(TessellatorTest, CircleVertexCounts) {
110 auto tessellator = std::make_shared<Tessellator>();
111
112 auto test = [&tessellator](const Matrix& transform, Scalar radius) {
113 auto generator = tessellator->FilledCircle(transform, {}, radius);
114 size_t quadrant_divisions = generator.GetVertexCount() / 4;
115
116 // Confirm the approximation error is within the currently accepted
117 // |kCircleTolerance| value advertised by |CircleTessellator|.
118 // (With an additional 1% tolerance for floating point rounding.)
119 double angle = kPiOver2 / quadrant_divisions;
120 Point first = {radius, 0};
121 Point next = {static_cast<Scalar>(cos(angle) * radius),
122 static_cast<Scalar>(sin(angle) * radius)};
123 Point midpoint = (first + next) * 0.5;
124 EXPECT_GE(midpoint.GetLength(),
125 radius - Tessellator::kCircleTolerance * 1.01)
126 << ", transform = " << transform << ", radius = " << radius
127 << ", divisions = " << quadrant_divisions;
128 };
129
130 test({}, 0.0);
131 test({}, 0.9);
132 test({}, 1.0);
133 test({}, 1.9);
134 test(Matrix::MakeScale(Vector2(2.0, 2.0)), 0.95);
135 test({}, 2.0);
136 test(Matrix::MakeScale(Vector2(2.0, 2.0)), 1.0);
137 test({}, 11.9);
138 test({}, 12.0);
139 test({}, 35.9);
140 for (int i = 36; i < 10000; i += 4) {
141 test({}, i);
142 }
143}
144
145TEST(TessellatorTest, FilledCircleTessellationVertices) {
146 auto tessellator = std::make_shared<Tessellator>();
147
148 auto test = [&tessellator](const Matrix& transform, const Point& center,
149 Scalar radius) {
150 auto generator = tessellator->FilledCircle(transform, center, radius);
151 EXPECT_EQ(generator.GetTriangleType(), PrimitiveType::kTriangleStrip);
152
153 auto vertex_count = generator.GetVertexCount();
154 auto vertices = std::vector<Point>();
155 generator.GenerateVertices([&vertices](const Point& p) { //
156 vertices.push_back(p);
157 });
158 EXPECT_EQ(vertices.size(), vertex_count);
159 ASSERT_EQ(vertex_count % 4, 0u);
160
161 auto quadrant_count = vertex_count / 4;
162 for (size_t i = 0; i < quadrant_count; i++) {
163 double angle = kPiOver2 * i / (quadrant_count - 1);
164 double degrees = angle * 180.0 / kPi;
165 double rsin = sin(angle) * radius;
166 // Note that cos(radians(90 degrees)) isn't exactly 0.0 like it should be
167 double rcos = (i == quadrant_count - 1) ? 0.0f : cos(angle) * radius;
168 EXPECT_POINT_NEAR(vertices[i * 2],
169 Point(center.x - rcos, center.y + rsin))
170 << "vertex " << i << ", angle = " << degrees << std::endl;
171 EXPECT_POINT_NEAR(vertices[i * 2 + 1],
172 Point(center.x - rcos, center.y - rsin))
173 << "vertex " << i << ", angle = " << degrees << std::endl;
174 EXPECT_POINT_NEAR(vertices[vertex_count - i * 2 - 1],
175 Point(center.x + rcos, center.y - rsin))
176 << "vertex " << i << ", angle = " << degrees << std::endl;
177 EXPECT_POINT_NEAR(vertices[vertex_count - i * 2 - 2],
178 Point(center.x + rcos, center.y + rsin))
179 << "vertex " << i << ", angle = " << degrees << std::endl;
180 }
181 };
182
183 test({}, {}, 2.0);
184 test({}, {10, 10}, 2.0);
185 test(Matrix::MakeScale({500.0, 500.0, 0.0}), {}, 2.0);
186 test(Matrix::MakeScale({0.002, 0.002, 0.0}), {}, 1000.0);
187}
188
189TEST(TessellatorTest, StrokedCircleTessellationVertices) {
190 auto tessellator = std::make_shared<Tessellator>();
191
192 auto test = [&tessellator](const Matrix& transform, const Point& center,
193 Scalar radius, Scalar half_width) {
194 ASSERT_GT(radius, half_width);
195 auto generator =
196 tessellator->StrokedCircle(transform, center, radius, half_width);
197 EXPECT_EQ(generator.GetTriangleType(), PrimitiveType::kTriangleStrip);
198
199 auto vertex_count = generator.GetVertexCount();
200 auto vertices = std::vector<Point>();
201 generator.GenerateVertices([&vertices](const Point& p) { //
202 vertices.push_back(p);
203 });
204 EXPECT_EQ(vertices.size(), vertex_count);
205 ASSERT_EQ(vertex_count % 4, 0u);
206
207 auto quadrant_count = vertex_count / 8;
208
209 // Test outer points first
210 for (size_t i = 0; i < quadrant_count; i++) {
211 double angle = kPiOver2 * i / (quadrant_count - 1);
212 double degrees = angle * 180.0 / kPi;
213 double rsin = sin(angle) * (radius + half_width);
214 // Note that cos(radians(90 degrees)) isn't exactly 0.0 like it should be
215 double rcos =
216 (i == quadrant_count - 1) ? 0.0f : cos(angle) * (radius + half_width);
217 EXPECT_POINT_NEAR(vertices[i * 2],
218 Point(center.x - rcos, center.y - rsin))
219 << "vertex " << i << ", angle = " << degrees << std::endl;
220 EXPECT_POINT_NEAR(vertices[quadrant_count * 2 + i * 2],
221 Point(center.x + rsin, center.y - rcos))
222 << "vertex " << i << ", angle = " << degrees << std::endl;
223 EXPECT_POINT_NEAR(vertices[quadrant_count * 4 + i * 2],
224 Point(center.x + rcos, center.y + rsin))
225 << "vertex " << i << ", angle = " << degrees << std::endl;
226 EXPECT_POINT_NEAR(vertices[quadrant_count * 6 + i * 2],
227 Point(center.x - rsin, center.y + rcos))
228 << "vertex " << i << ", angle = " << degrees << std::endl;
229 }
230
231 // Then test innerer points
232 for (size_t i = 0; i < quadrant_count; i++) {
233 double angle = kPiOver2 * i / (quadrant_count - 1);
234 double degrees = angle * 180.0 / kPi;
235 double rsin = sin(angle) * (radius - half_width);
236 // Note that cos(radians(90 degrees)) isn't exactly 0.0 like it should be
237 double rcos =
238 (i == quadrant_count - 1) ? 0.0f : cos(angle) * (radius - half_width);
239 EXPECT_POINT_NEAR(vertices[i * 2 + 1],
240 Point(center.x - rcos, center.y - rsin))
241 << "vertex " << i << ", angle = " << degrees << std::endl;
242 EXPECT_POINT_NEAR(vertices[quadrant_count * 2 + i * 2 + 1],
243 Point(center.x + rsin, center.y - rcos))
244 << "vertex " << i << ", angle = " << degrees << std::endl;
245 EXPECT_POINT_NEAR(vertices[quadrant_count * 4 + i * 2 + 1],
246 Point(center.x + rcos, center.y + rsin))
247 << "vertex " << i << ", angle = " << degrees << std::endl;
248 EXPECT_POINT_NEAR(vertices[quadrant_count * 6 + i * 2 + 1],
249 Point(center.x - rsin, center.y + rcos))
250 << "vertex " << i << ", angle = " << degrees << std::endl;
251 }
252 };
253
254 test({}, {}, 2.0, 1.0);
255 test({}, {}, 2.0, 0.5);
256 test({}, {10, 10}, 2.0, 1.0);
257 test(Matrix::MakeScale({500.0, 500.0, 0.0}), {}, 2.0, 1.0);
258 test(Matrix::MakeScale({0.002, 0.002, 0.0}), {}, 1000.0, 10.0);
259}
260
261TEST(TessellatorTest, RoundCapLineTessellationVertices) {
262 auto tessellator = std::make_shared<Tessellator>();
263
264 auto test = [&tessellator](const Matrix& transform, const Point& p0,
265 const Point& p1, Scalar radius) {
266 auto generator = tessellator->RoundCapLine(transform, p0, p1, radius);
267 EXPECT_EQ(generator.GetTriangleType(), PrimitiveType::kTriangleStrip);
268
269 auto vertex_count = generator.GetVertexCount();
270 auto vertices = std::vector<Point>();
271 generator.GenerateVertices([&vertices](const Point& p) { //
272 vertices.push_back(p);
273 });
274 EXPECT_EQ(vertices.size(), vertex_count);
275 ASSERT_EQ(vertex_count % 4, 0u);
276
277 Point along = p1 - p0;
278 Scalar length = along.GetLength();
279 if (length > 0) {
280 along *= radius / length;
281 } else {
282 along = {radius, 0};
283 }
284 Point across = {-along.y, along.x};
285
286 auto quadrant_count = vertex_count / 4;
287 for (size_t i = 0; i < quadrant_count; i++) {
288 double angle = kPiOver2 * i / (quadrant_count - 1);
289 double degrees = angle * 180.0 / kPi;
290 // Note that cos(radians(90 degrees)) isn't exactly 0.0 like it should be
291 Point relative_along =
292 along * ((i == quadrant_count - 1) ? 0.0f : cos(angle));
293 Point relative_across = across * sin(angle);
294 EXPECT_POINT_NEAR(vertices[i * 2], //
295 p0 - relative_along + relative_across)
296 << "vertex " << i << ", angle = " << degrees << ", " //
297 << "line = " << p0 << " => " << p1 << ", " //
298 << "radius = " << radius << std::endl;
299 EXPECT_POINT_NEAR(vertices[i * 2 + 1], //
300 p0 - relative_along - relative_across)
301 << "vertex " << i << ", angle = " << degrees << ", " //
302 << "line = " << p0 << " => " << p1 << ", " //
303 << "radius = " << radius << std::endl;
304 EXPECT_POINT_NEAR(vertices[vertex_count - i * 2 - 1], //
305 p1 + relative_along - relative_across)
306 << "vertex " << i << ", angle = " << degrees << ", " //
307 << "line = " << p0 << " => " << p1 << ", " //
308 << "radius = " << radius << std::endl;
309 EXPECT_POINT_NEAR(vertices[vertex_count - i * 2 - 2], //
310 p1 + relative_along + relative_across)
311 << "vertex " << i << ", angle = " << degrees << ", " //
312 << "line = " << p0 << " => " << p1 << ", " //
313 << "radius = " << radius << std::endl;
314 }
315 };
316
317 // Empty line should actually use the circle generator, but its
318 // results should match the same math as the round cap generator.
319 test({}, {0, 0}, {0, 0}, 10);
320
321 test({}, {0, 0}, {10, 0}, 2);
322 test({}, {10, 0}, {0, 0}, 2);
323 test({}, {0, 0}, {10, 10}, 2);
324
325 test(Matrix::MakeScale({500.0, 500.0, 0.0}), {0, 0}, {10, 0}, 2);
326 test(Matrix::MakeScale({500.0, 500.0, 0.0}), {10, 0}, {0, 0}, 2);
327 test(Matrix::MakeScale({500.0, 500.0, 0.0}), {0, 0}, {10, 10}, 2);
328
329 test(Matrix::MakeScale({0.002, 0.002, 0.0}), {0, 0}, {10, 0}, 2);
330 test(Matrix::MakeScale({0.002, 0.002, 0.0}), {10, 0}, {0, 0}, 2);
331 test(Matrix::MakeScale({0.002, 0.002, 0.0}), {0, 0}, {10, 10}, 2);
332}
333
334TEST(TessellatorTest, FilledEllipseTessellationVertices) {
335 auto tessellator = std::make_shared<Tessellator>();
336
337 auto test = [&tessellator](const Matrix& transform, const Rect& bounds) {
338 auto center = bounds.GetCenter();
339 auto half_size = bounds.GetSize() * 0.5f;
340
341 auto generator = tessellator->FilledEllipse(transform, bounds);
342 EXPECT_EQ(generator.GetTriangleType(), PrimitiveType::kTriangleStrip);
343
344 auto vertex_count = generator.GetVertexCount();
345 auto vertices = std::vector<Point>();
346 generator.GenerateVertices([&vertices](const Point& p) { //
347 vertices.push_back(p);
348 });
349 EXPECT_EQ(vertices.size(), vertex_count);
350 ASSERT_EQ(vertex_count % 4, 0u);
351
352 auto quadrant_count = vertex_count / 4;
353 for (size_t i = 0; i < quadrant_count; i++) {
354 double angle = kPiOver2 * i / (quadrant_count - 1);
355 double degrees = angle * 180.0 / kPi;
356 // Note that cos(radians(90 degrees)) isn't exactly 0.0 like it should be
357 double rcos =
358 (i == quadrant_count - 1) ? 0.0f : cos(angle) * half_size.width;
359 double rsin = sin(angle) * half_size.height;
360 EXPECT_POINT_NEAR(vertices[i * 2],
361 Point(center.x - rcos, center.y + rsin))
362 << "vertex " << i << ", angle = " << degrees << ", " //
363 << "bounds = " << bounds << std::endl;
364 EXPECT_POINT_NEAR(vertices[i * 2 + 1],
365 Point(center.x - rcos, center.y - rsin))
366 << "vertex " << i << ", angle = " << degrees << ", " //
367 << "bounds = " << bounds << std::endl;
368 EXPECT_POINT_NEAR(vertices[vertex_count - i * 2 - 1],
369 Point(center.x + rcos, center.y - rsin))
370 << "vertex " << i << ", angle = " << degrees << ", " //
371 << "bounds = " << bounds << std::endl;
372 EXPECT_POINT_NEAR(vertices[vertex_count - i * 2 - 2],
373 Point(center.x + rcos, center.y + rsin))
374 << "vertex " << i << ", angle = " << degrees << ", " //
375 << "bounds = " << bounds << std::endl;
376 }
377 };
378
379 // Square bounds should actually use the circle generator, but its
380 // results should match the same math as the ellipse generator.
381 test({}, Rect::MakeXYWH(0, 0, 2, 2));
382
383 test({}, Rect::MakeXYWH(0, 0, 2, 3));
384 test({}, Rect::MakeXYWH(0, 0, 3, 2));
385 test({}, Rect::MakeXYWH(5, 10, 2, 3));
386 test({}, Rect::MakeXYWH(16, 7, 3, 2));
387 test(Matrix::MakeScale({500.0, 500.0, 0.0}), Rect::MakeXYWH(5, 10, 3, 2));
388 test(Matrix::MakeScale({500.0, 500.0, 0.0}), Rect::MakeXYWH(5, 10, 2, 3));
389 test(Matrix::MakeScale({0.002, 0.002, 0.0}),
390 Rect::MakeXYWH(5000, 10000, 3000, 2000));
391 test(Matrix::MakeScale({0.002, 0.002, 0.0}),
392 Rect::MakeXYWH(5000, 10000, 2000, 3000));
393}
394
395TEST(TessellatorTest, FilledRoundRectTessellationVertices) {
396 auto tessellator = std::make_shared<Tessellator>();
397
398 auto test = [&tessellator](const Matrix& transform, const Rect& bounds,
399 const Size& radii) {
400 FML_DCHECK(radii.width * 2 <= bounds.GetWidth()) << radii << bounds;
401 FML_DCHECK(radii.height * 2 <= bounds.GetHeight()) << radii << bounds;
402
403 Scalar middle_left = bounds.GetX() + radii.width;
404 Scalar middle_top = bounds.GetY() + radii.height;
405 Scalar middle_right = bounds.GetX() + bounds.GetWidth() - radii.width;
406 Scalar middle_bottom = bounds.GetY() + bounds.GetHeight() - radii.height;
407
408 auto generator = tessellator->FilledRoundRect(transform, bounds, radii);
409 EXPECT_EQ(generator.GetTriangleType(), PrimitiveType::kTriangleStrip);
410
411 auto vertex_count = generator.GetVertexCount();
412 auto vertices = std::vector<Point>();
413 generator.GenerateVertices([&vertices](const Point& p) { //
414 vertices.push_back(p);
415 });
416 EXPECT_EQ(vertices.size(), vertex_count);
417 ASSERT_EQ(vertex_count % 4, 0u);
418
419 auto quadrant_count = vertex_count / 4;
420 for (size_t i = 0; i < quadrant_count; i++) {
421 double angle = kPiOver2 * i / (quadrant_count - 1);
422 double degrees = angle * 180.0 / kPi;
423 // Note that cos(radians(90 degrees)) isn't exactly 0.0 like it should be
424 double rcos = (i == quadrant_count - 1) ? 0.0f : cos(angle) * radii.width;
425 double rsin = sin(angle) * radii.height;
426 EXPECT_POINT_NEAR(vertices[i * 2],
427 Point(middle_left - rcos, middle_bottom + rsin))
428 << "vertex " << i << ", angle = " << degrees << ", " //
429 << "bounds = " << bounds << std::endl;
430 EXPECT_POINT_NEAR(vertices[i * 2 + 1],
431 Point(middle_left - rcos, middle_top - rsin))
432 << "vertex " << i << ", angle = " << degrees << ", " //
433 << "bounds = " << bounds << std::endl;
434 EXPECT_POINT_NEAR(vertices[vertex_count - i * 2 - 1],
435 Point(middle_right + rcos, middle_top - rsin))
436 << "vertex " << i << ", angle = " << degrees << ", " //
437 << "bounds = " << bounds << std::endl;
438 EXPECT_POINT_NEAR(vertices[vertex_count - i * 2 - 2],
439 Point(middle_right + rcos, middle_bottom + rsin))
440 << "vertex " << i << ", angle = " << degrees << ", " //
441 << "bounds = " << bounds << std::endl;
442 }
443 };
444
445 // Both radii spanning the bounds should actually use the circle/ellipse
446 // generator, but their results should match the same math as the round
447 // rect generator.
448 test({}, Rect::MakeXYWH(0, 0, 20, 20), {10, 10});
449
450 // One radius spanning the bounds, but not the other will not match the
451 // round rect math if the generator transfers to circle/ellipse
452 test({}, Rect::MakeXYWH(0, 0, 20, 20), {10, 5});
453 test({}, Rect::MakeXYWH(0, 0, 20, 20), {5, 10});
454
455 test({}, Rect::MakeXYWH(0, 0, 20, 30), {2, 2});
456 test({}, Rect::MakeXYWH(0, 0, 30, 20), {2, 2});
457 test({}, Rect::MakeXYWH(5, 10, 20, 30), {2, 3});
458 test({}, Rect::MakeXYWH(16, 7, 30, 20), {2, 3});
459 test(Matrix::MakeScale({500.0, 500.0, 0.0}), Rect::MakeXYWH(5, 10, 30, 20),
460 {2, 3});
461 test(Matrix::MakeScale({500.0, 500.0, 0.0}), Rect::MakeXYWH(5, 10, 20, 30),
462 {2, 3});
463 test(Matrix::MakeScale({0.002, 0.002, 0.0}),
464 Rect::MakeXYWH(5000, 10000, 3000, 2000), {50, 70});
465 test(Matrix::MakeScale({0.002, 0.002, 0.0}),
466 Rect::MakeXYWH(5000, 10000, 2000, 3000), {50, 70});
467}
468
469TEST(TessellatorTest, EarlyReturnEmptyConvexShape) {
470 // This path is not technically empty (it has a size in one dimension),
471 // but is otherwise completely flat.
472 auto tessellator = std::make_shared<Tessellator>();
473 PathBuilder builder;
474 builder.MoveTo({0, 0});
475 builder.MoveTo({10, 10}, /*relative=*/true);
476
477 auto points = tessellator->TessellateConvex(builder.TakePath(), 3.0);
478
479 EXPECT_TRUE(points.empty());
480}
481
482#if !NDEBUG
483TEST(TessellatorTest, ChecksConcurrentPolylineUsage) {
484 auto tessellator = std::make_shared<Tessellator>();
485 PathBuilder builder;
486 builder.AddLine({0, 0}, {100, 100});
487 auto path = builder.TakePath();
488
489 auto polyline = tessellator->CreateTempPolyline(path, 0.1);
490 EXPECT_DEBUG_DEATH(tessellator->CreateTempPolyline(path, 0.1),
491 "point_buffer_");
492}
493#endif // NDEBUG
494
495} // namespace testing
496} // namespace impeller
#define TEST(S, s, D, expected)
#define test(name)
static const int points[]
static float next(float f)
static SkScalar center(float pos0, float pos1)
PathBuilder & AddRect(Rect rect)
Path TakePath(FillType fill=FillType::kNonZero)
PathBuilder & LineTo(Point point, bool relative=false)
Insert a line from the current position to point.
PathBuilder & AddLine(const Point &p1, const Point &p2)
Move to point p1, then insert a line from p1 to p2.
An extended tessellator that offers arbitrary/concave tessellation via the libtess2 library.
Tessellator::Result Tessellate(const Path &path, Scalar tolerance, const BuilderCallback &callback)
Generates filled triangles from the path. A callback is invoked once for the entire tessellation.
A utility that generates triangles of the specified fill type given a polyline. This happens on the C...
Definition tessellator.h:29
std::vector< Point > TessellateConvex(const Path &path, Scalar tolerance)
Given a convex path, create a triangle fan structure.
static constexpr Scalar kCircleTolerance
The pixel tolerance used by the algorighm to determine how many divisions to create for a circle.
GAsyncResult * result
#define FML_DCHECK(condition)
Definition logging.h:103
#define EXPECT_POINT_NEAR(a, b)
size_t length
Point Vector2
Definition point.h:320
constexpr float kPi
Definition constants.h:26
float Scalar
Definition scalar.h:18
TPoint< Scalar > Point
Definition point.h:316
constexpr float kPiOver2
Definition constants.h:32
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
Definition p3.cpp:47
const Path::Polyline & polyline
A 4x4 matrix using column-major storage.
Definition matrix.h:37
static constexpr Matrix MakeScale(const Vector3 &s)
Definition matrix.h:104
constexpr Type GetLength() const
Definition point.h:206
static constexpr TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition rect.h:136
static constexpr TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition rect.h:129
#define EXPECT_TRUE(handle)
Definition unit_test.h:685