Skip to content

Commit

Permalink
[web] roll CanvasKit 0.32.0; fix frame order in animated images (#30680)
Browse files Browse the repository at this point in the history
* roll CanvasKit 0.32.0
* Fix frame order in WASM image codecs
  • Loading branch information
yjbanov authored Jan 5, 2022
1 parent 3a667ab commit 830abeb
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 19 deletions.
2 changes: 1 addition & 1 deletion DEPS
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ vars = {

# WARNING: DO NOT EDIT canvaskit_cipd_instance MANUALLY
# See `lib/web_ui/README.md` for how to roll CanvasKit to a new version.
'canvaskit_cipd_instance': 'NcwvqeeKK7urddCbEdDvHytdaCiCA_8-4oS_T_ouGfgC',
'canvaskit_cipd_instance': 'CQJGaIvKwSuYCIi4hxn3jdY-Pcrdkcnnu65ZVt18oW8C',

# When updating the Dart revision, ensure that all entries that are
# dependencies of Dart are also updated to match the entries in the
Expand Down
2 changes: 1 addition & 1 deletion lib/web_ui/dev/canvaskit_lock.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Specifies the version of CanvasKit to use for Flutter Web apps.
#
# See `lib/web_ui/README.md` for how to update this file.
canvaskit_version: "0.31.0"
canvaskit_version: "0.32.0"
7 changes: 6 additions & 1 deletion lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -758,9 +758,14 @@ class SkColorType {
class SkAnimatedImage {
external int getFrameCount();

/// Returns duration in milliseconds.
external int getRepetitionCount();

/// Returns duration in milliseconds.
external int currentFrameDuration();

/// Advances to the next frame and returns its duration in milliseconds.
external int decodeNextFrame();

external SkImage makeImageAtCurrentFrame();
external int width();
external int height();
Expand Down
38 changes: 28 additions & 10 deletions lib/web_ui/lib/src/engine/canvaskit/image_wasm_codecs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ class CkAnimatedImage extends ManagedSkiaObject<SkAnimatedImage>
int _frameCount = 0;
int _repetitionCount = -1;

/// The index to the next frame to be decoded.
int _nextFrameIndex = 0;
/// Current frame index.
int _currentFrameIndex = 0;

@override
SkAnimatedImage createDefault() {
Expand All @@ -48,11 +48,16 @@ class CkAnimatedImage extends ManagedSkiaObject<SkAnimatedImage>
_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++) {
// Normally CanvasKit initializes `SkAnimatedImage` to point to the first
// frame in the animation. However, if the Skia object has been deleted then
// resurrected, the framework/app may already have advanced to one of the
// subsequent frames. When that happens the value of _currentFrameIndex will
// be something other than zero, and we need to tell the decoder to skip
// over the previous frames to point to the current one.
for (int i = 0; i < _currentFrameIndex; i++) {
animatedImage.decodeNextFrame();
}

return animatedImage;
}

Expand Down Expand Up @@ -100,10 +105,23 @@ class CkAnimatedImage extends ManagedSkiaObject<SkAnimatedImage>
@override
Future<ui.FrameInfo> getNextFrame() {
assert(_debugCheckIsNotDisposed());
final int durationMillis = skiaObject.decodeNextFrame();
final Duration duration = Duration(milliseconds: durationMillis);
final CkImage image = CkImage(skiaObject.makeImageAtCurrentFrame());
_nextFrameIndex = (_nextFrameIndex + 1) % _frameCount;
return Future<ui.FrameInfo>.value(AnimatedImageFrameInfo(duration, image));
final SkAnimatedImage animatedImage = skiaObject;

// SkAnimatedImage comes pre-initialized to point to the current frame (by
// default the first frame, and, with some special resurrection logic in
// `createDefault`, to a subsequent frame if resurrection happens in the
// middle of animation). Flutter's `Codec` semantics is to initialize to
// point to "just before the first frame", i.e. the first invocation of
// `getNextFrame` returns the first frame. Therefore, we have to read the
// current Skia frame, then advance SkAnimatedImage to the next frame, and
// return the current frame.
final ui.FrameInfo currentFrame = AnimatedImageFrameInfo(
Duration(milliseconds: animatedImage.currentFrameDuration()),
CkImage(animatedImage.makeImageAtCurrentFrame()),
);

animatedImage.decodeNextFrame();
_currentFrameIndex = (_currentFrameIndex + 1) % _frameCount;
return Future<ui.FrameInfo>.value(currentFrame);
}
}
2 changes: 1 addition & 1 deletion lib/web_ui/lib/src/engine/configuration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import 'package:js/js.dart';
/// The version of CanvasKit used by the web engine by default.
// DO NOT EDIT THE NEXT LINE OF CODE MANUALLY
// See `lib/web_ui/README.md` for how to roll CanvasKit to a new version.
const String _canvaskitVersion = '0.31.0';
const String _canvaskitVersion = '0.32.0';

/// The Web Engine configuration for the current application.
FlutterConfiguration get configuration => _configuration ??= FlutterConfiguration(_jsConfiguration);
Expand Down
9 changes: 4 additions & 5 deletions lib/web_ui/test/canvaskit/image_golden_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -94,17 +94,17 @@ void _testForImageCodecs({required bool useBrowserImageDecoder}) {
expect(image.repetitionCount, -1);

final ui.FrameInfo frame1 = await image.getNextFrame();
await expectFrameData(frame1, <int>[0, 255, 0, 255]);
await expectFrameData(frame1, <int>[255, 0, 0, 255]);
final ui.FrameInfo frame2 = await image.getNextFrame();
await expectFrameData(frame2, <int>[0, 0, 255, 255]);
await expectFrameData(frame2, <int>[0, 255, 0, 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();
await expectFrameData(frame3, <int>[255, 0, 0, 255]);
await expectFrameData(frame3, <int>[0, 0, 255, 255]);

testCollector.collectNow();
});
Expand Down Expand Up @@ -548,11 +548,10 @@ void _testCkAnimatedImage() {

test('CkAnimatedImage toByteData(RGBA)', () async {
final CkAnimatedImage image = CkAnimatedImage.decodeFromBytes(kAnimatedGif, 'test');
// TODO(yjbanov): frame sequence is wrong (https://github.com/flutter/flutter/issues/95281)
const List<List<int>> expectedColors = <List<int>>[
<int>[255, 0, 0, 255],
<int>[0, 255, 0, 255],
<int>[0, 0, 255, 255],
<int>[255, 0, 0, 255],
];
for (int i = 0; i < image.frameCount; i++) {
final ui.FrameInfo frame = await image.getNextFrame();
Expand Down

0 comments on commit 830abeb

Please sign in to comment.