@@ -18,6 +18,7 @@ class MultiImageStreamCompleter extends ImageStreamCompleter {
1818 MultiImageStreamCompleter ({
1919 required Stream <ui.Codec > codec,
2020 required double scale,
21+ String ? debugLabel,
2122 Stream <ImageChunkEvent >? chunkEvents,
2223 InformationCollector ? informationCollector,
2324 }) : _informationCollector = informationCollector,
@@ -63,7 +64,7 @@ class MultiImageStreamCompleter extends ImageStreamCompleter {
6364 ui.FrameInfo ? _nextFrame;
6465
6566 // When the current was first shown.
66- Duration ? _shownTimestamp;
67+ late Duration _shownTimestamp;
6768
6869 // The requested duration for the current frame;
6970 Duration ? _frameDuration;
@@ -99,25 +100,36 @@ class MultiImageStreamCompleter extends ImageStreamCompleter {
99100
100101 void _handleAppFrame (Duration timestamp) {
101102 _frameCallbackScheduled = false ;
102- if (! hasListeners) return ;
103+ if (! hasListeners) {
104+ return ;
105+ }
106+ assert (_nextFrame != null );
103107 if (_isFirstFrame () || _hasFrameDurationPassed (timestamp)) {
104- _emitFrame (ImageInfo (image: _nextFrame! .image, scale: _scale));
108+ _emitFrame (ImageInfo (
109+ image: _nextFrame! .image.clone (),
110+ scale: _scale,
111+ debugLabel: debugLabel,
112+ ));
105113 _shownTimestamp = timestamp;
106114 _frameDuration = _nextFrame! .duration;
115+ _nextFrame! .image.dispose ();
107116 _nextFrame = null ;
117+
108118 if (_framesEmitted % _codec! .frameCount == 0 && _nextImageCodec != null ) {
109119 _switchToNewCodec ();
110120 } else {
111- final completedCycles = _framesEmitted ~ / _codec! .frameCount;
121+ final int completedCycles = _framesEmitted ~ / _codec! .frameCount;
112122 if (_codec! .repetitionCount == - 1 ||
113123 completedCycles <= _codec! .repetitionCount) {
114124 _decodeNextFrameAndSchedule ();
115125 }
116126 }
117127 return ;
118128 }
119- final delay = _frameDuration! - (timestamp - _shownTimestamp! );
120- _timer = Timer (delay * timeDilation, _scheduleAppFrame);
129+ final Duration delay = _frameDuration! - (timestamp - _shownTimestamp);
130+ _timer = Timer (delay * timeDilation, () {
131+ _scheduleAppFrame ();
132+ });
121133 }
122134
123135 bool _isFirstFrame () {
@@ -129,9 +141,13 @@ class MultiImageStreamCompleter extends ImageStreamCompleter {
129141 }
130142
131143 Future <void > _decodeNextFrameAndSchedule () async {
144+ // This will be null if we gave it away. If not, it's still ours and it
145+ // must be disposed of.
146+ _nextFrame? .image.dispose ();
147+ _nextFrame = null ;
132148 try {
133149 _nextFrame = await _codec! .getNextFrame ();
134- } on Object catch (exception, stack) {
150+ } catch (exception, stack) {
135151 reportError (
136152 context: ErrorDescription ('resolving an image frame' ),
137153 exception: exception,
@@ -148,10 +164,15 @@ class MultiImageStreamCompleter extends ImageStreamCompleter {
148164 if (! hasListeners) {
149165 return ;
150166 }
151-
152167 // This is not an animated image, just return it and don't schedule more
153168 // frames.
154- _emitFrame (ImageInfo (image: _nextFrame! .image, scale: _scale));
169+ _emitFrame (ImageInfo (
170+ image: _nextFrame! .image.clone (),
171+ scale: _scale,
172+ debugLabel: debugLabel,
173+ ));
174+ _nextFrame! .image.dispose ();
175+ _nextFrame = null ;
155176 return ;
156177 }
157178 _scheduleAppFrame ();
@@ -173,7 +194,12 @@ class MultiImageStreamCompleter extends ImageStreamCompleter {
173194 @override
174195 void addListener (ImageStreamListener listener) {
175196 __hadAtLeastOneListener = true ;
176- if (! hasListeners && _codec != null ) _decodeNextFrameAndSchedule ();
197+ if (! hasListeners &&
198+ _codec != null &&
199+ ( //_currentImage == null ||
200+ _codec! .frameCount > 1 )) {
201+ _decodeNextFrameAndSchedule ();
202+ }
177203 super .addListener (listener);
178204 }
179205
0 commit comments