Unverified Commit 5fc8eb82 authored by amirh's avatar amirh Committed by GitHub

Use MultiFrameImageStreamProvider in the various image providers. (#12997)

parent 86e23bdb
...@@ -3,12 +3,15 @@ ...@@ -3,12 +3,15 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show createHttpClient;
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import '../lib/card_collection.dart' as card_collection; import '../lib/card_collection.dart' as card_collection;
import 'mock_image_http.dart';
void main() { void main() {
testWidgets('Card Collection smoke test', (WidgetTester tester) async { 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 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(); // see https://github.com/flutter/flutter/issues/1865
await tester.pump(); // triggers a frame await tester.pump(); // triggers a frame
......
...@@ -3,12 +3,16 @@ ...@@ -3,12 +3,16 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show createHttpClient;
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import '../lib/color_testing_demo.dart' as color_testing_demo; import '../lib/color_testing_demo.dart' as color_testing_demo;
import 'mock_image_http.dart';
void main() { void main() {
testWidgets('Color testing demo smoke test', (WidgetTester tester) async { 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 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(); // see https://github.com/flutter/flutter/issues/1865
await tester.pump(); // triggers a frame await tester.pump(); // triggers a frame
......
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<http.Client> createMockImageHttpClient = () {
return new http.MockClient((http.BaseRequest request) {
return new Future<http.Response>.value(
new http.Response.bytes(kTransparentImage, 200, request: request)
);
});
};
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:io' show File; import 'dart:io' show File;
import 'dart:typed_data'; 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 'dart:ui' show Size, Locale, TextDirection, hashValues;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
...@@ -14,7 +14,6 @@ import 'package:http/http.dart' as http; ...@@ -14,7 +14,6 @@ import 'package:http/http.dart' as http;
import 'asset_bundle.dart'; import 'asset_bundle.dart';
import 'http_client.dart'; import 'http_client.dart';
import 'image_cache.dart'; import 'image_cache.dart';
import 'image_decoder.dart';
import 'image_stream.dart'; import 'image_stream.dart';
/// Configuration information passed to the [ImageProvider.resolve] method to /// Configuration information passed to the [ImageProvider.resolve] method to
...@@ -365,8 +364,9 @@ abstract class AssetBundleImageProvider extends ImageProvider<AssetBundleImageKe ...@@ -365,8 +364,9 @@ abstract class AssetBundleImageProvider extends ImageProvider<AssetBundleImageKe
/// image using [loadAsync]. /// image using [loadAsync].
@override @override
ImageStreamCompleter load(AssetBundleImageKey key) { ImageStreamCompleter load(AssetBundleImageKey key) {
return new OneFrameImageStreamCompleter( return new MultiFrameImageStreamCompleter(
loadAsync(key), codec: _loadAsync(key),
scale: key.scale,
informationCollector: (StringBuffer information) { informationCollector: (StringBuffer information) {
information.writeln('Image provider: $this'); information.writeln('Image provider: $this');
information.write('Image key: $key'); information.write('Image key: $key');
...@@ -379,23 +379,11 @@ abstract class AssetBundleImageProvider extends ImageProvider<AssetBundleImageKe ...@@ -379,23 +379,11 @@ abstract class AssetBundleImageProvider extends ImageProvider<AssetBundleImageKe
/// ///
/// This function is used by [load]. /// This function is used by [load].
@protected @protected
Future<ImageInfo> loadAsync(AssetBundleImageKey key) async { Future<ui.Codec> _loadAsync(AssetBundleImageKey key) async {
final ByteData data = await key.bundle.load(key.name); final ByteData data = await key.bundle.load(key.name);
if (data == null) if (data == null)
throw 'Unable to read data'; throw 'Unable to read data';
final ui.Image image = await decodeImage(data); return await ui.instantiateImageCodec(data.buffer.asUint8List());
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<ui.Image> decodeImage(ByteData data) {
return decodeImageFromList(data.buffer.asUint8List());
} }
} }
...@@ -426,8 +414,9 @@ class NetworkImage extends ImageProvider<NetworkImage> { ...@@ -426,8 +414,9 @@ class NetworkImage extends ImageProvider<NetworkImage> {
@override @override
ImageStreamCompleter load(NetworkImage key) { ImageStreamCompleter load(NetworkImage key) {
return new OneFrameImageStreamCompleter( return new MultiFrameImageStreamCompleter(
_loadAsync(key), codec: _loadAsync(key),
scale: key.scale,
informationCollector: (StringBuffer information) { informationCollector: (StringBuffer information) {
information.writeln('Image provider: $this'); information.writeln('Image provider: $this');
information.write('Image key: $key'); information.write('Image key: $key');
...@@ -437,26 +426,19 @@ class NetworkImage extends ImageProvider<NetworkImage> { ...@@ -437,26 +426,19 @@ class NetworkImage extends ImageProvider<NetworkImage> {
static final http.Client _httpClient = createHttpClient(); static final http.Client _httpClient = createHttpClient();
Future<ImageInfo> _loadAsync(NetworkImage key) async { Future<ui.Codec> _loadAsync(NetworkImage key) async {
assert(key == this); assert(key == this);
final Uri resolved = Uri.base.resolve(key.url); final Uri resolved = Uri.base.resolve(key.url);
final http.Response response = await _httpClient.get(resolved); final http.Response response = await _httpClient.get(resolved);
if (response == null || response.statusCode != 200) if (response == null || response.statusCode != 200)
return null; throw new Exception('HTTP request failed, statusCode: ${response?.statusCode}, $resolved');
final Uint8List bytes = response.bodyBytes; final Uint8List bytes = response.bodyBytes;
if (bytes.lengthInBytes == 0) 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( return await ui.instantiateImageCodec(bytes);
image: image,
scale: key.scale,
);
} }
@override @override
...@@ -498,29 +480,23 @@ class FileImage extends ImageProvider<FileImage> { ...@@ -498,29 +480,23 @@ class FileImage extends ImageProvider<FileImage> {
@override @override
ImageStreamCompleter load(FileImage key) { ImageStreamCompleter load(FileImage key) {
return new OneFrameImageStreamCompleter( return new MultiFrameImageStreamCompleter(
_loadAsync(key), codec: _loadAsync(key),
scale: key.scale,
informationCollector: (StringBuffer information) { informationCollector: (StringBuffer information) {
information.writeln('Path: ${file?.path}'); information.writeln('Path: ${file?.path}');
} }
); );
} }
Future<ImageInfo> _loadAsync(FileImage key) async { Future<ui.Codec> _loadAsync(FileImage key) async {
assert(key == this); assert(key == this);
final Uint8List bytes = await file.readAsBytes(); final Uint8List bytes = await file.readAsBytes();
if (bytes.lengthInBytes == 0) if (bytes.lengthInBytes == 0)
return null; return null;
final ui.Image image = await decodeImageFromList(bytes); return await ui.instantiateImageCodec(bytes);
if (image == null)
return null;
return new ImageInfo(
image: image,
scale: key.scale,
);
} }
@override @override
...@@ -568,20 +544,16 @@ class MemoryImage extends ImageProvider<MemoryImage> { ...@@ -568,20 +544,16 @@ class MemoryImage extends ImageProvider<MemoryImage> {
@override @override
ImageStreamCompleter load(MemoryImage key) { ImageStreamCompleter load(MemoryImage key) {
return new OneFrameImageStreamCompleter(_loadAsync(key)); return new MultiFrameImageStreamCompleter(
codec: _loadAsync(key),
scale: key.scale
);
} }
Future<ImageInfo> _loadAsync(MemoryImage key) async { Future<ui.Codec> _loadAsync(MemoryImage key) {
assert(key == this); assert(key == this);
final ui.Image image = await decodeImageFromList(bytes); return ui.instantiateImageCodec(bytes);
if (image == null)
return null;
return new ImageInfo(
image: image,
scale: key.scale,
);
} }
@override @override
......
...@@ -365,6 +365,8 @@ class MultiFrameImageStreamCompleter extends ImageStreamCompleter { ...@@ -365,6 +365,8 @@ class MultiFrameImageStreamCompleter extends ImageStreamCompleter {
void _handleCodecReady(ui.Codec codec) { void _handleCodecReady(ui.Codec codec) {
_codec = codec; _codec = codec;
assert(_codec != null);
_decodeNextFrameAndSchedule(); _decodeNextFrameAndSchedule();
} }
......
...@@ -88,24 +88,27 @@ class TestAssetBundle extends CachingAssetBundle { ...@@ -88,24 +88,27 @@ class TestAssetBundle extends CachingAssetBundle {
String toString() => '${describeIdentity(this)}()'; String toString() => '${describeIdentity(this)}()';
} }
class FakeImageStreamCompleter extends ImageStreamCompleter {
FakeImageStreamCompleter(Future<ImageInfo> image) {
image.then<Null>(setImage);
}
}
class TestAssetImage extends AssetImage { class TestAssetImage extends AssetImage {
TestAssetImage(String name) : super(name); TestAssetImage(String name) : super(name);
@override @override
Future<ImageInfo> loadAsync(AssetBundleImageKey key) { ImageStreamCompleter load(AssetBundleImageKey key) {
ImageInfo result; ImageInfo imageInfo;
key.bundle.load(key.name).then<Null>((ByteData data) { key.bundle.load(key.name).then<Null>((ByteData data) {
decodeImage(data).then<Null>((ui.Image image) { final TestByteData testData = data;
result = new ImageInfo(image: image, scale: key.scale); final ui.Image image = new TestImage(testData.scale);
}); imageInfo = new ImageInfo(image: image, scale: key.scale);
}); });
assert(result != null); assert(imageInfo != null);
return new SynchronousFuture<ImageInfo>(result); return new FakeImageStreamCompleter(
} new SynchronousFuture<ImageInfo>(imageInfo)
);
@override
Future<ui.Image> decodeImage(covariant TestByteData data) {
return new SynchronousFuture<ui.Image>(new TestImage(data.scale));
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment