Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support animated images in the CanvasKit backend #15678

Merged
Merged
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
82 changes: 70 additions & 12 deletions lib/web_ui/lib/src/engine/compositor/image.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,53 @@ part of engine;
/// Instantiates a [ui.Codec] backed by an `SkImage` from Skia.
void skiaInstantiateImageCodec(Uint8List list, Callback<ui.Codec> callback,
[int width, int height, int format, int rowBytes]) {
final js.JsObject skImage =
canvasKit.callMethod('MakeImageFromEncoded', <Uint8List>[list]);
final SkImage image = SkImage(skImage);
final SkImageCodec codec = SkImageCodec(image);
final js.JsObject skAnimatedImage =
canvasKit.callMethod('MakeAnimatedImageFromEncoded', <Uint8List>[list]);
final SkAnimatedImage animatedImage = SkAnimatedImage(skAnimatedImage);
final SkAnimatedImageCodec codec = SkAnimatedImageCodec(animatedImage);
callback(codec);
}

/// A wrapper for `SkAnimatedImage`.
class SkAnimatedImage implements ui.Image {
final js.JsObject _skAnimatedImage;

SkAnimatedImage(this._skAnimatedImage);

@override
void dispose() {
_skAnimatedImage.callMethod('delete');
}

int get frameCount => _skAnimatedImage.callMethod('getFrameCount');

/// Decodes the next frame and returns the frame duration.
Duration decodeNextFrame() {
final int durationMillis = _skAnimatedImage.callMethod('decodeNextFrame');
return Duration(milliseconds: durationMillis);
}

int get repetitionCount => _skAnimatedImage.callMethod('getRepetitionCount');

SkImage get currentFrameAsImage {
final js.JsObject _currentFrame =
_skAnimatedImage.callMethod('getCurrentFrame');
return SkImage(_currentFrame);
}

@override
int get width => _skAnimatedImage.callMethod('width');

@override
int get height => _skAnimatedImage.callMethod('height');

@override
Future<ByteData> toByteData(
{ui.ImageByteFormat format = ui.ImageByteFormat.rawRgba}) {
throw 'unimplemented';
}
}

/// A [ui.Image] backed by an `SkImage` from Skia.
class SkImage implements ui.Image {
js.JsObject skImage;
Expand All @@ -22,6 +62,7 @@ class SkImage implements ui.Image {

@override
void dispose() {
skImage.callMethod('delete');
skImage = null;
}

Expand All @@ -38,25 +79,42 @@ class SkImage implements ui.Image {
}
}

/// A [ui.Codec] backed by an `SkImage` from Skia.
class SkImageCodec implements ui.Codec {
final SkImage skImage;
/// A [Codec] that wraps an `SkAnimatedImage`.
class SkAnimatedImageCodec implements ui.Codec {
SkAnimatedImage animatedImage;

SkImageCodec(this.skImage);
SkAnimatedImageCodec(this.animatedImage);

@override
void dispose() {
// TODO: implement dispose
animatedImage.dispose();
animatedImage = null;
}

@override
int get frameCount => 1;
int get frameCount => animatedImage.frameCount;

@override
int get repetitionCount => animatedImage.repetitionCount;

@override
Future<ui.FrameInfo> getNextFrame() {
return Future<ui.FrameInfo>.value(SingleFrameInfo(skImage));
final Duration duration = animatedImage.decodeNextFrame();
final SkImage image = animatedImage.currentFrameAsImage;
return Future<ui.FrameInfo>.value(AnimatedImageFrameInfo(duration, image));
}
}

/// Data for a single frame of an animated image.
class AnimatedImageFrameInfo implements ui.FrameInfo {
final Duration _duration;
final SkImage _image;

AnimatedImageFrameInfo(this._duration, this._image);

@override
Duration get duration => _duration;

@override
int get repetitionCount => 0;
ui.Image get image => _image;
}