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