Skip to content

Commit

Permalink
Support animated images in the CanvasKit backend (#15678)
Browse files Browse the repository at this point in the history
  • Loading branch information
Harry Terkelsen committed Jan 16, 2020
1 parent c7d0fb7 commit 6693dd7
Showing 1 changed file with 70 additions and 12 deletions.
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;
}

0 comments on commit 6693dd7

Please sign in to comment.