Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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