diff --git a/lib/web_ui/dev/goldens_lock.yaml b/lib/web_ui/dev/goldens_lock.yaml index 4779b21430011..280ea2ccba9e0 100644 --- a/lib/web_ui/dev/goldens_lock.yaml +++ b/lib/web_ui/dev/goldens_lock.yaml @@ -1,2 +1,2 @@ repository: https://github.com/flutter/goldens.git -revision: ac75f12c6e93461369e1391da6cc20bf8cb08829 +revision: c808c28c81b6c3143ae969e8c49bed4a6d49aabb diff --git a/lib/web_ui/lib/src/engine/canvaskit/layer.dart b/lib/web_ui/lib/src/engine/canvaskit/layer.dart index b9c116ebb5006..0ca0a2f1627fd 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/layer.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/layer.dart @@ -449,8 +449,8 @@ class PhysicalShapeLayer extends ContainerLayer assert(needsPainting); if (_elevation != 0) { - drawShadow(paintContext.leafNodesCanvas!, _path, _shadowColor!, _elevation, - _color.alpha != 0xff); + drawShadow(paintContext.leafNodesCanvas!, _path, _shadowColor!, + _elevation, _color.alpha != 0xff); } final CkPaint paint = CkPaint()..color = _color; @@ -497,6 +497,25 @@ class PhysicalShapeLayer extends ContainerLayer } } +/// A layer which contains a [ui.ColorFilter]. +class ColorFilterLayer extends ContainerLayer { + ColorFilterLayer(this.filter); + + final ui.ColorFilter filter; + + @override + void paint(PaintContext paintContext) { + assert(needsPainting); + + CkPaint paint = CkPaint(); + paint.colorFilter = filter; + + paintContext.internalNodesCanvas.saveLayer(paintBounds, paint); + paintChildren(paintContext); + paintContext.internalNodesCanvas.restore(); + } +} + /// A layer which renders a platform view (an HTML element in this case). class PlatformViewLayer extends Layer { PlatformViewLayer(this.viewId, this.offset, this.width, this.height); diff --git a/lib/web_ui/lib/src/engine/canvaskit/layer_scene_builder.dart b/lib/web_ui/lib/src/engine/canvaskit/layer_scene_builder.dart index 9dfc895784015..e6f6ea19af3ec 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/layer_scene_builder.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/layer_scene_builder.dart @@ -138,12 +138,13 @@ class LayerSceneBuilder implements ui.SceneBuilder { } @override - ui.ColorFilterEngineLayer pushColorFilter( + ui.ColorFilterEngineLayer? pushColorFilter( ui.ColorFilter filter, { ui.ColorFilterEngineLayer? oldLayer, }) { assert(filter != null); // ignore: unnecessary_null_comparison - throw UnimplementedError(); + pushLayer(ColorFilterLayer(filter)); + return null; } ui.ImageFilterEngineLayer? pushImageFilter( diff --git a/lib/web_ui/test/canvaskit/color_filter_golden_test.dart b/lib/web_ui/test/canvaskit/color_filter_golden_test.dart new file mode 100644 index 0000000000000..01cfee2ceee1a --- /dev/null +++ b/lib/web_ui/test/canvaskit/color_filter_golden_test.dart @@ -0,0 +1,78 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// @dart = 2.12 +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +import 'package:web_engine_tester/golden_tester.dart'; + +import 'common.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +const ui.Rect region = const ui.Rect.fromLTRB(0, 0, 500, 250); + +Future matchSceneGolden(String goldenFile, LayerScene scene, + {bool write = false}) async { + final EnginePlatformDispatcher dispatcher = + ui.window.platformDispatcher as EnginePlatformDispatcher; + dispatcher.rasterizer!.draw(scene.layerTree); + await matchGoldenFile(goldenFile, region: region, write: write); +} + +void testMain() { + group('ColorFilter', () { + setUpCanvasKitTest(); + + test('ColorFilter.matrix applies a color filter', () async { + final LayerSceneBuilder builder = LayerSceneBuilder(); + + builder.pushOffset(0, 0); + + // Draw a red circle and apply it to the scene. + final CkPictureRecorder recorder = CkPictureRecorder(); + final CkCanvas canvas = recorder.beginRecording(region); + + canvas.drawCircle( + ui.Offset(75, 125), + 50, + CkPaint()..color = ui.Color.fromARGB(255, 255, 0, 0), + ); + final CkPicture redCircle = recorder.endRecording(); + + builder.addPicture(ui.Offset.zero, redCircle); + + // Apply a "greyscale" color filter. + builder.pushColorFilter(ui.ColorFilter.matrix([ + 0.2126, 0.7152, 0.0722, 0, 0, // + 0.2126, 0.7152, 0.0722, 0, 0, // + 0.2126, 0.7152, 0.0722, 0, 0, // + 0, 0, 0, 1, 0, // + ])); + + // Draw another red circle and apply it to the scene. + // This one should be grey since we have the color filter. + final CkPictureRecorder recorder2 = CkPictureRecorder(); + final CkCanvas canvas2 = recorder2.beginRecording(region); + + canvas2.drawCircle( + ui.Offset(425, 125), + 50, + CkPaint()..color = ui.Color.fromARGB(255, 255, 0, 0), + ); + final CkPicture greyCircle = recorder2.endRecording(); + + builder.addPicture(ui.Offset.zero, greyCircle); + + await matchSceneGolden('canvaskit_colorfilter.png', builder.build()); + }); + // TODO: https://github.com/flutter/flutter/issues/60040 + // TODO: https://github.com/flutter/flutter/issues/71520 + }, skip: isIosSafari || isFirefox); +} diff --git a/lib/web_ui/test/canvaskit/scene_test.dart b/lib/web_ui/test/canvaskit/scene_test.dart index 789e9b62d2116..e76e7e945d26b 100644 --- a/lib/web_ui/test/canvaskit/scene_test.dart +++ b/lib/web_ui/test/canvaskit/scene_test.dart @@ -49,6 +49,17 @@ void testMain() { final ui.Image sceneImage = await scene.toImage(100, 100); expect(sceneImage, isA()); }); - // TODO: https://github.com/flutter/flutter/issues/60040 + + test('pushColorFilter does not throw', () async { + final ui.SceneBuilder builder = ui.SceneBuilder(); + expect(builder, isA()); + + builder.pushOffset(0, 0); + builder.pushColorFilter(ui.ColorFilter.srgbToLinearGamma()); + + final ui.Scene scene = builder.build(); + expect(scene, isNotNull); + }); + // TODO: https://github.com/flutter/flutter/issues/60040 }, skip: isIosSafari); }