|
6 | 6 |
|
7 | 7 | #include "flutter/display_list/dl_builder.h" |
8 | 8 | #include "flutter/display_list/effects/dl_mask_filter.h" |
| 9 | +#include "flutter/impeller/golden_tests/screenshot.h" |
9 | 10 | #include "flutter/testing/testing.h" |
10 | 11 | #include "gtest/gtest.h" |
11 | 12 | #include "impeller/typographer/backends/skia/text_frame_skia.h" |
@@ -110,5 +111,120 @@ TEST_P(DlGoldenTest, TextBlurMaskFilterDisrespectCTM) { |
110 | 111 | ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); |
111 | 112 | } |
112 | 113 |
|
| 114 | +namespace { |
| 115 | +double CalculateDistance(const uint8_t* left, const uint8_t* right) { |
| 116 | + double diff[4] = { |
| 117 | + static_cast<double>(left[0]) - right[0], // |
| 118 | + static_cast<double>(left[1]) - right[1], // |
| 119 | + static_cast<double>(left[2]) - right[2], // |
| 120 | + static_cast<double>(left[3]) - right[3] // |
| 121 | + }; |
| 122 | + return sqrt((diff[0] * diff[0]) + // |
| 123 | + (diff[1] * diff[1]) + // |
| 124 | + (diff[2] * diff[2]) + // |
| 125 | + (diff[3] * diff[3])); |
| 126 | +} |
| 127 | + |
| 128 | +double RMSE(const impeller::testing::Screenshot* left, |
| 129 | + const impeller::testing::Screenshot* right) { |
| 130 | + FML_CHECK(left); |
| 131 | + FML_CHECK(right); |
| 132 | + FML_CHECK(left->GetWidth() == right->GetWidth()); |
| 133 | + FML_CHECK(left->GetHeight() == right->GetHeight()); |
| 134 | + |
| 135 | + int64_t samples = left->GetWidth() * left->GetHeight(); |
| 136 | + double tally = 0; |
| 137 | + |
| 138 | + const uint8_t* left_ptr = left->GetBytes(); |
| 139 | + const uint8_t* right_ptr = right->GetBytes(); |
| 140 | + for (int64_t i = 0; i < samples; ++i, left_ptr += 4, right_ptr += 4) { |
| 141 | + double distance = CalculateDistance(left_ptr, right_ptr); |
| 142 | + tally += distance * distance; |
| 143 | + } |
| 144 | + |
| 145 | + return sqrt(tally / static_cast<double>(samples)); |
| 146 | +} |
| 147 | +} // namespace |
| 148 | + |
| 149 | +// This is a test to make sure that we don't regress "shimmering" in the |
| 150 | +// gaussian blur. Shimmering is abrupt changes in signal when making tiny |
| 151 | +// changes to the blur parameters. |
| 152 | +// |
| 153 | +// See also: |
| 154 | +// - https://github.com/flutter/flutter/issues/152195 |
| 155 | +TEST_P(DlGoldenTest, ShimmerTest) { |
| 156 | + impeller::Point content_scale = GetContentScale(); |
| 157 | + auto draw = [&](DlCanvas* canvas, const std::vector<sk_sp<DlImage>>& images, |
| 158 | + float sigma) { |
| 159 | + canvas->DrawColor(DlColor(0xff111111)); |
| 160 | + canvas->Scale(content_scale.x, content_scale.y); |
| 161 | + |
| 162 | + DlPaint paint; |
| 163 | + canvas->DrawImage(images[0], SkPoint::Make(10.135, 10.36334), |
| 164 | + DlImageSampling::kLinear, &paint); |
| 165 | + |
| 166 | + SkRect save_layer_bounds = SkRect::MakeLTRB(0, 0, 1024, 768); |
| 167 | + DlBlurImageFilter blur(sigma, sigma, DlTileMode::kDecal); |
| 168 | + canvas->ClipRect(SkRect::MakeLTRB(11.125, 10.3737, 911.25, 755.3333)); |
| 169 | + canvas->SaveLayer(&save_layer_bounds, /*paint=*/nullptr, &blur); |
| 170 | + canvas->Restore(); |
| 171 | + }; |
| 172 | + |
| 173 | + std::vector<sk_sp<DlImage>> images; |
| 174 | + images.emplace_back(CreateDlImageForFixture("boston.jpg")); |
| 175 | + |
| 176 | + auto make_screenshot = [&](float sigma) { |
| 177 | + DisplayListBuilder builder; |
| 178 | + draw(&builder, images, sigma); |
| 179 | + |
| 180 | + std::unique_ptr<impeller::testing::Screenshot> screenshot = |
| 181 | + MakeScreenshot(builder.Build()); |
| 182 | + return screenshot; |
| 183 | + }; |
| 184 | + |
| 185 | + float start_sigma = 10.0f; |
| 186 | + std::unique_ptr<impeller::testing::Screenshot> left = |
| 187 | + make_screenshot(start_sigma); |
| 188 | + if (!left) { |
| 189 | + GTEST_SKIP() << "making screenshots not supported."; |
| 190 | + } |
| 191 | + |
| 192 | + double average_rmse = 0.0; |
| 193 | + const int32_t sample_count = 200; |
| 194 | + for (int i = 1; i <= sample_count; ++i) { |
| 195 | + float sigma = start_sigma + (i / 2.f); |
| 196 | + std::unique_ptr<impeller::testing::Screenshot> right = |
| 197 | + make_screenshot(sigma); |
| 198 | + double rmse = RMSE(left.get(), right.get()); |
| 199 | + average_rmse += rmse; |
| 200 | + |
| 201 | + // To debug this output the frames can be written out to disk then |
| 202 | + // transformed to a video with ffmpeg. |
| 203 | + // |
| 204 | + // ## save images command |
| 205 | + // std::stringstream ss; |
| 206 | + // ss << "_" << std::setw(3) << std::setfill('0') << (i - 1); |
| 207 | + // SaveScreenshot(std::move(left), ss.str()); |
| 208 | + // |
| 209 | + // ## ffmpeg command |
| 210 | + // ``` |
| 211 | + // ffmpeg -framerate 30 -pattern_type glob -i '*.png' \ |
| 212 | + // -c:v libx264 -pix_fmt yuv420p out.mp4 |
| 213 | + // ``` |
| 214 | + left = std::move(right); |
| 215 | + } |
| 216 | + |
| 217 | + average_rmse = average_rmse / sample_count; |
| 218 | + |
| 219 | + // This is a somewhat arbitrary threshold. It could be increased if we wanted. |
| 220 | + // In the problematic cases previously we should values like 28. Before |
| 221 | + // increasing this you should manually inspect the behavior in |
| 222 | + // `AiksTest.GaussianBlurAnimatedBackdrop`. Average RMSE is a able to catch |
| 223 | + // shimmer but it isn't perfect. |
| 224 | + EXPECT_TRUE(average_rmse < 1.0) << "average_rmse: " << average_rmse; |
| 225 | + // An average rmse of 0 would mean that the blur isn't blurring. |
| 226 | + EXPECT_TRUE(average_rmse >= 0.0) << "average_rmse: " << average_rmse; |
| 227 | +} |
| 228 | + |
113 | 229 | } // namespace testing |
114 | 230 | } // namespace flutter |
0 commit comments