diff --git a/display_list/skia/dl_sk_canvas.cc b/display_list/skia/dl_sk_canvas.cc index 73191964230f8..2dfd11e6547d4 100644 --- a/display_list/skia/dl_sk_canvas.cc +++ b/display_list/skia/dl_sk_canvas.cc @@ -58,8 +58,11 @@ void DlSkCanvasAdapter::SaveLayer(const SkRect* bounds, sk_sp sk_backdrop = ToSk(backdrop); SkOptionalPaint sk_paint(paint); TRACE_EVENT0("flutter", "Canvas::saveLayer"); - delegate_->saveLayer( - SkCanvas::SaveLayerRec{bounds, sk_paint(), sk_backdrop.get(), 0}); + SkCanvas::SaveLayerRec params(bounds, sk_paint(), sk_backdrop.get(), 0); + if (sk_backdrop && backdrop->asBlur()) { + params.fBackdropTileMode = ToSk(backdrop->asBlur()->tile_mode()); + } + delegate_->saveLayer(params); } void DlSkCanvasAdapter::Restore() { diff --git a/display_list/skia/dl_sk_dispatcher.cc b/display_list/skia/dl_sk_dispatcher.cc index 30f79f09f0163..33b11166cd5ae 100644 --- a/display_list/skia/dl_sk_dispatcher.cc +++ b/display_list/skia/dl_sk_dispatcher.cc @@ -70,8 +70,11 @@ void DlSkCanvasDispatcher::saveLayer(const DlRect& bounds, const sk_sp sk_backdrop = ToSk(backdrop); const SkRect* sl_bounds = options.bounds_from_caller() ? &ToSkRect(bounds) : nullptr; - canvas_->saveLayer( - SkCanvas::SaveLayerRec(sl_bounds, paint, sk_backdrop.get(), 0)); + SkCanvas::SaveLayerRec params(sl_bounds, paint, sk_backdrop.get(), 0); + if (sk_backdrop && backdrop->asBlur()) { + params.fBackdropTileMode = ToSk(backdrop->asBlur()->tile_mode()); + } + canvas_->saveLayer(params); // saveLayer will apply the current opacity on behalf of the children // so they will inherit an opaque opacity. save_opacity(SK_Scalar1); diff --git a/display_list/testing/dl_rendering_unittests.cc b/display_list/testing/dl_rendering_unittests.cc index 1cde7bdd7f4da..05b6ecc627561 100644 --- a/display_list/testing/dl_rendering_unittests.cc +++ b/display_list/testing/dl_rendering_unittests.cc @@ -1330,7 +1330,8 @@ class CanvasCompareTester { [=](const SkSetupContext& ctx) { sk_backdrop_setup(ctx); ctx.canvas->saveLayer(SkCanvas::SaveLayerRec( - nullptr, nullptr, sk_backdrop.get(), 0)); + nullptr, nullptr, sk_backdrop.get(), + SkTileMode::kDecal, nullptr, 0)); sk_content_setup(ctx); }, [=](const DlSetupContext& ctx) { @@ -1345,7 +1346,8 @@ class CanvasCompareTester { [=](const SkSetupContext& ctx) { sk_backdrop_setup(ctx); ctx.canvas->saveLayer(SkCanvas::SaveLayerRec( - &layer_bounds, nullptr, sk_backdrop.get(), 0)); + &layer_bounds, nullptr, sk_backdrop.get(), + SkTileMode::kDecal, nullptr, 0)); sk_content_setup(ctx); }, [=](const DlSetupContext& ctx) { @@ -1362,7 +1364,8 @@ class CanvasCompareTester { sk_backdrop_setup(ctx); ctx.canvas->clipRect(layer_bounds); ctx.canvas->saveLayer(SkCanvas::SaveLayerRec( - nullptr, nullptr, sk_backdrop.get(), 0)); + nullptr, nullptr, sk_backdrop.get(), + SkTileMode::kDecal, nullptr, 0)); sk_content_setup(ctx); }, [=](const DlSetupContext& ctx) { diff --git a/testing/dart/painting_test.dart b/testing/dart/painting_test.dart index 68388a09a4071..5ba22945c3767 100644 --- a/testing/dart/painting_test.dart +++ b/testing/dart/painting_test.dart @@ -8,6 +8,8 @@ import 'dart:ui'; import 'package:test/test.dart'; import 'package:vector_math/vector_math_64.dart'; +import 'goldens.dart'; + typedef CanvasCallback = void Function(Canvas canvas); void main() { @@ -107,6 +109,91 @@ void main() { redClippedPicture.dispose(); }); + Image backdropBlurWithTileMode(TileMode tileMode) { + Picture makePicture(CanvasCallback callback) { + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder); + callback(canvas); + return recorder.endRecording(); + } + + const double rectSize = 10; + const int count = 50; + const double imgSize = rectSize * count; + + final Picture blueGreenGridPicture = makePicture((Canvas canvas) { + const Color white = Color(0xFFFFFFFF); + const Color purple = Color(0xFFFF00FF); + const Color blue = Color(0xFF0000FF); + const Color green = Color(0xFF00FF00); + const Color yellow = Color(0xFFFFFF00); + const Color red = Color(0xFFFF0000); + canvas.drawColor(white, BlendMode.src); + for (int i = 0; i < count; i++) { + for (int j = 0; j < count; j++) { + final bool rectOdd = (i + j) & 1 == 0; + final Color fg = (i < count / 2) + ? ((j < count / 2) ? green : blue) + : ((j < count / 2) ? yellow : red); + canvas.drawRect(Rect.fromLTWH(i * rectSize, j * rectSize, rectSize, rectSize), + Paint()..color = rectOdd ? fg : white); + } + } + canvas.drawRect(const Rect.fromLTWH(0, 0, imgSize, 1), Paint()..color = purple); + canvas.drawRect(const Rect.fromLTWH(0, 0, 1, imgSize), Paint()..color = purple); + canvas.drawRect(const Rect.fromLTWH(0, imgSize - 1, imgSize, 1), Paint()..color = purple); + canvas.drawRect(const Rect.fromLTWH(imgSize - 1, 0, 1, imgSize), Paint()..color = purple); + }); + + final SceneBuilder sceneBuilder = SceneBuilder(); + sceneBuilder.addPicture(Offset.zero, blueGreenGridPicture); + sceneBuilder.pushBackdropFilter(ImageFilter.blur(sigmaX: 20, sigmaY: 20, tileMode: tileMode)); + + final Scene scene = sceneBuilder.build(); + final Image image = scene.toImageSync(imgSize.round(), imgSize.round()); + + scene.dispose(); + blueGreenGridPicture.dispose(); + + return image; + } + + test('BackdropFilter with Blur honors TileMode.decal', () async { + final Image image = backdropBlurWithTileMode(TileMode.decal); + + final ImageComparer comparer = await ImageComparer.create(); + await comparer.addGoldenImage(image, 'dart_ui_backdrop_filter_blur_decal_tile_mode.png'); + + image.dispose(); + }); + + test('BackdropFilter with Blur honors TileMode.clamp', () async { + final Image image = backdropBlurWithTileMode(TileMode.clamp); + + final ImageComparer comparer = await ImageComparer.create(); + await comparer.addGoldenImage(image, 'dart_ui_backdrop_filter_blur_clamp_tile_mode.png'); + + image.dispose(); + }); + + test('BackdropFilter with Blur honors TileMode.mirror', () async { + final Image image = backdropBlurWithTileMode(TileMode.mirror); + + final ImageComparer comparer = await ImageComparer.create(); + await comparer.addGoldenImage(image, 'dart_ui_backdrop_filter_blur_mirror_tile_mode.png'); + + image.dispose(); + }); + + test('BackdropFilter with Blur honors TileMode.repeated', () async { + final Image image = backdropBlurWithTileMode(TileMode.repeated); + + final ImageComparer comparer = await ImageComparer.create(); + await comparer.addGoldenImage(image, 'dart_ui_backdrop_filter_blur_repeated_tile_mode.png'); + + image.dispose(); + }); + test('ImageFilter.matrix defaults to FilterQuality.medium', () { final Float64List data = Matrix4.identity().storage; expect(