@@ -1574,9 +1574,8 @@ enum PixelFormat {
15741574/// disposed.
15751575///
15761576/// If `dart:ui` passes an `Image` object and the recipient wishes to share
1577- /// that handle with other callers, it is critical that [clone] is called
1578- /// _before_ [dispose] . A handle that has been disposed cannot create new
1579- /// handles anymore.
1577+ /// that handle with other callers, [clone] must be called _before_ [dispose] .
1578+ /// A handle that has been disposed cannot create new handles anymore.
15801579///
15811580/// See also:
15821581///
@@ -1609,6 +1608,7 @@ class Image {
16091608 return _image.height;
16101609 }
16111610
1611+ bool _disposed = false ;
16121612 /// Release this handle's claim on the underlying Image. This handle is no
16131613 /// longer usable after this method is called.
16141614 ///
@@ -1620,12 +1620,9 @@ class Image {
16201620 /// useful when trying to determine what parts of the program are keeping an
16211621 /// image resident in memory.
16221622 void dispose () {
1623- assert (() {
1624- assert (! _disposed && ! _image._disposed);
1625- assert (_image._handles.contains (this ));
1626- _disposed = true ;
1627- return true ;
1628- }());
1623+ assert (! _disposed && ! _image._disposed);
1624+ assert (_image._handles.contains (this ));
1625+ _disposed = true ;
16291626 final bool removed = _image._handles.remove (this );
16301627 assert (removed);
16311628 if (_image._handles.isEmpty) {
@@ -1645,8 +1642,6 @@ class Image {
16451642 return _image.toByteData (format: format);
16461643 }
16471644
1648- bool _disposed = false ;
1649-
16501645 /// If asserts are enabled, returns the [StackTrace] s of each open handle from
16511646 /// [clone] , in creation order.
16521647 ///
@@ -1662,6 +1657,71 @@ class Image {
16621657
16631658 /// Creates a disposable handle to this image.
16641659 ///
1660+ /// Holders of an [Image] must dispose of the image when they no longer need
1661+ /// to access it or draw it. However, once the underlying image is disposed,
1662+ /// it is no longer possible to use it. If a holder of an image needs to share
1663+ /// access to that image with another object or method, [clone] creates a
1664+ /// duplicate handle. The underlying image will only be disposed once all
1665+ /// outstanding handles are disposed. This allows for safe sharing of image
1666+ /// references while still disposing of the underlying resources when all
1667+ /// consumers are finished.
1668+ ///
1669+ /// It is safe to pass an [Image] handle to another object or method if the
1670+ /// current holder no longer needs it.
1671+ ///
1672+ /// The following example demonstrates valid usage.
1673+ ///
1674+ /// ```dart
1675+ /// import 'dart:async';
1676+ ///
1677+ /// Future<Image> _loadImage(int width, int height) {
1678+ /// final Completer<Image> completer = Completer<Image>();
1679+ /// decodeImageFromPixels(
1680+ /// Uint8List.fromList(List<int>.filled(width * height * 4, 0xFF)),
1681+ /// width,
1682+ /// height,
1683+ /// PixelFormat.rgba8888,
1684+ /// // Don't worry about disposing or cloning this image - responsibility
1685+ /// // is transferred to the caller, and that is safe since this method
1686+ /// // will not touch it again.
1687+ /// (Image image) => completer.complete(image),
1688+ /// );
1689+ /// return completer.future;
1690+ /// }
1691+ ///
1692+ /// Future<void> main() async {
1693+ /// final Image image = await _loadImage(5, 5);
1694+ /// // Make sure to clone the image, because MyHolder might dispose it
1695+ /// // and we need to access it again.
1696+ /// final MyImageHolder holder = MyImageHolder(image.clone());
1697+ /// final MyImageHolder holder2 = MyImageHolder(image.clone());
1698+ /// // Now we dispose it because we won't need it again.
1699+ /// image.dispose();
1700+ ///
1701+ /// final PictureRecorder recorder = PictureRecorder();
1702+ /// final Canvas canvas = Canvas(recorder);
1703+ ///
1704+ /// holder.draw(canvas);
1705+ /// holder.dispose();
1706+ ///
1707+ /// canvas.translate(50, 50);
1708+ /// holder2.draw(canvas);
1709+ /// holder2.dispose();
1710+ /// }
1711+ ///
1712+ /// class MyImageHolder {
1713+ /// MyImageLoader(this.image);
1714+ ///
1715+ /// final Image image;
1716+ ///
1717+ /// void draw(Canvas canvas) {
1718+ /// canvas.drawImage(image, Offset.zero, Paint());
1719+ /// }
1720+ ///
1721+ /// void dispose() => image.dispose();
1722+ /// }
1723+ /// ```
1724+ ///
16651725 /// The returned object behaves identically to this image. Calling
16661726 /// [dispose] on it will only dispose the underlying native resources if it
16671727 /// is the last remaining handle.
@@ -1674,6 +1734,7 @@ class Image {
16741734 'handles, as the underlying data may have been released.'
16751735 );
16761736 }
1737+ assert (! _image._disposed);
16771738 return Image ._(_image);
16781739 }
16791740
@@ -1686,7 +1747,8 @@ class _Image extends NativeFieldWrapperClass2 {
16861747 // This class is created by the engine, and should not be instantiated
16871748 // or extended directly.
16881749 //
1689- // To obtain an [Image] object, use [instantiateImageCodec].
1750+ // _Images are always handed out wrapped in [Image]s. To create an [Image],
1751+ // use the ImageDescriptor API.
16901752 @pragma ('vm:entry-point' )
16911753 _Image ._();
16921754
@@ -1707,18 +1769,15 @@ class _Image extends NativeFieldWrapperClass2 {
17071769
17081770 bool _disposed = false ;
17091771 void dispose () {
1710- assert (() {
1711- assert (! _disposed);
1712- assert (
1713- _handles.isEmpty,
1714- 'Attempted to dispose of an Image object that has ${_handles .length } '
1715- 'open handles.\n '
1716- 'If you see this, it is a bug in dart:ui. Please file an issue at '
1717- 'https://github.com/flutter/flutter/issues/new.' ,
1718- );
1719- _disposed = true ;
1720- return true ;
1721- }());
1772+ assert (! _disposed);
1773+ assert (
1774+ _handles.isEmpty,
1775+ 'Attempted to dispose of an Image object that has ${_handles .length } '
1776+ 'open handles.\n '
1777+ 'If you see this, it is a bug in dart:ui. Please file an issue at '
1778+ 'https://github.com/flutter/flutter/issues/new.' ,
1779+ );
1780+ _disposed = true ;
17221781 _dispose ();
17231782 }
17241783
@@ -1739,7 +1798,8 @@ typedef ImageDecoderCallback = void Function(Image result);
17391798/// [Codec.getNextFrame] .
17401799///
17411800/// The recipient of this class is responsible for calling [Image.dispose] on
1742- /// [image] .
1801+ /// [image] . To share the image with other interested parties, use
1802+ /// [Image.clone] .
17431803class FrameInfo {
17441804 /// This class is created by the engine, and should not be instantiated
17451805 /// or extended directly.
@@ -1757,6 +1817,8 @@ class FrameInfo {
17571817 /// The [Image] object for this frame.
17581818 ///
17591819 /// This object must be disposed by the recipient of this frame info.
1820+ ///
1821+ /// To share this image with other interested parties, use [Image.clone] .
17601822 final Image image;
17611823}
17621824
0 commit comments