diff --git a/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart b/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart index 8c0eba1f3daa3..c0284632069d1 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart @@ -1493,6 +1493,18 @@ extension SkImageFilterNamespaceExtension on SkImageFilterNamespace { SkImageFilter outer, SkImageFilter inner, ); + + external SkImageFilter MakeDilate( + double radiusX, + double radiusY, + void input, // we don't use this yet + ); + + external SkImageFilter MakeErode( + double radiusX, + double radiusY, + void input, // we don't use this yet + ); } @JS() diff --git a/lib/web_ui/lib/src/engine/canvaskit/image_filter.dart b/lib/web_ui/lib/src/engine/canvaskit/image_filter.dart index 19b89061144a7..9ab40927656c3 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/image_filter.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/image_filter.dart @@ -40,6 +40,11 @@ abstract class CkImageFilter implements CkManagedSkImageFilterConvertible { factory CkImageFilter.matrix( {required Float64List matrix, required ui.FilterQuality filterQuality}) = _CkMatrixImageFilter; + factory CkImageFilter.dilate( + {required double radiusX, + required double radiusY}) = _CkDilateImageFilter; + factory CkImageFilter.erode( + {required double radiusX, required double radiusY}) = _CkErodeImageFilter; factory CkImageFilter.compose( {required CkImageFilter outer, required CkImageFilter inner}) = _CkComposeImageFilter; @@ -94,10 +99,9 @@ class _CkBlurImageFilter extends CkImageFilter { final SkImageFilter skImageFilter; if (sigmaX == 0 && sigmaY == 0) { skImageFilter = canvasKit.ImageFilter.MakeMatrixTransform( - toSkMatrixFromFloat32(Matrix4.identity().storage), - toSkFilterOptions(ui.FilterQuality.none), - null - ); + toSkMatrixFromFloat32(Matrix4.identity().storage), + toSkFilterOptions(ui.FilterQuality.none), + null); } else { skImageFilter = canvasKit.ImageFilter.MakeBlur( sigmaX, @@ -146,7 +150,8 @@ class _CkMatrixImageFilter extends CkImageFilter { : matrix = Float64List.fromList(matrix), _transform = Matrix4.fromFloat32List(toMatrix32(matrix)), super._() { - final SkImageFilter skImageFilter = canvasKit.ImageFilter.MakeMatrixTransform( + final SkImageFilter skImageFilter = + canvasKit.ImageFilter.MakeMatrixTransform( toSkMatrixFromFloat64(matrix), toSkFilterOptions(filterQuality), null, @@ -185,6 +190,86 @@ class _CkMatrixImageFilter extends CkImageFilter { Matrix4 get transform => _transform; } +class _CkDilateImageFilter extends CkImageFilter { + _CkDilateImageFilter({required this.radiusX, required this.radiusY}) + : super._() { + final SkImageFilter skImageFilter = canvasKit.ImageFilter.MakeDilate( + radiusX, + radiusY, + null, + ); + _ref = UniqueRef(this, skImageFilter, 'ImageFilter.dilate'); + } + + final double radiusX; + final double radiusY; + + late final UniqueRef _ref; + + @override + void imageFilter(SkImageFilterBorrow borrow) { + borrow(_ref.nativeObject); + } + + @override + bool operator ==(Object other) { + if (runtimeType != other.runtimeType) { + return false; + } + return other is _CkDilateImageFilter && + other.radiusX == radiusX && + other.radiusY == radiusY; + } + + @override + int get hashCode => Object.hash(radiusX, radiusY); + + @override + String toString() { + return 'ImageFilter.dilate($radiusX, $radiusY)'; + } +} + +class _CkErodeImageFilter extends CkImageFilter { + _CkErodeImageFilter({required this.radiusX, required this.radiusY}) + : super._() { + final SkImageFilter skImageFilter = canvasKit.ImageFilter.MakeErode( + radiusX, + radiusY, + null, + ); + _ref = UniqueRef(this, skImageFilter, 'ImageFilter.erode'); + } + + final double radiusX; + final double radiusY; + + late final UniqueRef _ref; + + @override + void imageFilter(SkImageFilterBorrow borrow) { + borrow(_ref.nativeObject); + } + + @override + bool operator ==(Object other) { + if (runtimeType != other.runtimeType) { + return false; + } + return other is _CkErodeImageFilter && + other.radiusX == radiusX && + other.radiusY == radiusY; + } + + @override + int get hashCode => Object.hash(radiusX, radiusY); + + @override + String toString() { + return 'ImageFilter.erode($radiusX, $radiusY)'; + } +} + class _CkComposeImageFilter extends CkImageFilter { _CkComposeImageFilter({required this.outer, required this.inner}) : super._() { diff --git a/lib/web_ui/lib/src/engine/canvaskit/renderer.dart b/lib/web_ui/lib/src/engine/canvaskit/renderer.dart index ff73d6a9358fa..7f2e9ba31e684 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/renderer.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/renderer.dart @@ -188,19 +188,13 @@ class CanvasKitRenderer implements Renderer { @override ui.ImageFilter createDilateImageFilter( - {double radiusX = 0.0, double radiusY = 0.0}) { - // TODO(fzyzcjy): implement dilate. https://github.com/flutter/flutter/issues/101085 - throw UnimplementedError( - 'ImageFilter.dilate not implemented for CanvasKit.'); - } + {double radiusX = 0.0, double radiusY = 0.0}) => + CkImageFilter.dilate(radiusX: radiusX, radiusY: radiusY); @override ui.ImageFilter createErodeImageFilter( - {double radiusX = 0.0, double radiusY = 0.0}) { - // TODO(fzyzcjy): implement erode. https://github.com/flutter/flutter/issues/101085 - throw UnimplementedError( - 'ImageFilter.erode not implemented for CanvasKit.'); - } + {double radiusX = 0.0, double radiusY = 0.0}) => + CkImageFilter.erode(radiusX: radiusX, radiusY: radiusY); @override ui.ImageFilter createMatrixImageFilter(Float64List matrix4, diff --git a/lib/web_ui/test/canvaskit/canvaskit_api_test.dart b/lib/web_ui/test/canvaskit/canvaskit_api_test.dart index bc7eec8660b17..fac74150cd019 100644 --- a/lib/web_ui/test/canvaskit/canvaskit_api_test.dart +++ b/lib/web_ui/test/canvaskit/canvaskit_api_test.dart @@ -529,6 +529,20 @@ void _imageFilterTests() { isNotNull, ); }); + + test('MakeDilate', () { + expect( + canvasKit.ImageFilter.MakeDilate(1, 2, null), + isNotNull, + ); + }); + + test('MakeErode', () { + expect( + canvasKit.ImageFilter.MakeErode(1, 2, null), + isNotNull, + ); + }); } void _mallocTests() { diff --git a/lib/web_ui/test/canvaskit/filter_test.dart b/lib/web_ui/test/canvaskit/filter_test.dart index 1548911aa8c23..e397134cc96c4 100644 --- a/lib/web_ui/test/canvaskit/filter_test.dart +++ b/lib/web_ui/test/canvaskit/filter_test.dart @@ -42,6 +42,8 @@ void testMain() { CkImageFilter.blur(sigmaX: 5, sigmaY: 6, tileMode: ui.TileMode.clamp), CkImageFilter.blur(sigmaX: 6, sigmaY: 5, tileMode: ui.TileMode.clamp), CkImageFilter.blur(sigmaX: 6, sigmaY: 5, tileMode: ui.TileMode.decal), + CkImageFilter.dilate(radiusX: 5, radiusY: 6), + CkImageFilter.erode(radiusX: 7, radiusY: 8), for (final CkColorFilter colorFilter in createColorFilters()) CkImageFilter.color(colorFilter: colorFilter), ]; filters.add(CkImageFilter.compose(outer: filters[0], inner: filters[1])); diff --git a/lib/web_ui/test/ui/filters_test.dart b/lib/web_ui/test/ui/filters_test.dart index 530551727c33b..d2ca97291548e 100644 --- a/lib/web_ui/test/ui/filters_test.dart +++ b/lib/web_ui/test/ui/filters_test.dart @@ -61,7 +61,7 @@ Future testMain() async { radiusY: 5.0, )); await matchGoldenFile('ui_filter_dilate_imagefilter.png', region: region); - }, skip: !isSkwasm); // Only skwasm supports dilate filter right now + }, skip: isHtml); // HTML renderer does not support the dilate filter test('erode filter', () async { await drawTestImageWithPaint(ui.Paint()..imageFilter = ui.ImageFilter.erode( @@ -69,7 +69,7 @@ Future testMain() async { radiusY: 5.0, )); await matchGoldenFile('ui_filter_erode_imagefilter.png', region: region); - }, skip: !isSkwasm); // Only skwasm supports erode filter + }, skip: isHtml); // HTML renderer does not support the erode filter test('matrix filter', () async { await drawTestImageWithPaint(ui.Paint()..imageFilter = ui.ImageFilter.matrix(