Skip to content

Commit

Permalink
recall the last frame in the animated image after resurrection (#26277)
Browse files Browse the repository at this point in the history
  • Loading branch information
yjbanov authored May 20, 2021
1 parent 85201b8 commit acea7c4
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 2 deletions.
22 changes: 20 additions & 2 deletions lib/web_ui/lib/src/engine/canvaskit/image.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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();
Expand All @@ -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
Expand All @@ -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));
}
}
Expand Down
28 changes: 28 additions & 0 deletions lib/web_ui/test/canvaskit/image_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit acea7c4

Please sign in to comment.