Flutter Impeller
dl_golden_blur_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 "flutter/display_list/dl_builder.h"
8 #include "flutter/display_list/effects/dl_mask_filter.h"
9 #include "flutter/impeller/display_list/testing/render_text_in_canvas.h"
10 #include "flutter/impeller/display_list/testing/rmse.h"
13 #include "flutter/testing/testing.h"
14 #include "gtest/gtest.h"
15 
16 namespace flutter {
17 namespace testing {
18 
19 using impeller::Font;
20 
21 TEST_P(DlGoldenTest, TextBlurMaskFilterRespectCTM) {
22  impeller::Point content_scale = GetContentScale();
23  auto draw = [&](DlCanvas* canvas,
24  const std::vector<std::unique_ptr<DlImage>>& images) {
25  canvas->DrawColor(DlColor(0xff111111));
26  canvas->Scale(content_scale.x, content_scale.y);
27  canvas->Scale(2, 2);
28  TextRenderOptions options;
29  options.mask_filter =
30  DlBlurMaskFilter::Make(DlBlurStyle::kNormal, /*sigma=*/10,
31  /*respect_ctm=*/true);
32  ASSERT_TRUE(RenderTextInCanvasSkia(canvas, "hello world",
33  "Roboto-Regular.ttf", DlPoint(101, 101),
34  options));
35  options.mask_filter = nullptr;
36  options.color = DlColor::kRed();
37  ASSERT_TRUE(RenderTextInCanvasSkia(canvas, "hello world",
38  "Roboto-Regular.ttf", DlPoint(100, 100),
39  options));
40  };
41 
42  DisplayListBuilder builder;
43  draw(&builder, /*images=*/{});
44 
45  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
46 }
47 
48 TEST_P(DlGoldenTest, TextBlurMaskFilterDisrespectCTM) {
49  impeller::Point content_scale = GetContentScale();
50  auto draw = [&](DlCanvas* canvas,
51  const std::vector<std::unique_ptr<DlImage>>& images) {
52  canvas->DrawColor(DlColor(0xff111111));
53  canvas->Scale(content_scale.x, content_scale.y);
54  canvas->Scale(2, 2);
55  TextRenderOptions options;
56  options.mask_filter =
57  DlBlurMaskFilter::Make(DlBlurStyle::kNormal, /*sigma=*/10,
58  /*respect_ctm=*/false);
59  ASSERT_TRUE(RenderTextInCanvasSkia(canvas, "hello world",
60  "Roboto-Regular.ttf", DlPoint(101, 101),
61  options));
62  options.mask_filter = nullptr;
63  options.color = DlColor::kRed();
64  ASSERT_TRUE(RenderTextInCanvasSkia(canvas, "hello world",
65  "Roboto-Regular.ttf", DlPoint(100, 100),
66  options));
67  };
68 
69  DisplayListBuilder builder;
70  draw(&builder, /*images=*/{});
71 
72  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
73 }
74 
75 // This is a test to make sure that we don't regress "shimmering" in the
76 // gaussian blur. Shimmering is abrupt changes in signal when making tiny
77 // changes to the blur parameters.
78 //
79 // See also:
80 // - https://github.com/flutter/flutter/issues/152195
81 TEST_P(DlGoldenTest, ShimmerTest) {
82  impeller::Point content_scale = GetContentScale();
83  auto draw = [&](DlCanvas* canvas, const std::vector<sk_sp<DlImage>>& images,
84  float sigma) {
85  canvas->DrawColor(DlColor(0xff111111));
86  canvas->Scale(content_scale.x, content_scale.y);
87 
88  DlPaint paint;
89  canvas->DrawImage(images[0], SkPoint::Make(10.135, 10.36334),
90  DlImageSampling::kLinear, &paint);
91 
92  SkRect save_layer_bounds = SkRect::MakeLTRB(0, 0, 1024, 768);
93  auto blur = DlImageFilter::MakeBlur(sigma, sigma, DlTileMode::kDecal);
94  canvas->ClipRect(SkRect::MakeLTRB(11.125, 10.3737, 911.25, 755.3333));
95  canvas->SaveLayer(&save_layer_bounds, /*paint=*/nullptr, blur.get());
96  canvas->Restore();
97  };
98 
99  std::vector<sk_sp<DlImage>> images;
100  images.emplace_back(CreateDlImageForFixture("boston.jpg"));
101 
102  auto make_screenshot = [&](float sigma) {
103  DisplayListBuilder builder;
104  draw(&builder, images, sigma);
105 
106  std::unique_ptr<impeller::testing::Screenshot> screenshot =
107  MakeScreenshot(builder.Build());
108  return screenshot;
109  };
110 
111  float start_sigma = 10.0f;
112  std::unique_ptr<impeller::testing::Screenshot> left =
113  make_screenshot(start_sigma);
114  if (!left) {
115  GTEST_SKIP() << "making screenshots not supported.";
116  }
117 
118  double average_rmse = 0.0;
119  const int32_t sample_count = 200;
120  for (int i = 1; i <= sample_count; ++i) {
121  float sigma = start_sigma + (i / 2.f);
122  std::unique_ptr<impeller::testing::Screenshot> right =
123  make_screenshot(sigma);
124  double rmse = RMSE(left.get(), right.get());
125  average_rmse += rmse;
126 
127  // To debug this output the frames can be written out to disk then
128  // transformed to a video with ffmpeg.
129  //
130  // ## save images command
131  // std::stringstream ss;
132  // ss << "_" << std::setw(3) << std::setfill('0') << (i - 1);
133  // SaveScreenshot(std::move(left), ss.str());
134  //
135  // ## ffmpeg command
136  // ```
137  // ffmpeg -framerate 30 -pattern_type glob -i '*.png' \
138  // -c:v libx264 -pix_fmt yuv420p out.mp4
139  // ```
140  left = std::move(right);
141  }
142 
143  average_rmse = average_rmse / sample_count;
144 
145  // This is a somewhat arbitrary threshold. It could be increased if we wanted.
146  // In the problematic cases previously we should values like 28. Before
147  // increasing this you should manually inspect the behavior in
148  // `AiksTest.GaussianBlurAnimatedBackdrop`. Average RMSE is a able to catch
149  // shimmer but it isn't perfect.
150  EXPECT_TRUE(average_rmse < 1.0) << "average_rmse: " << average_rmse;
151  // An average rmse of 0 would mean that the blur isn't blurring.
152  EXPECT_TRUE(average_rmse >= 0.0) << "average_rmse: " << average_rmse;
153 }
154 
155 TEST_P(DlGoldenTest, StrokedRRectFastBlur) {
156  impeller::Point content_scale = GetContentScale();
157 
158  DlRect rect = DlRect::MakeXYWH(50, 50, 100, 100);
159  DlRoundRect rrect = DlRoundRect::MakeRectRadius(rect, 10.0f);
160  DlPaint fill = DlPaint().setColor(DlColor::kBlue());
161  DlPaint stroke =
162  DlPaint(fill).setDrawStyle(DlDrawStyle::kStroke).setStrokeWidth(10.0f);
163  DlPaint blur = DlPaint(fill).setMaskFilter(
164  DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 5.0, true));
165  DlPaint blur_stroke =
166  DlPaint(blur).setDrawStyle(DlDrawStyle::kStroke).setStrokeWidth(10.0f);
167 
168  DisplayListBuilder builder;
169  builder.DrawColor(DlColor(0xff111111), DlBlendMode::kSrc);
170  builder.Scale(content_scale.x, content_scale.y);
171  builder.DrawRoundRect(rrect, fill);
172  builder.DrawRoundRect(rrect.Shift(150, 0), stroke);
173  builder.DrawRoundRect(rrect.Shift(0, 150), blur);
174  builder.DrawRoundRect(rrect.Shift(150, 150), blur_stroke);
175 
176  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
177 }
178 
179 } // namespace testing
180 } // namespace flutter
Describes a typeface along with any modifications to its intrinsic properties.
Definition: font.h:35
TEST_P(DlGoldenTest, TextBlurMaskFilterRespectCTM)
bool RenderTextInCanvasSkia(const std::shared_ptr< Context > &context, DisplayListBuilder &canvas, const std::string &text, const std::string_view &font_fixture, const TextRenderOptions &options={})
flutter::DlRect DlRect
Definition: dl_dispatcher.h:25
flutter::DlRoundRect DlRoundRect
Definition: dl_dispatcher.h:27
flutter::DlPoint DlPoint
Definition: dl_dispatcher.h:24