| Flutter Impeller |

aiks_dl_basic_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 "display_list/display_list.h"
6 #include "display_list/dl_sampling_options.h"
7 #include "display_list/dl_tile_mode.h"
8 #include "display_list/effects/dl_color_filter.h"
9 #include "display_list/effects/dl_color_source.h"
10 #include "display_list/effects/dl_image_filter.h"
11 #include "display_list/effects/dl_mask_filter.h"
12 #include "flutter/impeller/display_list/aiks_unittests.h"
13
14 #include "flutter/display_list/dl_blend_mode.h"
15 #include "flutter/display_list/dl_builder.h"
16 #include "flutter/display_list/dl_color.h"
17 #include "flutter/display_list/dl_paint.h"
18 #include "flutter/display_list/geometry/dl_path_builder.h"
19 #include "flutter/impeller/display_list/dl_image_impeller.h"
20 #include "flutter/impeller/geometry/scalar.h"
21 #include "flutter/testing/display_list_testing.h"
22 #include "flutter/testing/testing.h"
23 #include "impeller/playground/widgets.h"
24
25 namespace impeller {
26 namespace testing {
27
28 using namespace flutter;
29
30 TEST_P(AiksTest, CanRenderColoredRect) {
31 DisplayListBuilder builder;
32 DlPaint paint;
33 paint.setColor(DlColor::kBlue());
34 builder.DrawPath(DlPath::MakeRectXYWH(100.0f, 100.0f, 100.0f, 100.0f), paint);
35 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
36 }
37
38 TEST_P(AiksTest, CanRenderImage) {
39 DisplayListBuilder builder;
40 DlPaint paint;
41 paint.setColor(DlColor::kRed());
42 auto image = DlImageImpeller::Make(CreateTextureForFixture("kalimba.jpg"));
43 builder.DrawImage(image, DlPoint(100.0, 100.0),
44 DlImageSampling::kNearestNeighbor, &paint);
45 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
46 }
47
48 TEST_P(AiksTest, CanRenderInvertedImageWithColorFilter) {
49 DisplayListBuilder builder;
50 DlPaint paint;
51 paint.setColor(DlColor::kRed());
52 paint.setColorFilter(
53 DlColorFilter::MakeBlend(DlColor::kYellow(), DlBlendMode::kSrcOver));
54 paint.setInvertColors(true);
55 auto image = DlImageImpeller::Make(CreateTextureForFixture("kalimba.jpg"));
56
57 builder.DrawImage(image, DlPoint(100.0, 100.0),
58 DlImageSampling::kNearestNeighbor, &paint);
59 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
60 }
61
62 TEST_P(AiksTest, CanRenderColorFilterWithInvertColors) {
63 DisplayListBuilder builder;
64 DlPaint paint;
65 paint.setColor(DlColor::kRed());
66 paint.setColorFilter(
67 DlColorFilter::MakeBlend(DlColor::kYellow(), DlBlendMode::kSrcOver));
68 paint.setInvertColors(true);
69
70 builder.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), paint);
71 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
72 }
73
74 TEST_P(AiksTest, CanRenderColorFilterWithInvertColorsDrawPaint) {
75 DisplayListBuilder builder;
76 DlPaint paint;
77 paint.setColor(DlColor::kRed());
78 paint.setColorFilter(
79 DlColorFilter::MakeBlend(DlColor::kYellow(), DlBlendMode::kSrcOver));
80 paint.setInvertColors(true);
81
82 builder.DrawPaint(paint);
83 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
84 }
85
86 namespace {
87 bool GenerateMipmap(const std::shared_ptr<Context>& context,
88 std::shared_ptr<Texture> texture,
89 std::string_view label) {
90 auto buffer = context->CreateCommandBuffer();
91 if (!buffer) {
92 return false;
93 }
94 auto pass = buffer->CreateBlitPass();
95 if (!pass) {
96 return false;
97 }
98 pass->GenerateMipmap(std::move(texture), label);
99
100 pass->EncodeCommands();
101 return context->GetCommandQueue()->Submit({buffer}).ok();
102 }
103
104 void CanRenderTiledTexture(AiksTest* aiks_test,
105 DlTileMode tile_mode,
106 Matrix local_matrix = {}) {
107 auto context = aiks_test->GetContext();
108 ASSERT_TRUE(context);
109 auto texture = aiks_test->CreateTextureForFixture("table_mountain_nx.png",
110 /*enable_mipmapping=*/true);
111 GenerateMipmap(context, texture, "table_mountain_nx");
112 auto image = DlImageImpeller::Make(texture);
113 auto color_source = DlColorSource::MakeImage(
114 image, tile_mode, tile_mode, DlImageSampling::kNearestNeighbor,
115 &local_matrix);
116
117 DisplayListBuilder builder;
118 DlPaint paint;
119 paint.setColor(DlColor::kWhite());
120 paint.setColorSource(color_source);
121
122 builder.Scale(aiks_test->GetContentScale().x, aiks_test->GetContentScale().y);
123 builder.Translate(100.0f, 100.0f);
124 builder.DrawRect(DlRect::MakeXYWH(0, 0, 600, 600), paint);
125
126 // Should not change the image.
127 constexpr auto stroke_width = 64;
128 paint.setDrawStyle(DlDrawStyle::kStroke);
129 paint.setStrokeWidth(stroke_width);
130 if (tile_mode == DlTileMode::kDecal) {
131 builder.DrawRect(DlRect::MakeXYWH(stroke_width, stroke_width, 600, 600),
132 paint);
133 } else {
134 builder.DrawRect(DlRect::MakeXYWH(0, 0, 600, 600), paint);
135 }
136
137 {
138 // Should not change the image.
139 DlPathBuilder path_builder;
140 path_builder.AddCircle(DlPoint(150, 150), 150);
141 path_builder.AddRoundRect(
142 RoundRect::MakeRectXY(DlRect::MakeLTRB(300, 300, 600, 600), 10, 10));
143 DlPath path = path_builder.TakePath();
144
145 // Make sure path cannot be simplified...
146 EXPECT_FALSE(path.IsRect(nullptr));
147 EXPECT_FALSE(path.IsOval(nullptr));
148 EXPECT_FALSE(path.IsRoundRect(nullptr));
149
150 // Make sure path will not trigger the optimal convex code
151 EXPECT_FALSE(path.IsConvex());
152
153 paint.setDrawStyle(DlDrawStyle::kFill);
154 builder.DrawPath(path, paint);
155 }
156
157 {
158 // Should not change the image. Tests the Convex short-cut code.
159 DlPath circle = DlPath::MakeCircle(DlPoint(150, 450), 150);
160
161 // Unfortunately, the circle path can be simplified...
162 EXPECT_TRUE(circle.IsOval(nullptr));
163 // At least it's convex, though...
164 EXPECT_TRUE(circle.IsConvex());
165
166 // Let's make a copy that doesn't remember that it's just a circle...
167 DlPathBuilder path_builder;
168 // This moveTo confuses addPath into appending rather than replacing,
169 // which prevents it from noticing that it's just a circle...
170 path_builder.MoveTo({10, 10});
171 path_builder.AddPath(circle);
172 DlPath path = path_builder.TakePath();
173
174 // Make sure path cannot be simplified...
175 EXPECT_FALSE(path.IsRect(nullptr));
176 EXPECT_FALSE(path.IsOval(nullptr));
177 EXPECT_FALSE(path.IsRoundRect(nullptr));
178
179 // But check that we will trigger the optimal convex code
180 EXPECT_TRUE(path.IsConvex());
181
182 paint.setDrawStyle(DlDrawStyle::kFill);
183 builder.DrawPath(path, paint);
184 }
185
186 ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build()));
187 }
188 } // namespace
189
190 TEST_P(AiksTest, CanRenderTiledTextureClamp) {
191 CanRenderTiledTexture(this, DlTileMode::kClamp);
192 }
193
194 TEST_P(AiksTest, CanRenderTiledTextureRepeat) {
195 CanRenderTiledTexture(this, DlTileMode::kRepeat);
196 }
197
198 TEST_P(AiksTest, CanRenderTiledTextureMirror) {
199 CanRenderTiledTexture(this, DlTileMode::kMirror);
200 }
201
202 TEST_P(AiksTest, CanRenderTiledTextureDecal) {
203 CanRenderTiledTexture(this, DlTileMode::kDecal);
204 }
205
206 TEST_P(AiksTest, CanRenderTiledTextureClampWithTranslate) {
207 CanRenderTiledTexture(this, DlTileMode::kClamp,
208 Matrix::MakeTranslation({172.f, 172.f, 0.f}));
209 }
210
211 TEST_P(AiksTest, CanRenderImageRect) {
212 DisplayListBuilder builder;
213 auto image = DlImageImpeller::Make(CreateTextureForFixture("kalimba.jpg"));
214
215 DlISize image_half_size =
216 DlISize(image->GetSize().width * 0.5f, image->GetSize().height * 0.5f);
217
218 // Render the bottom right quarter of the source image in a stretched rect.
219 auto source_rect = DlRect::MakeSize(image_half_size);
220 source_rect =
221 source_rect.Shift(image_half_size.width, image_half_size.height);
222
223 builder.DrawImageRect(image, source_rect,
224 DlRect::MakeXYWH(100, 100, 600, 600),
225 DlImageSampling::kNearestNeighbor);
226 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
227 }
228
229 TEST_P(AiksTest, DrawImageRectSrcOutsideBounds) {
230 DisplayListBuilder builder;
231 auto image = DlImageImpeller::Make(CreateTextureForFixture("kalimba.jpg"));
232
233 // Use a source rect that is partially outside the bounds of the image.
234 auto source_rect = DlRect::MakeXYWH(
235 image->GetSize().width * 0.25f, image->GetSize().height * 0.4f,
236 image->GetSize().width, image->GetSize().height);
237
238 auto dest_rect = DlRect::MakeXYWH(100, 100, 600, 600);
239
240 DlPaint paint;
241 paint.setColor(DlColor::kMidGrey());
242 builder.DrawRect(dest_rect, paint);
243
244 builder.DrawImageRect(image, source_rect, dest_rect,
245 DlImageSampling::kNearestNeighbor);
246 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
247 }
248
249 TEST_P(AiksTest, CanRenderSimpleClips) {
250 DisplayListBuilder builder;
251 builder.Scale(GetContentScale().x, GetContentScale().y);
252 DlPaint paint;
253
254 paint.setColor(DlColor::kWhite());
255 builder.DrawPaint(paint);
256
257 auto draw = [&builder](const DlPaint& paint, Scalar x, Scalar y) {
258 builder.Save();
259 builder.Translate(x, y);
260 {
261 builder.Save();
262 builder.ClipRect(DlRect::MakeLTRB(50, 50, 150, 150));
263 builder.DrawPaint(paint);
264 builder.Restore();
265 }
266 {
267 builder.Save();
268 builder.ClipOval(DlRect::MakeLTRB(200, 50, 300, 150));
269 builder.DrawPaint(paint);
270 builder.Restore();
271 }
272 {
273 builder.Save();
274 builder.ClipRoundRect(
275 DlRoundRect::MakeRectXY(DlRect::MakeLTRB(50, 200, 150, 300), 20, 20));
276 builder.DrawPaint(paint);
277 builder.Restore();
278 }
279 {
280 builder.Save();
281 builder.ClipRoundRect(DlRoundRect::MakeRectXY(
282 DlRect::MakeLTRB(200, 230, 300, 270), 20, 20));
283 builder.DrawPaint(paint);
284 builder.Restore();
285 }
286 {
287 builder.Save();
288 builder.ClipRoundRect(DlRoundRect::MakeRectXY(
289 DlRect::MakeLTRB(230, 200, 270, 300), 20, 20));
290 builder.DrawPaint(paint);
291 builder.Restore();
292 }
293 builder.Restore();
294 };
295
296 paint.setColor(DlColor::kBlue());
297 draw(paint, 0, 0);
298
299 DlColor gradient_colors[7] = {
300 DlColor::RGBA(0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0),
301 DlColor::RGBA(0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0),
302 DlColor::RGBA(0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0),
303 DlColor::RGBA(0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0),
304 DlColor::RGBA(0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0),
305 DlColor::RGBA(0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0),
306 DlColor::RGBA(0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0),
307 };
308 Scalar stops[7] = {
309 0.0,
310 (1.0 / 6.0) * 1,
311 (1.0 / 6.0) * 2,
312 (1.0 / 6.0) * 3,
313 (1.0 / 6.0) * 4,
314 (1.0 / 6.0) * 5,
315 1.0,
316 };
317 auto texture = CreateTextureForFixture("airplane.jpg",
318 /*enable_mipmapping=*/true);
319 auto image = DlImageImpeller::Make(texture);
320
321 paint.setColorSource(DlColorSource::MakeRadial(
322 DlPoint(500, 600), 75, 7, gradient_colors, stops, DlTileMode::kMirror));
323 draw(paint, 0, 300);
324
325 paint.setColorSource(
326 DlColorSource::MakeImage(image, DlTileMode::kRepeat, DlTileMode::kRepeat,
327 DlImageSampling::kNearestNeighbor));
328 draw(paint, 300, 0);
329
330 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
331 }
332
333 TEST_P(AiksTest, CanSaveLayerStandalone) {
334 DisplayListBuilder builder;
335
336 DlPaint red;
337 red.setColor(DlColor::kRed());
338
339 DlPaint alpha;
340 alpha.setColor(DlColor::kRed().modulateOpacity(0.5));
341
342 builder.SaveLayer(std::nullopt, &alpha);
343
344 builder.DrawCircle(DlPoint(125, 125), 125, red);
345
346 builder.Restore();
347
348 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
349 }
350
351 TEST_P(AiksTest, CanRenderDifferentShapesWithSameColorSource) {
352 DisplayListBuilder builder;
353 DlPaint paint;
354
355 DlColor colors[2] = {
356 DlColor::RGBA(0.9568, 0.2627, 0.2118, 1.0),
357 DlColor::RGBA(0.1294, 0.5882, 0.9529, 1.0),
358 };
359 DlScalar stops[2] = {
360 0.0,
361 1.0,
362 };
363
364 paint.setColorSource(DlColorSource::MakeLinear(
365 /*start_point=*/DlPoint(0, 0), //
366 /*end_point=*/DlPoint(100, 100), //
367 /*stop_count=*/2, //
368 /*colors=*/colors, //
369 /*stops=*/stops, //
370 /*tile_mode=*/DlTileMode::kRepeat //
371 ));
372
373 builder.Save();
374 builder.Translate(100, 100);
375 builder.DrawRect(DlRect::MakeXYWH(0, 0, 200, 200), paint);
376 builder.Restore();
377
378 builder.Save();
379 builder.Translate(100, 400);
380 builder.DrawCircle(DlPoint(100, 100), 100, paint);
381 builder.Restore();
382 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
383 }
384
385 TEST_P(AiksTest, CanRenderRoundedRectWithNonUniformRadii) {
386 DisplayListBuilder builder;
387 DlPaint paint;
388 paint.setColor(DlColor::kRed());
389
390 RoundingRadii radii = {
391 .top_left = DlSize(50, 25),
392 .top_right = DlSize(25, 50),
393 .bottom_left = DlSize(25, 50),
394 .bottom_right = DlSize(50, 25),
395 };
396 DlRoundRect rrect =
397 DlRoundRect::MakeRectRadii(DlRect::MakeXYWH(100, 100, 500, 500), radii);
398
399 builder.DrawRoundRect(rrect, paint);
400
401 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
402 }
403
404 TEST_P(AiksTest, CanDrawPaint) {
405 auto medium_turquoise =
406 DlColor::RGBA(72.0f / 255.0f, 209.0f / 255.0f, 204.0f / 255.0f, 1.0f);
407
408 DisplayListBuilder builder;
409 builder.Scale(0.2, 0.2);
410 builder.DrawPaint(DlPaint().setColor(medium_turquoise));
411 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
412 }
413
414 TEST_P(AiksTest, CanDrawPaintMultipleTimes) {
415 auto medium_turquoise =
416 DlColor::RGBA(72.0f / 255.0f, 209.0f / 255.0f, 204.0f / 255.0f, 1.0f);
417 auto orange_red =
418 DlColor::RGBA(255.0f / 255.0f, 69.0f / 255.0f, 0.0f / 255.0f, 1.0f);
419
420 DisplayListBuilder builder;
421 builder.Scale(0.2, 0.2);
422 builder.DrawPaint(DlPaint().setColor(medium_turquoise));
423 builder.DrawPaint(DlPaint().setColor(orange_red.modulateOpacity(0.5f)));
424 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
425 }
426
427 TEST_P(AiksTest, StrokedRectsRenderCorrectly) {
428 DisplayListBuilder builder;
429 builder.Scale(GetContentScale().x, GetContentScale().y);
430
431 DlPaint paint;
432 paint.setColor(DlColor::kPurple());
433 paint.setDrawStyle(DlDrawStyle::kStroke);
434 paint.setStrokeWidth(20.0f);
435
436 DlPaint thin_paint = paint;
437 thin_paint.setColor(DlColor::kYellow());
438 thin_paint.setStrokeWidth(0.0f);
439
440 DlRect rect = DlRect::MakeLTRB(10, 10, 90, 90);
441 DlRect thin_tall_rect = DlRect::MakeLTRB(120, 10, 120, 90);
442 DlRect thin_wide_rect = DlRect::MakeLTRB(10, 120, 90, 120);
443 DlRect empty_rect = DlRect::MakeLTRB(120, 120, 120, 120);
444
445 // We draw the following sets of rectangles:
446 //
447 // A E X
448 // X
449 // B F X
450 // X
451 // C D G H X
452 //
453 // Purple A,B,C,D are all drawn with stroke width 20 (non-overflowing).
454 // Each of those sets has 4 rectangles of dimension 80x80, 80x0, 0x80,
455 // and 0,0 to demonstrate the basic behavior and also the behavior of
456 // empty dimensions.
457 //
458 // Blue E,F,G,H are the same 80x80 rectangles, but with an overflowing
459 // stroke width of 120 to show the behavior with degenerately large
460 // stroke widths.
461 //
462 // A,E are drawn with Bevel joins.
463 // B,F are drawn with Round joins.
464 // C,G are drawn with Miter joins and a large enough miter limit.
465 // D,H are drawn with Miter joins and a too small miter limit (== Bevel).
466 //
467 // All orange X rectangles are drawn with round joins and increasing stroke
468 // widths to demonstrate fidelity of the rounding code at various arc sizes.
469 // These X rectangles also help test that the variable sizing estimates in
470 // the round join code are accurate.
471
472 // rects (A)
473 paint.setStrokeJoin(DlStrokeJoin::kBevel);
474 builder.DrawRect(rect.Shift({100, 100}), paint);
475 builder.DrawRect(rect.Shift({100, 100}), thin_paint);
476 builder.DrawRect(thin_tall_rect.Shift({100, 100}), paint);
477 builder.DrawRect(thin_tall_rect.Shift({100, 100}), thin_paint);
478 builder.DrawRect(thin_wide_rect.Shift({100, 100}), paint);
479 builder.DrawRect(thin_wide_rect.Shift({100, 100}), thin_paint);
480 builder.DrawRect(empty_rect.Shift({100, 100}), paint);
481 builder.DrawRect(empty_rect.Shift({100, 100}), thin_paint);
482
483 // rects (B)
484 paint.setStrokeJoin(DlStrokeJoin::kRound);
485 builder.DrawRect(rect.Shift({100, 300}), paint);
486 builder.DrawRect(rect.Shift({100, 300}), thin_paint);
487 builder.DrawRect(thin_tall_rect.Shift({100, 300}), paint);
488 builder.DrawRect(thin_tall_rect.Shift({100, 300}), thin_paint);
489 builder.DrawRect(thin_wide_rect.Shift({100, 300}), paint);
490 builder.DrawRect(thin_wide_rect.Shift({100, 300}), thin_paint);
491 builder.DrawRect(empty_rect.Shift({100, 300}), paint);
492 builder.DrawRect(empty_rect.Shift({100, 300}), thin_paint);
493
494 // rects (C)
495 paint.setStrokeJoin(DlStrokeJoin::kMiter);
496 paint.setStrokeMiter(kSqrt2 + flutter::kEhCloseEnough);
497 builder.DrawRect(rect.Shift({100, 500}), paint);
498 builder.DrawRect(rect.Shift({100, 500}), thin_paint);
499 builder.DrawRect(thin_tall_rect.Shift({100, 500}), paint);
500 builder.DrawRect(thin_tall_rect.Shift({100, 500}), thin_paint);
501 builder.DrawRect(thin_wide_rect.Shift({100, 500}), paint);
502 builder.DrawRect(thin_wide_rect.Shift({100, 500}), thin_paint);
503 builder.DrawRect(empty_rect.Shift({100, 500}), paint);
504 builder.DrawRect(empty_rect.Shift({100, 500}), thin_paint);
505
506 // rects (D)
507 paint.setStrokeJoin(DlStrokeJoin::kMiter);
508 paint.setStrokeMiter(kSqrt2 - flutter::kEhCloseEnough);
509 builder.DrawRect(rect.Shift({300, 500}), paint);
510 builder.DrawRect(rect.Shift({300, 500}), thin_paint);
511 builder.DrawRect(thin_tall_rect.Shift({300, 500}), paint);
512 builder.DrawRect(thin_tall_rect.Shift({300, 500}), thin_paint);
513 builder.DrawRect(thin_wide_rect.Shift({300, 500}), paint);
514 builder.DrawRect(thin_wide_rect.Shift({300, 500}), thin_paint);
515 builder.DrawRect(empty_rect.Shift({300, 500}), paint);
516 builder.DrawRect(empty_rect.Shift({300, 500}), thin_paint);
517
518 paint.setStrokeWidth(120.0f);
519 paint.setColor(DlColor::kBlue());
520 rect = rect.Expand(-20);
521
522 // rect (E)
523 paint.setStrokeJoin(DlStrokeJoin::kBevel);
524 builder.DrawRect(rect.Shift({500, 100}), paint);
525 builder.DrawRect(rect.Shift({500, 100}), thin_paint);
526
527 // rect (F)
528 paint.setStrokeJoin(DlStrokeJoin::kRound);
529 builder.DrawRect(rect.Shift({500, 300}), paint);
530 builder.DrawRect(rect.Shift({500, 300}), thin_paint);
531
532 // rect (G)
533 paint.setStrokeJoin(DlStrokeJoin::kMiter);
534 paint.setStrokeMiter(kSqrt2 + flutter::kEhCloseEnough);
535 builder.DrawRect(rect.Shift({500, 500}), paint);
536 builder.DrawRect(rect.Shift({500, 500}), thin_paint);
537
538 // rect (H)
539 paint.setStrokeJoin(DlStrokeJoin::kMiter);
540 paint.setStrokeMiter(kSqrt2 - flutter::kEhCloseEnough);
541 builder.DrawRect(rect.Shift({700, 500}), paint);
542 builder.DrawRect(rect.Shift({700, 500}), thin_paint);
543
544 DlPaint round_mock_paint;
545 round_mock_paint.setColor(DlColor::kGreen());
546 round_mock_paint.setDrawStyle(DlDrawStyle::kFill);
547
548 // array of rects (X)
549 Scalar x = 900;
550 Scalar y = 50;
551 for (int i = 0; i < 15; i++) {
552 paint.setStrokeWidth(i);
553 paint.setColor(DlColor::kOrange());
554 paint.setStrokeJoin(DlStrokeJoin::kRound);
555 builder.DrawRect(DlRect::MakeXYWH(x, y, 30, 30), paint);
556 y += 32 + i;
557 }
558
559 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
560 }
561
562 TEST_P(AiksTest, FilledCirclesRenderCorrectly) {
563 DisplayListBuilder builder;
564 builder.Scale(GetContentScale().x, GetContentScale().y);
565 DlPaint paint;
566 const int color_count = 3;
567 DlColor colors[color_count] = {
568 DlColor::kBlue(),
569 DlColor::kGreen(),
570 DlColor::RGBA(220.0f / 255.0f, 20.0f / 255.0f, 60.0f / 255.0f, 1.0f),
571 };
572
573 paint.setColor(DlColor::kWhite());
574 builder.DrawPaint(paint);
575
576 int c_index = 0;
577 int radius = 600;
578 while (radius > 0) {
579 paint.setColor(colors[(c_index++) % color_count]);
580 builder.DrawCircle(DlPoint(10, 10), radius, paint);
581 if (radius > 30) {
582 radius -= 10;
583 } else {
584 radius -= 2;
585 }
586 }
587
588 DlColor gradient_colors[7] = {
589 DlColor::RGBA(0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0),
590 DlColor::RGBA(0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0),
591 DlColor::RGBA(0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0),
592 DlColor::RGBA(0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0),
593 DlColor::RGBA(0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0),
594 DlColor::RGBA(0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0),
595 DlColor::RGBA(0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0),
596 };
597 DlScalar stops[7] = {
598 0.0,
599 (1.0 / 6.0) * 1,
600 (1.0 / 6.0) * 2,
601 (1.0 / 6.0) * 3,
602 (1.0 / 6.0) * 4,
603 (1.0 / 6.0) * 5,
604 1.0,
605 };
606 auto texture = CreateTextureForFixture("airplane.jpg",
607 /*enable_mipmapping=*/true);
608 auto image = DlImageImpeller::Make(texture);
609
610 paint.setColorSource(DlColorSource::MakeRadial(
611 DlPoint(500, 600), 75, 7, gradient_colors, stops, DlTileMode::kMirror));
612 builder.DrawCircle(DlPoint(500, 600), 100, paint);
613
614 DlMatrix local_matrix = DlMatrix::MakeTranslation({700, 200});
615 paint.setColorSource(DlColorSource::MakeImage(
616 image, DlTileMode::kRepeat, DlTileMode::kRepeat,
617 DlImageSampling::kNearestNeighbor, &local_matrix));
618 builder.DrawCircle(DlPoint(800, 300), 100, paint);
619
620 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
621 }
622
623 TEST_P(AiksTest, StrokedCirclesRenderCorrectly) {
624 DisplayListBuilder builder;
625 builder.Scale(GetContentScale().x, GetContentScale().y);
626 DlPaint paint;
627 const int color_count = 3;
628 DlColor colors[color_count] = {
629 DlColor::kBlue(),
630 DlColor::kGreen(),
631 DlColor::RGBA(220.0f / 255.0f, 20.0f / 255.0f, 60.0f / 255.0f, 1.0f),
632 };
633
634 paint.setColor(DlColor::kWhite());
635 builder.DrawPaint(paint);
636
637 int c_index = 0;
638
639 auto draw = [&paint, &colors, &c_index](DlCanvas& canvas, DlPoint center,
640 Scalar r, Scalar dr, int n) {
641 for (int i = 0; i < n; i++) {
642 paint.setColor(colors[(c_index++) % color_count]);
643 canvas.DrawCircle(center, r, paint);
644 r += dr;
645 }
646 };
647
648 paint.setDrawStyle(DlDrawStyle::kStroke);
649 paint.setStrokeWidth(1);
650 draw(builder, DlPoint(10, 10), 2, 2, 14); // r = [2, 28], covers [1,29]
651 paint.setStrokeWidth(5);
652 draw(builder, DlPoint(10, 10), 35, 10, 56); // r = [35, 585], covers [30,590]
653
654 DlColor gradient_colors[7] = {
655 DlColor::RGBA(0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0),
656 DlColor::RGBA(0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0),
657 DlColor::RGBA(0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0),
658 DlColor::RGBA(0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0),
659 DlColor::RGBA(0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0),
660 DlColor::RGBA(0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0),
661 DlColor::RGBA(0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0),
662 };
663 DlScalar stops[7] = {
664 0.0,
665 (1.0 / 6.0) * 1,
666 (1.0 / 6.0) * 2,
667 (1.0 / 6.0) * 3,
668 (1.0 / 6.0) * 4,
669 (1.0 / 6.0) * 5,
670 1.0,
671 };
672 auto texture = CreateTextureForFixture("airplane.jpg",
673 /*enable_mipmapping=*/true);
674 auto image = DlImageImpeller::Make(texture);
675
676 paint.setColorSource(DlColorSource::MakeRadial(
677 DlPoint(500, 600), 75, 7, gradient_colors, stops, DlTileMode::kMirror));
678 draw(builder, DlPoint(500, 600), 5, 10, 10);
679
680 DlMatrix local_matrix = DlMatrix::MakeTranslation({700, 200});
681 paint.setColorSource(DlColorSource::MakeImage(
682 image, DlTileMode::kRepeat, DlTileMode::kRepeat,
683 DlImageSampling::kNearestNeighbor, &local_matrix));
684 draw(builder, DlPoint(800, 300), 5, 10, 10);
685
686 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
687 }
688
689 TEST_P(AiksTest, FilledEllipsesRenderCorrectly) {
690 DisplayListBuilder builder;
691 builder.Scale(GetContentScale().x, GetContentScale().y);
692 DlPaint paint;
693 const int color_count = 3;
694 DlColor colors[color_count] = {
695 DlColor::kBlue(),
696 DlColor::kGreen(),
697 DlColor::RGBA(220.0f / 255.0f, 20.0f / 255.0f, 60.0f / 255.0f, 1.0f),
698 };
699
700 paint.setColor(DlColor::kWhite());
701 builder.DrawPaint(paint);
702
703 int c_index = 0;
704 int long_radius = 600;
705 int short_radius = 600;
706 while (long_radius > 0 && short_radius > 0) {
707 paint.setColor(colors[(c_index++) % color_count]);
708 builder.DrawOval(DlRect::MakeXYWH(10 - long_radius, 10 - short_radius,
709 long_radius * 2, short_radius * 2),
710 paint);
711 builder.DrawOval(DlRect::MakeXYWH(1000 - short_radius, 750 - long_radius,
712 short_radius * 2, long_radius * 2),
713 paint);
714 if (short_radius > 30) {
715 short_radius -= 10;
716 long_radius -= 5;
717 } else {
718 short_radius -= 2;
719 long_radius -= 1;
720 }
721 }
722
723 DlColor gradient_colors[7] = {
724 DlColor::RGBA(0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0),
725 DlColor::RGBA(0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0),
726 DlColor::RGBA(0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0),
727 DlColor::RGBA(0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0),
728 DlColor::RGBA(0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0),
729 DlColor::RGBA(0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0),
730 DlColor::RGBA(0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0),
731 };
732 DlScalar stops[7] = {
733 0.0,
734 (1.0 / 6.0) * 1,
735 (1.0 / 6.0) * 2,
736 (1.0 / 6.0) * 3,
737 (1.0 / 6.0) * 4,
738 (1.0 / 6.0) * 5,
739 1.0,
740 };
741 auto texture = CreateTextureForFixture("airplane.jpg",
742 /*enable_mipmapping=*/true);
743 auto image = DlImageImpeller::Make(texture);
744
745 paint.setColor(DlColor::kWhite().modulateOpacity(0.5));
746
747 paint.setColorSource(DlColorSource::MakeRadial(
748 DlPoint(300, 650), 75, 7, gradient_colors, stops, DlTileMode::kMirror));
749 builder.DrawOval(DlRect::MakeXYWH(200, 625, 200, 50), paint);
750 builder.DrawOval(DlRect::MakeXYWH(275, 550, 50, 200), paint);
751
752 DlMatrix local_matrix = DlMatrix::MakeTranslation({610, 15});
753 paint.setColorSource(DlColorSource::MakeImage(
754 image, DlTileMode::kRepeat, DlTileMode::kRepeat,
755 DlImageSampling::kNearestNeighbor, &local_matrix));
756 builder.DrawOval(DlRect::MakeXYWH(610, 90, 200, 50), paint);
757 builder.DrawOval(DlRect::MakeXYWH(685, 15, 50, 200), paint);
758
759 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
760 }
761
762 namespace {
763 struct ArcFarmOptions {
764 bool use_center = false;
765 bool full_circles = false;
766 bool sweeps_over_360 = false;
767 Scalar vertical_scale = 1.0f;
768 };
769
770 void RenderArcFarm(DisplayListBuilder& builder,
771 const DlPaint& paint,
772 const ArcFarmOptions& opts) {
773 builder.Save();
774 builder.Translate(50, 50);
775 const Rect arc_bounds = Rect::MakeLTRB(0, 0, 42, 42 * opts.vertical_scale);
776 const int sweep_limit = opts.sweeps_over_360 ? 420 : 360;
777 for (int start = 0; start <= 360; start += 30) {
778 builder.Save();
779 for (int sweep = 30; sweep <= sweep_limit; sweep += 30) {
780 builder.DrawArc(arc_bounds, start, opts.full_circles ? 360 : sweep,
781 opts.use_center, paint);
782 builder.Translate(50, 0);
783 }
784 builder.Restore();
785 builder.Translate(0, 50);
786 }
787 builder.Restore();
788 }
789 } // namespace
790
791 TEST_P(AiksTest, FilledArcsRenderCorrectly) {
792 DisplayListBuilder builder;
793 builder.Scale(GetContentScale().x, GetContentScale().y);
794 builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
795
796 DlPaint paint;
797 paint.setColor(DlColor::kBlue());
798
799 RenderArcFarm(builder, paint,
800 {
801 .use_center = false,
802 .full_circles = false,
803 });
804
805 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
806 }
807
808 TEST_P(AiksTest, FilledArcsRenderCorrectlyWithCenter) {
809 DisplayListBuilder builder;
810 builder.Scale(GetContentScale().x, GetContentScale().y);
811 builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
812
813 DlPaint paint;
814 paint.setColor(DlColor::kBlue());
815
816 RenderArcFarm(builder, paint,
817 {
818 .use_center = true,
819 .full_circles = false,
820 });
821
822 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
823 }
824
825 TEST_P(AiksTest, NonSquareFilledArcsRenderCorrectly) {
826 DisplayListBuilder builder;
827 builder.Scale(GetContentScale().x, GetContentScale().y);
828 builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
829
830 DlPaint paint;
831 paint.setColor(DlColor::kBlue());
832
833 RenderArcFarm(builder, paint,
834 {
835 .use_center = false,
836 .full_circles = false,
837 .vertical_scale = 0.8f,
838 });
839
840 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
841 }
842
843 TEST_P(AiksTest, NonSquareFilledArcsRenderCorrectlyWithCenter) {
844 DisplayListBuilder builder;
845 builder.Scale(GetContentScale().x, GetContentScale().y);
846 builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
847
848 DlPaint paint;
849 paint.setColor(DlColor::kBlue());
850
851 RenderArcFarm(builder, paint,
852 {
853 .use_center = true,
854 .full_circles = false,
855 .vertical_scale = 0.8f,
856 });
857
858 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
859 }
860
861 TEST_P(AiksTest, StrokedArcsRenderCorrectlyWithButtEnds) {
862 DisplayListBuilder builder;
863 builder.Scale(GetContentScale().x, GetContentScale().y);
864 builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
865
866 DlPaint paint;
867 paint.setDrawStyle(DlDrawStyle::kStroke);
868 paint.setStrokeWidth(6.0f);
869 paint.setStrokeCap(DlStrokeCap::kButt);
870 paint.setColor(DlColor::kBlue());
871
872 RenderArcFarm(builder, paint,
873 {
874 .use_center = false,
875 .full_circles = false,
876 });
877
878 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
879 }
880
881 TEST_P(AiksTest, StrokedArcsRenderCorrectlyWithSquareEnds) {
882 DisplayListBuilder builder;
883 builder.Scale(GetContentScale().x, GetContentScale().y);
884 builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
885
886 DlPaint paint;
887 paint.setDrawStyle(DlDrawStyle::kStroke);
888 paint.setStrokeWidth(6.0f);
889 paint.setStrokeCap(DlStrokeCap::kSquare);
890 paint.setColor(DlColor::kBlue());
891
892 RenderArcFarm(builder, paint,
893 {
894 .use_center = false,
895 .full_circles = false,
896 });
897
898 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
899 }
900
901 TEST_P(AiksTest, StrokedArcsRenderCorrectlyWithRoundEnds) {
902 DisplayListBuilder builder;
903 builder.Scale(GetContentScale().x, GetContentScale().y);
904 builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
905
906 DlPaint paint;
907 paint.setDrawStyle(DlDrawStyle::kStroke);
908 paint.setStrokeWidth(6.0f);
909 paint.setStrokeCap(DlStrokeCap::kRound);
910 paint.setColor(DlColor::kBlue());
911
912 RenderArcFarm(builder, paint,
913 {
914 .use_center = false,
915 .full_circles = false,
916 });
917
918 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
919 }
920
921 TEST_P(AiksTest, StrokedArcsRenderCorrectlyWithBevelJoinsAndCenter) {
922 DisplayListBuilder builder;
923 builder.Scale(GetContentScale().x, GetContentScale().y);
924 builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
925
926 DlPaint paint;
927 paint.setDrawStyle(DlDrawStyle::kStroke);
928 paint.setStrokeWidth(6.0f);
929 paint.setStrokeJoin(DlStrokeJoin::kBevel);
930 paint.setColor(DlColor::kBlue());
931
932 RenderArcFarm(builder, paint,
933 {
934 .use_center = true,
935 .full_circles = false,
936 .sweeps_over_360 = true,
937 });
938
939 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
940 }
941
942 TEST_P(AiksTest, StrokedArcsRenderCorrectlyWithMiterJoinsAndCenter) {
943 DisplayListBuilder builder;
944 builder.Scale(GetContentScale().x, GetContentScale().y);
945 builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
946
947 DlPaint paint;
948 paint.setDrawStyle(DlDrawStyle::kStroke);
949 paint.setStrokeWidth(6.0f);
950 paint.setStrokeJoin(DlStrokeJoin::kMiter);
951 // Default miter of 4.0 does a miter on all of the centers, but
952 // using 3.0 will show some bevels on the widest interior angles...
953 paint.setStrokeMiter(3.0f);
954 paint.setColor(DlColor::kBlue());
955
956 RenderArcFarm(builder, paint,
957 {
958 .use_center = true,
959 .full_circles = false,
960 .sweeps_over_360 = true,
961 });
962
963 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
964 }
965
966 TEST_P(AiksTest, StrokedArcsRenderCorrectlyWithRoundJoinsAndCenter) {
967 DisplayListBuilder builder;
968 builder.Scale(GetContentScale().x, GetContentScale().y);
969 builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
970
971 DlPaint paint;
972 paint.setDrawStyle(DlDrawStyle::kStroke);
973 paint.setStrokeWidth(6.0f);
974 paint.setStrokeJoin(DlStrokeJoin::kRound);
975 paint.setColor(DlColor::kBlue());
976
977 RenderArcFarm(builder, paint,
978 {
979 .use_center = true,
980 .full_circles = false,
981 .sweeps_over_360 = true,
982 });
983
984 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
985 }
986
987 TEST_P(AiksTest, StrokedArcsRenderCorrectlyWithSquareAndButtEnds) {
988 DisplayListBuilder builder;
989 builder.Scale(GetContentScale().x, GetContentScale().y);
990 builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
991
992 DlPaint paint;
993 paint.setDrawStyle(DlDrawStyle::kStroke);
994 paint.setStrokeWidth(8.0f);
995 paint.setStrokeCap(DlStrokeCap::kSquare);
996 paint.setColor(DlColor::kRed());
997
998 RenderArcFarm(builder, paint,
999 {
1000 .use_center = false,
1001 .full_circles = false,
1002 });
1003
1004 paint.setStrokeCap(DlStrokeCap::kButt);
1005 paint.setColor(DlColor::kBlue());
1006
1007 RenderArcFarm(builder, paint,
1008 {
1009 .use_center = false,
1010 .full_circles = false,
1011 });
1012
1013 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1014 }
1015
1016 TEST_P(AiksTest, StrokedArcsRenderCorrectlyWithSquareAndButtAndRoundEnds) {
1017 DisplayListBuilder builder;
1018 builder.Scale(GetContentScale().x, GetContentScale().y);
1019 builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
1020
1021 DlPaint paint;
1022 paint.setDrawStyle(DlDrawStyle::kStroke);
1023 paint.setStrokeWidth(8.0f);
1024 paint.setStrokeCap(DlStrokeCap::kSquare);
1025 paint.setColor(DlColor::kRed());
1026
1027 RenderArcFarm(builder, paint,
1028 {
1029 .use_center = false,
1030 .full_circles = false,
1031 });
1032
1033 paint.setStrokeCap(DlStrokeCap::kRound);
1034 paint.setColor(DlColor::kGreen());
1035
1036 RenderArcFarm(builder, paint,
1037 {
1038 .use_center = false,
1039 .full_circles = false,
1040 });
1041
1042 paint.setStrokeCap(DlStrokeCap::kButt);
1043 paint.setColor(DlColor::kBlue());
1044
1045 RenderArcFarm(builder, paint,
1046 {
1047 .use_center = false,
1048 .full_circles = false,
1049 });
1050
1051 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1052 }
1053
1054 TEST_P(AiksTest, StrokedArcsCoverFullArcWithButtEnds) {
1055 // This test compares the rendering of a full circle arc against a partial
1056 // arc by drawing a one over the other in high contrast. If the partial
1057 // arc misses any pixels that were drawn by the full arc, there will be
1058 // some "pixel dirt" around the missing "erased" parts of the arcs. This
1059 // case arises while rendering a CircularProgressIndicator with a background
1060 // color where we want the rendering of the background full arc to hit the
1061 // same pixels around the edges as the partial arc that covers it.
1062 //
1063 // In this case we draw a full blue circle and then draw a partial arc
1064 // over it in the background color (white).
1065
1066 DisplayListBuilder builder;
1067 builder.Scale(GetContentScale().x, GetContentScale().y);
1068 builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
1069
1070 DlPaint paint;
1071 paint.setDrawStyle(DlDrawStyle::kStroke);
1072 paint.setStrokeWidth(6.0f);
1073 paint.setStrokeCap(DlStrokeCap::kButt);
1074 paint.setColor(DlColor::kBlue());
1075
1076 // First draw full circles in blue to establish the pixels to be erased
1077 RenderArcFarm(builder, paint,
1078 {
1079 .use_center = false,
1080 .full_circles = true,
1081 });
1082
1083 paint.setColor(DlColor::kWhite());
1084
1085 // Then draw partial arcs in white over the circles to "erase" them
1086 RenderArcFarm(builder, paint,
1087 {
1088 .use_center = false,
1089 .full_circles = false,
1090 });
1091
1092 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1093 }
1094
1095 TEST_P(AiksTest, FilledRoundRectsRenderCorrectly) {
1096 DisplayListBuilder builder;
1097 builder.Scale(GetContentScale().x, GetContentScale().y);
1098 DlPaint paint;
1099 const int color_count = 3;
1100 DlColor colors[color_count] = {
1101 DlColor::kBlue(),
1102 DlColor::kGreen(),
1103 DlColor::RGBA(220.0f / 255.0f, 20.0f / 255.0f, 60.0f / 255.0f, 1.0f),
1104 };
1105
1106 paint.setColor(DlColor::kWhite());
1107 builder.DrawPaint(paint);
1108
1109 int c_index = 0;
1110 for (int i = 0; i < 4; i++) {
1111 for (int j = 0; j < 4; j++) {
1112 paint.setColor(colors[(c_index++) % color_count]);
1113 builder.DrawRoundRect(
1114 DlRoundRect::MakeRectXY(
1115 DlRect::MakeXYWH(i * 100 + 10, j * 100 + 20, 80, 80), //
1116 i * 5 + 10, j * 5 + 10),
1117 paint);
1118 }
1119 }
1120 paint.setColor(colors[(c_index++) % color_count]);
1121 builder.DrawRoundRect(
1122 DlRoundRect::MakeRectXY(DlRect::MakeXYWH(10, 420, 380, 80), 40, 40),
1123 paint);
1124 paint.setColor(colors[(c_index++) % color_count]);
1125 builder.DrawRoundRect(
1126 DlRoundRect::MakeRectXY(DlRect::MakeXYWH(410, 20, 80, 380), 40, 40),
1127 paint);
1128
1129 DlColor gradient_colors[7] = {
1130 DlColor::RGBA(0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0),
1131 DlColor::RGBA(0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0),
1132 DlColor::RGBA(0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0),
1133 DlColor::RGBA(0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0),
1134 DlColor::RGBA(0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0),
1135 DlColor::RGBA(0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0),
1136 DlColor::RGBA(0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0),
1137 };
1138 DlScalar stops[7] = {
1139 0.0,
1140 (1.0 / 6.0) * 1,
1141 (1.0 / 6.0) * 2,
1142 (1.0 / 6.0) * 3,
1143 (1.0 / 6.0) * 4,
1144 (1.0 / 6.0) * 5,
1145 1.0,
1146 };
1147 auto texture = CreateTextureForFixture("airplane.jpg",
1148 /*enable_mipmapping=*/true);
1149 auto image = DlImageImpeller::Make(texture);
1150
1151 paint.setColor(DlColor::kWhite().modulateOpacity(0.1));
1152 paint.setColorSource(DlColorSource::MakeRadial(
1153 DlPoint(550, 550), 75, 7, gradient_colors, stops, DlTileMode::kMirror));
1154 for (int i = 1; i <= 10; i++) {
1155 int j = 11 - i;
1156 builder.DrawRoundRect(
1157 DlRoundRect::MakeRectXY(DlRect::MakeLTRB(550 - i * 20, 550 - j * 20, //
1158 550 + i * 20, 550 + j * 20),
1159 i * 10, j * 10),
1160 paint);
1161 }
1162
1163 paint.setColor(DlColor::kWhite().modulateOpacity(0.5));
1164 paint.setColorSource(DlColorSource::MakeRadial(
1165 DlPoint(200, 650), 75, 7, gradient_colors, stops, DlTileMode::kMirror));
1166 paint.setColor(DlColor::kWhite().modulateOpacity(0.5));
1167 builder.DrawRoundRect(
1168 DlRoundRect::MakeRectXY(DlRect::MakeLTRB(100, 610, 300, 690), 40, 40),
1169 paint);
1170 builder.DrawRoundRect(
1171 DlRoundRect::MakeRectXY(DlRect::MakeLTRB(160, 550, 240, 750), 40, 40),
1172 paint);
1173
1174 paint.setColor(DlColor::kWhite().modulateOpacity(0.1));
1175 DlMatrix local_matrix = DlMatrix::MakeTranslation({520, 20});
1176 paint.setColorSource(DlColorSource::MakeImage(
1177 image, DlTileMode::kRepeat, DlTileMode::kRepeat,
1178 DlImageSampling::kNearestNeighbor, &local_matrix));
1179 for (int i = 1; i <= 10; i++) {
1180 int j = 11 - i;
1181 builder.DrawRoundRect(
1182 DlRoundRect::MakeRectXY(DlRect::MakeLTRB(720 - i * 20, 220 - j * 20, //
1183 720 + i * 20, 220 + j * 20),
1184 i * 10, j * 10),
1185 paint);
1186 }
1187
1188 paint.setColor(DlColor::kWhite().modulateOpacity(0.5));
1189 local_matrix = DlMatrix::MakeTranslation({800, 300});
1190 paint.setColorSource(DlColorSource::MakeImage(
1191 image, DlTileMode::kRepeat, DlTileMode::kRepeat,
1192 DlImageSampling::kNearestNeighbor, &local_matrix));
1193 builder.DrawRoundRect(
1194 DlRoundRect::MakeRectXY(DlRect::MakeLTRB(800, 410, 1000, 490), 40, 40),
1195 paint);
1196 builder.DrawRoundRect(
1197 DlRoundRect::MakeRectXY(DlRect::MakeLTRB(860, 350, 940, 550), 40, 40),
1198 paint);
1199
1200 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1201 }
1202
1203 TEST_P(AiksTest, SolidColorCirclesOvalsRRectsMaskBlurCorrectly) {
1204 DisplayListBuilder builder;
1205 builder.Scale(GetContentScale().x, GetContentScale().y);
1206 DlPaint paint;
1207 paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 1.0f));
1208
1209 builder.DrawPaint(DlPaint().setColor(DlColor::kWhite()));
1210
1211 paint.setColor(
1212 DlColor::RGBA(220.0f / 255.0f, 20.0f / 255.0f, 60.0f / 255.0f, 1.0f));
1213 Scalar y = 100.0f;
1214 for (int i = 0; i < 5; i++) {
1215 Scalar x = (i + 1) * 100;
1216 Scalar radius = x / 10.0f;
1217 builder.DrawRect(DlRect::MakeXYWH(x + 25 - radius / 2, y + radius / 2, //
1218 radius, 60.0f - radius),
1219 paint);
1220 }
1221
1222 paint.setColor(DlColor::kBlue());
1223 y += 100.0f;
1224 for (int i = 0; i < 5; i++) {
1225 Scalar x = (i + 1) * 100;
1226 Scalar radius = x / 10.0f;
1227 builder.DrawCircle(DlPoint(x + 25, y + 25), radius, paint);
1228 }
1229
1230 paint.setColor(DlColor::kGreen());
1231 y += 100.0f;
1232 for (int i = 0; i < 5; i++) {
1233 Scalar x = (i + 1) * 100;
1234 Scalar radius = x / 10.0f;
1235 builder.DrawOval(DlRect::MakeXYWH(x + 25 - radius / 2, y + radius / 2, //
1236 radius, 60.0f - radius),
1237 paint);
1238 }
1239
1240 paint.setColor(
1241 DlColor::RGBA(128.0f / 255.0f, 0.0f / 255.0f, 128.0f / 255.0f, 1.0f));
1242 y += 100.0f;
1243 for (int i = 0; i < 5; i++) {
1244 Scalar x = (i + 1) * 100;
1245 Scalar radius = x / 20.0f;
1246 builder.DrawRoundRect(
1247 DlRoundRect::MakeRectXY(DlRect::MakeXYWH(x, y, 60.0f, 60.0f), //
1248 radius, radius),
1249 paint);
1250 }
1251
1252 paint.setColor(
1253 DlColor::RGBA(255.0f / 255.0f, 165.0f / 255.0f, 0.0f / 255.0f, 1.0f));
1254 y += 100.0f;
1255 for (int i = 0; i < 5; i++) {
1256 Scalar x = (i + 1) * 100;
1257 Scalar radius = x / 20.0f;
1258 builder.DrawRoundRect(
1259 DlRoundRect::MakeRectXY(DlRect::MakeXYWH(x, y, 60.0f, 60.0f), //
1260 radius, 5.0f),
1261 paint);
1262 }
1263
1264 auto dl = builder.Build();
1265 ASSERT_TRUE(OpenPlaygroundHere(dl));
1266 }
1267
1268 TEST_P(AiksTest, CanRenderClippedBackdropFilter) {
1269 DisplayListBuilder builder;
1270
1271 builder.Scale(GetContentScale().x, GetContentScale().y);
1272
1273 // Draw something interesting in the background.
1274 std::vector<DlColor> colors = {DlColor::RGBA(0.9568, 0.2627, 0.2118, 1.0),
1275 DlColor::RGBA(0.1294, 0.5882, 0.9529, 1.0)};
1276 std::vector<Scalar> stops = {
1277 0.0,
1278 1.0,
1279 };
1280 DlPaint paint;
1281 paint.setColorSource(DlColorSource::MakeLinear(
1282 /*start_point=*/DlPoint(0, 0), //
1283 /*end_point=*/DlPoint(100, 100), //
1284 /*stop_count=*/2, //
1285 /*colors=*/colors.data(), //
1286 /*stops=*/stops.data(), //
1287 /*tile_mode=*/DlTileMode::kRepeat //
1288 ));
1289
1290 builder.DrawPaint(paint);
1291
1292 DlRect clip_rect = DlRect::MakeLTRB(50, 50, 400, 300);
1293 DlRoundRect clip_rrect = DlRoundRect::MakeRectXY(clip_rect, 100, 100);
1294
1295 // Draw a clipped SaveLayer, where the clip coverage and SaveLayer size are
1296 // the same.
1297 builder.ClipRoundRect(clip_rrect, DlClipOp::kIntersect);
1298
1299 DlPaint save_paint;
1300 auto backdrop_filter = DlImageFilter::MakeColorFilter(
1301 DlColorFilter::MakeBlend(DlColor::kRed(), DlBlendMode::kExclusion));
1302 builder.SaveLayer(clip_rect, &save_paint, backdrop_filter.get());
1303
1304 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1305 }
1306
1307 TEST_P(AiksTest, CanDrawPerspectiveTransformWithClips) {
1308 // Avoiding `GetSecondsElapsed()` to reduce risk of golden flakiness.
1309 int time = 0;
1310 auto callback = [&]() -> sk_sp<DisplayList> {
1311 DisplayListBuilder builder;
1312
1313 builder.Save();
1314 {
1315 builder.Translate(300, 300);
1316
1317 // 1. Draw/restore a clip before drawing the image, which will get drawn
1318 // to the depth buffer behind the image.
1319 builder.Save();
1320 {
1321 DlPaint paint;
1322 paint.setColor(DlColor::kGreen());
1323 builder.DrawPaint(paint);
1324 builder.ClipRect(DlRect::MakeLTRB(-180, -180, 180, 180),
1325 DlClipOp::kDifference);
1326
1327 paint.setColor(DlColor::kBlack());
1328 builder.DrawPaint(paint);
1329 }
1330 builder.Restore(); // Restore rectangle difference clip.
1331
1332 builder.Save();
1333 {
1334 // 2. Draw an oval clip that applies to the image, which will get drawn
1335 // in front of the image on the depth buffer.
1336 builder.ClipOval(DlRect::MakeLTRB(-200, -200, 200, 200));
1337
1338 Matrix result =
1339 Matrix(1.0, 0.0, 0.0, 0.0, //
1340 0.0, 1.0, 0.0, 0.0, //
1341 0.0, 0.0, 1.0, 0.003, //
1342 0.0, 0.0, 0.0, 1.0) *
1343 Matrix::MakeRotationY({Radians{-1.0f + (time++ / 60.0f)}});
1344
1345 // 3. Draw the rotating image with a perspective transform.
1346 builder.Transform(result);
1347
1348 auto image =
1349 DlImageImpeller::Make(CreateTextureForFixture("airplane.jpg"));
1350 auto position =
1351 -DlPoint(image->GetSize().width, image->GetSize().height) * 0.5;
1352 builder.DrawImage(image, position, {});
1353 }
1354 builder.Restore(); // Restore oval intersect clip.
1355
1356 // 4. Draw a semi-translucent blue circle atop all previous draws.
1357 DlPaint paint;
1358 paint.setColor(DlColor::kBlue().modulateOpacity(0.4));
1359 builder.DrawCircle(DlPoint(), 230, paint);
1360 }
1361 builder.Restore(); // Restore translation.
1362
1363 return builder.Build();
1364 };
1365 ASSERT_TRUE(OpenPlaygroundHere(callback));
1366 }
1367
1368 TEST_P(AiksTest, ImageColorSourceEffectTransform) {
1369 // Compare with https://fiddle.skia.org/c/6cdc5aefb291fda3833b806ca347a885
1370
1371 DisplayListBuilder builder;
1372 auto texture = DlImageImpeller::Make(CreateTextureForFixture("monkey.png"));
1373
1374 DlPaint paint;
1375 paint.setColor(DlColor::kWhite());
1376 builder.DrawPaint(paint);
1377
1378 // Translation
1379 {
1380 DlMatrix matrix = DlMatrix::MakeTranslation({50, 50});
1381 DlPaint paint;
1382 paint.setColorSource(DlColorSource::MakeImage(
1383 texture, DlTileMode::kRepeat, DlTileMode::kRepeat,
1384 DlImageSampling::kNearestNeighbor, &matrix));
1385
1386 builder.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), paint);
1387 }
1388
1389 // Rotation/skew
1390 {
1391 builder.Save();
1392 builder.Rotate(45);
1393 DlPaint paint;
1394
1395 Matrix matrix(1, -1, 0, 0, //
1396 1, 1, 0, 0, //
1397 0, 0, 1, 0, //
1398 0, 0, 0, 1);
1399 paint.setColorSource(DlColorSource::MakeImage(
1400 texture, DlTileMode::kRepeat, DlTileMode::kRepeat,
1401 DlImageSampling::kNearestNeighbor, &matrix));
1402 builder.DrawRect(DlRect::MakeLTRB(100, 0, 200, 100), paint);
1403 builder.Restore();
1404 }
1405
1406 // Scale
1407 {
1408 builder.Save();
1409 builder.Translate(100, 0);
1410 builder.Scale(100, 100);
1411 DlPaint paint;
1412
1413 DlMatrix matrix = DlMatrix::MakeScale({0.005, 0.005, 1});
1414 paint.setColorSource(DlColorSource::MakeImage(
1415 texture, DlTileMode::kRepeat, DlTileMode::kRepeat,
1416 DlImageSampling::kNearestNeighbor, &matrix));
1417
1418 builder.DrawRect(DlRect::MakeLTRB(0, 0, 1, 1), paint);
1419 builder.Restore();
1420 }
1421
1422 // Perspective
1423 {
1424 builder.Save();
1425 builder.Translate(150, 150);
1426 DlPaint paint;
1427
1428 DlMatrix matrix =
1429 DlMatrix::MakePerspective(Radians{0.5}, ISize{200, 200}, 0.05, 1);
1430 paint.setColorSource(DlColorSource::MakeImage(
1431 texture, DlTileMode::kRepeat, DlTileMode::kRepeat,
1432 DlImageSampling::kNearestNeighbor, &matrix));
1433
1434 builder.DrawRect(DlRect::MakeLTRB(0, 0, 200, 200), paint);
1435 builder.Restore();
1436 }
1437
1438 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1439 }
1440
1441 TEST_P(AiksTest, SubpassWithClearColorOptimization) {
1442 DisplayListBuilder builder;
1443
1444 // Use a non-srcOver blend mode to ensure that we don't detect this as an
1445 // opacity peephole optimization.
1446 DlPaint paint;
1447 paint.setColor(DlColor::kBlue().modulateOpacity(0.5));
1448 paint.setBlendMode(DlBlendMode::kSrc);
1449
1450 DlRect bounds = DlRect::MakeLTRB(0, 0, 200, 200);
1451 builder.SaveLayer(bounds, &paint);
1452
1453 paint.setColor(DlColor::kTransparent());
1454 paint.setBlendMode(DlBlendMode::kSrc);
1455 builder.DrawPaint(paint);
1456 builder.Restore();
1457
1458 paint.setColor(DlColor::kBlue());
1459 paint.setBlendMode(DlBlendMode::kDstOver);
1460 builder.SaveLayer(std::nullopt, &paint);
1461 builder.Restore();
1462
1463 // This playground should appear blank on CI since we are only drawing
1464 // transparent black. If the clear color optimization is broken, the texture
1465 // will be filled with NaNs and may produce a magenta texture on macOS or iOS.
1466 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1467 }
1468
1469 // Render a white circle at the top left corner of the screen.
1470 TEST_P(AiksTest, MatrixImageFilterDoesntCullWhenTranslatedFromOffscreen) {
1471 DisplayListBuilder builder;
1472 builder.Scale(GetContentScale().x, GetContentScale().y);
1473 builder.Translate(100, 100);
1474 // Draw a circle in a SaveLayer at -300, but move it back on-screen with a
1475 // +300 translation applied by a SaveLayer image filter.
1476 DlPaint paint;
1477 DlMatrix translate = DlMatrix::MakeTranslation({300, 0});
1478 paint.setImageFilter(
1479 DlImageFilter::MakeMatrix(translate, DlImageSampling::kLinear));
1480 builder.SaveLayer(std::nullopt, &paint);
1481
1482 DlPaint circle_paint;
1483 circle_paint.setColor(DlColor::kGreen());
1484 builder.DrawCircle(DlPoint(-300, 0), 100, circle_paint);
1485 builder.Restore();
1486
1487 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1488 }
1489
1490 // Render a white circle at the top left corner of the screen.
1491 TEST_P(AiksTest,
1492 MatrixImageFilterDoesntCullWhenScaledAndTranslatedFromOffscreen) {
1493 DisplayListBuilder builder;
1494 builder.Scale(GetContentScale().x, GetContentScale().y);
1495 builder.Translate(100, 100);
1496 // Draw a circle in a SaveLayer at -300, but move it back on-screen with a
1497 // +300 translation applied by a SaveLayer image filter.
1498
1499 DlPaint paint;
1500 paint.setImageFilter(DlImageFilter::MakeMatrix(
1501 DlMatrix::MakeTranslation({300, 0}) * DlMatrix::MakeScale({2, 2, 1}),
1502 DlImageSampling::kNearestNeighbor));
1503 builder.SaveLayer(std::nullopt, &paint);
1504
1505 DlPaint circle_paint;
1506 circle_paint.setColor(DlColor::kGreen());
1507 builder.DrawCircle(DlPoint(-150, 0), 50, circle_paint);
1508 builder.Restore();
1509
1510 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1511 }
1512
1513 // This should be solid red, if you see a little red box this is broken.
1514 TEST_P(AiksTest, ClearColorOptimizationWhenSubpassIsBiggerThanParentPass) {
1515 SetWindowSize({400, 400});
1516 DisplayListBuilder builder;
1517
1518 builder.Scale(GetContentScale().x, GetContentScale().y);
1519
1520 DlPaint paint;
1521 paint.setColor(DlColor::kRed());
1522 builder.DrawRect(DlRect::MakeLTRB(200, 200, 300, 300), paint);
1523
1524 paint.setImageFilter(DlImageFilter::MakeMatrix(DlMatrix::MakeScale({2, 2, 1}),
1525 DlImageSampling::kLinear));
1526 builder.SaveLayer(std::nullopt, &paint);
1527 // Draw a rectangle that would fully cover the parent pass size, but not
1528 // the subpass that it is rendered in.
1529 paint.setColor(DlColor::kGreen());
1530 builder.DrawRect(DlRect::MakeLTRB(0, 0, 400, 400), paint);
1531 // Draw a bigger rectangle to force the subpass to be bigger.
1532
1533 paint.setColor(DlColor::kRed());
1534 builder.DrawRect(DlRect::MakeLTRB(0, 0, 800, 800), paint);
1535 builder.Restore();
1536
1537 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1538 }
1539
1540 TEST_P(AiksTest, EmptySaveLayerIgnoresPaint) {
1541 DisplayListBuilder builder;
1542 builder.Scale(GetContentScale().x, GetContentScale().y);
1543
1544 DlPaint paint;
1545 paint.setColor(DlColor::kRed());
1546 builder.DrawPaint(paint);
1547 builder.ClipRect(DlRect::MakeXYWH(100, 100, 200, 200));
1548 paint.setColor(DlColor::kBlue());
1549 builder.SaveLayer(std::nullopt, &paint);
1550 builder.Restore();
1551
1552 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1553 }
1554
1555 TEST_P(AiksTest, EmptySaveLayerRendersWithClear) {
1556 DisplayListBuilder builder;
1557 builder.Scale(GetContentScale().x, GetContentScale().y);
1558 auto image = DlImageImpeller::Make(CreateTextureForFixture("airplane.jpg"));
1559 builder.DrawImage(image, DlPoint(10, 10), {});
1560 builder.ClipRect(DlRect::MakeXYWH(100, 100, 200, 200));
1561
1562 DlPaint paint;
1563 paint.setBlendMode(DlBlendMode::kClear);
1564 builder.SaveLayer(std::nullopt, &paint);
1565 builder.Restore();
1566
1567 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1568 }
1569
1570 TEST_P(AiksTest,
1571 CanPerformSaveLayerWithBoundsAndLargerIntermediateIsNotAllocated) {
1572 DisplayListBuilder builder;
1573
1574 DlPaint red;
1575 red.setColor(DlColor::kRed());
1576
1577 DlPaint green;
1578 green.setColor(DlColor::kGreen());
1579
1580 DlPaint blue;
1581 blue.setColor(DlColor::kBlue());
1582
1583 DlPaint save;
1584 save.setColor(DlColor::kBlack().modulateOpacity(0.5));
1585
1586 DlRect huge_bounds = DlRect::MakeXYWH(0, 0, 100000, 100000);
1587 builder.SaveLayer(huge_bounds, &save);
1588
1589 builder.DrawRect(DlRect::MakeXYWH(0, 0, 100, 100), red);
1590 builder.DrawRect(DlRect::MakeXYWH(10, 10, 100, 100), green);
1591 builder.DrawRect(DlRect::MakeXYWH(20, 20, 100, 100), blue);
1592
1593 builder.Restore();
1594
1595 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1596 }
1597
1598 // This makes sure the WideGamut named tests use 16bit float pixel format.
1599 TEST_P(AiksTest, FormatWideGamut) {
1600 EXPECT_EQ(GetContext()->GetCapabilities()->GetDefaultColorFormat(),
1601 PixelFormat::kB10G10R10A10XR);
1602 }
1603
1604 TEST_P(AiksTest, FormatSRGB) {
1605 PixelFormat pixel_format =
1606 GetContext()->GetCapabilities()->GetDefaultColorFormat();
1607 EXPECT_TRUE(pixel_format == PixelFormat::kR8G8B8A8UNormInt ||
1608 pixel_format == PixelFormat::kB8G8R8A8UNormInt)
1609 << "pixel format: " << PixelFormatToString(pixel_format);
1610 }
1611
1612 TEST_P(AiksTest, CoordinateConversionsAreCorrect) {
1613 DisplayListBuilder builder;
1614
1615 // Render a texture directly.
1616 {
1617 auto image = DlImageImpeller::Make(CreateTextureForFixture("kalimba.jpg"));
1618
1619 builder.Save();
1620 builder.Translate(100, 200);
1621 builder.Scale(0.5, 0.5);
1622 builder.DrawImage(image, DlPoint(100.0, 100.0),
1623 DlImageSampling::kNearestNeighbor);
1624 builder.Restore();
1625 }
1626
1627 // Render an offscreen rendered texture.
1628 {
1629 DlPaint alpha;
1630 alpha.setColor(DlColor::kRed().modulateOpacity(0.5));
1631
1632 builder.SaveLayer(std::nullopt, &alpha);
1633
1634 DlPaint paint;
1635 paint.setColor(DlColor::kRed());
1636 builder.DrawRect(DlRect::MakeXYWH(000, 000, 100, 100), paint);
1637 paint.setColor(DlColor::kGreen());
1638 builder.DrawRect(DlRect::MakeXYWH(020, 020, 100, 100), paint);
1639 paint.setColor(DlColor::kBlue());
1640 builder.DrawRect(DlRect::MakeXYWH(040, 040, 100, 100), paint);
1641
1642 builder.Restore();
1643 }
1644
1645 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1646 }
1647
1648 TEST_P(AiksTest, CanPerformFullScreenMSAA) {
1649 DisplayListBuilder builder;
1650
1651 DlPaint paint;
1652 paint.setColor(DlColor::kRed());
1653 builder.DrawCircle(DlPoint(250, 250), 125, paint);
1654
1655 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1656 }
1657
1658 TEST_P(AiksTest, CanPerformSkew) {
1659 DisplayListBuilder builder;
1660
1661 DlPaint red;
1662 red.setColor(DlColor::kRed());
1663 builder.Skew(2, 5);
1664 builder.DrawRect(DlRect::MakeXYWH(0, 0, 100, 100), red);
1665
1666 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1667 }
1668
1669 TEST_P(AiksTest, CanPerformSaveLayerWithBounds) {
1670 DisplayListBuilder builder;
1671
1672 DlPaint save;
1673 save.setColor(DlColor::kBlack());
1674
1675 DlRect save_bounds = DlRect::MakeXYWH(0, 0, 50, 50);
1676 builder.SaveLayer(save_bounds, &save);
1677
1678 DlPaint paint;
1679 paint.setColor(DlColor::kRed());
1680 builder.DrawRect(DlRect::MakeXYWH(0, 0, 100, 100), paint);
1681 paint.setColor(DlColor::kGreen());
1682 builder.DrawRect(DlRect::MakeXYWH(10, 10, 100, 100), paint);
1683 paint.setColor(DlColor::kBlue());
1684 builder.DrawRect(DlRect::MakeXYWH(20, 20, 100, 100), paint);
1685
1686 builder.Restore();
1687
1688 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1689 }
1690
1691 TEST_P(AiksTest, FilledRoundRectPathsRenderCorrectly) {
1692 DisplayListBuilder builder;
1693 builder.Scale(GetContentScale().x, GetContentScale().y);
1694
1695 DlPaint paint;
1696 const int color_count = 3;
1697 DlColor colors[color_count] = {
1698 DlColor::kBlue(),
1699 DlColor::kGreen(),
1700 DlColor::ARGB(1.0, 220.0f / 255.0f, 20.0f / 255.0f, 60.0f / 255.0f),
1701 };
1702
1703 paint.setColor(DlColor::kWhite());
1704 builder.DrawPaint(paint);
1705
1706 auto draw_rrect_as_path = [&builder](const DlRect& rect, Scalar x, Scalar y,
1707 const DlPaint& paint) {
1708 builder.DrawPath(DlPath::MakeRoundRectXY(rect, x, y), paint);
1709 };
1710
1711 int c_index = 0;
1712 for (int i = 0; i < 4; i++) {
1713 for (int j = 0; j < 4; j++) {
1714 paint.setColor(colors[(c_index++) % color_count]);
1715 draw_rrect_as_path(DlRect::MakeXYWH(i * 100 + 10, j * 100 + 20, 80, 80),
1716 i * 5 + 10, j * 5 + 10, paint);
1717 }
1718 }
1719 paint.setColor(colors[(c_index++) % color_count]);
1720 draw_rrect_as_path(DlRect::MakeXYWH(10, 420, 380, 80), 40, 40, paint);
1721 paint.setColor(colors[(c_index++) % color_count]);
1722 draw_rrect_as_path(DlRect::MakeXYWH(410, 20, 80, 380), 40, 40, paint);
1723
1724 std::vector<DlColor> gradient_colors = {
1725 DlColor::RGBA(0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0),
1726 DlColor::RGBA(0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0),
1727 DlColor::RGBA(0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0),
1728 DlColor::RGBA(0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0),
1729 DlColor::RGBA(0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0),
1730 DlColor::RGBA(0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0),
1731 DlColor::RGBA(0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0)};
1732 std::vector<Scalar> stops = {
1733 0.0,
1734 (1.0 / 6.0) * 1,
1735 (1.0 / 6.0) * 2,
1736 (1.0 / 6.0) * 3,
1737 (1.0 / 6.0) * 4,
1738 (1.0 / 6.0) * 5,
1739 1.0,
1740 };
1741 auto texture = DlImageImpeller::Make(
1742 CreateTextureForFixture("airplane.jpg",
1743 /*enable_mipmapping=*/true));
1744
1745 paint.setColor(DlColor::kWhite().modulateOpacity(0.1));
1746 paint.setColorSource(DlColorSource::MakeRadial(
1747 /*center=*/DlPoint(550, 550),
1748 /*radius=*/75,
1749 /*stop_count=*/gradient_colors.size(),
1750 /*colors=*/gradient_colors.data(),
1751 /*stops=*/stops.data(),
1752 /*tile_mode=*/DlTileMode::kMirror));
1753 for (int i = 1; i <= 10; i++) {
1754 int j = 11 - i;
1755 draw_rrect_as_path(DlRect::MakeLTRB(550 - i * 20, 550 - j * 20, //
1756 550 + i * 20, 550 + j * 20),
1757 i * 10, j * 10, paint);
1758 }
1759 paint.setColor(DlColor::kWhite().modulateOpacity(0.5));
1760 paint.setColorSource(DlColorSource::MakeRadial(
1761 /*center=*/DlPoint(200, 650),
1762 /*radius=*/75,
1763 /*stop_count=*/gradient_colors.size(),
1764 /*colors=*/gradient_colors.data(),
1765 /*stops=*/stops.data(),
1766 /*tile_mode=*/DlTileMode::kMirror));
1767 draw_rrect_as_path(DlRect::MakeLTRB(100, 610, 300, 690), 40, 40, paint);
1768 draw_rrect_as_path(DlRect::MakeLTRB(160, 550, 240, 750), 40, 40, paint);
1769
1770 auto matrix = DlMatrix::MakeTranslation({520, 20});
1771 paint.setColor(DlColor::kWhite().modulateOpacity(0.1));
1772 paint.setColorSource(DlColorSource::MakeImage(
1773 texture, DlTileMode::kRepeat, DlTileMode::kRepeat,
1774 DlImageSampling::kMipmapLinear, &matrix));
1775 for (int i = 1; i <= 10; i++) {
1776 int j = 11 - i;
1777 draw_rrect_as_path(DlRect::MakeLTRB(720 - i * 20, 220 - j * 20, //
1778 720 + i * 20, 220 + j * 20),
1779 i * 10, j * 10, paint);
1780 }
1781 matrix = DlMatrix::MakeTranslation({800, 300});
1782 paint.setColor(DlColor::kWhite().modulateOpacity(0.5));
1783 paint.setColorSource(DlColorSource::MakeImage(
1784 texture, DlTileMode::kRepeat, DlTileMode::kRepeat,
1785 DlImageSampling::kMipmapLinear, &matrix));
1786
1787 draw_rrect_as_path(DlRect::MakeLTRB(800, 410, 1000, 490), 40, 40, paint);
1788 draw_rrect_as_path(DlRect::MakeLTRB(860, 350, 940, 550), 40, 40, paint);
1789
1790 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1791 }
1792
1793 TEST_P(AiksTest, CoverageOriginShouldBeAccountedForInSubpasses) {
1794 auto callback = [&]() -> sk_sp<DisplayList> {
1795 DisplayListBuilder builder;
1796 builder.Scale(GetContentScale().x, GetContentScale().y);
1797
1798 DlPaint alpha;
1799 alpha.setColor(DlColor::kRed().modulateOpacity(0.5));
1800
1801 auto current = Point{25, 25};
1802 const auto offset = Point{25, 25};
1803 const auto size = Size(100, 100);
1804
1805 static PlaygroundPoint point_a(Point(40, 40), 10, Color::White());
1806 static PlaygroundPoint point_b(Point(160, 160), 10, Color::White());
1807 auto [b0, b1] = DrawPlaygroundLine(point_a, point_b);
1808 DlRect bounds = DlRect::MakeLTRB(b0.x, b0.y, b1.x, b1.y);
1809
1810 DlPaint stroke_paint;
1811 stroke_paint.setColor(DlColor::kYellow());
1812 stroke_paint.setStrokeWidth(5);
1813 stroke_paint.setDrawStyle(DlDrawStyle::kStroke);
1814 builder.DrawRect(bounds, stroke_paint);
1815
1816 builder.SaveLayer(bounds, &alpha);
1817
1818 DlPaint paint;
1819 paint.setColor(DlColor::kRed());
1820 builder.DrawRect(
1821 DlRect::MakeXYWH(current.x, current.y, size.width, size.height), paint);
1822
1823 paint.setColor(DlColor::kGreen());
1824 current += offset;
1825 builder.DrawRect(
1826 DlRect::MakeXYWH(current.x, current.y, size.width, size.height), paint);
1827
1828 paint.setColor(DlColor::kBlue());
1829 current += offset;
1830 builder.DrawRect(
1831 DlRect::MakeXYWH(current.x, current.y, size.width, size.height), paint);
1832
1833 builder.Restore();
1834
1835 return builder.Build();
1836 };
1837
1838 ASSERT_TRUE(OpenPlaygroundHere(callback));
1839 }
1840
1841 TEST_P(AiksTest, SaveLayerDrawsBehindSubsequentEntities) {
1842 // Compare with https://fiddle.skia.org/c/9e03de8567ffb49e7e83f53b64bcf636
1843 DisplayListBuilder builder;
1844 DlPaint paint;
1845
1846 paint.setColor(DlColor::kBlack());
1847 DlRect rect = DlRect::MakeXYWH(25, 25, 25, 25);
1848 builder.DrawRect(rect, paint);
1849
1850 builder.Translate(10, 10);
1851
1852 DlPaint save_paint;
1853 builder.SaveLayer(std::nullopt, &save_paint);
1854
1855 paint.setColor(DlColor::kGreen());
1856 builder.DrawRect(rect, paint);
1857
1858 builder.Restore();
1859
1860 builder.Translate(10, 10);
1861 paint.setColor(DlColor::kRed());
1862 builder.DrawRect(rect, paint);
1863
1864 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1865 }
1866
1867 TEST_P(AiksTest, SiblingSaveLayerBoundsAreRespected) {
1868 DisplayListBuilder builder;
1869 DlPaint paint;
1870 DlRect rect = DlRect::MakeXYWH(0, 0, 1000, 1000);
1871
1872 // Black, green, and red squares offset by [10, 10].
1873 {
1874 DlPaint save_paint;
1875 DlRect bounds = DlRect::MakeXYWH(25, 25, 25, 25);
1876 builder.SaveLayer(bounds, &save_paint);
1877 paint.setColor(DlColor::kBlack());
1878 builder.DrawRect(rect, paint);
1879 builder.Restore();
1880 }
1881
1882 {
1883 DlPaint save_paint;
1884 DlRect bounds = DlRect::MakeXYWH(35, 35, 25, 25);
1885 builder.SaveLayer(bounds, &save_paint);
1886 paint.setColor(DlColor::kGreen());
1887 builder.DrawRect(rect, paint);
1888 builder.Restore();
1889 }
1890
1891 {
1892 DlPaint save_paint;
1893 DlRect bounds = DlRect::MakeXYWH(45, 45, 25, 25);
1894 builder.SaveLayer(bounds, &save_paint);
1895 paint.setColor(DlColor::kRed());
1896 builder.DrawRect(rect, paint);
1897 builder.Restore();
1898 }
1899
1900 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1901 }
1902
1903 TEST_P(AiksTest, CanRenderClippedLayers) {
1904 DisplayListBuilder builder;
1905
1906 DlPaint paint;
1907 paint.setColor(DlColor::kWhite());
1908 builder.DrawPaint(paint);
1909
1910 // Draw a green circle on the screen.
1911 {
1912 // Increase the clip depth for the savelayer to contend with.
1913 DlPath path = DlPath::MakeCircle(DlPoint(100, 100), 50);
1914 builder.ClipPath(path);
1915
1916 DlRect bounds = DlRect::MakeXYWH(50, 50, 100, 100);
1917 DlPaint save_paint;
1918 builder.SaveLayer(bounds, &save_paint);
1919
1920 // Fill the layer with white.
1921 paint.setColor(DlColor::kWhite());
1922 builder.DrawRect(DlRect::MakeSize(DlSize(400, 400)), paint);
1923 // Fill the layer with green, but do so with a color blend that can't be
1924 // collapsed into the parent pass.
1925 paint.setColor(DlColor::kGreen());
1926 paint.setBlendMode(DlBlendMode::kHardLight);
1927 builder.DrawRect(DlRect::MakeSize(DlSize(400, 400)), paint);
1928 }
1929
1930 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1931 }
1932
1933 TEST_P(AiksTest, SaveLayerFiltersScaleWithTransform) {
1934 DisplayListBuilder builder;
1935
1936 builder.Scale(GetContentScale().x, GetContentScale().y);
1937 builder.Translate(100, 100);
1938
1939 auto texture = DlImageImpeller::Make(CreateTextureForFixture("boston.jpg"));
1940 auto draw_image_layer = [&builder, &texture](const DlPaint& paint) {
1941 builder.SaveLayer(std::nullopt, &paint);
1942 builder.DrawImage(texture, DlPoint(), DlImageSampling::kLinear);
1943 builder.Restore();
1944 };
1945
1946 DlPaint effect_paint;
1947 effect_paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 6));
1948 draw_image_layer(effect_paint);
1949
1950 builder.Translate(300, 300);
1951 builder.Scale(3, 3);
1952 draw_image_layer(effect_paint);
1953
1954 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1955 }
1956
1957 TEST_P(AiksTest, FastEllipticalRRectMaskBlursRenderCorrectly) {
1958 DisplayListBuilder builder;
1959
1960 builder.Scale(GetContentScale().x, GetContentScale().y);
1961 DlPaint paint;
1962 paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 1));
1963
1964 DlPaint save_paint;
1965 save_paint.setColor(DlColor::kWhite());
1966 builder.DrawPaint(save_paint);
1967
1968 paint.setColor(DlColor::kBlue());
1969 for (int i = 0; i < 5; i++) {
1970 Scalar y = i * 125;
1971 Scalar y_radius = i * 15;
1972 for (int j = 0; j < 5; j++) {
1973 Scalar x = j * 125;
1974 Scalar x_radius = j * 15;
1975 builder.DrawRoundRect(
1976 DlRoundRect::MakeRectXY(
1977 DlRect::MakeXYWH(x + 50, y + 50, 100.0f, 100.0f), //
1978 x_radius, y_radius),
1979 paint);
1980 }
1981 }
1982
1983 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1984 }
1985
1986 TEST_P(AiksTest, PipelineBlendSingleParameter) {
1987 DisplayListBuilder builder;
1988
1989 // Should render a green square in the middle of a blue circle.
1990 DlPaint paint;
1991 builder.SaveLayer(std::nullopt, &paint);
1992 {
1993 builder.Translate(100, 100);
1994 paint.setColor(DlColor::kBlue());
1995 builder.DrawCircle(DlPoint(200, 200), 200, paint);
1996 builder.ClipRect(DlRect::MakeXYWH(100, 100, 200, 200));
1997
1998 paint.setColor(DlColor::kGreen());
1999 paint.setBlendMode(DlBlendMode::kSrcOver);
2000 paint.setImageFilter(DlImageFilter::MakeColorFilter(
2001 DlColorFilter::MakeBlend(DlColor::kWhite(), DlBlendMode::kDst)));
2002 builder.DrawCircle(DlPoint(200, 200), 200, paint);
2003 builder.Restore();
2004 }
2005
2006 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
2007 }
2008
2009 // Creates an image matrix filter that scales large content such that it would
2010 // exceed the max texture size. See
2011 // https://github.com/flutter/flutter/issues/128912
2012 TEST_P(AiksTest, MassiveScalingMatrixImageFilter) {
2013 if (GetBackend() == PlaygroundBackend::kVulkan) {
2014 GTEST_SKIP() << "Swiftshader is running out of memory on this example.";
2015 }
2016 DisplayListBuilder builder(DlRect::MakeSize(DlSize(1000, 1000)));
2017
2018 auto filter = DlImageFilter::MakeMatrix(
2019 DlMatrix::MakeScale({0.001, 0.001, 1}), DlImageSampling::kLinear);
2020
2021 DlPaint paint;
2022 paint.setImageFilter(filter);
2023 builder.SaveLayer(std::nullopt, &paint);
2024 {
2025 DlPaint paint;
2026 paint.setColor(DlColor::kRed());
2027 builder.DrawRect(DlRect::MakeLTRB(0, 0, 100000, 100000), paint);
2028 }
2029 builder.Restore();
2030
2031 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
2032 }
2033
2034 TEST_P(AiksTest, NoDimplesInRRectPath) {
2035 Scalar width = 200.f;
2036 Scalar height = 60.f;
2037 Scalar corner = 1.f;
2038 auto callback = [&]() -> sk_sp<DisplayList> {
2039 if (AiksTest::ImGuiBegin("Controls", nullptr,
2040 ImGuiWindowFlags_AlwaysAutoResize)) {
2041 ImGui::SliderFloat("width", &width, 0, 200);
2042 ImGui::SliderFloat("height", &height, 0, 200);
2043 ImGui::SliderFloat("corner", &corner, 0, 1);
2044 ImGui::End();
2045 }
2046
2047 DisplayListBuilder builder;
2048 builder.Scale(GetContentScale().x, GetContentScale().y);
2049
2050 DlPaint background_paint;
2051 background_paint.setColor(DlColor(1, 0.1, 0.1, 0.1, DlColorSpace::kSRGB));
2052 builder.DrawPaint(background_paint);
2053
2054 std::vector<DlColor> colors = {DlColor::kRed(), DlColor::kBlue()};
2055 std::vector<Scalar> stops = {0.0, 1.0};
2056
2057 DlPaint paint;
2058 auto gradient = DlColorSource::MakeLinear(DlPoint(0, 0), DlPoint(200, 200),
2059 2, colors.data(), stops.data(),
2060 DlTileMode::kClamp);
2061 paint.setColorSource(gradient);
2062 paint.setColor(DlColor::kWhite());
2063 paint.setDrawStyle(DlDrawStyle::kStroke);
2064 paint.setStrokeWidth(20);
2065
2066 builder.Save();
2067 builder.Translate(100, 100);
2068
2069 Scalar corner_x = ((1 - corner) * 50) + 50;
2070 Scalar corner_y = corner * 50 + 50;
2071 DlRoundRect rrect = DlRoundRect::MakeRectXY(
2072 DlRect::MakeXYWH(0, 0, width, height), corner_x, corner_y);
2073 builder.DrawRoundRect(rrect, paint);
2074 builder.Restore();
2075 return builder.Build();
2076 };
2077 ASSERT_TRUE(OpenPlaygroundHere(callback));
2078 }
2079
2080 TEST_P(AiksTest, BackdropFilterOverUnclosedClip) {
2081 DisplayListBuilder builder;
2082
2083 builder.DrawPaint(DlPaint().setColor(DlColor::kWhite()));
2084 builder.Save();
2085 {
2086 builder.ClipRect(DlRect::MakeLTRB(100, 100, 800, 800));
2087
2088 builder.Save();
2089 {
2090 builder.ClipRect(DlRect::MakeLTRB(600, 600, 800, 800));
2091 builder.DrawPaint(DlPaint().setColor(DlColor::kRed()));
2092 builder.DrawPaint(DlPaint().setColor(DlColor::kBlue().withAlphaF(0.5)));
2093 builder.ClipRect(DlRect::MakeLTRB(700, 700, 750, 800));
2094 builder.DrawPaint(DlPaint().setColor(DlColor::kRed().withAlphaF(0.5)));
2095 }
2096 builder.Restore();
2097
2098 auto image_filter = DlImageFilter::MakeBlur(10, 10, DlTileMode::kDecal);
2099 builder.SaveLayer(std::nullopt, nullptr, image_filter.get());
2100 }
2101 builder.Restore();
2102 builder.DrawCircle(DlPoint(100, 100), 100,
2103 DlPaint().setColor(DlColor::kAqua()));
2104
2105 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
2106 }
2107
2108 } // namespace testing
2109 } // namespace impeller
full_circles
bool full_circles
Definition: aiks_dl_basic_unittests.cc:765
vertical_scale
Scalar vertical_scale
Definition: aiks_dl_basic_unittests.cc:767
sweeps_over_360
bool sweeps_over_360
Definition: aiks_dl_basic_unittests.cc:766
use_center
bool use_center
Definition: aiks_dl_basic_unittests.cc:764
aiks_unittests.h
impeller::AiksPlayground
Definition: aiks_playground.h:16
impeller::AiksPlayground::ImGuiBegin
static bool ImGuiBegin(const char *name, bool *p_open, ImGuiWindowFlags flags)
Definition: aiks_playground.cc:30
impeller::DlImageImpeller::Make
static sk_sp< DlImageImpeller > Make(std::shared_ptr< Texture > texture, OwningContext owning_context=OwningContext::kIO)
Definition: dl_image_impeller.cc:23
x
int32_t x
Definition: dl_golden_unittests.cc:458
dl_image_impeller.h
flutter
Definition: dl_golden_blur_unittests.cc:16
impeller::testing::AiksTest
AiksPlayground AiksTest
Definition: aiks_unittests.h:17
impeller::testing::TEST_P
TEST_P(AiksTest, DrawAtlasNoColor)
Definition: aiks_dl_atlas_unittests.cc:60
impeller
Definition: allocation.cc:12
impeller::DlRect
flutter::DlRect DlRect
Definition: dl_dispatcher.h:25
impeller::Scalar
float Scalar
Definition: scalar.h:19
impeller::DrawPlaygroundLine
std::tuple< Point, Point > DrawPlaygroundLine(PlaygroundPoint &point_a, PlaygroundPoint &point_b)
Definition: widgets.cc:50
impeller::kEhCloseEnough
constexpr float kEhCloseEnough
Definition: constants.h:57
impeller::DlRoundRect
flutter::DlRoundRect DlRoundRect
Definition: dl_dispatcher.h:27
impeller::Rect
TRect< Scalar > Rect
Definition: rect.h:792
impeller::Point
TPoint< Scalar > Point
Definition: point.h:327
impeller::PixelFormat
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
Definition: formats.h:99
impeller::PixelFormat::kB10G10R10A10XR
@ kB10G10R10A10XR
impeller::PixelFormat::kB8G8R8A8UNormInt
@ kB8G8R8A8UNormInt
impeller::PixelFormat::kR8G8B8A8UNormInt
@ kR8G8B8A8UNormInt
impeller::DlPoint
flutter::DlPoint DlPoint
Definition: dl_dispatcher.h:24
impeller::DlPath
flutter::DlPath DlPath
Definition: dl_dispatcher.h:29
impeller::PlaygroundBackend::kVulkan
@ kVulkan
impeller::Size
TSize< Scalar > Size
Definition: size.h:159
impeller::kSqrt2
constexpr float kSqrt2
Definition: constants.h:47
impeller::PixelFormatToString
constexpr const char * PixelFormatToString(PixelFormat format)
Definition: formats.h:140
impeller::DlScalar
flutter::DlScalar DlScalar
Definition: dl_dispatcher.h:23
scalar.h
impeller::Color::White
static constexpr Color White()
Definition: color.h:264
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
impeller::Matrix::MakeTranslation
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
impeller::Matrix::MakeRotationY
static Matrix MakeRotationY(Radians r)
Definition: matrix.h:208
impeller::Matrix::Transform
constexpr Quad Transform(const Quad &quad) const
Definition: matrix.h:623
impeller::PlaygroundPoint
Definition: widgets.h:21
impeller::Radians
Definition: scalar.h:44
impeller::RoundRect::MakeRectXY
static RoundRect MakeRectXY(const Rect &rect, Scalar x_radius, Scalar y_radius)
Definition: round_rect.h:31
impeller::RoundingRadii
Definition: rounding_radii.h:14
impeller::RoundingRadii::top_left
Size top_left
Definition: rounding_radii.h:15
impeller::TPoint< Scalar >
impeller::TRect< Scalar >::MakeLTRB
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129
impeller::TSize
Definition: size.h:25
start
const size_t start
Definition: tessellator_libtess.cc:62
widgets.h