Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 9eef907

Browse files
committed
review
1 parent 4f15600 commit 9eef907

File tree

1 file changed

+87
-25
lines changed

1 file changed

+87
-25
lines changed

lib/ui/painting.dart

Lines changed: 87 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -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].
17431803
class 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

Comments
 (0)