@@ -15,10 +15,10 @@ import 'canvas.dart';
1515import 'canvaskit_api.dart' ;
1616import 'image_wasm_codecs.dart' ;
1717import 'image_web_codecs.dart' ;
18- import 'native_memory.dart' ;
1918import 'painting.dart' ;
2019import 'picture.dart' ;
2120import 'picture_recorder.dart' ;
21+ import 'skia_object_cache.dart' ;
2222
2323/// Instantiates a [ui.Codec] backed by an `SkAnimatedImage` from Skia.
2424FutureOr <ui.Codec > skiaInstantiateImageCodec (Uint8List list,
@@ -227,8 +227,52 @@ Future<Uint8List> readChunked(HttpFetchPayload payload, int contentLength, WebOn
227227/// A [ui.Image] backed by an `SkImage` from Skia.
228228class CkImage implements ui.Image , StackTraceDebugger {
229229 CkImage (SkImage skImage, { this .videoFrame }) {
230- box = CountedRef <CkImage , SkImage >(skImage, this , 'SkImage' );
231230 _init ();
231+ if (browserSupportsFinalizationRegistry) {
232+ box = SkiaObjectBox <CkImage , SkImage >(this , skImage);
233+ } else {
234+ // If finalizers are not supported we need to be able to resurrect the
235+ // image if it was temporarily deleted. To do that, we keep the original
236+ // pixels and ask the SkiaObjectBox to make an image from them when
237+ // resurrecting.
238+ //
239+ // IMPORTANT: the alphaType, colorType, and colorSpace passed to
240+ // _encodeImage and to canvasKit.MakeImage must be the same. Otherwise
241+ // Skia will misinterpret the pixels and corrupt the image.
242+ final ByteData ? originalBytes = _encodeImage (
243+ skImage: skImage,
244+ format: ui.ImageByteFormat .rawRgba,
245+ alphaType: canvasKit.AlphaType .Premul ,
246+ colorType: canvasKit.ColorType .RGBA_8888 ,
247+ colorSpace: SkColorSpaceSRGB ,
248+ );
249+ if (originalBytes == null ) {
250+ printWarning ('Unable to encode image to bytes. We will not '
251+ 'be able to resurrect it once it has been garbage collected.' );
252+ return ;
253+ }
254+ final int originalWidth = skImage.width ().toInt ();
255+ final int originalHeight = skImage.height ().toInt ();
256+ box = SkiaObjectBox <CkImage , SkImage >.resurrectable (this , skImage, () {
257+ final SkImage ? skImage = canvasKit.MakeImage (
258+ SkImageInfo (
259+ alphaType: canvasKit.AlphaType .Premul ,
260+ colorType: canvasKit.ColorType .RGBA_8888 ,
261+ colorSpace: SkColorSpaceSRGB ,
262+ width: originalWidth.toDouble (),
263+ height: originalHeight.toDouble (),
264+ ),
265+ originalBytes.buffer.asUint8List (),
266+ (4 * originalWidth).toDouble (),
267+ );
268+ if (skImage == null ) {
269+ throw ImageCodecException (
270+ 'Failed to resurrect image from pixels.'
271+ );
272+ }
273+ return skImage;
274+ });
275+ }
232276 }
233277
234278 CkImage .cloneOf (this .box, {this .videoFrame}) {
@@ -247,9 +291,9 @@ class CkImage implements ui.Image, StackTraceDebugger {
247291 StackTrace get debugStackTrace => _debugStackTrace;
248292 late StackTrace _debugStackTrace;
249293
250- // Use ref counting because `SkImage` may be deleted either due to this object
294+ // Use a box because `SkImage` may be deleted either due to this object
251295 // being garbage-collected, or by an explicit call to [delete].
252- late final CountedRef <CkImage , SkImage > box;
296+ late final SkiaObjectBox <CkImage , SkImage > box;
253297
254298 /// For browsers that support `ImageDecoder` this field holds the video frame
255299 /// from which this image was created.
@@ -261,9 +305,9 @@ class CkImage implements ui.Image, StackTraceDebugger {
261305
262306 /// The underlying Skia image object.
263307 ///
264- /// Do not store the returned value. It is memory-managed by [CountedRef ] .
308+ /// Do not store the returned value. It is memory-managed by [SkiaObjectBox ] .
265309 /// Storing it may result in use-after-free bugs.
266- SkImage get skImage => box.nativeObject ;
310+ SkImage get skImage => box.skiaObject ;
267311
268312 bool _disposed = false ;
269313
0 commit comments