Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions lib/ui/painting.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1671,6 +1671,10 @@ class Image {
/// It is safe to pass an [Image] handle to another object or method if the
/// current holder no longer needs it.
///
/// To check whether two [Image] references are refering to the same
/// underlying image memory, use [isCloneOf] rather than the equality operator
/// or [identical].
///
/// The following example demonstrates valid usage.
///
/// ```dart
Expand Down Expand Up @@ -1740,6 +1744,17 @@ class Image {
return Image._(_image);
}

/// Returns true if `other` is a [clone] of this and thus shares the same
/// underlying image memory, even if this or `other` is [dispose]d.
///
/// This method may return false for two images that were decoded from the
/// same underlying asset, if they are not sharing the same memory. For
/// example, if the same file is decoded using [instantiateImageCodec] twice,
/// or the same bytes are decoded using [decodeImageFromPixels] twice, there
/// will be two distinct [Image]s that render the same but do not share
/// underlying memory, and so will not be treated as clones of each other.
bool isCloneOf(Image other) => other._image == _image;

@override
String toString() => _image.toString();
}
Expand Down
6 changes: 6 additions & 0 deletions lib/web_ui/lib/src/engine/canvaskit/image.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ class CkAnimatedImage implements ui.Image {
@override
ui.Image clone() => this;

@override
bool isCloneOf(ui.Image other) => other == this;

@override
List<StackTrace>? debugGetOpenHandleStackTraces() => null;
int get frameCount => _skAnimatedImage.getFrameCount();
Expand Down Expand Up @@ -125,6 +128,9 @@ class CkImage implements ui.Image {
@override
ui.Image clone() => this;

@override
bool isCloneOf(ui.Image other) => other == this;

@override
List<StackTrace>? debugGetOpenHandleStackTraces() => null;

Expand Down
3 changes: 3 additions & 0 deletions lib/web_ui/lib/src/engine/html_image_codec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ class HtmlImage implements ui.Image {
@override
ui.Image clone() => this;

@override
bool isCloneOf(ui.Image other) => other == this;

@override
List<StackTrace>? debugGetOpenHandleStackTraces() => null;

Expand Down
2 changes: 2 additions & 0 deletions lib/web_ui/lib/src/ui/painting.dart
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,8 @@ abstract class Image {

Image clone() => this;

bool isCloneOf(Image other) => other == this;

List<StackTrace>? debugGetOpenHandleStackTraces() => null;

@override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,9 @@ class TestImage implements Image {
@override
Image clone() => this;

@override
bool isCloneOf(Image other) => other == this;

@override
List<StackTrace>/*?*/ debugGetOpenHandleStackTraces() => <StackTrace>[];
}
Expand Down
23 changes: 23 additions & 0 deletions testing/dart/image_dispose_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,29 @@ void main() {
frame.image.dispose();
expect(frame.image.debugGetOpenHandleStackTraces(), isEmpty);
}, skip: !assertsEnabled);

test('Clones can be compared', () async {
final Uint8List bytes = await readFile('2x2.png');
final Codec codec = await instantiateImageCodec(bytes);
final FrameInfo frame = await codec.getNextFrame();

final Image handle1 = frame.image.clone();
final Image handle2 = handle1.clone();

expect(handle1.isCloneOf(handle2), true);
expect(handle2.isCloneOf(handle1), true);
expect(handle1.isCloneOf(frame.image), true);

handle1.dispose();
expect(handle1.isCloneOf(handle2), true);
expect(handle2.isCloneOf(handle1), true);
expect(handle1.isCloneOf(frame.image), true);

final Codec codec2 = await instantiateImageCodec(bytes);
final FrameInfo frame2 = await codec2.getNextFrame();

expect(frame2.image.isCloneOf(frame.image), false);
});
}

Future<Uint8List> readFile(String fileName) async {
Expand Down