recall the last frame in the animated image after resurrection (flutter/engine#26277)

This commit is contained in:
Yegor 2021-05-20 16:38:27 -07:00 committed by GitHub
parent 28e7fbfd72
commit e3646a04a3
2 changed files with 48 additions and 2 deletions

View File

@ -101,6 +101,11 @@ class CkAnimatedImage extends ManagedSkiaObject<SkAnimatedImage>
final String src;
final Uint8List _bytes;
int _frameCount = 0;
int _repetitionCount = -1;
/// The index to the next frame to be decoded.
int _nextFrameIndex = 0;
@override
SkAnimatedImage createDefault() {
@ -112,12 +117,24 @@ class CkAnimatedImage extends ManagedSkiaObject<SkAnimatedImage>
'Image source: $src',
);
}
_frameCount = animatedImage.getFrameCount();
_repetitionCount = animatedImage.getRepetitionCount();
// If the object has been deleted then resurrected, it may already have
// iterated over some frames. We need to skip over them.
for (int i = 0; i < _nextFrameIndex; i++) {
animatedImage.decodeNextFrame();
}
return animatedImage;
}
@override
SkAnimatedImage resurrect() => createDefault();
@override
bool get isResurrectionExpensive => true;
@override
void delete() {
rawSkiaObject?.delete();
@ -144,13 +161,13 @@ class CkAnimatedImage extends ManagedSkiaObject<SkAnimatedImage>
@override
int get frameCount {
assert(_debugCheckIsNotDisposed());
return skiaObject.getFrameCount();
return _frameCount;
}
@override
int get repetitionCount {
assert(_debugCheckIsNotDisposed());
return skiaObject.getRepetitionCount();
return _repetitionCount;
}
@override
@ -159,6 +176,7 @@ class CkAnimatedImage extends ManagedSkiaObject<SkAnimatedImage>
final int durationMillis = skiaObject.decodeNextFrame();
final Duration duration = Duration(milliseconds: durationMillis);
final CkImage image = CkImage(skiaObject.getCurrentFrame());
_nextFrameIndex = (_nextFrameIndex + 1) % _frameCount;
return Future<ui.FrameInfo>.value(AnimatedImageFrameInfo(duration, image));
}
}

View File

@ -43,6 +43,34 @@ void testMain() {
testCollector.collectNow();
});
test('CkAnimatedImage remembers last animation position after resurrection', () async {
browserSupportsFinalizationRegistry = false;
Future<void> expectFrameData(ui.FrameInfo frame, List<int> data) async {
final ByteData frameData = await frame.image.toByteData();
expect(frameData.buffer.asUint8List(), Uint8List.fromList(data));
}
final CkAnimatedImage image = CkAnimatedImage.decodeFromBytes(kAnimatedGif, 'test');
expect(image.frameCount, 3);
expect(image.repetitionCount, -1);
final ui.FrameInfo frame1 = await image.getNextFrame();
expectFrameData(frame1, <int>[0, 255, 0, 255]);
final ui.FrameInfo frame2 = await image.getNextFrame();
expectFrameData(frame2, <int>[0, 0, 255, 255]);
// Pretend that the image is temporarily deleted.
image.delete();
image.didDelete();
// Check that we got the 3rd frame after resurrection.
final ui.FrameInfo frame3 = await image.getNextFrame();
expectFrameData(frame3, <int>[255, 0, 0, 255]);
testCollector.collectNow();
});
test('CkImage toString', () {
final SkImage skImage =
canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)