From 125bf3bbc8c870e39998f2d3b56f42364065e2a0 Mon Sep 17 00:00:00 2001 From: Harry Terkelsen Date: Thu, 30 Nov 2023 14:27:16 -0800 Subject: [PATCH 1/4] Add ImageFilter.compose --- .../src/engine/canvaskit/image_filter.dart | 59 ++- .../lib/src/engine/canvaskit/renderer.dart | 445 ++++++++---------- lib/web_ui/test/canvaskit/filter_test.dart | 47 +- 3 files changed, 305 insertions(+), 246 deletions(-) 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 2032ccdd88670..700b409f78c77 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/image_filter.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/image_filter.dart @@ -28,8 +28,6 @@ abstract class CkManagedSkImageFilterConvertible implements ui.ImageFilter { } /// The CanvasKit implementation of [ui.ImageFilter]. -/// -/// Currently only supports `blur`, `matrix`, and ColorFilters. abstract class CkImageFilter implements CkManagedSkImageFilterConvertible { factory CkImageFilter.blur( {required double sigmaX, @@ -40,6 +38,9 @@ abstract class CkImageFilter implements CkManagedSkImageFilterConvertible { factory CkImageFilter.matrix( {required Float64List matrix, required ui.FilterQuality filterQuality}) = _CkMatrixImageFilter; + factory CkImageFilter.compose( + {required CkImageFilter outer, + required CkImageFilter inner}) = _CkComposeImageFilter; CkImageFilter._(); @@ -91,10 +92,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, @@ -156,7 +156,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, @@ -194,3 +195,47 @@ class _CkMatrixImageFilter extends CkImageFilter { @override Matrix4 get transform => _transform; } + +class _CkComposeImageFilter extends CkImageFilter { + _CkComposeImageFilter({required this.outer, required this.inner}) + : super._() { + outer.imageFilter((SkImageFilter outerFilter) { + inner.imageFilter((SkImageFilter innerFilter) { + final SkImageFilter skImageFilter = canvasKit.ImageFilter.MakeCompose( + outerFilter, + innerFilter, + ); + _ref = UniqueRef( + this, skImageFilter, 'ImageFilter.compose'); + }); + }); + } + + final CkImageFilter outer; + final CkImageFilter inner; + + 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 _CkComposeImageFilter && + other.outer == outer && + other.inner == inner; + } + + @override + int get hashCode => Object.hash(outer, inner); + + @override + String toString() { + return 'ImageFilter.compose($outer, $inner)'; + } +} diff --git a/lib/web_ui/lib/src/engine/canvaskit/renderer.dart b/lib/web_ui/lib/src/engine/canvaskit/renderer.dart index a948b5b55f08c..7e7e6ea531ef4 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/renderer.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/renderer.dart @@ -84,12 +84,11 @@ class CanvasKitRenderer implements Renderer { List? textureCoordinates, List? colors, List? indices, - }) => CkVertices( - mode, - positions, - textureCoordinates: textureCoordinates, - colors: colors, - indices: indices); + }) => + CkVertices(mode, positions, + textureCoordinates: textureCoordinates, + colors: colors, + indices: indices); @override ui.Vertices createVerticesRaw( @@ -98,26 +97,23 @@ class CanvasKitRenderer implements Renderer { Float32List? textureCoordinates, Int32List? colors, Uint16List? indices, - }) => CkVertices.raw( - mode, - positions, - textureCoordinates: textureCoordinates, - colors: colors, - indices: indices); + }) => + CkVertices.raw(mode, positions, + textureCoordinates: textureCoordinates, + colors: colors, + indices: indices); @override ui.Canvas createCanvas(ui.PictureRecorder recorder, [ui.Rect? cullRect]) => - CanvasKitCanvas(recorder, cullRect); + CanvasKitCanvas(recorder, cullRect); @override ui.Gradient createLinearGradient( - ui.Offset from, - ui.Offset to, - List colors, [ - List? colorStops, - ui.TileMode tileMode = ui.TileMode.clamp, - Float32List? matrix4 - ]) => CkGradientLinear(from, to, colors, colorStops, tileMode, matrix4); + ui.Offset from, ui.Offset to, List colors, + [List? colorStops, + ui.TileMode tileMode = ui.TileMode.clamp, + Float32List? matrix4]) => + CkGradientLinear(from, to, colors, colorStops, tileMode, matrix4); @override ui.Gradient createRadialGradient( @@ -127,38 +123,27 @@ class CanvasKitRenderer implements Renderer { List? colorStops, ui.TileMode tileMode = ui.TileMode.clamp, Float32List? matrix4, - ]) => CkGradientRadial(center, radius, colors, colorStops, tileMode, matrix4); + ]) => + CkGradientRadial(center, radius, colors, colorStops, tileMode, matrix4); @override - ui.Gradient createConicalGradient( - ui.Offset focal, - double focalRadius, - ui.Offset center, - double radius, - List colors, - [List? colorStops, - ui.TileMode tileMode = ui.TileMode.clamp, - Float32List? matrix] - ) => CkGradientConical( - focal, - focalRadius, - center, - radius, - colors, - colorStops, - tileMode, - matrix); - - @override - ui.Gradient createSweepGradient( - ui.Offset center, - List colors, [ - List? colorStops, - ui.TileMode tileMode = ui.TileMode.clamp, - double startAngle = 0.0, - double endAngle = math.pi * 2, - Float32List? matrix4 - ]) => CkGradientSweep(center, colors, colorStops, tileMode, startAngle, endAngle, matrix4); + ui.Gradient createConicalGradient(ui.Offset focal, double focalRadius, + ui.Offset center, double radius, List colors, + [List? colorStops, + ui.TileMode tileMode = ui.TileMode.clamp, + Float32List? matrix]) => + CkGradientConical(focal, focalRadius, center, radius, colors, colorStops, + tileMode, matrix); + + @override + ui.Gradient createSweepGradient(ui.Offset center, List colors, + [List? colorStops, + ui.TileMode tileMode = ui.TileMode.clamp, + double startAngle = 0.0, + double endAngle = math.pi * 2, + Float32List? matrix4]) => + CkGradientSweep( + center, colors, colorStops, tileMode, startAngle, endAngle, matrix4); @override ui.PictureRecorder createPictureRecorder() => CkPictureRecorder(); @@ -167,60 +152,55 @@ class CanvasKitRenderer implements Renderer { ui.SceneBuilder createSceneBuilder() => LayerSceneBuilder(); @override - ui.ImageFilter createBlurImageFilter({ - double sigmaX = 0.0, - double sigmaY = 0.0, - ui.TileMode tileMode = ui.TileMode.clamp - }) => CkImageFilter.blur(sigmaX: sigmaX, sigmaY: sigmaY, tileMode: tileMode); + ui.ImageFilter createBlurImageFilter( + {double sigmaX = 0.0, + double sigmaY = 0.0, + ui.TileMode tileMode = ui.TileMode.clamp}) => + CkImageFilter.blur(sigmaX: sigmaX, sigmaY: sigmaY, tileMode: tileMode); @override - ui.ImageFilter createDilateImageFilter({double radiusX = 0.0, double radiusY = 0.0}) { + 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.'); + throw UnimplementedError( + 'ImageFilter.dilate not implemented for CanvasKit.'); } @override - ui.ImageFilter createErodeImageFilter({double radiusX = 0.0, double radiusY = 0.0}) { + 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.'); + throw UnimplementedError( + 'ImageFilter.erode not implemented for CanvasKit.'); } @override - ui.ImageFilter createMatrixImageFilter( - Float64List matrix4, { - ui.FilterQuality filterQuality = ui.FilterQuality.low - }) => CkImageFilter.matrix(matrix: matrix4, filterQuality: filterQuality); + ui.ImageFilter createMatrixImageFilter(Float64List matrix4, + {ui.FilterQuality filterQuality = ui.FilterQuality.low}) => + CkImageFilter.matrix(matrix: matrix4, filterQuality: filterQuality); @override - ui.ImageFilter composeImageFilters({required ui.ImageFilter outer, required ui.ImageFilter inner}) { - // TODO(ferhat): add implementation - throw UnimplementedError('ImageFilter.compose not implemented for CanvasKit.'); - } + ui.ImageFilter composeImageFilters( + {required ui.ImageFilter outer, required ui.ImageFilter inner}) => + CkImageFilter.compose( + outer: outer as CkImageFilter, inner: inner as CkImageFilter); @override - Future instantiateImageCodec( - Uint8List list, { - int? targetWidth, - int? targetHeight, - bool allowUpscaling = true - }) async => skiaInstantiateImageCodec( - list, - targetWidth, - targetHeight - ); + Future instantiateImageCodec(Uint8List list, + {int? targetWidth, + int? targetHeight, + bool allowUpscaling = true}) async => + skiaInstantiateImageCodec(list, targetWidth, targetHeight); @override - Future instantiateImageCodecFromUrl( - Uri uri, { - ui_web.ImageCodecChunkCallback? chunkCallback - }) => skiaInstantiateWebImageCodec(uri.toString(), chunkCallback); + Future instantiateImageCodecFromUrl(Uri uri, + {ui_web.ImageCodecChunkCallback? chunkCallback}) => + skiaInstantiateWebImageCodec(uri.toString(), chunkCallback); @override ui.Image createImageFromImageBitmap(DomImageBitmap imageBitmap) { - final SkImage? skImage = canvasKit.MakeLazyImageFromImageBitmap( - imageBitmap, - true - ); + final SkImage? skImage = + canvasKit.MakeLazyImageFromImageBitmap(imageBitmap, true); if (skImage == null) { throw Exception('Failed to convert image bitmap to an SkImage.'); } @@ -228,36 +208,26 @@ class CanvasKitRenderer implements Renderer { } @override - void decodeImageFromPixels( - Uint8List pixels, - int width, - int height, - ui.PixelFormat format, - ui.ImageDecoderCallback callback, { - int? rowBytes, - int? targetWidth, - int? targetHeight, - bool allowUpscaling = true - }) => skiaDecodeImageFromPixels( - pixels, - width, - height, - format, - callback, - rowBytes: rowBytes, - targetWidth: targetWidth, - targetHeight: targetHeight, - allowUpscaling: allowUpscaling - ); + void decodeImageFromPixels(Uint8List pixels, int width, int height, + ui.PixelFormat format, ui.ImageDecoderCallback callback, + {int? rowBytes, + int? targetWidth, + int? targetHeight, + bool allowUpscaling = true}) => + skiaDecodeImageFromPixels(pixels, width, height, format, callback, + rowBytes: rowBytes, + targetWidth: targetWidth, + targetHeight: targetHeight, + allowUpscaling: allowUpscaling); @override ui.ImageShader createImageShader( - ui.Image image, - ui.TileMode tmx, - ui.TileMode tmy, - Float64List matrix4, - ui.FilterQuality? filterQuality - ) => CkImageShader(image, tmx, tmy, matrix4, filterQuality); + ui.Image image, + ui.TileMode tmx, + ui.TileMode tmy, + Float64List matrix4, + ui.FilterQuality? filterQuality) => + CkImageShader(image, tmx, tmy, matrix4, filterQuality); @override ui.Path createPath() => CkPath(); @@ -267,111 +237,111 @@ class CanvasKitRenderer implements Renderer { @override ui.Path combinePaths(ui.PathOperation op, ui.Path path1, ui.Path path2) => - CkPath.combine(op, path1, path2); - - @override - ui.TextStyle createTextStyle({ - ui.Color? color, - ui.TextDecoration? decoration, - ui.Color? decorationColor, - ui.TextDecorationStyle? decorationStyle, - double? decorationThickness, - ui.FontWeight? fontWeight, - ui.FontStyle? fontStyle, - ui.TextBaseline? textBaseline, - String? fontFamily, - List? fontFamilyFallback, - double? fontSize, - double? letterSpacing, - double? wordSpacing, - double? height, - ui.TextLeadingDistribution? leadingDistribution, - ui.Locale? locale, - ui.Paint? background, - ui.Paint? foreground, - List? shadows, - List? fontFeatures, - List? fontVariations - }) => CkTextStyle( - color: color, - decoration: decoration, - decorationColor: decorationColor, - decorationStyle: decorationStyle, - decorationThickness: decorationThickness, - fontWeight: fontWeight, - fontStyle: fontStyle, - textBaseline: textBaseline, - fontFamily: fontFamily, - fontFamilyFallback: fontFamilyFallback, - fontSize: fontSize, - letterSpacing: letterSpacing, - wordSpacing: wordSpacing, - height: height, - leadingDistribution: leadingDistribution, - locale: locale, - background: background as CkPaint?, - foreground: foreground as CkPaint?, - shadows: shadows, - fontFeatures: fontFeatures, - fontVariations: fontVariations, - ); - - @override - ui.ParagraphStyle createParagraphStyle({ - ui.TextAlign? textAlign, - ui.TextDirection? textDirection, - int? maxLines, - String? fontFamily, - double? fontSize, - double? height, - ui.TextHeightBehavior? textHeightBehavior, - ui.FontWeight? fontWeight, - ui.FontStyle? fontStyle, - ui.StrutStyle? strutStyle, - String? ellipsis, - ui.Locale? locale - }) => CkParagraphStyle( - textAlign: textAlign, - textDirection: textDirection, - maxLines: maxLines, - fontFamily: fontFamily, - fontSize: fontSize, - height: height, - textHeightBehavior: textHeightBehavior, - fontWeight: fontWeight, - fontStyle: fontStyle, - strutStyle: strutStyle, - ellipsis: ellipsis, - locale: locale, - applyRoundingHack: !ui.ParagraphBuilder.shouldDisableRoundingHack, - ); - - @override - ui.StrutStyle createStrutStyle({ - String? fontFamily, - List? fontFamilyFallback, - double? fontSize, - double? height, - ui.TextLeadingDistribution? leadingDistribution, - double? leading, - ui.FontWeight? fontWeight, - ui.FontStyle? fontStyle, - bool? forceStrutHeight - }) => CkStrutStyle( - fontFamily: fontFamily, - fontFamilyFallback: fontFamilyFallback, - fontSize: fontSize, - height: height, - leadingDistribution: leadingDistribution, - leading: leading, - fontWeight: fontWeight, - fontStyle: fontStyle, - forceStrutHeight: forceStrutHeight, - ); + CkPath.combine(op, path1, path2); + + @override + ui.TextStyle createTextStyle( + {ui.Color? color, + ui.TextDecoration? decoration, + ui.Color? decorationColor, + ui.TextDecorationStyle? decorationStyle, + double? decorationThickness, + ui.FontWeight? fontWeight, + ui.FontStyle? fontStyle, + ui.TextBaseline? textBaseline, + String? fontFamily, + List? fontFamilyFallback, + double? fontSize, + double? letterSpacing, + double? wordSpacing, + double? height, + ui.TextLeadingDistribution? leadingDistribution, + ui.Locale? locale, + ui.Paint? background, + ui.Paint? foreground, + List? shadows, + List? fontFeatures, + List? fontVariations}) => + CkTextStyle( + color: color, + decoration: decoration, + decorationColor: decorationColor, + decorationStyle: decorationStyle, + decorationThickness: decorationThickness, + fontWeight: fontWeight, + fontStyle: fontStyle, + textBaseline: textBaseline, + fontFamily: fontFamily, + fontFamilyFallback: fontFamilyFallback, + fontSize: fontSize, + letterSpacing: letterSpacing, + wordSpacing: wordSpacing, + height: height, + leadingDistribution: leadingDistribution, + locale: locale, + background: background as CkPaint?, + foreground: foreground as CkPaint?, + shadows: shadows, + fontFeatures: fontFeatures, + fontVariations: fontVariations, + ); + + @override + ui.ParagraphStyle createParagraphStyle( + {ui.TextAlign? textAlign, + ui.TextDirection? textDirection, + int? maxLines, + String? fontFamily, + double? fontSize, + double? height, + ui.TextHeightBehavior? textHeightBehavior, + ui.FontWeight? fontWeight, + ui.FontStyle? fontStyle, + ui.StrutStyle? strutStyle, + String? ellipsis, + ui.Locale? locale}) => + CkParagraphStyle( + textAlign: textAlign, + textDirection: textDirection, + maxLines: maxLines, + fontFamily: fontFamily, + fontSize: fontSize, + height: height, + textHeightBehavior: textHeightBehavior, + fontWeight: fontWeight, + fontStyle: fontStyle, + strutStyle: strutStyle, + ellipsis: ellipsis, + locale: locale, + applyRoundingHack: !ui.ParagraphBuilder.shouldDisableRoundingHack, + ); + + @override + ui.StrutStyle createStrutStyle( + {String? fontFamily, + List? fontFamilyFallback, + double? fontSize, + double? height, + ui.TextLeadingDistribution? leadingDistribution, + double? leading, + ui.FontWeight? fontWeight, + ui.FontStyle? fontStyle, + bool? forceStrutHeight}) => + CkStrutStyle( + fontFamily: fontFamily, + fontFamilyFallback: fontFamilyFallback, + fontSize: fontSize, + height: height, + leadingDistribution: leadingDistribution, + leading: leading, + fontWeight: fontWeight, + fontStyle: fontStyle, + forceStrutHeight: forceStrutHeight, + ); @override ui.ParagraphBuilder createParagraphBuilder(ui.ParagraphStyle style) => - CkParagraphBuilder(style); + CkParagraphBuilder(style); @override void renderScene(ui.Scene scene, ui.FlutterView view) { @@ -423,38 +393,39 @@ class CanvasKitRenderer implements Renderer { _programs.clear(); } - static final Map> _programs = >{}; + static final Map> _programs = + >{}; @override Future createFragmentProgram(String assetKey) { if (_programs.containsKey(assetKey)) { return _programs[assetKey]!; } - return _programs[assetKey] = ui_web.assetManager.load(assetKey).then((ByteData data) { + return _programs[assetKey] = + ui_web.assetManager.load(assetKey).then((ByteData data) { return CkFragmentProgram.fromBytes(assetKey, data.buffer.asUint8List()); }); } @override - ui.LineMetrics createLineMetrics({ - required bool hardBreak, - required double ascent, - required double descent, - required double unscaledAscent, - required double height, - required double width, - required double left, - required double baseline, - required int lineNumber - }) => EngineLineMetrics( - hardBreak: hardBreak, - ascent: ascent, - descent: descent, - unscaledAscent: unscaledAscent, - height: height, - width: width, - left: left, - baseline: baseline, - lineNumber: lineNumber - ); + ui.LineMetrics createLineMetrics( + {required bool hardBreak, + required double ascent, + required double descent, + required double unscaledAscent, + required double height, + required double width, + required double left, + required double baseline, + required int lineNumber}) => + EngineLineMetrics( + hardBreak: hardBreak, + ascent: ascent, + descent: descent, + unscaledAscent: unscaledAscent, + height: height, + width: width, + left: left, + baseline: baseline, + lineNumber: lineNumber); } diff --git a/lib/web_ui/test/canvaskit/filter_test.dart b/lib/web_ui/test/canvaskit/filter_test.dart index 5ca14f831a03c..750df247d85ec 100644 --- a/lib/web_ui/test/canvaskit/filter_test.dart +++ b/lib/web_ui/test/canvaskit/filter_test.dart @@ -38,12 +38,15 @@ void testMain() { } List createImageFilters() { - return [ + final List filters = [ 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), for (final CkColorFilter colorFilter in createColorFilters()) CkImageFilter.color(colorFilter: colorFilter), ]; + filters.add(CkImageFilter.compose(outer: filters[0], inner: filters[1])); + filters.add(CkImageFilter.compose(outer: filters[1], inner: filters[3])); + return filters; } setUpCanvasKitTest(); @@ -156,7 +159,47 @@ void testMain() { builder.addPicture(ui.Offset.zero, redCircle1); // The drawn red circle should actually be green with the colorFilter. - await matchSceneGolden('canvaskit_imageFilter_using_colorFilter.png', builder.build(), region: region); + await matchSceneGolden( + 'canvaskit_imageFilter_using_colorFilter.png', builder.build(), + region: region); + }); + + test('using a compose filter', () async { + final CkImageFilter blurFilter = CkImageFilter.blur( + sigmaX: 5, + sigmaY: 5, + tileMode: ui.TileMode.clamp, + ); + final CkColorFilter colorFilter = createCkColorFilter( + const EngineColorFilter.mode( + ui.Color.fromARGB(255, 0, 255, 0), ui.BlendMode.srcIn))!; + final CkImageFilter colorImageFilter = + CkImageFilter.color(colorFilter: colorFilter); + final CkImageFilter composeFilter = + CkImageFilter.compose(outer: blurFilter, inner: colorImageFilter); + + const ui.Rect region = ui.Rect.fromLTRB(0, 0, 500, 250); + + final LayerSceneBuilder builder = LayerSceneBuilder(); + builder.pushOffset(0, 0); + + builder.pushImageFilter(composeFilter); + + final CkPictureRecorder recorder = CkPictureRecorder(); + final CkCanvas canvas = recorder.beginRecording(region); + + canvas.drawCircle( + const ui.Offset(75, 125), + 50, + CkPaint()..color = const ui.Color.fromARGB(255, 255, 0, 0), + ); + final CkPicture redCircle1 = recorder.endRecording(); + builder.addPicture(ui.Offset.zero, redCircle1); + // The drawn red circle should actually be green and blurred. + + await matchSceneGolden( + 'canvaskit_composeImageFilter.png', builder.build(), + region: region); }); }); From 7900561bccba39d0889f1c2a87020423809e7ad0 Mon Sep 17 00:00:00 2001 From: Harry Terkelsen Date: Thu, 30 Nov 2023 14:33:27 -0800 Subject: [PATCH 2/4] Undo dartfmt carnage --- .../src/engine/canvaskit/image_filter.dart | 12 +- .../lib/src/engine/canvaskit/renderer.dart | 437 ++++++++++-------- 2 files changed, 240 insertions(+), 209 deletions(-) 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 700b409f78c77..8ce379fb1b064 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/image_filter.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/image_filter.dart @@ -28,6 +28,8 @@ abstract class CkManagedSkImageFilterConvertible implements ui.ImageFilter { } /// The CanvasKit implementation of [ui.ImageFilter]. +/// +/// Currently only supports `blur`, `matrix`, and ColorFilters. abstract class CkImageFilter implements CkManagedSkImageFilterConvertible { factory CkImageFilter.blur( {required double sigmaX, @@ -92,9 +94,10 @@ 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, @@ -156,8 +159,7 @@ 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, diff --git a/lib/web_ui/lib/src/engine/canvaskit/renderer.dart b/lib/web_ui/lib/src/engine/canvaskit/renderer.dart index 7e7e6ea531ef4..18596d8f92b8a 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/renderer.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/renderer.dart @@ -84,11 +84,12 @@ class CanvasKitRenderer implements Renderer { List? textureCoordinates, List? colors, List? indices, - }) => - CkVertices(mode, positions, - textureCoordinates: textureCoordinates, - colors: colors, - indices: indices); + }) => CkVertices( + mode, + positions, + textureCoordinates: textureCoordinates, + colors: colors, + indices: indices); @override ui.Vertices createVerticesRaw( @@ -97,23 +98,26 @@ class CanvasKitRenderer implements Renderer { Float32List? textureCoordinates, Int32List? colors, Uint16List? indices, - }) => - CkVertices.raw(mode, positions, - textureCoordinates: textureCoordinates, - colors: colors, - indices: indices); + }) => CkVertices.raw( + mode, + positions, + textureCoordinates: textureCoordinates, + colors: colors, + indices: indices); @override ui.Canvas createCanvas(ui.PictureRecorder recorder, [ui.Rect? cullRect]) => - CanvasKitCanvas(recorder, cullRect); + CanvasKitCanvas(recorder, cullRect); @override ui.Gradient createLinearGradient( - ui.Offset from, ui.Offset to, List colors, - [List? colorStops, - ui.TileMode tileMode = ui.TileMode.clamp, - Float32List? matrix4]) => - CkGradientLinear(from, to, colors, colorStops, tileMode, matrix4); + ui.Offset from, + ui.Offset to, + List colors, [ + List? colorStops, + ui.TileMode tileMode = ui.TileMode.clamp, + Float32List? matrix4 + ]) => CkGradientLinear(from, to, colors, colorStops, tileMode, matrix4); @override ui.Gradient createRadialGradient( @@ -123,27 +127,38 @@ class CanvasKitRenderer implements Renderer { List? colorStops, ui.TileMode tileMode = ui.TileMode.clamp, Float32List? matrix4, - ]) => - CkGradientRadial(center, radius, colors, colorStops, tileMode, matrix4); + ]) => CkGradientRadial(center, radius, colors, colorStops, tileMode, matrix4); @override - ui.Gradient createConicalGradient(ui.Offset focal, double focalRadius, - ui.Offset center, double radius, List colors, - [List? colorStops, - ui.TileMode tileMode = ui.TileMode.clamp, - Float32List? matrix]) => - CkGradientConical(focal, focalRadius, center, radius, colors, colorStops, - tileMode, matrix); - - @override - ui.Gradient createSweepGradient(ui.Offset center, List colors, - [List? colorStops, - ui.TileMode tileMode = ui.TileMode.clamp, - double startAngle = 0.0, - double endAngle = math.pi * 2, - Float32List? matrix4]) => - CkGradientSweep( - center, colors, colorStops, tileMode, startAngle, endAngle, matrix4); + ui.Gradient createConicalGradient( + ui.Offset focal, + double focalRadius, + ui.Offset center, + double radius, + List colors, + [List? colorStops, + ui.TileMode tileMode = ui.TileMode.clamp, + Float32List? matrix] + ) => CkGradientConical( + focal, + focalRadius, + center, + radius, + colors, + colorStops, + tileMode, + matrix); + + @override + ui.Gradient createSweepGradient( + ui.Offset center, + List colors, [ + List? colorStops, + ui.TileMode tileMode = ui.TileMode.clamp, + double startAngle = 0.0, + double endAngle = math.pi * 2, + Float32List? matrix4 + ]) => CkGradientSweep(center, colors, colorStops, tileMode, startAngle, endAngle, matrix4); @override ui.PictureRecorder createPictureRecorder() => CkPictureRecorder(); @@ -152,32 +167,29 @@ class CanvasKitRenderer implements Renderer { ui.SceneBuilder createSceneBuilder() => LayerSceneBuilder(); @override - ui.ImageFilter createBlurImageFilter( - {double sigmaX = 0.0, - double sigmaY = 0.0, - ui.TileMode tileMode = ui.TileMode.clamp}) => - CkImageFilter.blur(sigmaX: sigmaX, sigmaY: sigmaY, tileMode: tileMode); + ui.ImageFilter createBlurImageFilter({ + double sigmaX = 0.0, + double sigmaY = 0.0, + ui.TileMode tileMode = ui.TileMode.clamp + }) => CkImageFilter.blur(sigmaX: sigmaX, sigmaY: sigmaY, tileMode: tileMode); @override - ui.ImageFilter createDilateImageFilter( - {double radiusX = 0.0, double radiusY = 0.0}) { + 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.'); + throw UnimplementedError('ImageFilter.dilate not implemented for CanvasKit.'); } @override - ui.ImageFilter createErodeImageFilter( - {double radiusX = 0.0, double radiusY = 0.0}) { + 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.'); + throw UnimplementedError('ImageFilter.erode not implemented for CanvasKit.'); } @override - ui.ImageFilter createMatrixImageFilter(Float64List matrix4, - {ui.FilterQuality filterQuality = ui.FilterQuality.low}) => - CkImageFilter.matrix(matrix: matrix4, filterQuality: filterQuality); + ui.ImageFilter createMatrixImageFilter( + Float64List matrix4, { + ui.FilterQuality filterQuality = ui.FilterQuality.low + }) => CkImageFilter.matrix(matrix: matrix4, filterQuality: filterQuality); @override ui.ImageFilter composeImageFilters( @@ -186,21 +198,29 @@ class CanvasKitRenderer implements Renderer { outer: outer as CkImageFilter, inner: inner as CkImageFilter); @override - Future instantiateImageCodec(Uint8List list, - {int? targetWidth, - int? targetHeight, - bool allowUpscaling = true}) async => - skiaInstantiateImageCodec(list, targetWidth, targetHeight); + Future instantiateImageCodec( + Uint8List list, { + int? targetWidth, + int? targetHeight, + bool allowUpscaling = true + }) async => skiaInstantiateImageCodec( + list, + targetWidth, + targetHeight + ); @override - Future instantiateImageCodecFromUrl(Uri uri, - {ui_web.ImageCodecChunkCallback? chunkCallback}) => - skiaInstantiateWebImageCodec(uri.toString(), chunkCallback); + Future instantiateImageCodecFromUrl( + Uri uri, { + ui_web.ImageCodecChunkCallback? chunkCallback + }) => skiaInstantiateWebImageCodec(uri.toString(), chunkCallback); @override ui.Image createImageFromImageBitmap(DomImageBitmap imageBitmap) { - final SkImage? skImage = - canvasKit.MakeLazyImageFromImageBitmap(imageBitmap, true); + final SkImage? skImage = canvasKit.MakeLazyImageFromImageBitmap( + imageBitmap, + true + ); if (skImage == null) { throw Exception('Failed to convert image bitmap to an SkImage.'); } @@ -208,26 +228,36 @@ class CanvasKitRenderer implements Renderer { } @override - void decodeImageFromPixels(Uint8List pixels, int width, int height, - ui.PixelFormat format, ui.ImageDecoderCallback callback, - {int? rowBytes, - int? targetWidth, - int? targetHeight, - bool allowUpscaling = true}) => - skiaDecodeImageFromPixels(pixels, width, height, format, callback, - rowBytes: rowBytes, - targetWidth: targetWidth, - targetHeight: targetHeight, - allowUpscaling: allowUpscaling); + void decodeImageFromPixels( + Uint8List pixels, + int width, + int height, + ui.PixelFormat format, + ui.ImageDecoderCallback callback, { + int? rowBytes, + int? targetWidth, + int? targetHeight, + bool allowUpscaling = true + }) => skiaDecodeImageFromPixels( + pixels, + width, + height, + format, + callback, + rowBytes: rowBytes, + targetWidth: targetWidth, + targetHeight: targetHeight, + allowUpscaling: allowUpscaling + ); @override ui.ImageShader createImageShader( - ui.Image image, - ui.TileMode tmx, - ui.TileMode tmy, - Float64List matrix4, - ui.FilterQuality? filterQuality) => - CkImageShader(image, tmx, tmy, matrix4, filterQuality); + ui.Image image, + ui.TileMode tmx, + ui.TileMode tmy, + Float64List matrix4, + ui.FilterQuality? filterQuality + ) => CkImageShader(image, tmx, tmy, matrix4, filterQuality); @override ui.Path createPath() => CkPath(); @@ -237,111 +267,111 @@ class CanvasKitRenderer implements Renderer { @override ui.Path combinePaths(ui.PathOperation op, ui.Path path1, ui.Path path2) => - CkPath.combine(op, path1, path2); - - @override - ui.TextStyle createTextStyle( - {ui.Color? color, - ui.TextDecoration? decoration, - ui.Color? decorationColor, - ui.TextDecorationStyle? decorationStyle, - double? decorationThickness, - ui.FontWeight? fontWeight, - ui.FontStyle? fontStyle, - ui.TextBaseline? textBaseline, - String? fontFamily, - List? fontFamilyFallback, - double? fontSize, - double? letterSpacing, - double? wordSpacing, - double? height, - ui.TextLeadingDistribution? leadingDistribution, - ui.Locale? locale, - ui.Paint? background, - ui.Paint? foreground, - List? shadows, - List? fontFeatures, - List? fontVariations}) => - CkTextStyle( - color: color, - decoration: decoration, - decorationColor: decorationColor, - decorationStyle: decorationStyle, - decorationThickness: decorationThickness, - fontWeight: fontWeight, - fontStyle: fontStyle, - textBaseline: textBaseline, - fontFamily: fontFamily, - fontFamilyFallback: fontFamilyFallback, - fontSize: fontSize, - letterSpacing: letterSpacing, - wordSpacing: wordSpacing, - height: height, - leadingDistribution: leadingDistribution, - locale: locale, - background: background as CkPaint?, - foreground: foreground as CkPaint?, - shadows: shadows, - fontFeatures: fontFeatures, - fontVariations: fontVariations, - ); - - @override - ui.ParagraphStyle createParagraphStyle( - {ui.TextAlign? textAlign, - ui.TextDirection? textDirection, - int? maxLines, - String? fontFamily, - double? fontSize, - double? height, - ui.TextHeightBehavior? textHeightBehavior, - ui.FontWeight? fontWeight, - ui.FontStyle? fontStyle, - ui.StrutStyle? strutStyle, - String? ellipsis, - ui.Locale? locale}) => - CkParagraphStyle( - textAlign: textAlign, - textDirection: textDirection, - maxLines: maxLines, - fontFamily: fontFamily, - fontSize: fontSize, - height: height, - textHeightBehavior: textHeightBehavior, - fontWeight: fontWeight, - fontStyle: fontStyle, - strutStyle: strutStyle, - ellipsis: ellipsis, - locale: locale, - applyRoundingHack: !ui.ParagraphBuilder.shouldDisableRoundingHack, - ); - - @override - ui.StrutStyle createStrutStyle( - {String? fontFamily, - List? fontFamilyFallback, - double? fontSize, - double? height, - ui.TextLeadingDistribution? leadingDistribution, - double? leading, - ui.FontWeight? fontWeight, - ui.FontStyle? fontStyle, - bool? forceStrutHeight}) => - CkStrutStyle( - fontFamily: fontFamily, - fontFamilyFallback: fontFamilyFallback, - fontSize: fontSize, - height: height, - leadingDistribution: leadingDistribution, - leading: leading, - fontWeight: fontWeight, - fontStyle: fontStyle, - forceStrutHeight: forceStrutHeight, - ); + CkPath.combine(op, path1, path2); + + @override + ui.TextStyle createTextStyle({ + ui.Color? color, + ui.TextDecoration? decoration, + ui.Color? decorationColor, + ui.TextDecorationStyle? decorationStyle, + double? decorationThickness, + ui.FontWeight? fontWeight, + ui.FontStyle? fontStyle, + ui.TextBaseline? textBaseline, + String? fontFamily, + List? fontFamilyFallback, + double? fontSize, + double? letterSpacing, + double? wordSpacing, + double? height, + ui.TextLeadingDistribution? leadingDistribution, + ui.Locale? locale, + ui.Paint? background, + ui.Paint? foreground, + List? shadows, + List? fontFeatures, + List? fontVariations + }) => CkTextStyle( + color: color, + decoration: decoration, + decorationColor: decorationColor, + decorationStyle: decorationStyle, + decorationThickness: decorationThickness, + fontWeight: fontWeight, + fontStyle: fontStyle, + textBaseline: textBaseline, + fontFamily: fontFamily, + fontFamilyFallback: fontFamilyFallback, + fontSize: fontSize, + letterSpacing: letterSpacing, + wordSpacing: wordSpacing, + height: height, + leadingDistribution: leadingDistribution, + locale: locale, + background: background as CkPaint?, + foreground: foreground as CkPaint?, + shadows: shadows, + fontFeatures: fontFeatures, + fontVariations: fontVariations, + ); + + @override + ui.ParagraphStyle createParagraphStyle({ + ui.TextAlign? textAlign, + ui.TextDirection? textDirection, + int? maxLines, + String? fontFamily, + double? fontSize, + double? height, + ui.TextHeightBehavior? textHeightBehavior, + ui.FontWeight? fontWeight, + ui.FontStyle? fontStyle, + ui.StrutStyle? strutStyle, + String? ellipsis, + ui.Locale? locale + }) => CkParagraphStyle( + textAlign: textAlign, + textDirection: textDirection, + maxLines: maxLines, + fontFamily: fontFamily, + fontSize: fontSize, + height: height, + textHeightBehavior: textHeightBehavior, + fontWeight: fontWeight, + fontStyle: fontStyle, + strutStyle: strutStyle, + ellipsis: ellipsis, + locale: locale, + applyRoundingHack: !ui.ParagraphBuilder.shouldDisableRoundingHack, + ); + + @override + ui.StrutStyle createStrutStyle({ + String? fontFamily, + List? fontFamilyFallback, + double? fontSize, + double? height, + ui.TextLeadingDistribution? leadingDistribution, + double? leading, + ui.FontWeight? fontWeight, + ui.FontStyle? fontStyle, + bool? forceStrutHeight + }) => CkStrutStyle( + fontFamily: fontFamily, + fontFamilyFallback: fontFamilyFallback, + fontSize: fontSize, + height: height, + leadingDistribution: leadingDistribution, + leading: leading, + fontWeight: fontWeight, + fontStyle: fontStyle, + forceStrutHeight: forceStrutHeight, + ); @override ui.ParagraphBuilder createParagraphBuilder(ui.ParagraphStyle style) => - CkParagraphBuilder(style); + CkParagraphBuilder(style); @override void renderScene(ui.Scene scene, ui.FlutterView view) { @@ -393,39 +423,38 @@ class CanvasKitRenderer implements Renderer { _programs.clear(); } - static final Map> _programs = - >{}; + static final Map> _programs = >{}; @override Future createFragmentProgram(String assetKey) { if (_programs.containsKey(assetKey)) { return _programs[assetKey]!; } - return _programs[assetKey] = - ui_web.assetManager.load(assetKey).then((ByteData data) { + return _programs[assetKey] = ui_web.assetManager.load(assetKey).then((ByteData data) { return CkFragmentProgram.fromBytes(assetKey, data.buffer.asUint8List()); }); } @override - ui.LineMetrics createLineMetrics( - {required bool hardBreak, - required double ascent, - required double descent, - required double unscaledAscent, - required double height, - required double width, - required double left, - required double baseline, - required int lineNumber}) => - EngineLineMetrics( - hardBreak: hardBreak, - ascent: ascent, - descent: descent, - unscaledAscent: unscaledAscent, - height: height, - width: width, - left: left, - baseline: baseline, - lineNumber: lineNumber); + ui.LineMetrics createLineMetrics({ + required bool hardBreak, + required double ascent, + required double descent, + required double unscaledAscent, + required double height, + required double width, + required double left, + required double baseline, + required int lineNumber + }) => EngineLineMetrics( + hardBreak: hardBreak, + ascent: ascent, + descent: descent, + unscaledAscent: unscaledAscent, + height: height, + width: width, + left: left, + baseline: baseline, + lineNumber: lineNumber + ); } From 6950d01a15c407e6068fb9ba6df4a979c782b74f Mon Sep 17 00:00:00 2001 From: Harry Terkelsen Date: Thu, 30 Nov 2023 15:42:45 -0800 Subject: [PATCH 3/4] Make ImageFilter.compose work with ColorFilters --- lib/web_ui/lib/src/engine/canvaskit/renderer.dart | 13 +++++++++++-- lib/web_ui/test/ui/filters_test.dart | 6 ++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/lib/web_ui/lib/src/engine/canvaskit/renderer.dart b/lib/web_ui/lib/src/engine/canvaskit/renderer.dart index 18596d8f92b8a..1caf88ffaa80e 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/renderer.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/renderer.dart @@ -193,9 +193,18 @@ class CanvasKitRenderer implements Renderer { @override ui.ImageFilter composeImageFilters( - {required ui.ImageFilter outer, required ui.ImageFilter inner}) => - CkImageFilter.compose( + {required ui.ImageFilter outer, required ui.ImageFilter inner}) { + if (outer is EngineColorFilter) { + final CkColorFilter colorFilter = createCkColorFilter(outer)!; + outer = CkColorFilterImageFilter(colorFilter: colorFilter); + } + if (inner is EngineColorFilter) { + final CkColorFilter colorFilter = createCkColorFilter(inner)!; + inner = CkColorFilterImageFilter(colorFilter: colorFilter); + } + return CkImageFilter.compose( outer: outer as CkImageFilter, inner: inner as CkImageFilter); + } @override Future instantiateImageCodec( diff --git a/lib/web_ui/test/ui/filters_test.dart b/lib/web_ui/test/ui/filters_test.dart index a2151002c8e92..fdf725a8d939f 100644 --- a/lib/web_ui/test/ui/filters_test.dart +++ b/lib/web_ui/test/ui/filters_test.dart @@ -101,7 +101,8 @@ Future testMain() async { ); await drawTestImageWithPaint(ui.Paint()..imageFilter = filter); await matchGoldenFile('ui_filter_composed_imagefilters.png', region: region); - }, skip: !isSkwasm); // Only Skwasm implements composable filters right now. + }, + skip: isHtml); // Only Skwasm and CanvasKit implement composable filters right now. test('compose with colorfilter', () async { final ui.ImageFilter filter = ui.ImageFilter.compose( @@ -116,7 +117,8 @@ Future testMain() async { ); await drawTestImageWithPaint(ui.Paint()..imageFilter = filter); await matchGoldenFile('ui_filter_composed_colorfilter.png', region: region); - }, skip: !isSkwasm); // Only Skwasm implements composable filters right now. + }, + skip: isHtml); // Only Skwasm and CanvasKit implements composable filters right now. test('color filter as image filter', () async { const ui.ColorFilter colorFilter = ui.ColorFilter.mode( From b8d9a9621d35bc87db563202753e95b876c42898 Mon Sep 17 00:00:00 2001 From: Harry Terkelsen Date: Thu, 30 Nov 2023 15:46:14 -0800 Subject: [PATCH 4/4] Fix formatting --- lib/web_ui/test/ui/filters_test.dart | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/web_ui/test/ui/filters_test.dart b/lib/web_ui/test/ui/filters_test.dart index fdf725a8d939f..b486447c6186c 100644 --- a/lib/web_ui/test/ui/filters_test.dart +++ b/lib/web_ui/test/ui/filters_test.dart @@ -101,8 +101,7 @@ Future testMain() async { ); await drawTestImageWithPaint(ui.Paint()..imageFilter = filter); await matchGoldenFile('ui_filter_composed_imagefilters.png', region: region); - }, - skip: isHtml); // Only Skwasm and CanvasKit implement composable filters right now. + }, skip: isHtml); // Only Skwasm and CanvasKit implement composable filters right now. test('compose with colorfilter', () async { final ui.ImageFilter filter = ui.ImageFilter.compose( @@ -117,8 +116,7 @@ Future testMain() async { ); await drawTestImageWithPaint(ui.Paint()..imageFilter = filter); await matchGoldenFile('ui_filter_composed_colorfilter.png', region: region); - }, - skip: isHtml); // Only Skwasm and CanvasKit implements composable filters right now. + }, skip: isHtml); // Only Skwasm and CanvasKit implements composable filters right now. test('color filter as image filter', () async { const ui.ColorFilter colorFilter = ui.ColorFilter.mode(