Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
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
13
24
25namespace impeller {
26namespace testing {
27
28using namespace flutter;
29
30TEST_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
38namespace {
39using DrawRectProc =
40 std::function<void(DisplayListBuilder&, const DlRect&, const DlPaint&)>;
41
42sk_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
89TEST_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
98TEST_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
107TEST_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
117TEST_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
131TEST_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
143TEST_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
155namespace {
156bool 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
173void 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");
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
256TEST_P(AiksTest, CanRenderTiledTextureClamp) {
257 CanRenderTiledTexture(this, DlTileMode::kClamp);
258}
259
260TEST_P(AiksTest, CanRenderTiledTextureRepeat) {
261 CanRenderTiledTexture(this, DlTileMode::kRepeat);
262}
263
264TEST_P(AiksTest, CanRenderTiledTextureMirror) {
265 CanRenderTiledTexture(this, DlTileMode::kMirror);
266}
267
268TEST_P(AiksTest, CanRenderTiledTextureDecal) {
269 CanRenderTiledTexture(this, DlTileMode::kDecal);
270}
271
272TEST_P(AiksTest, CanRenderTiledTextureClampWithTranslate) {
273 CanRenderTiledTexture(this, DlTileMode::kClamp,
274 Matrix::MakeTranslation({172.f, 172.f, 0.f}));
275}
276
277TEST_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
295TEST_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;
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
315TEST_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();
348 DlRect::MakeLTRB(200, 230, 300, 270), 20, 20));
349 builder.DrawPaint(paint);
350 builder.Restore();
351 }
352 {
353 builder.Save();
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);
386
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
399TEST_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
417TEST_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
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
451TEST_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
470TEST_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
480TEST_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
493TEST_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);
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);
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);
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);
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
628TEST_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] = {
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);
675
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});
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
689TEST_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] = {
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);
741
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});
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
755namespace {
756DlPath 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
783void 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
840TEST_P(AiksTest, ZoomedStrokedPathRendersCorrectly) {
841 DrawStrokedAndFilledCirclesWithZoom(this, /*zoom=*/80.0f, /*radius=*/2.0f,
842 /*stroke_width=*/0.05f);
843}
844
845TEST_P(AiksTest, StrokedPathWithLargeStrokeWidthRendersCorrectly) {
846 DrawStrokedAndFilledCirclesWithZoom(this, /*zoom=*/1.0f, /*radius=*/1.0f,
847 /*stroke_width=*/5.0f);
848}
849
850TEST_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] = {
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);
905
906 paint.setColor(DlColor::kWhite().modulateOpacity(0.5));
907
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});
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
923namespace {
924struct ArcFarmOptions {
925 bool use_center = false;
926 bool full_circles = false;
927 bool sweeps_over_360 = false;
929};
930
931void 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
951void 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
971TEST_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
988TEST_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
1005TEST_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
1022TEST_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
1040TEST_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
1058TEST_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
1078TEST_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
1098TEST_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
1113TEST_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
1133TEST_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
1148TEST_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
1169TEST_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
1193TEST_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
1214TEST_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
1243TEST_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
1281TEST_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
1322TEST_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] = {
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(
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);
1377
1378 paint.setColor(DlColor::kWhite().modulateOpacity(0.1));
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));
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});
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});
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
1430TEST_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
1495TEST_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;
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
1534TEST_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
1595TEST_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;
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);
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});
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);
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
1668TEST_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
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.
1697TEST_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.
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;
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.
1741TEST_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
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
1767TEST_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
1782TEST_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
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.
1826TEST_P(AiksTest, FormatWideGamut) {
1827 EXPECT_EQ(GetContext()->GetCapabilities()->GetDefaultColorFormat(),
1829}
1830
1831TEST_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
1839TEST_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
1875TEST_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
1885TEST_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
1896TEST_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
1918TEST_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] = {
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 };
1969 CreateTextureForFixture("airplane.jpg",
1970 /*enable_mipmapping=*/true));
1971
1972 paint.setColor(DlColor::kWhite().modulateOpacity(0.1));
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));
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));
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));
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
2020TEST_P(AiksTest, CoverageOriginShouldBeAccountedForInSubpasses) {
2021 auto callback = [&]() -> sk_sp<DisplayList> {
2022 DisplayListBuilder builder;
2023 builder.Scale(GetContentScale().x, GetContentScale().y);
2024
2025 DlPaint alpha;
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
2068TEST_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
2094TEST_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
2130TEST_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
2160TEST_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
2184TEST_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(
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
2213TEST_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);
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
2239TEST_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
2261TEST_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;
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
2307TEST_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
2335TEST_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;
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
bool full_circles
Scalar vertical_scale
bool sweeps_over_360
void DrawOval(const DlRect &bounds, const DlPaint &paint) override
void ClipRect(const DlRect &rect, DlClipOp clip_op=DlClipOp::kIntersect, bool is_aa=false) override
void DrawImageRect(const sk_sp< DlImage > &image, const DlRect &src, const DlRect &dst, DlImageSampling sampling, const DlPaint *paint=nullptr, DlSrcRectConstraint constraint=DlSrcRectConstraint::kFast) override
void DrawRoundRect(const DlRoundRect &rrect, const DlPaint &paint) override
void DrawArc(const DlRect &bounds, DlScalar start, DlScalar sweep, bool useCenter, const DlPaint &paint) override
void DrawImage(const sk_sp< DlImage > &image, const DlPoint &point, DlImageSampling sampling, const DlPaint *paint=nullptr) override
void DrawColor(DlColor color, DlBlendMode mode) override
void DrawCircle(const DlPoint &center, DlScalar radius, const DlPaint &paint) override
void SaveLayer(const std::optional< DlRect > &bounds, const DlPaint *paint=nullptr, const DlImageFilter *backdrop=nullptr, std::optional< int64_t > backdrop_id=std::nullopt) override
void ClipRoundRect(const DlRoundRect &rrect, DlClipOp clip_op=DlClipOp::kIntersect, bool is_aa=false) override
void Rotate(DlScalar degrees) override
void Scale(DlScalar sx, DlScalar sy) override
void Skew(DlScalar sx, DlScalar sy) override
void Translate(DlScalar tx, DlScalar ty) override
void DrawPaint(const DlPaint &paint) override
sk_sp< DisplayList > Build()
Definition dl_builder.cc:66
void DrawPath(const DlPath &path, const DlPaint &paint) override
void ClipPath(const DlPath &path, DlClipOp clip_op=DlClipOp::kIntersect, bool is_aa=false) override
void ClipOval(const DlRect &bounds, DlClipOp clip_op=DlClipOp::kIntersect, bool is_aa=false) override
void TransformFullPerspective(DlScalar mxx, DlScalar mxy, DlScalar mxz, DlScalar mxt, DlScalar myx, DlScalar myy, DlScalar myz, DlScalar myt, DlScalar mzx, DlScalar mzy, DlScalar mzz, DlScalar mzt, DlScalar mwx, DlScalar mwy, DlScalar mwz, DlScalar mwt) override
void Transform(const DlMatrix &matrix) override
void DrawRect(const DlRect &rect, const DlPaint &paint) override
static std::shared_ptr< DlMaskFilter > Make(DlBlurStyle style, SkScalar sigma, bool respect_ctm=true)
Developer-facing API for rendering anything within the engine.
Definition dl_canvas.h:32
static std::shared_ptr< const DlColorFilter > MakeBlend(DlColor color, DlBlendMode mode)
static std::shared_ptr< DlColorSource > MakeImage(const sk_sp< const DlImage > &image, DlTileMode horizontal_tile_mode, DlTileMode vertical_tile_mode, DlImageSampling sampling=DlImageSampling::kLinear, const DlMatrix *matrix=nullptr)
static std::shared_ptr< DlColorSource > MakeLinear(const DlPoint start_point, const DlPoint end_point, uint32_t stop_count, const DlColor *colors, const float *stops, DlTileMode tile_mode, const DlMatrix *matrix=nullptr)
static std::shared_ptr< DlColorSource > MakeRadial(DlPoint center, DlScalar radius, uint32_t stop_count, const DlColor *colors, const float *stops, DlTileMode tile_mode, const DlMatrix *matrix=nullptr)
static std::shared_ptr< DlImageFilter > MakeBlur(DlScalar sigma_x, DlScalar sigma_y, DlTileMode tile_mode)
static std::shared_ptr< DlImageFilter > MakeColorFilter(const std::shared_ptr< const DlColorFilter > &filter)
static std::shared_ptr< DlImageFilter > MakeMatrix(const DlMatrix &matrix, DlImageSampling sampling)
DlPaint & setColor(DlColor color)
Definition dl_paint.h:70
DlPaint & setInvertColors(bool isInvertColors)
Definition dl_paint.h:64
DlPaint & setStrokeCap(DlStrokeCap cap)
Definition dl_paint.h:101
DlPaint & setStrokeWidth(float width)
Definition dl_paint.h:115
DlPaint & setStrokeMiter(float miter)
Definition dl_paint.h:121
DlPaint & setBlendMode(DlBlendMode mode)
Definition dl_paint.h:85
DlPaint & setImageFilter(std::nullptr_t filter)
Definition dl_paint.h:167
DlPaint & setMaskFilter(std::nullptr_t filter)
Definition dl_paint.h:185
DlPaint & setDrawStyle(DlDrawStyle style)
Definition dl_paint.h:93
DlPaint & setStrokeJoin(DlStrokeJoin join)
Definition dl_paint.h:109
DlPaint & setColorFilter(std::nullptr_t filter)
Definition dl_paint.h:149
DlPaint & setColorSource(std::nullptr_t source)
Definition dl_paint.h:131
DlPathBuilder & MoveTo(DlPoint p2)
Start a new contour that will originate at the indicated point p2.
const DlPath TakePath()
Returns the path constructed by this path builder and resets its internal state to the default state ...
DlPathBuilder & AddCircle(DlPoint center, DlScalar radius)
Append a closed circular contour to the path centered on the provided point at the provided radius.
DlPathBuilder & AddRoundRect(const DlRoundRect &round_rect)
Append a closed rounded rect contour to the path.
DlPathBuilder & Close()
The path is closed back to the location of the most recent MoveTo call. Contours that are filled are ...
DlPathBuilder & CubicCurveTo(DlPoint cp1, DlPoint cp2, DlPoint p2)
Draw a cubic bezier curve from the current point to the indicated point p2, using the indicated point...
static DlPath MakeCircle(const DlPoint center, DlScalar radius)
Definition dl_path.cc:68
static DlPath MakeRectXYWH(DlScalar x, DlScalar y, DlScalar width, DlScalar height)
Definition dl_path.cc:50
static DlPath MakeRect(const DlRect &rect)
Definition dl_path.cc:39
static DlPath MakeRoundRectXY(const DlRect &rect, DlScalar x_radius, DlScalar y_radius, bool counter_clock_wise=false)
Definition dl_path.cc:76
static bool ImGuiBegin(const char *name, bool *p_open, ImGuiWindowFlags flags)
static sk_sp< DlImageImpeller > Make(std::shared_ptr< Texture > texture, OwningContext owning_context=OwningContext::kIO)
int32_t x
FlutterVulkanImage * image
FlutterDesktopBinaryReply callback
FlTexture * texture
double y
impeller::Scalar DlScalar
impeller::ISize32 DlISize
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
impeller::Size DlSize
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition switch_defs.h:52
static constexpr DlScalar kEhCloseEnough
impeller::Point DlPoint
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set profile Make the profiler discard new samples once the profiler sample buffer is full When this flag is not the profiler sample buffer is used as a ring buffer
Definition switch_defs.h:98
AiksPlayground AiksTest
TEST_P(AiksTest, DrawAtlasNoColor)
float Scalar
Definition scalar.h:19
std::tuple< Point, Point > DrawPlaygroundLine(PlaygroundPoint &point_a, PlaygroundPoint &point_b)
Definition widgets.cc:51
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
Definition formats.h:99
constexpr float kSqrt2
Definition constants.h:47
void Close(PathBuilder *builder)
constexpr const char * PixelFormatToString(PixelFormat format)
Definition formats.h:141
int32_t height
int32_t width
static constexpr DlColor kWhite()
Definition dl_color.h:70
static constexpr DlColor kBlue()
Definition dl_color.h:73
static constexpr DlColor RGBA(DlScalar r, DlScalar g, DlScalar b, DlScalar a)
Construct a 32 bit color from floating point R, G, B, and A color channels.
Definition dl_color.h:48
static constexpr DlColor kBlack()
Definition dl_color.h:69
static constexpr DlColor ARGB(DlScalar a, DlScalar r, DlScalar g, DlScalar b)
Construct a 32 bit color from floating point A, R, G, and B color channels.
Definition dl_color.h:57
static constexpr DlColor kAqua()
Definition dl_color.h:86
static constexpr DlColor kYellow()
Definition dl_color.h:76
static constexpr DlColor kPurple()
Definition dl_color.h:88
static constexpr DlColor kMidGrey()
Definition dl_color.h:78
static constexpr DlColor kTransparent()
Definition dl_color.h:68
static constexpr DlColor kRed()
Definition dl_color.h:71
static constexpr DlColor kGreen()
Definition dl_color.h:72
static constexpr DlColor kOrange()
Definition dl_color.h:87
constexpr DlColor modulateOpacity(DlScalar opacity) const
Definition dl_color.h:150
static constexpr Color White()
Definition color.h:264
A 4x4 matrix using column-major storage.
Definition matrix.h:37
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition matrix.h:95
static Matrix MakeRotationY(Radians r)
Definition matrix.h:208
static Matrix MakePerspective(Radians fov_y, Scalar aspect_ratio, Scalar z_near, Scalar z_far)
Definition matrix.h:642
static constexpr Matrix MakeScale(const Vector3 &s)
Definition matrix.h:104
static RoundRect MakeRectRadii(const Rect &rect, const RoundingRadii &radii)
Definition round_rect.cc:9
static RoundRect MakeRectXY(const Rect &rect, Scalar x_radius, Scalar y_radius)
Definition round_rect.h:31
static constexpr TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition rect.h:136
static constexpr TRect MakeSize(const TSize< U > &size)
Definition rect.h:150
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
constexpr TRect< T > Shift(T dx, T dy) const
Returns a new rectangle translated by the given offset.
Definition rect.h:602
static constexpr TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition rect.h:129
Type height
Definition size.h:29
Type width
Definition size.h:28
const size_t start