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", //
34  DlPoint(101, 101), options));
35  options.mask_filter = nullptr;
36  options.color = DlColor::kRed();
37  ASSERT_TRUE(RenderTextInCanvasSkia(canvas, "hello world",
38  "Roboto-Regular.ttf", //
39  DlPoint(100, 100), 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", //
61  DlPoint(101, 101), options));
62  options.mask_filter = nullptr;
63  options.color = DlColor::kRed();
64  ASSERT_TRUE(RenderTextInCanvasSkia(canvas, "hello world",
65  "Roboto-Regular.ttf", //
66  DlPoint(100, 100), 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], DlPoint(10.135, 10.36334),
90  DlImageSampling::kLinear, &paint);
91 
92  DlRect save_layer_bounds = DlRect::MakeLTRB(0, 0, 1024, 768);
93  auto blur = DlImageFilter::MakeBlur(sigma, sigma, DlTileMode::kDecal);
94  canvas->ClipRect(DlRect::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  DlRect rect = DlRect::MakeXYWH(50, 50, 100, 100);
158  DlRoundRect rrect = DlRoundRect::MakeRectRadius(rect, 10.0f);
159  DlPaint fill = DlPaint().setColor(DlColor::kBlue());
160  DlPaint stroke =
161  DlPaint(fill).setDrawStyle(DlDrawStyle::kStroke).setStrokeWidth(10.0f);
162  DlPaint blur = DlPaint(fill).setMaskFilter(
163  DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 5.0, true));
164  DlPaint blur_stroke =
165  DlPaint(blur).setDrawStyle(DlDrawStyle::kStroke).setStrokeWidth(10.0f);
166 
167  DisplayListBuilder builder;
168  builder.DrawColor(DlColor(0xff111111), DlBlendMode::kSrc);
169  builder.Scale(content_scale.x, content_scale.y);
170  builder.DrawRoundRect(rrect, fill);
171  builder.DrawRoundRect(rrect.Shift(150, 0), stroke);
172  builder.DrawRoundRect(rrect.Shift(0, 150), blur);
173  builder.DrawRoundRect(rrect.Shift(150, 150), blur_stroke);
174 
175  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
176 }
177 
178 // Top left and bottom right circles are expected to be comparable (not exactly
179 // equal).
180 // See also: https://github.com/flutter/flutter/issues/152778
181 TEST_P(DlGoldenTest, LargeDownscaleRrect) {
182  impeller::Point content_scale = GetContentScale();
183  auto draw = [&](DlCanvas* canvas, const std::vector<sk_sp<DlImage>>& images) {
184  canvas->Scale(content_scale.x, content_scale.y);
185  canvas->DrawColor(DlColor(0xff111111));
186  {
187  canvas->Save();
188  canvas->Scale(0.25, 0.25);
189  DlPaint paint;
190  paint.setColor(DlColor::kYellow());
191  paint.setMaskFilter(
192  DlBlurMaskFilter::Make(DlBlurStyle::kNormal, /*sigma=*/1000));
193  canvas->DrawCircle(DlPoint(0, 0), 1200, paint);
194  canvas->Restore();
195  }
196 
197  DlPaint paint;
198  paint.setColor(DlColor::kYellow());
199  paint.setMaskFilter(
200  DlBlurMaskFilter::Make(DlBlurStyle::kNormal, /*sigma=*/250));
201  canvas->DrawCircle(DlPoint(1024, 768), 300, paint);
202  };
203 
204  DisplayListBuilder builder;
205  draw(&builder, /*images=*/{});
206 
207  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
208 }
209 
210 } // namespace testing
211 } // 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={}, const std::optional< SkFont > &font=std::nullopt)
flutter::DlRect DlRect
Definition: dl_dispatcher.h:25
flutter::DlRoundRect DlRoundRect
Definition: dl_dispatcher.h:27
flutter::DlPoint DlPoint
Definition: dl_dispatcher.h:24