Make the public ui.Codec API Future based instead of callback based. (flutter/engine#4341)
This commit is contained in:
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
@@ -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,
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user