From ed95b491f26078fb6ced9e795dc49ec27f90fb52 Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Wed, 31 Jul 2024 15:40:51 -0700 Subject: [PATCH] [web] Dispose a temporary image that may be created by instantiateImageCodecWithSize (#54096) Fixes https://github.com/flutter/flutter/issues/147066 --- .../lib/src/engine/canvaskit/image.dart | 1 + lib/web_ui/test/canvaskit/image_test.dart | 33 ++++++++++++++++--- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/lib/web_ui/lib/src/engine/canvaskit/image.dart b/lib/web_ui/lib/src/engine/canvaskit/image.dart index 9727ed31a599f..204b4972a1370 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/image.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/image.dart @@ -130,6 +130,7 @@ class CkResizingCodec extends ResizingCodec { return image; } + image.dispose(); return CkImage(skImage, imageSource: ImageBitmapImageSource(bitmap)); } } diff --git a/lib/web_ui/test/canvaskit/image_test.dart b/lib/web_ui/test/canvaskit/image_test.dart index 9c3362ea0e2fb..c23ae19fd196a 100644 --- a/lib/web_ui/test/canvaskit/image_test.dart +++ b/lib/web_ui/test/canvaskit/image_test.dart @@ -21,6 +21,11 @@ void main() { void testMain() { setUpCanvasKitTest(); + tearDown(() { + ui.Image.onCreate = null; + ui.Image.onDispose = null; + }); + test('toImage succeeds', () async { final ui.Image image = await _createImage(); expect(image.runtimeType.toString(), equals('CkImage')); @@ -44,8 +49,6 @@ void testMain() { expect(onCreateInvokedCount, 2); expect(createdImage, image2); - - ui.Image.onCreate = null; }); test('dispose() invokes onDispose once', () async { @@ -65,8 +68,6 @@ void testMain() { expect(onDisposeInvokedCount, 2); expect(disposedImage, image2); - - ui.Image.onDispose = null; }); test('fetchImage fetches image in chunks', () async { @@ -106,6 +107,30 @@ void testMain() { expect(size?.width, 600); expect(size?.height, 300); }); + + test('instantiateImageCodecWithSize disposes temporary image', () async { + final Set activeImages = {}; + ui.Image.onCreate = activeImages.add; + ui.Image.onDispose = activeImages.remove; + + final ui.Image image = await _createImage(); + final ByteData? imageData = await image.toByteData(format: ui.ImageByteFormat.png); + final ui.ImmutableBuffer imageBuffer = await ui.ImmutableBuffer.fromUint8List(imageData!.buffer.asUint8List()); + image.dispose(); + + final ui.Codec codec = await ui.instantiateImageCodecWithSize( + imageBuffer, + getTargetSize: (w, h) => ui.TargetImageSize(width: w ~/ 2, height: h ~/ 2) + ); + final ui.FrameInfo frameInfo = await codec.getNextFrame(); + + expect(activeImages.length, 1); + + frameInfo.image.dispose(); + codec.dispose(); + + expect(activeImages.length, 0); + }); } Future _createImage() => _createPicture().toImage(10, 10);