Flutter Impeller
dl_golden_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 
6 
7 #include "display_list/dl_color.h"
8 #include "display_list/dl_paint.h"
9 #include "display_list/geometry/dl_geometry_types.h"
10 #include "flutter/display_list/dl_builder.h"
11 #include "flutter/impeller/display_list/testing/render_text_in_canvas.h"
12 #include "flutter/impeller/display_list/testing/rmse.h"
14 #include "flutter/testing/testing.h"
15 #include "gtest/gtest.h"
16 
17 namespace flutter {
18 namespace testing {
19 
20 using impeller::Degrees;
23 using impeller::Point;
24 using impeller::Radians;
25 using impeller::Scalar;
26 
28 
29 TEST_P(DlGoldenTest, CanDrawPaint) {
30  auto draw = [](DlCanvas* canvas,
31  const std::vector<std::unique_ptr<DlImage>>& images) {
32  canvas->Scale(0.2, 0.2);
33  DlPaint paint;
34  paint.setColor(DlColor::kCyan());
35  canvas->DrawPaint(paint);
36  };
37 
38  DisplayListBuilder builder;
39  draw(&builder, /*images=*/{});
40 
41  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
42 }
43 
44 TEST_P(DlGoldenTest, CanRenderImage) {
45  auto draw = [](DlCanvas* canvas, const std::vector<sk_sp<DlImage>>& images) {
46  FML_CHECK(images.size() >= 1);
47  DlPaint paint;
48  paint.setColor(DlColor::kRed());
49  canvas->DrawImage(images[0], SkPoint::Make(100.0, 100.0),
50  DlImageSampling::kLinear, &paint);
51  };
52 
53  DisplayListBuilder builder;
54  std::vector<sk_sp<DlImage>> images;
55  images.emplace_back(CreateDlImageForFixture("kalimba.jpg"));
56  draw(&builder, images);
57 
58  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
59 }
60 
61 // Asserts that subpass rendering of MatrixImageFilters works.
62 // https://github.com/flutter/flutter/issues/147807
63 TEST_P(DlGoldenTest, Bug147807) {
64  Point content_scale = GetContentScale();
65  auto draw = [content_scale](DlCanvas* canvas,
66  const std::vector<sk_sp<DlImage>>& images) {
67  canvas->Scale(content_scale.x, content_scale.y);
68  DlPaint paint;
69  paint.setColor(DlColor(0xfffef7ff));
70  canvas->DrawRect(SkRect::MakeLTRB(0, 0, 375, 667), paint);
71  paint.setColor(DlColor(0xffff9800));
72  canvas->DrawRect(SkRect::MakeLTRB(0, 0, 187.5, 333.5), paint);
73  paint.setColor(DlColor(0xff9c27b0));
74  canvas->DrawRect(SkRect::MakeLTRB(187.5, 0, 375, 333.5), paint);
75  paint.setColor(DlColor(0xff4caf50));
76  canvas->DrawRect(SkRect::MakeLTRB(0, 333.5, 187.5, 667), paint);
77  paint.setColor(DlColor(0xfff44336));
78  canvas->DrawRect(SkRect::MakeLTRB(187.5, 333.5, 375, 667), paint);
79 
80  canvas->Save();
81  {
82  canvas->ClipRRect(
83  SkRRect::MakeOval(SkRect::MakeLTRB(201.25, 10, 361.25, 170)),
84  DlCanvas::ClipOp::kIntersect, true);
85  SkRect save_layer_bounds = SkRect::MakeLTRB(201.25, 10, 361.25, 170);
86  auto backdrop =
87  DlImageFilter::MakeMatrix(DlMatrix::MakeRow(3, 0, 0.0, -280, //
88  0, 3, 0.0, -920, //
89  0, 0, 1.0, 0.0, //
90  0, 0, 0.0, 1.0),
91  DlImageSampling::kLinear);
92  canvas->SaveLayer(&save_layer_bounds, /*paint=*/nullptr, backdrop.get());
93  {
94  canvas->Translate(201.25, 10);
95  auto paint = DlPaint()
96  .setAntiAlias(true)
97  .setColor(DlColor(0xff2196f3))
98  .setStrokeWidth(5)
99  .setDrawStyle(DlDrawStyle::kStroke);
100  canvas->DrawCircle(SkPoint::Make(80, 80), 80, paint);
101  }
102  canvas->Restore();
103  }
104  canvas->Restore();
105  };
106 
107  DisplayListBuilder builder;
108  std::vector<sk_sp<DlImage>> images;
109  draw(&builder, images);
110 
111  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
112 }
113 
114 namespace {
115 void DrawBlurGrid(DlCanvas* canvas) {
116  DlPaint paint;
117  paint.setColor(DlColor(0xfffef7ff));
118  Scalar width = 150;
119  Scalar height = 150;
120  Scalar gap = 80;
121  std::vector<Scalar> blur_radii = {10, 30, 50};
122  for (size_t i = 0; i < blur_radii.size(); ++i) {
123  Scalar blur_radius = blur_radii[i];
124  auto blur_filter = std::make_shared<flutter::DlBlurMaskFilter>(
125  flutter::DlBlurStyle::kNormal, blur_radius);
126  paint.setMaskFilter(blur_filter);
127  SkRRect rrect;
128  Scalar yval = gap + i * (gap + height);
129  rrect.setNinePatch(SkRect::MakeXYWH(gap, yval, width, height), 10, 10, 10,
130  10);
131  canvas->DrawRRect(rrect, paint);
132  rrect.setNinePatch(SkRect::MakeXYWH(2.0 * gap + width, yval, width, height),
133  9, 10, 10, 10);
134  canvas->DrawRRect(rrect, paint);
135  }
136 }
137 } // namespace
138 
139 TEST_P(DlGoldenTest, GaussianVsRRectBlur) {
140  Point content_scale = GetContentScale();
141  auto draw = [content_scale](DlCanvas* canvas,
142  const std::vector<sk_sp<DlImage>>& images) {
143  canvas->Scale(content_scale.x, content_scale.y);
144  canvas->DrawPaint(DlPaint().setColor(DlColor(0xff112233)));
145  DrawBlurGrid(canvas);
146  };
147 
148  DisplayListBuilder builder;
149  std::vector<sk_sp<DlImage>> images;
150  draw(&builder, images);
151 
152  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
153 }
154 
155 TEST_P(DlGoldenTest, GaussianVsRRectBlurScaled) {
156  Point content_scale = GetContentScale();
157  auto draw = [content_scale](DlCanvas* canvas,
158  const std::vector<sk_sp<DlImage>>& images) {
159  canvas->Scale(content_scale.x, content_scale.y);
160  canvas->DrawPaint(DlPaint().setColor(DlColor(0xff112233)));
161  canvas->Scale(0.33, 0.33);
162  DrawBlurGrid(canvas);
163  };
164 
165  DisplayListBuilder builder;
166  std::vector<sk_sp<DlImage>> images;
167  draw(&builder, images);
168 
169  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
170 }
171 
172 TEST_P(DlGoldenTest, GaussianVsRRectBlurScaledRotated) {
173  Point content_scale = GetContentScale();
174  auto draw = [content_scale](DlCanvas* canvas,
175  const std::vector<sk_sp<DlImage>>& images) {
176  canvas->Scale(content_scale.x, content_scale.y);
177  canvas->Translate(200, 200);
178  canvas->DrawPaint(DlPaint().setColor(DlColor(0xff112233)));
179  canvas->Scale(0.33, 0.33);
180  canvas->Translate(300, 300);
181  canvas->Rotate(45);
182  canvas->Translate(-300, -300);
183  DrawBlurGrid(canvas);
184  };
185 
186  DisplayListBuilder builder;
187  std::vector<sk_sp<DlImage>> images;
188  draw(&builder, images);
189 
190  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
191 }
192 
193 TEST_P(DlGoldenTest, FastVsGeneralGaussianMaskBlur) {
194  DisplayListBuilder builder;
195  builder.Scale(GetContentScale().x, GetContentScale().y);
196  builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
197 
198  auto blur_sigmas = std::array{5.0f, 10.0f, 20.0f};
199  auto blur_colors = std::array{
200  DlColor::kBlue(),
201  DlColor::kGreen(),
202  DlColor::kMaroon(),
203  };
204 
205  auto make_rrect_path = [](const SkRect& rect, DlScalar rx,
206  DlScalar ry) -> SkPath {
207  auto add_corner = [](SkPath& path, SkPoint rCorner, SkPoint rEnd) {
208  static const auto magic = impeller::PathBuilder::kArcApproximationMagic;
209  path.rCubicTo(rCorner.fX * (1.0f - magic), rCorner.fY * (1.0f - magic),
210  rCorner.fX + rEnd.fX * magic, rCorner.fY + rEnd.fY * magic,
211  rCorner.fX + rEnd.fX, rCorner.fY + rEnd.fY);
212  };
213 
214  SkPath path;
215  path.moveTo(rect.fRight - rx, rect.fTop);
216  add_corner(path, {rx, 0.0f}, {0.0f, ry});
217  path.lineTo(rect.fRight, rect.fBottom - ry);
218  add_corner(path, {0.0f, ry}, {-rx, 0.0f});
219  path.lineTo(rect.fLeft + rx, rect.fBottom);
220  add_corner(path, {-rx, 0.0f}, {0.0f, -ry});
221  path.lineTo(rect.fLeft, rect.fTop + ry);
222  add_corner(path, {0.0f, -ry}, {rx, 0.0f});
223  path.close();
224  return path;
225  };
226 
227  for (size_t i = 0; i < blur_sigmas.size(); i++) {
228  auto rect = SkRect::MakeXYWH(i * 320.0f + 50.0f, 50.0f, 100.0f, 100.0f);
229  DlPaint paint = DlPaint() //
230  .setColor(blur_colors[i])
231  .setMaskFilter(DlBlurMaskFilter::Make(
232  DlBlurStyle::kNormal, blur_sigmas[i]));
233 
234  builder.DrawRRect(SkRRect::MakeRectXY(rect, 10.0f, 10.0f), paint);
235  rect = rect.makeOffset(150.0f, 0.0f);
236  builder.DrawPath(make_rrect_path(rect, 10.0f, 10.0f), paint);
237  rect = rect.makeOffset(-150.0f, 0.0f);
238 
239  rect = rect.makeOffset(0.0f, 200.0f);
240  builder.DrawRRect(SkRRect::MakeRectXY(rect, 10.0f, 30.0f), paint);
241  rect = rect.makeOffset(150.0f, 0.0f);
242  builder.DrawPath(make_rrect_path(rect, 10.0f, 20.0f), paint);
243  rect = rect.makeOffset(-150.0f, 0.0f);
244 
245  rect = rect.makeOffset(0.0f, 200.0f);
246  builder.DrawRRect(SkRRect::MakeRectXY(rect, 30.0f, 10.0f), paint);
247  rect = rect.makeOffset(150.0f, 0.0f);
248  builder.DrawPath(make_rrect_path(rect, 20.0f, 10.0f), paint);
249  rect = rect.makeOffset(-150.0f, 0.0f);
250  }
251 
252  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
253 }
254 
255 TEST_P(DlGoldenTest, DashedLinesTest) {
256  Point content_scale = GetContentScale();
257  auto draw = [content_scale](DlCanvas* canvas,
258  const std::vector<sk_sp<DlImage>>& images) {
259  canvas->Scale(content_scale.x, content_scale.y);
260  canvas->DrawPaint(DlPaint().setColor(DlColor::kWhite()));
261 
262  auto draw_one = [canvas](DlStrokeCap cap, Scalar x, Scalar y,
263  Scalar dash_on, Scalar dash_off) {
264  Point center = Point(x, y);
265  Scalar inner = 20.0f;
266  Scalar outer = 100.0f;
267  DlPaint thick_paint = DlPaint()
268  .setColor(DlColor::kBlue())
269  .setStrokeCap(cap)
270  .setStrokeWidth(8.0f);
271  DlPaint middle_paint = DlPaint()
272  .setColor(DlColor::kGreen())
273  .setStrokeCap(cap)
274  .setStrokeWidth(5.0f);
275  DlPaint thin_paint = DlPaint()
276  .setColor(DlColor::kMagenta())
277  .setStrokeCap(cap)
278  .setStrokeWidth(2.0f);
279  for (int degrees = 0; degrees < 360; degrees += 30) {
280  Point delta = Point(1.0f, 0.0f).Rotate(Degrees(degrees));
281  canvas->DrawDashedLine(center + inner * delta, center + outer * delta,
282  dash_on, dash_off, thick_paint);
283  canvas->DrawDashedLine(center + inner * delta, center + outer * delta,
284  dash_on, dash_off, middle_paint);
285  canvas->DrawDashedLine(center + inner * delta, center + outer * delta,
286  dash_on, dash_off, thin_paint);
287  }
288  };
289 
290  draw_one(DlStrokeCap::kButt, 150.0f, 150.0f, 15.0f, 10.0f);
291  draw_one(DlStrokeCap::kSquare, 400.0f, 150.0f, 15.0f, 10.0f);
292  draw_one(DlStrokeCap::kRound, 150.0f, 400.0f, 15.0f, 10.0f);
293  draw_one(DlStrokeCap::kRound, 400.0f, 400.0f, 0.0f, 11.0f);
294 
295  // Make sure the rendering op responds appropriately to clipping
296  canvas->Save();
297  SkPath clip_path = SkPath();
298  clip_path.moveTo(275.0f, 225.0f);
299  clip_path.lineTo(325.0f, 275.0f);
300  clip_path.lineTo(275.0f, 325.0f);
301  clip_path.lineTo(225.0f, 275.0f);
302  canvas->ClipPath(clip_path);
303  canvas->DrawColor(DlColor::kYellow());
304  draw_one(DlStrokeCap::kRound, 275.0f, 275.0f, 15.0f, 10.0f);
305  canvas->Restore();
306  };
307 
308  DisplayListBuilder builder;
309  std::vector<sk_sp<DlImage>> images;
310  draw(&builder, images);
311 
312  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
313 }
314 
315 TEST_P(DlGoldenTest, SaveLayerAtFractionalValue) {
316  // Draws a stroked rounded rect at a fractional pixel value. The coverage must
317  // be adjusted so that we still have room to draw it, even though it lies on
318  // the fractional bounds of the saveLayer.
319  DisplayListBuilder builder;
320  builder.DrawPaint(DlPaint().setColor(DlColor::kWhite()));
321  auto save_paint = DlPaint().setAlpha(100);
322  builder.SaveLayer(nullptr, &save_paint);
323 
324  builder.DrawRoundRect(DlRoundRect::MakeRectRadius(
325  DlRect::MakeLTRB(10.5, 10.5, 200.5, 200.5), 10),
326  DlPaint()
327  .setDrawStyle(DlDrawStyle::kStroke)
328  .setStrokeWidth(1.5)
329  .setColor(DlColor::kBlack()));
330  builder.DrawCircle(DlPoint::MakeXY(100, 100), 50.5,
331  DlPaint().setColor(DlColor::kAqua()));
332  builder.DrawCircle(DlPoint::MakeXY(110, 110), 50.5,
333  DlPaint().setColor(DlColor::kCyan()));
334 
335  builder.Restore();
336 
337  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
338 }
339 
340 namespace {
341 int32_t CalculateMaxY(const impeller::testing::Screenshot* img) {
342  const uint32_t* ptr = reinterpret_cast<const uint32_t*>(img->GetBytes());
343  int32_t max_y = 0;
344  for (uint32_t i = 0; i < img->GetHeight(); ++i) {
345  for (uint32_t j = 0; j < img->GetWidth(); ++j) {
346  uint32_t pixel = *ptr++;
347  if ((pixel & 0x00ffffff) != 0) {
348  max_y = std::max(max_y, static_cast<int32_t>(i));
349  }
350  }
351  }
352  return max_y;
353 }
354 
355 int32_t CalculateSpaceBetweenUI(const impeller::testing::Screenshot* img) {
356  const uint32_t* ptr = reinterpret_cast<const uint32_t*>(img->GetBytes());
357  ptr += img->GetWidth() * static_cast<int32_t>(img->GetHeight() / 2.0);
358  std::vector<size_t> boundaries;
359  uint32_t value = *ptr++;
360  for (size_t i = 1; i < img->GetWidth(); ++i) {
361  if (((*ptr & 0x00ffffff) != 0) != ((value & 0x00ffffff) != 0)) {
362  boundaries.push_back(i);
363  }
364  value = *ptr++;
365  }
366 
367  assert(boundaries.size() == 6);
368  return boundaries[4] - boundaries[3];
369 }
370 } // namespace
371 
372 TEST_P(DlGoldenTest, BaselineHE) {
373  SetWindowSize(impeller::ISize(1024, 200));
374  impeller::Scalar font_size = 300;
375  auto callback = [&](const char* text,
376  impeller::Scalar scale) -> sk_sp<DisplayList> {
377  DisplayListBuilder builder;
378  DlPaint paint;
379  paint.setColor(DlColor::ARGB(1, 0, 0, 0));
380  builder.DrawPaint(paint);
381  builder.Scale(scale, scale);
382  RenderTextInCanvasSkia(&builder, text, "Roboto-Regular.ttf",
383  DlPoint::MakeXY(10, 300),
384  TextRenderOptions{
385  .font_size = font_size,
386  });
387  return builder.Build();
388  };
389 
390  std::unique_ptr<impeller::testing::Screenshot> right =
391  MakeScreenshot(callback("h", 0.444));
392  if (!right) {
393  GTEST_SKIP() << "making screenshots not supported.";
394  }
395  std::unique_ptr<impeller::testing::Screenshot> left =
396  MakeScreenshot(callback("e", 0.444));
397 
398  int32_t left_max_y = CalculateMaxY(left.get());
399  int32_t right_max_y = CalculateMaxY(right.get());
400  int32_t y_diff = std::abs(left_max_y - right_max_y);
401  EXPECT_TRUE(y_diff <= 2) << "y diff: " << y_diff;
402 }
403 
404 TEST_P(DlGoldenTest, MaintainsSpace) {
405  SetWindowSize(impeller::ISize(1024, 200));
406  impeller::Scalar font_size = 300;
407  auto callback = [&](const char* text,
408  impeller::Scalar scale) -> sk_sp<DisplayList> {
409  DisplayListBuilder builder;
410  DlPaint paint;
411  paint.setColor(DlColor::ARGB(1, 0, 0, 0));
412  builder.DrawPaint(paint);
413  builder.Scale(scale, scale);
414  RenderTextInCanvasSkia(&builder, text, "Roboto-Regular.ttf",
415  DlPoint::MakeXY(10, 300),
416  TextRenderOptions{
417  .font_size = font_size,
418  });
419  return builder.Build();
420  };
421 
422  std::optional<int32_t> last_space;
423  for (int i = 0; i <= 100; ++i) {
424  Scalar scale = 0.440 + i / 1000.0;
425  std::unique_ptr<impeller::testing::Screenshot> right =
426  MakeScreenshot(callback("ui", scale));
427  if (!right) {
428  GTEST_SKIP() << "making screenshots not supported.";
429  }
430 
431  int32_t space = CalculateSpaceBetweenUI(right.get());
432  if (last_space.has_value()) {
433  int32_t diff = abs(space - *last_space);
434  EXPECT_TRUE(diff <= 1)
435  << "i:" << i << " space:" << space << " last_space:" << *last_space;
436  }
437  last_space = space;
438  }
439 }
440 
441 namespace {
442 struct LeftmostIntensity {
443  int32_t x;
444  int32_t value;
445 };
446 
447 /// Returns the highest value in the green channel for leftmost column that
448 /// isn't all black.
449 LeftmostIntensity CalculateLeftmostIntensity(
450  const impeller::testing::Screenshot* img) {
451  LeftmostIntensity result = {.x = static_cast<int32_t>(img->GetWidth()),
452  .value = 0};
453  const uint32_t* ptr = reinterpret_cast<const uint32_t*>(img->GetBytes());
454  for (size_t i = 0; i < img->GetHeight(); ++i) {
455  for (int32_t j = 0; j < static_cast<int32_t>(img->GetWidth()); ++j) {
456  if (((*ptr & 0x00ffffff) != 0)) {
457  if (j < result.x) {
458  result.x = j;
459  result.value = (*ptr & 0xff00) >> 8;
460  } else if (j == result.x) {
461  result.value =
462  std::max(static_cast<int32_t>(*ptr & 0xff), result.value);
463  }
464  }
465  ptr++;
466  }
467  }
468  return result;
469 }
470 } // namespace
471 
472 // Checks that the left most edge of the glyph is fading out as we push
473 // it to the right by fractional pixels.
474 TEST_P(DlGoldenTest, Subpixel) {
475  SetWindowSize(impeller::ISize(1024, 200));
476  impeller::Scalar font_size = 200;
477  auto callback = [&](Scalar offset_x) -> sk_sp<DisplayList> {
478  DisplayListBuilder builder;
479  DlPaint paint;
480  paint.setColor(DlColor::ARGB(1, 0, 0, 0));
481  builder.DrawPaint(paint);
482  RenderTextInCanvasSkia(&builder, "ui", "Roboto-Regular.ttf",
483  DlPoint::MakeXY(offset_x, 180),
484  TextRenderOptions{
485  .font_size = font_size,
486  .is_subpixel = true,
487  });
488  return builder.Build();
489  };
490 
491  LeftmostIntensity intensity[5];
492  for (int i = 0; i <= 4; ++i) {
493  Scalar offset = 10 + (i / 4.0);
494  std::unique_ptr<impeller::testing::Screenshot> right =
495  MakeScreenshot(callback(offset));
496  if (!right) {
497  GTEST_SKIP() << "making screenshots not supported.";
498  }
499  intensity[i] = CalculateLeftmostIntensity(right.get());
500  ASSERT_NE(intensity[i].value, 0);
501  }
502  for (int i = 1; i < 5; ++i) {
503  EXPECT_TRUE(intensity[i].x - intensity[i - 1].x == 1 ||
504  intensity[i].value < intensity[i - 1].value)
505  << i;
506  }
507  EXPECT_EQ(intensity[4].x - intensity[0].x, 1);
508 }
509 
510 // Checks that the left most edge of the glyph is fading out as we push
511 // it to the right by fractional pixels.
512 TEST_P(DlGoldenTest, SubpixelScaled) {
513  SetWindowSize(impeller::ISize(1024, 200));
514  impeller::Scalar font_size = 200;
515  Scalar scalar = 0.75;
516  auto callback = [&](Scalar offset_x) -> sk_sp<DisplayList> {
517  DisplayListBuilder builder;
518  builder.Scale(scalar, scalar);
519  DlPaint paint;
520  paint.setColor(DlColor::ARGB(1, 0, 0, 0));
521  builder.DrawPaint(paint);
522  RenderTextInCanvasSkia(&builder, "ui", "Roboto-Regular.ttf",
523  DlPoint::MakeXY(offset_x, 180),
524  TextRenderOptions{
525  .font_size = font_size,
526  .is_subpixel = true,
527  });
528  return builder.Build();
529  };
530 
531  LeftmostIntensity intensity[5];
532  Scalar offset_fraction = 0.25 / scalar;
533  for (int i = 0; i <= 4; ++i) {
534  Scalar offset = 10 + (offset_fraction * i);
535  std::unique_ptr<impeller::testing::Screenshot> right =
536  MakeScreenshot(callback(offset));
537  if (!right) {
538  GTEST_SKIP() << "making screenshots not supported.";
539  }
540  intensity[i] = CalculateLeftmostIntensity(right.get());
541  ASSERT_NE(intensity[i].value, 0);
542  }
543  for (int i = 1; i < 5; ++i) {
544  EXPECT_TRUE(intensity[i].x - intensity[i - 1].x == 1 ||
545  intensity[i].value < intensity[i - 1].value)
546  << i;
547  }
548  EXPECT_EQ(intensity[4].x - intensity[0].x, 1);
549 }
550 
551 // Checks that the left most edge of the glyph is fading out as we push
552 // it to the right by fractional pixels.
553 TEST_P(DlGoldenTest, SubpixelScaledTranslated) {
554  SetWindowSize(impeller::ISize(1024, 200));
555  impeller::Scalar font_size = 200;
556  Scalar scalar = 0.75;
557  auto callback = [&](Scalar offset_x) -> sk_sp<DisplayList> {
558  DisplayListBuilder builder;
559  builder.Scale(scalar, scalar);
560  DlPaint paint;
561  paint.setColor(DlColor::ARGB(1, 0, 0, 0));
562  builder.DrawPaint(paint);
563  builder.Translate(offset_x, 180);
564  RenderTextInCanvasSkia(&builder, "ui", "Roboto-Regular.ttf",
565  DlPoint::MakeXY(0, 0),
566  TextRenderOptions{
567  .font_size = font_size,
568  .is_subpixel = true,
569  });
570  return builder.Build();
571  };
572 
573  LeftmostIntensity intensity[5];
574  Scalar offset_fraction = 0.25 / scalar;
575  for (int i = 0; i <= 4; ++i) {
576  Scalar offset = 10 + (offset_fraction * i);
577  std::unique_ptr<impeller::testing::Screenshot> right =
578  MakeScreenshot(callback(offset));
579  if (!right) {
580  GTEST_SKIP() << "making screenshots not supported.";
581  }
582  intensity[i] = CalculateLeftmostIntensity(right.get());
583  ASSERT_NE(intensity[i].value, 0);
584  }
585  for (int i = 1; i < 5; ++i) {
586  EXPECT_TRUE(intensity[i].x - intensity[i - 1].x == 1 ||
587  intensity[i].value < intensity[i - 1].value)
588  << i;
589  }
590  EXPECT_EQ(intensity[4].x - intensity[0].x, 1);
591 }
592 
593 } // namespace testing
594 } // namespace flutter
constexpr static const Scalar kArcApproximationMagic
Definition: path_builder.h:24
virtual size_t GetHeight() const =0
Returns the height of the image in pixels.
virtual const uint8_t * GetBytes() const =0
Access raw data of the screenshot.
virtual size_t GetWidth() const =0
Returns the width of the image in pixels.
int32_t value
int32_t x
Vector2 blur_radius
Blur radius in source pixels based on scaled_sigma.
TEST_P(DlGoldenTest, TextBlurMaskFilterRespectCTM)
INSTANTIATE_PLAYGROUND_SUITE(DlGoldenTest)
bool RenderTextInCanvasSkia(const std::shared_ptr< Context > &context, DisplayListBuilder &canvas, const std::string &text, const std::string_view &font_fixture, const TextRenderOptions &options={})
float Scalar
Definition: scalar.h:18
TPoint< Scalar > Point
Definition: point.h:327
PlaygroundBackend
Definition: playground.h:27
flutter::DlScalar DlScalar
Definition: dl_dispatcher.h:23
const Scalar scale
SeparatedVector2 offset
constexpr TPoint Rotate(const Radians &angle) const
Definition: point.h:226