From 5fc8eb8263801c17c360eaf189e0f6b94bfdc686 Mon Sep 17 00:00:00 2001 From: amirh Date: Fri, 17 Nov 2017 13:48:28 -0800 Subject: [PATCH] Use MultiFrameImageStreamProvider in the various image providers. (#12997) --- .../test/card_collection_test.dart | 3 + .../test/color_testing_demo_test.dart | 4 + dev/manual_tests/test/mock_image_http.dart | 15 ++++ .../lib/src/services/image_provider.dart | 76 ++++++------------- .../lib/src/services/image_stream.dart | 2 + .../test/widgets/image_resolution_test.dart | 27 ++++--- 6 files changed, 63 insertions(+), 64 deletions(-) create mode 100644 dev/manual_tests/test/mock_image_http.dart diff --git a/dev/manual_tests/test/card_collection_test.dart b/dev/manual_tests/test/card_collection_test.dart index 93cb39761c..428e30e270 100644 --- a/dev/manual_tests/test/card_collection_test.dart +++ b/dev/manual_tests/test/card_collection_test.dart @@ -3,12 +3,15 @@ // found in the LICENSE file. import 'package:flutter/material.dart'; +import 'package:flutter/services.dart' show createHttpClient; import 'package:flutter_test/flutter_test.dart'; import '../lib/card_collection.dart' as card_collection; +import 'mock_image_http.dart'; void main() { testWidgets('Card Collection smoke test', (WidgetTester tester) async { + createHttpClient = createMockImageHttpClient; card_collection.main(); // builds the app and schedules a frame but doesn't trigger one await tester.pump(); // see https://github.com/flutter/flutter/issues/1865 await tester.pump(); // triggers a frame diff --git a/dev/manual_tests/test/color_testing_demo_test.dart b/dev/manual_tests/test/color_testing_demo_test.dart index c00f64c8e4..1d6b2d4644 100644 --- a/dev/manual_tests/test/color_testing_demo_test.dart +++ b/dev/manual_tests/test/color_testing_demo_test.dart @@ -3,12 +3,16 @@ // found in the LICENSE file. import 'package:flutter/material.dart'; +import 'package:flutter/services.dart' show createHttpClient; import 'package:flutter_test/flutter_test.dart'; import '../lib/color_testing_demo.dart' as color_testing_demo; +import 'mock_image_http.dart'; void main() { + testWidgets('Color testing demo smoke test', (WidgetTester tester) async { + createHttpClient = createMockImageHttpClient; color_testing_demo.main(); // builds the app and schedules a frame but doesn't trigger one await tester.pump(); // see https://github.com/flutter/flutter/issues/1865 await tester.pump(); // triggers a frame diff --git a/dev/manual_tests/test/mock_image_http.dart b/dev/manual_tests/test/mock_image_http.dart new file mode 100644 index 0000000000..b491bd9a61 --- /dev/null +++ b/dev/manual_tests/test/mock_image_http.dart @@ -0,0 +1,15 @@ +import 'dart:async'; +import 'package:flutter/foundation.dart' show ValueGetter; +import 'package:http/http.dart' as http; +import 'package:http/testing.dart' as http; + +import '../../../packages/flutter/test/services/image_data.dart'; + +// Returns a mock HTTP client that responds with an image to all requests. +ValueGetter createMockImageHttpClient = () { + return new http.MockClient((http.BaseRequest request) { + return new Future.value( + new http.Response.bytes(kTransparentImage, 200, request: request) + ); + }); +}; diff --git a/packages/flutter/lib/src/services/image_provider.dart b/packages/flutter/lib/src/services/image_provider.dart index e92719a92d..fa707809f8 100644 --- a/packages/flutter/lib/src/services/image_provider.dart +++ b/packages/flutter/lib/src/services/image_provider.dart @@ -5,7 +5,7 @@ import 'dart:async'; import 'dart:io' show File; import 'dart:typed_data'; -import 'dart:ui' as ui show Image; +import 'dart:ui' as ui show instantiateImageCodec, Codec; import 'dart:ui' show Size, Locale, TextDirection, hashValues; import 'package:flutter/foundation.dart'; @@ -14,7 +14,6 @@ import 'package:http/http.dart' as http; import 'asset_bundle.dart'; import 'http_client.dart'; import 'image_cache.dart'; -import 'image_decoder.dart'; import 'image_stream.dart'; /// Configuration information passed to the [ImageProvider.resolve] method to @@ -365,8 +364,9 @@ abstract class AssetBundleImageProvider extends ImageProvider loadAsync(AssetBundleImageKey key) async { + Future _loadAsync(AssetBundleImageKey key) async { final ByteData data = await key.bundle.load(key.name); if (data == null) throw 'Unable to read data'; - final ui.Image image = await decodeImage(data); - if (image == null) - throw 'Unable to decode image data'; - return new ImageInfo(image: image, scale: key.scale); - } - - /// Converts raw image data from a [ByteData] buffer into a decoded - /// [ui.Image] which can be passed to a [Canvas]. - /// - /// By default, this just uses [decodeImageFromList]. This method could be - /// overridden in subclasses (e.g. for testing). - Future decodeImage(ByteData data) { - return decodeImageFromList(data.buffer.asUint8List()); + return await ui.instantiateImageCodec(data.buffer.asUint8List()); } } @@ -426,8 +414,9 @@ class NetworkImage extends ImageProvider { @override ImageStreamCompleter load(NetworkImage key) { - return new OneFrameImageStreamCompleter( - _loadAsync(key), + return new MultiFrameImageStreamCompleter( + codec: _loadAsync(key), + scale: key.scale, informationCollector: (StringBuffer information) { information.writeln('Image provider: $this'); information.write('Image key: $key'); @@ -437,26 +426,19 @@ class NetworkImage extends ImageProvider { static final http.Client _httpClient = createHttpClient(); - Future _loadAsync(NetworkImage key) async { + Future _loadAsync(NetworkImage key) async { assert(key == this); final Uri resolved = Uri.base.resolve(key.url); final http.Response response = await _httpClient.get(resolved); if (response == null || response.statusCode != 200) - return null; + throw new Exception('HTTP request failed, statusCode: ${response?.statusCode}, $resolved'); final Uint8List bytes = response.bodyBytes; if (bytes.lengthInBytes == 0) - return null; + throw new Exception('NetworkImage is an empty file: $resolved'); - final ui.Image image = await decodeImageFromList(bytes); - if (image == null) - return null; - - return new ImageInfo( - image: image, - scale: key.scale, - ); + return await ui.instantiateImageCodec(bytes); } @override @@ -498,29 +480,23 @@ class FileImage extends ImageProvider { @override ImageStreamCompleter load(FileImage key) { - return new OneFrameImageStreamCompleter( - _loadAsync(key), + return new MultiFrameImageStreamCompleter( + codec: _loadAsync(key), + scale: key.scale, informationCollector: (StringBuffer information) { information.writeln('Path: ${file?.path}'); } ); } - Future _loadAsync(FileImage key) async { + Future _loadAsync(FileImage key) async { assert(key == this); final Uint8List bytes = await file.readAsBytes(); if (bytes.lengthInBytes == 0) return null; - final ui.Image image = await decodeImageFromList(bytes); - if (image == null) - return null; - - return new ImageInfo( - image: image, - scale: key.scale, - ); + return await ui.instantiateImageCodec(bytes); } @override @@ -568,20 +544,16 @@ class MemoryImage extends ImageProvider { @override ImageStreamCompleter load(MemoryImage key) { - return new OneFrameImageStreamCompleter(_loadAsync(key)); + return new MultiFrameImageStreamCompleter( + codec: _loadAsync(key), + scale: key.scale + ); } - Future _loadAsync(MemoryImage key) async { + Future _loadAsync(MemoryImage key) { assert(key == this); - final ui.Image image = await decodeImageFromList(bytes); - if (image == null) - return null; - - return new ImageInfo( - image: image, - scale: key.scale, - ); + return ui.instantiateImageCodec(bytes); } @override diff --git a/packages/flutter/lib/src/services/image_stream.dart b/packages/flutter/lib/src/services/image_stream.dart index a802ae0851..91843baf09 100644 --- a/packages/flutter/lib/src/services/image_stream.dart +++ b/packages/flutter/lib/src/services/image_stream.dart @@ -365,6 +365,8 @@ class MultiFrameImageStreamCompleter extends ImageStreamCompleter { void _handleCodecReady(ui.Codec codec) { _codec = codec; + assert(_codec != null); + _decodeNextFrameAndSchedule(); } diff --git a/packages/flutter/test/widgets/image_resolution_test.dart b/packages/flutter/test/widgets/image_resolution_test.dart index 02f9ced802..301d221eae 100644 --- a/packages/flutter/test/widgets/image_resolution_test.dart +++ b/packages/flutter/test/widgets/image_resolution_test.dart @@ -88,24 +88,27 @@ class TestAssetBundle extends CachingAssetBundle { String toString() => '${describeIdentity(this)}()'; } +class FakeImageStreamCompleter extends ImageStreamCompleter { + FakeImageStreamCompleter(Future image) { + image.then(setImage); + } +} + class TestAssetImage extends AssetImage { TestAssetImage(String name) : super(name); @override - Future loadAsync(AssetBundleImageKey key) { - ImageInfo result; + ImageStreamCompleter load(AssetBundleImageKey key) { + ImageInfo imageInfo; key.bundle.load(key.name).then((ByteData data) { - decodeImage(data).then((ui.Image image) { - result = new ImageInfo(image: image, scale: key.scale); - }); + final TestByteData testData = data; + final ui.Image image = new TestImage(testData.scale); + imageInfo = new ImageInfo(image: image, scale: key.scale); }); - assert(result != null); - return new SynchronousFuture(result); - } - - @override - Future decodeImage(covariant TestByteData data) { - return new SynchronousFuture(new TestImage(data.scale)); + assert(imageInfo != null); + return new FakeImageStreamCompleter( + new SynchronousFuture(imageInfo) + ); } }