| 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
5 #include "impeller/display_list/dl_golden_unittests.h"
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"
11 #include "flutter/impeller/geometry/round_rect.h"
12 #include "flutter/impeller/golden_tests/screenshot.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
impeller::DlPlayground
Definition: dl_playground.h:16
impeller::Font
Describes a typeface along with any modifications to its intrinsic properties.
Definition: font.h:35
dl_golden_unittests.h
flutter::testing::TEST_P
TEST_P(DlGoldenTest, TextBlurMaskFilterRespectCTM)
Definition: dl_golden_blur_unittests.cc:21
flutter
Definition: dl_golden_blur_unittests.cc:16
impeller::testing::RenderTextInCanvasSkia
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)
Definition: aiks_dl_text_unittests.cc:45
impeller::DlRect
flutter::DlRect DlRect
Definition: dl_dispatcher.h:25
impeller::DlRoundRect
flutter::DlRoundRect DlRoundRect
Definition: dl_dispatcher.h:27
impeller::DlPoint
flutter::DlPoint DlPoint
Definition: dl_dispatcher.h:24
round_rect.h
screenshot.h
impeller::TPoint< Scalar >
impeller::TPoint::y
Type y
Definition: point.h:31
impeller::TPoint::x
Type x
Definition: point.h:30