@@ -18,6 +18,7 @@ class MultiImageStreamCompleter extends ImageStreamCompleter {
18
18
MultiImageStreamCompleter ({
19
19
required Stream <ui.Codec > codec,
20
20
required double scale,
21
+ String ? debugLabel,
21
22
Stream <ImageChunkEvent >? chunkEvents,
22
23
InformationCollector ? informationCollector,
23
24
}) : _informationCollector = informationCollector,
@@ -63,7 +64,7 @@ class MultiImageStreamCompleter extends ImageStreamCompleter {
63
64
ui.FrameInfo ? _nextFrame;
64
65
65
66
// When the current was first shown.
66
- Duration ? _shownTimestamp;
67
+ late Duration _shownTimestamp;
67
68
68
69
// The requested duration for the current frame;
69
70
Duration ? _frameDuration;
@@ -99,25 +100,36 @@ class MultiImageStreamCompleter extends ImageStreamCompleter {
99
100
100
101
void _handleAppFrame (Duration timestamp) {
101
102
_frameCallbackScheduled = false ;
102
- if (! hasListeners) return ;
103
+ if (! hasListeners) {
104
+ return ;
105
+ }
106
+ assert (_nextFrame != null );
103
107
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
+ ));
105
113
_shownTimestamp = timestamp;
106
114
_frameDuration = _nextFrame! .duration;
115
+ _nextFrame! .image.dispose ();
107
116
_nextFrame = null ;
117
+
108
118
if (_framesEmitted % _codec! .frameCount == 0 && _nextImageCodec != null ) {
109
119
_switchToNewCodec ();
110
120
} else {
111
- final completedCycles = _framesEmitted ~ / _codec! .frameCount;
121
+ final int completedCycles = _framesEmitted ~ / _codec! .frameCount;
112
122
if (_codec! .repetitionCount == - 1 ||
113
123
completedCycles <= _codec! .repetitionCount) {
114
124
_decodeNextFrameAndSchedule ();
115
125
}
116
126
}
117
127
return ;
118
128
}
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
+ });
121
133
}
122
134
123
135
bool _isFirstFrame () {
@@ -129,9 +141,13 @@ class MultiImageStreamCompleter extends ImageStreamCompleter {
129
141
}
130
142
131
143
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 ;
132
148
try {
133
149
_nextFrame = await _codec! .getNextFrame ();
134
- } on Object catch (exception, stack) {
150
+ } catch (exception, stack) {
135
151
reportError (
136
152
context: ErrorDescription ('resolving an image frame' ),
137
153
exception: exception,
@@ -148,10 +164,15 @@ class MultiImageStreamCompleter extends ImageStreamCompleter {
148
164
if (! hasListeners) {
149
165
return ;
150
166
}
151
-
152
167
// This is not an animated image, just return it and don't schedule more
153
168
// 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 ;
155
176
return ;
156
177
}
157
178
_scheduleAppFrame ();
@@ -173,7 +194,12 @@ class MultiImageStreamCompleter extends ImageStreamCompleter {
173
194
@override
174
195
void addListener (ImageStreamListener listener) {
175
196
__hadAtLeastOneListener = true ;
176
- if (! hasListeners && _codec != null ) _decodeNextFrameAndSchedule ();
197
+ if (! hasListeners &&
198
+ _codec != null &&
199
+ ( //_currentImage == null ||
200
+ _codec! .frameCount > 1 )) {
201
+ _decodeNextFrameAndSchedule ();
202
+ }
177
203
super .addListener (listener);
178
204
}
179
205
0 commit comments