Skip to content

Commit

Permalink
Make the public ui.Codec API Future based instead of callback based. (f…
Browse files Browse the repository at this point in the history
  • Loading branch information
amirh authored Nov 9, 2017
1 parent 0b7582e commit 2920d61
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 45 deletions.
68 changes: 58 additions & 10 deletions lib/ui/painting.dart
Original file line number Diff line number Diff line change
Expand Up @@ -879,9 +879,6 @@ abstract class FrameInfo extends NativeFieldWrapperClass2 {
Image get image native "FrameInfo_image";
}

/// Callback signature for [Codec.getNextFrame].
typedef void NextFrameInfoCallback(FrameInfo frameInfo);

/// A handle to an image codec.
abstract class Codec extends NativeFieldWrapperClass2 {
/// Number of frames in this image.
Expand All @@ -897,24 +894,36 @@ abstract class Codec extends NativeFieldWrapperClass2 {
///
/// Wraps back to the first frame after returning the last frame.
///
/// The returned future can complete with an error if the decoding has failed.
Future<FrameInfo> getNextFrame() {
return _futurize(_getNextFrame);
}

/// Returns an error message on failure, null on success.
String getNextFrame(NextFrameInfoCallback callback) native "Codec_getNextFrame";
String _getNextFrame(_Callback<FrameInfo> callback) native "Codec_getNextFrame";

/// Release the resources used by this object. The object is no longer usable
/// after this method is called.
void dispose() native "Codec_dispose";
}

/// Callback signature for [imageCodecFromList].
/// Instantiates an image codec [Codec] object.
///
/// If parsing the data passed to [imageCodecFromList] failed, the result will
/// be null.
typedef void CodecCallback(Codec result);
/// [list] is the binary image data (e.g a PNG or GIF binary data).
/// The data can be for either static or animated images.
///
/// The returned future can complete with an error if the image decoding has
/// failed.
Future<Codec> instantiateImageCodec(Uint8List list) {
return _futurize(
(_Callback<Codec> callback) => _instantiateImageCodec(list, callback)
);
}

/// Instatiates a [Codec] object for an image binary data.
/// Instantiates a [Codec] object for an image binary data.
///
/// Returns an error message if the instantiation has failed, null otherwise.
String instantiateImageCodec(Uint8List list, CodecCallback callback)
String _instantiateImageCodec(Uint8List list, _Callback<Codec> callback)
native "instantiateImageCodec";

/// Convert an image file from a byte array into an [Image] object.
Expand Down Expand Up @@ -2433,3 +2442,42 @@ class PictureRecorder extends NativeFieldWrapperClass2 {
/// Returns null if the PictureRecorder is not associated with a canvas.
Picture endRecording() native "PictureRecorder_endRecording";
}

/// Generic callback signature, used by [_futurize].
typedef void _Callback<T>(T result);

/// Signature for a method that receives a [_Callback].
///
/// Return value should be null on success, and a string error message on
/// failure.
typedef String _Callbacker<T>(_Callback<T> callback);

/// Converts a method that receives a value-returning callback to a method that
/// returns a Future.
///
/// Example usage:
/// ```dart
/// typedef void IntCallback(int result);
///
/// void doSomethingAndCallback(IntCallback callback) {
/// new Timer(new Duration(seconds: 1), () { callback(1); });
/// }
///
/// Future<int> doSomething() {
/// return _futurize(domeSomethingAndCallback);
/// }
/// ```
///
Future<T> _futurize<T>(_Callbacker<T> callbacker) async {
Completer<T> completer = new Completer<T>();
String err = callbacker(completer.complete);
if (err != null) {
throw new Exception(err);
}
T result = await completer.future;
if (result == null) {
throw new Exception('operation failed');
}
return result;
}

52 changes: 17 additions & 35 deletions testing/dart/codec_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,52 +14,32 @@ void main() {

test('Animation metadata', () async {
Uint8List data = await _getSkiaResource('alphabetAnim.gif').readAsBytes();
Completer<ui.Codec> completer = new Completer<ui.Codec>();
expect(ui.instantiateImageCodec(data, completer.complete), null);
ui.Codec codec = await completer.future;
ui.Codec codec = await ui.instantiateImageCodec(data);
expect(codec, isNotNull);
expect(codec.frameCount, 13);
expect(codec.repetitionCount, 0);
codec.dispose();

data = await _getSkiaResource('test640x479.gif').readAsBytes();
completer = new Completer<ui.Codec>();
expect(ui.instantiateImageCodec(data, completer.complete), null);
codec = await completer.future;
codec = await ui.instantiateImageCodec(data);
expect(codec.frameCount, 4);
expect(codec.repetitionCount, -1);
});

test('Fails when no callback provided', () async {
Uint8List data = await _getSkiaResource('alphabetAnim.gif').readAsBytes();
expect(ui.instantiateImageCodec(data, null), 'Callback must be a function');
});

test('Fails with invalid data', () async {
Uint8List data = new Uint8List.fromList([1, 2, 3]);
Completer<ui.Codec> completer = new Completer<ui.Codec>();
expect(ui.instantiateImageCodec(data, completer.complete), null);
ui.Codec codec = await completer.future;
expect(codec, null);
});

test('nextFrame fails when no callback provided', () async {
Uint8List data = await _getSkiaResource('alphabetAnim.gif').readAsBytes();
Completer<ui.Codec> completer = new Completer<ui.Codec>();
expect(ui.instantiateImageCodec(data, completer.complete), null);
ui.Codec codec = await completer.future;
expect(codec.getNextFrame(null), 'Callback must be a function');
expect(
ui.instantiateImageCodec(data),
throwsA(exceptionWithMessage('operation failed'))
);
});

test('nextFrame', () async {
Uint8List data = await _getSkiaResource('test640x479.gif').readAsBytes();
Completer<ui.Codec> completer = new Completer<ui.Codec>();
expect(ui.instantiateImageCodec(data, completer.complete), null);
ui.Codec codec = await completer.future;
ui.Codec codec = await ui.instantiateImageCodec(data);
List<List<int>> decodedFrameInfos = [];
for (int i = 0; i < 5; i++) {
Completer<ui.FrameInfo> frameCompleter = new Completer<ui.FrameInfo>();
codec.getNextFrame(frameCompleter.complete);
ui.FrameInfo frameInfo = await frameCompleter.future;
ui.FrameInfo frameInfo = await codec.getNextFrame();
decodedFrameInfos.add([
frameInfo.durationMillis,
frameInfo.image.width,
Expand All @@ -77,14 +57,10 @@ void main() {

test('non animated image', () async {
Uint8List data = await _getSkiaResource('baby_tux.png').readAsBytes();
Completer<ui.Codec> completer = new Completer<ui.Codec>();
expect(ui.instantiateImageCodec(data, completer.complete), null);
ui.Codec codec = await completer.future;
ui.Codec codec = await ui.instantiateImageCodec(data);
List<List<int>> decodedFrameInfos = [];
for (int i = 0; i < 2; i++) {
Completer<ui.FrameInfo> frameCompleter = new Completer<ui.FrameInfo>();
codec.getNextFrame(frameCompleter.complete);
ui.FrameInfo frameInfo = await frameCompleter.future;
ui.FrameInfo frameInfo = await codec.getNextFrame();
decodedFrameInfos.add([
frameInfo.durationMillis,
frameInfo.image.width,
Expand All @@ -109,3 +85,9 @@ File _getSkiaResource(String fileName) {
path.join('third_party', 'skia', 'resources', fileName);
return new File(assetPath);
}

Matcher exceptionWithMessage(String m) {
return predicate((e) {
return e is Exception && e.message == m;
});
}

0 comments on commit 2920d61

Please sign in to comment.