diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/canvas.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/canvas.dart index 9a5dd90426c..b458795ad80 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/canvas.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/canvas.dart @@ -291,18 +291,20 @@ class CkCanvas { void saveLayerWithFilter(ui.Rect bounds, ui.ImageFilter filter, [CkPaint? paint]) { - final CkManagedSkImageFilterConvertible convertible; - if (filter is ui.ColorFilter) { - convertible = createCkColorFilter(filter as EngineColorFilter)!; - } else { - convertible = filter as CkManagedSkImageFilterConvertible; - } - return skCanvas.saveLayer( - paint?.skiaObject, - toSkRect(bounds), - convertible.imageFilter.skiaObject, - 0, - ); + final CkManagedSkImageFilterConvertible convertible; + if (filter is ui.ColorFilter) { + convertible = createCkColorFilter(filter as EngineColorFilter)!; + } else { + convertible = filter as CkManagedSkImageFilterConvertible; + } + convertible.imageFilter((SkImageFilter filter) { + skCanvas.saveLayer( + paint?.skiaObject, + toSkRect(bounds), + filter, + 0, + ); + }); } void scale(double sx, double sy) { @@ -1173,11 +1175,13 @@ class CkSaveLayerWithFilterCommand extends CkPaintCommand { } else { convertible = filter as CkManagedSkImageFilterConvertible; } - return canvas.saveLayer( - paint?.skiaObject, - toSkRect(bounds), - convertible.imageFilter.skiaObject, - 0, - ); + convertible.imageFilter((SkImageFilter filter) { + canvas.saveLayer( + paint?.skiaObject, + toSkRect(bounds), + filter, + 0, + ); + }); } } diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/color_filter.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/color_filter.dart index bc3422cb05e..bb40093ae73 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/color_filter.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/color_filter.dart @@ -10,31 +10,25 @@ import '../color_filter.dart'; import '../util.dart'; import 'canvaskit_api.dart'; import 'image_filter.dart'; -import 'skia_object_cache.dart'; +import 'native_memory.dart'; -/// A concrete [ManagedSkiaObject] subclass that owns a [SkColorFilter] and -/// manages its lifecycle. +/// Owns a [SkColorFilter] and manages its lifecycle. /// -/// Seealso: +/// See also: /// /// * [CkPaint.colorFilter], which uses a [ManagedSkColorFilter] to manage /// the lifecycle of its [SkColorFilter]. -class ManagedSkColorFilter extends ManagedSkiaObject { +class ManagedSkColorFilter { ManagedSkColorFilter(CkColorFilter ckColorFilter) - : colorFilter = ckColorFilter; + : colorFilter = ckColorFilter { + _ref = UniqueRef(this, colorFilter._initRawColorFilter(), 'ColorFilter'); + } final CkColorFilter colorFilter; - @override - SkColorFilter createDefault() => colorFilter._initRawColorFilter(); + late final UniqueRef _ref; - @override - SkColorFilter resurrect() => colorFilter._initRawColorFilter(); - - @override - void delete() { - rawSkiaObject?.delete(); - } + SkColorFilter get skiaObject => _ref.nativeObject; @override int get hashCode => colorFilter.hashCode; @@ -51,64 +45,83 @@ class ManagedSkColorFilter extends ManagedSkiaObject { String toString() => colorFilter.toString(); } -/// A [ui.ColorFilter] backed by Skia's [SkColorFilter]. -/// -/// Additionally, this class provides the interface for converting itself to a -/// [ManagedSkiaObject] that manages a skia image filter. -abstract class CkColorFilter - implements CkManagedSkImageFilterConvertible { +/// CanvasKit implementation of [ui.ColorFilter]. +abstract class CkColorFilter implements CkManagedSkImageFilterConvertible { const CkColorFilter(); - /// Called by [ManagedSkiaObject.createDefault] and - /// [ManagedSkiaObject.resurrect] to create a new [SkImageFilter], when this - /// filter is used as an [ImageFilter]. - SkImageFilter initRawImageFilter() => - canvasKit.ImageFilter.MakeColorFilter(_initRawColorFilter(), null); + /// Converts this color filter into an image filter. + /// + /// Passes the ownership of the returned [SkImageFilter] to the caller. It is + /// the caller's responsibility to manage the lifecycle of the returned value. + SkImageFilter initRawImageFilter() { + final SkColorFilter skColorFilter = _initRawColorFilter(); + final SkImageFilter result = canvasKit.ImageFilter.MakeColorFilter(skColorFilter, null); - /// Called by [ManagedSkiaObject.createDefault] and - /// [ManagedSkiaObject.resurrect] to create a new [SkColorFilter], when this - /// filter is used as a [ColorFilter]. + // The underlying SkColorFilter is now owned by the SkImageFilter, so we + // need to drop the reference to allow it to be collected. + skColorFilter.delete(); + return result; + } + + /// Creates a Skia object based on the properties of this color filter. + /// + /// Passes the ownership of the returned [SkColorFilter] to the caller. It is + /// the caller's responsibility to manage the lifecycle of the returned value. SkColorFilter _initRawColorFilter(); @override - ManagedSkiaObject get imageFilter => - CkColorFilterImageFilter(colorFilter: this); + void imageFilter(SkImageFilterBorrow borrow) { + // Since ColorFilter has a const constructor it cannot store dynamically + // created Skia objects. Therefore a new SkImageFilter is created every time + // it's used. However, once used it's no longer needed, so it's deleted + // immediately to free memory. + final SkImageFilter skImageFilter = initRawImageFilter(); + borrow(skImageFilter); + skImageFilter.delete(); + } } +/// A reusable identity transform matrix. +/// +/// WARNING: DO NOT MUTATE THIS MATRIX! It is a shared global singleton. +Float32List _identityTransform = _computeIdentityTransform(); + +Float32List _computeIdentityTransform() { + final Float32List result = Float32List(20); + const List translationIndices = [0, 6, 12, 18]; + for (final int i in translationIndices) { + result[i] = 1; + } + _identityTransform = result; + return result; +} + +SkColorFilter createSkColorFilterFromColorAndBlendMode(ui.Color color, ui.BlendMode blendMode) { + /// Return the identity matrix when the color opacity is 0. Replicates + /// effect of applying no filter + if (color.opacity == 0) { + return canvasKit.ColorFilter.MakeMatrix(_identityTransform); + } + final SkColorFilter? filter = canvasKit.ColorFilter.MakeBlend( + toSharedSkColor1(color), + toSkBlendMode(blendMode), + ); + if (filter == null) { + throw ArgumentError('Invalid parameters for blend mode ColorFilter'); + } + return filter; +} + + class CkBlendModeColorFilter extends CkColorFilter { const CkBlendModeColorFilter(this.color, this.blendMode); final ui.Color color; final ui.BlendMode blendMode; - static Float32List get identityTransform => _identityTransform ?? _computeIdentityTransform(); - static Float32List? _identityTransform; - - static Float32List _computeIdentityTransform() { - final Float32List result = Float32List(20); - const List translationIndices = [0, 6, 12, 18]; - for (final int i in translationIndices) { - result[i] = 1; - } - _identityTransform = result; - return result; - } - @override SkColorFilter _initRawColorFilter() { - /// Return the identity matrix when the color opacity is 0. Replicates - /// effect of applying no filter - if (color.opacity == 0) { - return canvasKit.ColorFilter.MakeMatrix(identityTransform); - } - final SkColorFilter? filter = canvasKit.ColorFilter.MakeBlend( - toSharedSkColor1(color), - toSkBlendMode(blendMode), - ); - if (filter == null) { - throw ArgumentError('Invalid parameters for blend mode ColorFilter'); - } - return filter; + return createSkColorFilterFromColorAndBlendMode(color, blendMode); } @override diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/image.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/image.dart index 3e167422d99..f527294bb4f 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/image.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/image.dart @@ -137,12 +137,14 @@ CkImage scaleImage(SkImage image, int? targetWidth, int? targetHeight) { final CkPictureRecorder recorder = CkPictureRecorder(); final CkCanvas canvas = recorder.beginRecording(ui.Rect.largest); + final CkPaint paint = CkPaint(); canvas.drawImageRect( CkImage(image), ui.Rect.fromLTWH(0, 0, image.width(), image.height()), ui.Rect.fromLTWH(0, 0, targetWidth!.toDouble(), targetHeight!.toDouble()), - CkPaint() + paint, ); + paint.dispose(); final CkPicture picture = recorder.endRecording(); final ui.Image finalImage = picture.toImageSync( diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/image_filter.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/image_filter.dart index 63caf9d6ed6..46eda4f2ce4 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/image_filter.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/image_filter.dart @@ -10,7 +10,9 @@ import 'package:ui/ui.dart' as ui; import '../util.dart'; import 'canvaskit_api.dart'; import 'color_filter.dart'; -import 'skia_object_cache.dart'; +import 'native_memory.dart'; + +typedef SkImageFilterBorrow = void Function(SkImageFilter); /// An [ImageFilter] that can create a managed skia [SkImageFilter] object. /// @@ -20,14 +22,13 @@ import 'skia_object_cache.dart'; /// /// Currently implemented by [CkImageFilter] and [CkColorFilter]. abstract class CkManagedSkImageFilterConvertible implements ui.ImageFilter { - ManagedSkiaObject get imageFilter; + void imageFilter(SkImageFilterBorrow borrow); } /// The CanvasKit implementation of [ui.ImageFilter]. /// /// Currently only supports `blur`, `matrix`, and ColorFilters. -abstract class CkImageFilter extends ManagedSkiaObject - implements CkManagedSkImageFilterConvertible { +abstract class CkImageFilter implements CkManagedSkImageFilterConvertible { factory CkImageFilter.blur( {required double sigmaX, required double sigmaY, @@ -39,31 +40,26 @@ abstract class CkImageFilter extends ManagedSkiaObject required ui.FilterQuality filterQuality}) = _CkMatrixImageFilter; CkImageFilter._(); - - @override - ManagedSkiaObject get imageFilter => this; - - SkImageFilter _initSkiaObject(); - - @override - SkImageFilter createDefault() => _initSkiaObject(); - - @override - SkImageFilter resurrect() => _initSkiaObject(); - - @override - void delete() { - rawSkiaObject?.delete(); - } } class CkColorFilterImageFilter extends CkImageFilter { - CkColorFilterImageFilter({required this.colorFilter}) : super._(); + CkColorFilterImageFilter({required this.colorFilter}) : super._() { + final SkImageFilter skImageFilter = colorFilter.initRawImageFilter(); + _ref = UniqueRef(this, skImageFilter, 'ImageFilter.color'); + } final CkColorFilter colorFilter; + late final UniqueRef _ref; + @override - SkImageFilter _initSkiaObject() => colorFilter.initRawImageFilter(); + void imageFilter(SkImageFilterBorrow borrow) { + borrow(_ref.nativeObject); + } + + void dispose() { + _ref.dispose(); + } @override int get hashCode => colorFilter.hashCode; @@ -84,12 +80,38 @@ class CkColorFilterImageFilter extends CkImageFilter { class _CkBlurImageFilter extends CkImageFilter { _CkBlurImageFilter( {required this.sigmaX, required this.sigmaY, required this.tileMode}) - : super._(); + : super._() { + /// Return the identity matrix when both sigmaX and sigmaY are 0. Replicates + /// effect of applying no filter + final SkImageFilter skImageFilter; + if (sigmaX == 0 && sigmaY == 0) { + skImageFilter = canvasKit.ImageFilter.MakeMatrixTransform( + toSkMatrixFromFloat32(Matrix4.identity().storage), + toSkFilterOptions(ui.FilterQuality.none), + null + ); + } else { + skImageFilter = canvasKit.ImageFilter.MakeBlur( + sigmaX, + sigmaY, + toSkTileMode(tileMode), + null, + ); + } + _ref = UniqueRef(this, skImageFilter, 'ImageFilter.blur'); + } final double sigmaX; final double sigmaY; final ui.TileMode tileMode; + late final UniqueRef _ref; + + @override + void imageFilter(SkImageFilterBorrow borrow) { + borrow(_ref.nativeObject); + } + String get _modeString { switch (tileMode) { case ui.TileMode.clamp: @@ -103,25 +125,6 @@ class _CkBlurImageFilter extends CkImageFilter { } } - @override - SkImageFilter _initSkiaObject() { - /// Return the identity matrix when both sigmaX and sigmaY are 0. Replicates - /// effect of applying no filter - if (sigmaX == 0 && sigmaY == 0) { - return canvasKit.ImageFilter.MakeMatrixTransform( - toSkMatrixFromFloat32(Matrix4.identity().storage), - toSkFilterOptions(ui.FilterQuality.none), - null - ); - } - return canvasKit.ImageFilter.MakeBlur( - sigmaX, - sigmaY, - toSkTileMode(tileMode), - null, - ); - } - @override bool operator ==(Object other) { if (runtimeType != other.runtimeType) { @@ -146,18 +149,23 @@ class _CkMatrixImageFilter extends CkImageFilter { _CkMatrixImageFilter( {required Float64List matrix, required this.filterQuality}) : matrix = Float64List.fromList(matrix), - super._(); - - final Float64List matrix; - final ui.FilterQuality filterQuality; - - @override - SkImageFilter _initSkiaObject() { - return canvasKit.ImageFilter.MakeMatrixTransform( + super._() { + final SkImageFilter skImageFilter = canvasKit.ImageFilter.MakeMatrixTransform( toSkMatrixFromFloat64(matrix), toSkFilterOptions(filterQuality), null, ); + _ref = UniqueRef(this, skImageFilter, 'ImageFilter.matrix'); + } + + final Float64List matrix; + final ui.FilterQuality filterQuality; + + late final UniqueRef _ref; + + @override + void imageFilter(SkImageFilterBorrow borrow) { + borrow(_ref.nativeObject); } @override diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/layer.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/layer.dart index 7e8dd289693..640b3712e36 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/layer.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/layer.dart @@ -179,6 +179,7 @@ class BackdropFilterEngineLayer extends ContainerLayer final CkPaint paint = CkPaint()..blendMode = _blendMode; paintContext.internalNodesCanvas .saveLayerWithFilter(paintBounds, _filter, paint); + paint.dispose(); paintChildren(paintContext); paintContext.internalNodesCanvas.restore(); } @@ -340,6 +341,7 @@ class OpacityEngineLayer extends ContainerLayer final ui.Rect saveLayerBounds = paintBounds.shift(-_offset); paintContext.internalNodesCanvas.saveLayer(saveLayerBounds, paint); + paint.dispose(); paintChildren(paintContext); // Restore twice: once for the translate and once for the saveLayer. paintContext.internalNodesCanvas.restore(); @@ -403,6 +405,7 @@ class ImageFilterEngineLayer extends ContainerLayer final CkPaint paint = CkPaint(); paint.imageFilter = _filter; paintContext.internalNodesCanvas.saveLayer(paintBounds, paint); + paint.dispose(); paintChildren(paintContext); paintContext.internalNodesCanvas.restore(); paintContext.internalNodesCanvas.restore(); @@ -439,6 +442,7 @@ class ShaderMaskEngineLayer extends ContainerLayer paintContext.leafNodesCanvas!.drawRect( ui.Rect.fromLTWH(0, 0, maskRect.width, maskRect.height), paint); + paint.dispose(); paintContext.leafNodesCanvas!.restore(); paintContext.internalNodesCanvas.restore(); @@ -539,6 +543,7 @@ class PhysicalShapeEngineLayer extends ContainerLayer // anti-aliased drawPath will always have such artifacts. paintContext.leafNodesCanvas!.drawPaint(paint); } + paint.dispose(); paintChildren(paintContext); @@ -570,6 +575,7 @@ class ColorFilterEngineLayer extends ContainerLayer paint.colorFilter = filter; paintContext.internalNodesCanvas.saveLayer(paintBounds, paint); + paint.dispose(); paintChildren(paintContext); paintContext.internalNodesCanvas.restore(); } diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/mask_filter.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/mask_filter.dart index be67b48b35c..331960ba484 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/mask_filter.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/mask_filter.dart @@ -5,33 +5,25 @@ import 'package:ui/ui.dart' as ui; import 'canvaskit_api.dart'; -import 'skia_object_cache.dart'; +import 'native_memory.dart'; /// The CanvasKit implementation of [ui.MaskFilter]. -class CkMaskFilter extends ManagedSkiaObject { +class CkMaskFilter { CkMaskFilter.blur(ui.BlurStyle blurStyle, double sigma) : _blurStyle = blurStyle, - _sigma = sigma; - - final ui.BlurStyle _blurStyle; - final double _sigma; - - @override - SkMaskFilter createDefault() => _initSkiaObject(); - - @override - SkMaskFilter resurrect() => _initSkiaObject(); - - SkMaskFilter _initSkiaObject() { - return canvasKit.MaskFilter.MakeBlur( + _sigma = sigma { + final SkMaskFilter skMaskFilter = canvasKit.MaskFilter.MakeBlur( toSkBlurStyle(_blurStyle), _sigma, true, )!; + _ref = UniqueRef(this, skMaskFilter, 'MaskFilter'); } - @override - void delete() { - rawSkiaObject?.delete(); - } + final ui.BlurStyle _blurStyle; + final double _sigma; + + late final UniqueRef _ref; + + SkMaskFilter get skiaObject => _ref.nativeObject; } diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/painting.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/painting.dart index 6e03bd930a7..dd9ac780fcf 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/painting.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/painting.dart @@ -22,11 +22,25 @@ import 'skia_object_cache.dart'; /// /// This class is backed by a Skia object that must be explicitly /// deleted to avoid a memory leak. This is done by extending [SkiaObject]. -class CkPaint extends ManagedSkiaObject implements ui.Paint { - CkPaint(); +class CkPaint implements ui.Paint { + CkPaint() : skiaObject = SkPaint() { + skiaObject.setAntiAlias(_isAntiAlias); + skiaObject.setColorInt(_defaultPaintColor.toDouble()); + _ref = UniqueRef(this, skiaObject, 'Paint'); + } + + final SkPaint skiaObject; + late final UniqueRef _ref; + CkManagedSkImageFilterConvertible? _imageFilter; static const int _defaultPaintColor = 0xFF000000; + /// Returns the native reference to the underlying [SkPaint] object. + /// + /// This should only be used in tests. + @visibleForTesting + UniqueRef get debugRef => _ref; + @override ui.BlendMode get blendMode => _blendMode; @override @@ -260,50 +274,28 @@ class CkPaint extends ManagedSkiaObject implements ui.Paint { if (_imageFilter == value) { return; } + final CkManagedSkImageFilterConvertible? filter; if (value is ui.ColorFilter) { - _imageFilter = createCkColorFilter(value as EngineColorFilter); + filter = createCkColorFilter(value as EngineColorFilter); } else { - _imageFilter = value as CkManagedSkImageFilterConvertible?; + filter = value as CkManagedSkImageFilterConvertible?; } - _managedImageFilter = _imageFilter?.imageFilter; - skiaObject.setImageFilter(_managedImageFilter?.skiaObject); + + if (filter != null) { + filter.imageFilter((SkImageFilter skImageFilter) { + skiaObject.setImageFilter(skImageFilter); + }); + } + + _imageFilter = filter; } - CkManagedSkImageFilterConvertible? _imageFilter; - ManagedSkiaObject? _managedImageFilter; - - @override - SkPaint createDefault() { - final SkPaint paint = SkPaint(); - paint.setAntiAlias(_isAntiAlias); - paint.setColorInt(_color.toDouble()); - return paint; - } - - @override - SkPaint resurrect() { - final SkPaint paint = SkPaint(); - // No need to do anything for `invertColors`. If it was set, then it - // updated `_managedColorFilter`. - paint.setBlendMode(toSkBlendMode(_blendMode)); - paint.setStyle(toSkPaintStyle(_style)); - paint.setStrokeWidth(_strokeWidth); - paint.setAntiAlias(_isAntiAlias); - paint.setColorInt(_color.toDouble()); - paint.setShader(_shader?.getSkShader(_filterQuality)); - paint.setMaskFilter(_ckMaskFilter?.skiaObject); - paint.setColorFilter(_effectiveColorFilter?.skiaObject); - paint.setImageFilter(_managedImageFilter?.skiaObject); - paint.setStrokeCap(toSkStrokeCap(_strokeCap)); - paint.setStrokeJoin(toSkStrokeJoin(_strokeJoin)); - paint.setStrokeMiter(_strokeMiterLimit); - return paint; - } - - @override - void delete() { - rawSkiaObject?.delete(); + /// Disposes of this paint object. + /// + /// This object cannot be used again after calling this method. + void dispose() { + _ref.dispose(); } } diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/text.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/text.dart index ed14a916890..815012dcd84 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/text.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/text.dart @@ -13,7 +13,6 @@ import 'font_fallbacks.dart'; import 'native_memory.dart'; import 'painting.dart'; import 'renderer.dart'; -import 'skia_object_cache.dart'; import 'text_fragmenter.dart'; import 'util.dart'; @@ -552,16 +551,6 @@ SkFontStyle toSkFontStyle(ui.FontWeight? fontWeight, ui.FontStyle? fontStyle) { } /// The CanvasKit implementation of [ui.Paragraph]. -/// -/// This class does not use [ManagedSkiaObject] because it requires that its -/// memory is reclaimed synchronously. This protects our memory usage from -/// blowing up if within a single frame the framework needs to layout a lot of -/// paragraphs. One common use-case is `ListView.builder`, which needs to layout -/// more of its content than it actually renders to compute the scroll position. -/// More generally, this protects from the pattern of laying out a lot of text -/// while painting a small subset of it. To achieve this a -/// [SynchronousSkiaObjectCache] is used that limits the number of live laid out -/// paragraphs at any point in time within or outside the frame. class CkParagraph implements ui.Paragraph { CkParagraph(SkParagraph skParagraph, this._paragraphStyle) { _ref = UniqueRef(this, skParagraph, 'Paragraph'); diff --git a/engine/src/flutter/lib/web_ui/test/canvaskit/filter_test.dart b/engine/src/flutter/lib/web_ui/test/canvaskit/filter_test.dart index 6ab7dac78a9..5ca14f831a0 100644 --- a/engine/src/flutter/lib/web_ui/test/canvaskit/filter_test.dart +++ b/engine/src/flutter/lib/web_ui/test/canvaskit/filter_test.dart @@ -52,8 +52,11 @@ void testMain() { test('can be constructed', () { final CkImageFilter imageFilter = CkImageFilter.blur(sigmaX: 5, sigmaY: 10, tileMode: ui.TileMode.clamp); expect(imageFilter, isA()); - expect(imageFilter.createDefault(), isNotNull); - expect(imageFilter.resurrect(), isNotNull); + SkImageFilter? skFilter; + imageFilter.imageFilter((SkImageFilter value) { + skFilter = value; + }); + expect(skFilter, isNotNull); }); @@ -82,11 +85,12 @@ void testMain() { final CkPaint paint = CkPaint(); paint.imageFilter = CkImageFilter.blur(sigmaX: 5, sigmaY: 10, tileMode: ui.TileMode.clamp); - final ManagedSkiaObject managedFilter = paint.imageFilter! as ManagedSkiaObject; - final Object skiaFilter = managedFilter.skiaObject; + final CkManagedSkImageFilterConvertible managedFilter1 = paint.imageFilter! as CkManagedSkImageFilterConvertible; paint.imageFilter = CkImageFilter.blur(sigmaX: 5, sigmaY: 10, tileMode: ui.TileMode.clamp); - expect((paint.imageFilter! as ManagedSkiaObject).skiaObject, same(skiaFilter)); + final CkManagedSkImageFilterConvertible managedFilter2 = paint.imageFilter! as CkManagedSkImageFilterConvertible; + + expect(managedFilter1, same(managedFilter2)); }); test('does not throw for both sigmaX and sigmaY set to 0', () async { diff --git a/engine/src/flutter/lib/web_ui/test/canvaskit/painting_test.dart b/engine/src/flutter/lib/web_ui/test/canvaskit/painting_test.dart new file mode 100644 index 00000000000..c2ab64cab62 --- /dev/null +++ b/engine/src/flutter/lib/web_ui/test/canvaskit/painting_test.dart @@ -0,0 +1,34 @@ +// 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. + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; + +import 'package:ui/src/engine.dart'; + +import '../common/matchers.dart'; +import 'common.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + group('CkPaint', () { + setUpCanvasKitTest(); + + test('lifecycle', () { + final CkPaint paint = CkPaint(); + expect(paint.skiaObject, isNotNull); + expect(paint.debugRef.isDisposed, isFalse); + paint.dispose(); + expect(paint.debugRef.isDisposed, isTrue); + expect( + reason: 'Cannot dispose more than once', + () => paint.dispose(), + throwsA(isAssertionError), + ); + }); + }); +}