Unverified Commit 7f8e89b5 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

Revert "Expose API for custom image decode and cache sizes (#41415)" (#42764)

parent b8576323
......@@ -9,6 +9,7 @@ import 'dart:ui' as ui;
import 'package:flutter/foundation.dart';
import 'binding.dart';
import 'debug.dart';
import 'image_provider.dart' as image_provider;
import 'image_stream.dart';
......@@ -37,14 +38,14 @@ class NetworkImage extends image_provider.ImageProvider<image_provider.NetworkIm
}
@override
ImageStreamCompleter load(image_provider.NetworkImage key, image_provider.DecoderCallback decode) {
ImageStreamCompleter load(image_provider.NetworkImage key) {
// Ownership of this controller is handed off to [_loadAsync]; it is that
// method's responsibility to close the controller's stream when the image
// has been loaded or an error is thrown.
final StreamController<ImageChunkEvent> chunkEvents = StreamController<ImageChunkEvent>();
return MultiFrameImageStreamCompleter(
codec: _loadAsync(key, chunkEvents, decode),
codec: _loadAsync(key, chunkEvents),
chunkEvents: chunkEvents.stream,
scale: key.scale,
informationCollector: () {
......@@ -75,7 +76,6 @@ class NetworkImage extends image_provider.ImageProvider<image_provider.NetworkIm
Future<ui.Codec> _loadAsync(
NetworkImage key,
StreamController<ImageChunkEvent> chunkEvents,
image_provider.DecoderCallback decode,
) async {
try {
assert(key == this);
......@@ -101,7 +101,7 @@ class NetworkImage extends image_provider.ImageProvider<image_provider.NetworkIm
if (bytes.lengthInBytes == 0)
throw Exception('NetworkImage is an empty file: $resolved');
return decode(bytes);
return PaintingBinding.instance.instantiateImageCodec(bytes);
} finally {
chunkEvents.close();
}
......
......@@ -11,8 +11,6 @@ import 'image_provider.dart' as image_provider;
import 'image_stream.dart';
/// The dart:html implemenation of [image_provider.NetworkImage].
///
/// NetworkImage on the web does not support decoding to a specified size.
class NetworkImage extends image_provider.ImageProvider<image_provider.NetworkImage> implements image_provider.NetworkImage {
/// Creates an object that fetches the image at the given URL.
///
......@@ -36,9 +34,9 @@ class NetworkImage extends image_provider.ImageProvider<image_provider.NetworkIm
}
@override
ImageStreamCompleter load(image_provider.NetworkImage key, image_provider.DecoderCallback decode) {
ImageStreamCompleter load(image_provider.NetworkImage key) {
return MultiFrameImageStreamCompleter(
codec: _loadAsync(key, decode),
codec: _loadAsync(key),
scale: key.scale,
informationCollector: () {
return <DiagnosticsNode>[
......@@ -49,10 +47,7 @@ class NetworkImage extends image_provider.ImageProvider<image_provider.NetworkIm
);
}
// Web does not support decoding network images to a specified size. The decode parameter
// here is ignored and the web-only `ui.webOnlyInstantiateImageCodecFromUrl` will be used
// directly in place of the typical `instantiateImageCodec` method.
Future<ui.Codec> _loadAsync(NetworkImage key, image_provider.DecoderCallback decode) async {
Future<ui.Codec> _loadAsync(NetworkImage key) async {
assert(key == this);
final Uri resolved = Uri.base.resolve(key.url);
......
......@@ -70,26 +70,8 @@ mixin PaintingBinding on BindingBase, ServicesBinding {
ImageCache createImageCache() => ImageCache();
/// Calls through to [dart:ui] with [decodedCacheRatioCap] from [ImageCache].
///
/// The [cacheWidth] and [cacheHeight] parameters, when specified, indicate the
/// size to decode the image to.
///
/// Both [cacheWidth] and [cacheHeight] must be positive values greater than or
/// equal to 1 or null. It is valid to specify only one of [cacheWidth] and
/// [cacheHeight] with the other remaining null, in which case the omitted
/// dimension will decode to its original size. When both are null or omitted,
/// the image will be decoded at its native resolution.
Future<ui.Codec> instantiateImageCodec(Uint8List bytes, {
int cacheWidth,
int cacheHeight,
}) {
assert(cacheWidth == null || cacheWidth > 0);
assert(cacheHeight == null || cacheHeight > 0);
return ui.instantiateImageCodec(
bytes,
targetWidth: cacheWidth,
targetHeight: cacheHeight,
);
Future<ui.Codec> instantiateImageCodec(Uint8List list) {
return ui.instantiateImageCodec(list);
}
@override
......
......@@ -152,16 +152,6 @@ class ImageConfiguration {
}
}
/// Performs the decode process for use in [ImageProvider.load].
///
/// This callback allows decoupling of the `cacheWidth` and `cacheHeight`
/// parameters from implementations of [ImageProvider] that do not use them.
///
/// See also:
///
/// * [ResizeImage], which uses this to override the `cacheWidth` and `cacheHeight` parameters.
typedef DecoderCallback = Future<ui.Codec> Function(Uint8List bytes, {int cacheWidth, int cacheHeight});
/// Identifies an image without committing to the precise final asset. This
/// allows a set of images to be identified and for the precise image to later
/// be resolved based on the environment, e.g. the device pixel ratio.
......@@ -322,11 +312,8 @@ abstract class ImageProvider<T> {
}
key.then<void>((T key) {
obtainedKey = key;
final ImageStreamCompleter completer = PaintingBinding.instance.imageCache.putIfAbsent(
key,
() => load(key, PaintingBinding.instance.instantiateImageCodec),
onError: handleError,
);
final ImageStreamCompleter completer = PaintingBinding.instance
.imageCache.putIfAbsent(key, () => load(key), onError: handleError);
if (completer != null) {
stream.setCompleter(completer);
}
......@@ -392,15 +379,8 @@ abstract class ImageProvider<T> {
/// Converts a key into an [ImageStreamCompleter], and begins fetching the
/// image.
///
/// The [decode] callback provides the logic to obtain the codec for the
/// image.
///
/// See also:
///
/// * [ResizeImage], for modifying the key to account for cache dimensions.
@protected
ImageStreamCompleter load(T key, DecoderCallback decode);
ImageStreamCompleter load(T key);
@override
String toString() => '$runtimeType()';
......@@ -464,9 +444,9 @@ abstract class AssetBundleImageProvider extends ImageProvider<AssetBundleImageKe
/// Converts a key into an [ImageStreamCompleter], and begins fetching the
/// image using [loadAsync].
@override
ImageStreamCompleter load(AssetBundleImageKey key, DecoderCallback decode) {
ImageStreamCompleter load(AssetBundleImageKey key) {
return MultiFrameImageStreamCompleter(
codec: _loadAsync(key, decode),
codec: _loadAsync(key),
scale: key.scale,
informationCollector: () sync* {
yield DiagnosticsProperty<ImageProvider>('Image provider', this);
......@@ -480,82 +460,11 @@ abstract class AssetBundleImageProvider extends ImageProvider<AssetBundleImageKe
///
/// This function is used by [load].
@protected
Future<ui.Codec> _loadAsync(AssetBundleImageKey key, DecoderCallback decode) async {
Future<ui.Codec> _loadAsync(AssetBundleImageKey key) async {
final ByteData data = await key.bundle.load(key.name);
if (data == null)
throw 'Unable to read data';
return await decode(data.buffer.asUint8List());
}
}
class _SizeAwareCacheKey {
const _SizeAwareCacheKey(this.providerCacheKey, this.width, this.height);
final Object providerCacheKey;
final int width;
final int height;
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType)
return false;
final _SizeAwareCacheKey typedOther = other;
return providerCacheKey == typedOther.providerCacheKey
&& width == typedOther.width
&& height == typedOther.height;
}
@override
int get hashCode => hashValues(providerCacheKey, width, height);
}
/// Instructs Flutter to decode the image at the specified dimensions
/// instead of at its native size.
///
/// This allows finer control of the size of the image in [ImageCache] and is
/// generally used to reduce the memory footprint of [ImageCache].
///
/// The decoded image may still be displayed at sizes other than the
/// cached size provided here.
class ResizeImage extends ImageProvider<_SizeAwareCacheKey> {
/// Creates an ImageProvider that decodes the image to the specified size.
///
/// The cached image will be directly decoded and stored at the resolution
/// defined by `width` and `height`. The image will lose detail and
/// use less memory if resized to a size smaller than the native size.
const ResizeImage(
this.imageProvider, {
this.width,
this.height,
}) : assert(width != null || height != null);
/// The [ImageProvider] that this class wraps.
final ImageProvider imageProvider;
/// The width the image should decode to and cache.
final int width;
/// The height the image should decode to and cache.
final int height;
@override
ImageStreamCompleter load(_SizeAwareCacheKey key, DecoderCallback decode) {
final DecoderCallback decodeResize = (Uint8List bytes, {int cacheWidth, int cacheHeight}) {
assert(
cacheWidth == null && cacheHeight == null,
'ResizeImage cannot be composed with another ImageProvider that applies cacheWidth or cacheHeight.'
);
return decode(bytes, cacheWidth: width, cacheHeight: height);
};
return imageProvider.load(key.providerCacheKey, decodeResize);
}
@override
Future<_SizeAwareCacheKey> obtainKey(ImageConfiguration configuration) async {
final Object providerCacheKey = await imageProvider.obtainKey(configuration);
return _SizeAwareCacheKey(providerCacheKey, width, height);
return await PaintingBinding.instance.instantiateImageCodec(data.buffer.asUint8List());
}
}
......@@ -563,11 +472,6 @@ class ResizeImage extends ImageProvider<_SizeAwareCacheKey> {
///
/// The image will be cached regardless of cache headers from the server.
///
/// When a network image is used on the Web platform, the [cacheWidth] and
/// [cacheHeight] parameters of the [DecoderCallback] are ignored as the Web
/// engine delegates image decoding of network images to the Web, which does
/// not support custom decode sizes.
///
/// See also:
///
/// * [Image.network] for a shorthand of an [Image] widget backed by [NetworkImage].
......@@ -592,7 +496,7 @@ abstract class NetworkImage extends ImageProvider<NetworkImage> {
Map<String, String> get headers;
@override
ImageStreamCompleter load(NetworkImage key, DecoderCallback decode);
ImageStreamCompleter load(NetworkImage key);
}
/// Decodes the given [File] object as an image, associating it with the given
......@@ -621,9 +525,9 @@ class FileImage extends ImageProvider<FileImage> {
}
@override
ImageStreamCompleter load(FileImage key, DecoderCallback decode) {
ImageStreamCompleter load(FileImage key) {
return MultiFrameImageStreamCompleter(
codec: _loadAsync(key, decode),
codec: _loadAsync(key),
scale: key.scale,
informationCollector: () sync* {
yield ErrorDescription('Path: ${file?.path}');
......@@ -631,14 +535,14 @@ class FileImage extends ImageProvider<FileImage> {
);
}
Future<ui.Codec> _loadAsync(FileImage key, DecoderCallback decode) async {
Future<ui.Codec> _loadAsync(FileImage key) async {
assert(key == this);
final Uint8List bytes = await file.readAsBytes();
if (bytes.lengthInBytes == 0)
return null;
return await decode(bytes);
return await PaintingBinding.instance.instantiateImageCodec(bytes);
}
@override
......@@ -689,17 +593,17 @@ class MemoryImage extends ImageProvider<MemoryImage> {
}
@override
ImageStreamCompleter load(MemoryImage key, DecoderCallback decode) {
ImageStreamCompleter load(MemoryImage key) {
return MultiFrameImageStreamCompleter(
codec: _loadAsync(key, decode),
codec: _loadAsync(key),
scale: key.scale,
);
}
Future<ui.Codec> _loadAsync(MemoryImage key, DecoderCallback decode) {
Future<ui.Codec> _loadAsync(MemoryImage key) {
assert(key == this);
return decode(bytes);
return PaintingBinding.instance.instantiateImageCodec(bytes);
}
@override
......
......@@ -55,13 +55,6 @@ ImageConfiguration createLocalImageConfiguration(BuildContext context, { Size si
);
}
ImageProvider<dynamic> _resizeIfNeeded(int cacheWidth, int cacheHeight, ImageProvider<dynamic> provider) {
if (cacheWidth != null || cacheHeight != null) {
return ResizeImage(provider, width: cacheWidth, height: cacheHeight);
}
return provider;
}
/// Prefetches an image into the image cache.
///
/// Returns a [Future] that will complete when the first image yielded by the
......@@ -239,17 +232,6 @@ typedef ImageLoadingBuilder = Widget Function(
/// ```
/// {@end-tool}
///
/// The [Image.asset], [Image.network], [Image.file], and [Image.memory]
/// constructors allow a custom decode size to be specified through
/// [cacheWidth] and [cacheHeight] parameters. The engine will decode the
/// image to the specified size, which is primarily intended to reduce the
/// memory usage of [ImageCache].
///
/// In the case where a network image is used on the Web platform, the
/// [cacheWidth] and [cacheHeight] parameters are ignored as the Web engine
/// delegates image decoding of network images to the Web, which does not support
/// custom decode sizes.
///
/// See also:
///
/// * [Icon], which shows an image from a font.
......@@ -323,16 +305,6 @@ class Image extends StatefulWidget {
/// [FilterQuality.none] which corresponds to nearest-neighbor.
///
/// If [excludeFromSemantics] is true, then [semanticLabel] will be ignored.
///
/// If [cacheWidth] or [cacheHeight] are provided, it indicates to the
/// engine that the image should be decoded at the specified size. The image
/// will be rendered to the constraints of the layout or [width] and [height]
/// regardless of these parameters. These parameters are primarily intended
/// to reduce the memory usage of [ImageCache].
///
/// In the case where the network image is on the Web platform, the [cacheWidth]
/// and [cacheHeight] parameters are ignored as the web engine delegates
/// image decoding to the web which does not support custom decode sizes.
Image.network(
String src, {
Key key,
......@@ -353,14 +325,10 @@ class Image extends StatefulWidget {
this.gaplessPlayback = false,
this.filterQuality = FilterQuality.low,
Map<String, String> headers,
int cacheWidth,
int cacheHeight,
}) : image = _resizeIfNeeded(cacheWidth, cacheHeight, NetworkImage(src, scale: scale, headers: headers)),
}) : image = NetworkImage(src, scale: scale, headers: headers),
assert(alignment != null),
assert(repeat != null),
assert(matchTextDirection != null),
assert(cacheWidth == null || cacheWidth > 0),
assert(cacheHeight == null || cacheHeight > 0),
super(key: key);
/// Creates a widget that displays an [ImageStream] obtained from a [File].
......@@ -381,12 +349,6 @@ class Image extends StatefulWidget {
/// [FilterQuality.none] which corresponds to nearest-neighbor.
///
/// If [excludeFromSemantics] is true, then [semanticLabel] will be ignored.
///
/// If [cacheWidth] or [cacheHeight] are provided, it indicates to the
/// engine that the image must be decoded at the specified size. The image
/// will be rendered to the constraints of the layout or [width] and [height]
/// regardless of these parameters. These parameters are primarily intended
/// to reduce the memory usage of [ImageCache].
Image.file(
File file, {
Key key,
......@@ -405,16 +367,12 @@ class Image extends StatefulWidget {
this.matchTextDirection = false,
this.gaplessPlayback = false,
this.filterQuality = FilterQuality.low,
int cacheWidth,
int cacheHeight,
}) : image = _resizeIfNeeded(cacheWidth, cacheHeight, FileImage(file, scale: scale)),
}) : image = FileImage(file, scale: scale),
loadingBuilder = null,
assert(alignment != null),
assert(repeat != null),
assert(filterQuality != null),
assert(matchTextDirection != null),
assert(cacheWidth == null || cacheWidth > 0),
assert(cacheHeight == null || cacheHeight > 0),
super(key: key);
......@@ -446,12 +404,6 @@ class Image extends StatefulWidget {
///
/// If [excludeFromSemantics] is true, then [semanticLabel] will be ignored.
///
/// If [cacheWidth] or [cacheHeight] are provided, it indicates to the
/// engine that the image must be decoded at the specified size. The image
/// will be rendered to the constraints of the layout or [width] and [height]
/// regardless of these parameters. These parameters are primarily intended
/// to reduce the memory usage of [ImageCache].
///
/// The [name] and [repeat] arguments must not be null.
///
/// Either the [width] and [height] arguments should be specified, or the
......@@ -568,18 +520,13 @@ class Image extends StatefulWidget {
this.gaplessPlayback = false,
String package,
this.filterQuality = FilterQuality.low,
int cacheWidth,
int cacheHeight,
}) : image = _resizeIfNeeded(cacheWidth, cacheHeight, scale != null
}) : image = scale != null
? ExactAssetImage(name, bundle: bundle, scale: scale, package: package)
: AssetImage(name, bundle: bundle, package: package)
),
: AssetImage(name, bundle: bundle, package: package),
loadingBuilder = null,
assert(alignment != null),
assert(repeat != null),
assert(matchTextDirection != null),
assert(cacheWidth == null || cacheWidth > 0),
assert(cacheHeight == null || cacheHeight > 0),
super(key: key);
/// Creates a widget that displays an [ImageStream] obtained from a [Uint8List].
......@@ -601,12 +548,6 @@ class Image extends StatefulWidget {
/// [FilterQuality.none] which corresponds to nearest-neighbor.
///
/// If [excludeFromSemantics] is true, then [semanticLabel] will be ignored.
///
/// If [cacheWidth] or [cacheHeight] are provided, it indicates to the
/// engine that the image must be decoded at the specified size. The image
/// will be rendered to the constraints of the layout or [width] and [height]
/// regardless of these parameters. These parameters are primarily intended
/// to reduce the memory usage of [ImageCache].
Image.memory(
Uint8List bytes, {
Key key,
......@@ -625,15 +566,11 @@ class Image extends StatefulWidget {
this.matchTextDirection = false,
this.gaplessPlayback = false,
this.filterQuality = FilterQuality.low,
int cacheWidth,
int cacheHeight,
}) : image = _resizeIfNeeded(cacheWidth, cacheHeight, MemoryImage(bytes, scale: scale)),
}) : image = MemoryImage(bytes, scale: scale),
loadingBuilder = null,
assert(alignment != null),
assert(repeat != null),
assert(matchTextDirection != null),
assert(cacheWidth == null || cacheWidth > 0),
assert(cacheHeight == null || cacheHeight > 0),
super(key: key);
/// The image to display.
......
......@@ -19,9 +19,7 @@ void main() {
final Uint8List bytes = Uint8List.fromList(kTransparentImage);
final MemoryImage memoryImage = MemoryImage(bytes);
memoryImage.load(memoryImage, (Uint8List bytes, {int cacheWidth, int cacheHeight}) {
return PaintingBinding.instance.instantiateImageCodec(bytes, cacheWidth: cacheWidth, cacheHeight: cacheHeight);
});
memoryImage.load(memoryImage);
expect(binding.instantiateImageCodecCalledCount, 1);
});
}
......@@ -33,7 +33,7 @@ class SynchronousTestImageProvider extends ImageProvider<int> {
}
@override
ImageStreamCompleter load(int key, DecoderCallback decode) {
ImageStreamCompleter load(int key) {
return OneFrameImageStreamCompleter(
SynchronousFuture<ImageInfo>(TestImageInfo(key, image: TestImage(), scale: 1.0))
);
......@@ -47,7 +47,7 @@ class AsyncTestImageProvider extends ImageProvider<int> {
}
@override
ImageStreamCompleter load(int key, DecoderCallback decode) {
ImageStreamCompleter load(int key) {
return OneFrameImageStreamCompleter(
Future<ImageInfo>.value(TestImageInfo(key))
);
......@@ -63,7 +63,7 @@ class DelayedImageProvider extends ImageProvider<DelayedImageProvider> {
}
@override
ImageStreamCompleter load(DelayedImageProvider key, DecoderCallback decode) {
ImageStreamCompleter load(DelayedImageProvider key) {
return OneFrameImageStreamCompleter(_completer.future);
}
......
......@@ -26,7 +26,7 @@ class FakeImageProvider extends ImageProvider<FakeImageProvider> {
}
@override
ImageStreamCompleter load(FakeImageProvider key, DecoderCallback decode) {
ImageStreamCompleter load(FakeImageProvider key) {
assert(key == this);
return MultiFrameImageStreamCompleter(
codec: SynchronousFuture<ui.Codec>(_codec),
......
......@@ -133,9 +133,9 @@ void main() {
test('Returns null if an error is caught resolving an image', () {
final ErrorImageProvider errorImage = ErrorImageProvider();
expect(() => imageCache.putIfAbsent(errorImage, () => errorImage.load(errorImage, null)), throwsA(isInstanceOf<Error>()));
expect(() => imageCache.putIfAbsent(errorImage, () => errorImage.load(errorImage)), throwsA(isInstanceOf<Error>()));
bool caughtError = false;
final ImageStreamCompleter result = imageCache.putIfAbsent(errorImage, () => errorImage.load(errorImage, null), onError: (dynamic error, StackTrace stackTrace) {
final ImageStreamCompleter result = imageCache.putIfAbsent(errorImage, () => errorImage.load(errorImage), onError: (dynamic error, StackTrace stackTrace) {
caughtError = true;
});
expect(result, null);
......
......@@ -18,11 +18,6 @@ import 'image_data.dart';
import 'mocks_for_image_cache.dart';
void main() {
final DecoderCallback basicDecoder = (Uint8List bytes, {int cacheWidth, int cacheHeight}) {
return PaintingBinding.instance.instantiateImageCodec(bytes, cacheWidth: cacheWidth, cacheHeight: cacheHeight);
};
group(ImageProvider, () {
setUpAll(() {
TestRenderingFlutterBinding(); // initializes the imageCache
......@@ -51,7 +46,7 @@ void main() {
final Uint8List bytes = Uint8List.fromList(kTransparentImage);
final MemoryImage imageProvider = MemoryImage(bytes);
final ImageStreamCompleter cacheStream = otherCache.putIfAbsent(
imageProvider, () => imageProvider.load(imageProvider, basicDecoder),
imageProvider, () => imageProvider.load(imageProvider),
);
final ImageStream stream = imageProvider.resolve(ImageConfiguration.empty);
final Completer<void> completer = Completer<void>();
......@@ -200,7 +195,7 @@ void main() {
Future<void> loadNetworkImage() async {
final NetworkImage networkImage = NetworkImage(nonconst('foo'));
final ImageStreamCompleter completer = networkImage.load(networkImage, basicDecoder);
final ImageStreamCompleter completer = networkImage.load(networkImage);
completer.addListener(ImageStreamListener(
(ImageInfo image, bool synchronousCall) { },
onError: (dynamic error, StackTrace stackTrace) {
......@@ -298,83 +293,6 @@ void main() {
}, skip: isBrowser);
});
});
test('ResizeImage resizes to the correct dimensions', () async {
final Uint8List bytes = Uint8List.fromList(kTransparentImage);
final MemoryImage imageProvider = MemoryImage(bytes);
final Size rawImageSize = await _resolveAndGetSize(imageProvider);
expect(rawImageSize, const Size(1, 1));
const Size resizeDims = Size(14, 7);
final ResizeImage resizedImage = ResizeImage(MemoryImage(bytes), width: resizeDims.width.round(), height: resizeDims.height.round());
const ImageConfiguration resizeConfig = ImageConfiguration(size: resizeDims);
final Size resizedImageSize = await _resolveAndGetSize(resizedImage, configuration: resizeConfig);
expect(resizedImageSize, resizeDims);
});
test('ResizeImage does not resize when no size is passed', () async {
final Uint8List bytes = Uint8List.fromList(kTransparentImage);
final MemoryImage imageProvider = MemoryImage(bytes);
final Size rawImageSize = await _resolveAndGetSize(imageProvider);
expect(rawImageSize, const Size(1, 1));
// Cannot pass in two null arguments for cache dimensions, so will use the regular
// MemoryImage
final MemoryImage resizedImage = MemoryImage(bytes);
final Size resizedImageSize = await _resolveAndGetSize(resizedImage);
expect(resizedImageSize, const Size(1, 1));
});
test('ResizeImage stores values', () async {
final Uint8List bytes = Uint8List.fromList(kTransparentImage);
final MemoryImage memoryImage = MemoryImage(bytes);
final ResizeImage resizeImage = ResizeImage(memoryImage, width: 10, height: 20);
expect(resizeImage.width, 10);
expect(resizeImage.height, 20);
expect(resizeImage.imageProvider, memoryImage);
expect(memoryImage.resolve(ImageConfiguration.empty) != resizeImage.resolve(ImageConfiguration.empty), true);
});
test('ResizeImage takes one dim', () async {
final Uint8List bytes = Uint8List.fromList(kTransparentImage);
final MemoryImage memoryImage = MemoryImage(bytes);
final ResizeImage resizeImage = ResizeImage(memoryImage, width: 10, height: null);
expect(resizeImage.width, 10);
expect(resizeImage.height, null);
expect(resizeImage.imageProvider, memoryImage);
expect(memoryImage.resolve(ImageConfiguration.empty) != resizeImage.resolve(ImageConfiguration.empty), true);
});
test('ResizeImage forms closure', () async {
final Uint8List bytes = Uint8List.fromList(kTransparentImage);
final MemoryImage memoryImage = MemoryImage(bytes);
final ResizeImage resizeImage = ResizeImage(memoryImage, width: 123, height: 321);
final DecoderCallback decode = (Uint8List bytes, {int cacheWidth, int cacheHeight}) {
expect(cacheWidth, 123);
expect(cacheHeight, 321);
return PaintingBinding.instance.instantiateImageCodec(bytes, cacheWidth: cacheWidth, cacheHeight: cacheHeight);
};
resizeImage.load(await resizeImage.obtainKey(ImageConfiguration.empty), decode);
});
}
Future<Size> _resolveAndGetSize(ImageProvider imageProvider,
{ImageConfiguration configuration = ImageConfiguration.empty}) async {
final ImageStream stream = imageProvider.resolve(configuration);
final Completer<Size> completer = Completer<Size>();
final ImageStreamListener listener =
ImageStreamListener((ImageInfo image, bool synchronousCall) {
final int height = image.image.height;
final int width = image.image.width;
completer.complete(Size(width.toDouble(), height.toDouble()));
}
);
stream.addListener(listener);
return await completer.future;
}
class MockHttpClient extends Mock implements HttpClient {}
......
......@@ -31,7 +31,7 @@ class TestImageProvider extends ImageProvider<TestImageProvider> {
}
@override
ImageStreamCompleter load(TestImageProvider key, DecoderCallback decode) =>
ImageStreamCompleter load(TestImageProvider key) =>
OneFrameImageStreamCompleter(_completer.future);
ImageInfo complete() {
......
......@@ -37,7 +37,7 @@ class TestImageProvider extends ImageProvider<int> {
}
@override
ImageStreamCompleter load(int key, DecoderCallback decode) {
ImageStreamCompleter load(int key) {
return OneFrameImageStreamCompleter(
SynchronousFuture<ImageInfo>(TestImageInfo(imageValue, image: image))
);
......@@ -51,7 +51,7 @@ class FailingTestImageProvider extends TestImageProvider {
const FailingTestImageProvider(int key, int imageValue, { ui.Image image }) : super(key, imageValue, image: image);
@override
ImageStreamCompleter load(int key, DecoderCallback decode) {
ImageStreamCompleter load(int key) {
return OneFrameImageStreamCompleter(Future<ImageInfo>.sync(() => Future<ImageInfo>.error('loading failed!')));
}
}
......@@ -85,7 +85,7 @@ class TestImage implements ui.Image {
class ErrorImageProvider extends ImageProvider<ErrorImageProvider> {
@override
ImageStreamCompleter load(ErrorImageProvider key, DecoderCallback decode) {
ImageStreamCompleter load(ErrorImageProvider key) {
throw Error();
}
......@@ -97,7 +97,7 @@ class ErrorImageProvider extends ImageProvider<ErrorImageProvider> {
class ObtainKeyErrorImageProvider extends ImageProvider<ObtainKeyErrorImageProvider> {
@override
ImageStreamCompleter load(ObtainKeyErrorImageProvider key, DecoderCallback decode) {
ImageStreamCompleter load(ObtainKeyErrorImageProvider key) {
throw Error();
}
......@@ -109,7 +109,7 @@ class ObtainKeyErrorImageProvider extends ImageProvider<ObtainKeyErrorImageProvi
class LoadErrorImageProvider extends ImageProvider<LoadErrorImageProvider> {
@override
ImageStreamCompleter load(LoadErrorImageProvider key, DecoderCallback decode) {
ImageStreamCompleter load(LoadErrorImageProvider key) {
throw Error();
}
......@@ -121,7 +121,7 @@ class LoadErrorImageProvider extends ImageProvider<LoadErrorImageProvider> {
class LoadErrorCompleterImageProvider extends ImageProvider<LoadErrorCompleterImageProvider> {
@override
ImageStreamCompleter load(LoadErrorCompleterImageProvider key, DecoderCallback decode) {
ImageStreamCompleter load(LoadErrorCompleterImageProvider key) {
final Completer<void> completer = Completer<void>.sync();
completer.completeError(Error());
return OneFrameImageStreamCompleter(completer.future);
......
......@@ -14,7 +14,7 @@ class PaintingBindingSpy extends BindingBase with ServicesBinding, PaintingBindi
int get instantiateImageCodecCalledCount => counter;
@override
Future<ui.Codec> instantiateImageCodec(Uint8List list, {int cacheWidth, int cacheHeight}) {
Future<ui.Codec> instantiateImageCodec(Uint8List list) {
counter++;
return ui.instantiateImageCodec(list);
}
......
......@@ -107,7 +107,7 @@ class TestImageProvider extends ImageProvider<TestImageProvider> {
}
@override
ImageStreamCompleter load(TestImageProvider key, DecoderCallback decode) {
ImageStreamCompleter load(TestImageProvider key) {
return OneFrameImageStreamCompleter(
SynchronousFuture<ImageInfo>(ImageInfo(image: TestImage(), scale: 1.0)),
);
......
......@@ -28,7 +28,7 @@ class TestImageProvider extends ImageProvider<TestImageProvider> {
}
@override
ImageStreamCompleter load(TestImageProvider key, DecoderCallback decode) {
ImageStreamCompleter load(TestImageProvider key) {
return OneFrameImageStreamCompleter(
future.then<ImageInfo>((void value) => ImageInfo(image: image))
);
......
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
const List<int> kTransparentImage = <int>[
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49,
0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x06,
0x00, 0x00, 0x00, 0x1F, 0x15, 0xC4, 0x89, 0x00, 0x00, 0x00, 0x0A, 0x49, 0x44,
0x41, 0x54, 0x78, 0x9C, 0x63, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x01, 0x0D,
0x0A, 0x2D, 0xB4, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE,
];
/// An animated GIF image with 3 1x1 pixel frames (a red, green, and blue
/// frames). The gif animates forever, and each frame has a 100ms delay.
const List<int> kAnimatedGif = <int> [
0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00, 0xa1, 0x03, 0x00,
0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x21,
0xff, 0x0b, 0x4e, 0x45, 0x54, 0x53, 0x43, 0x41, 0x50, 0x45, 0x32, 0x2e, 0x30,
0x03, 0x01, 0x00, 0x00, 0x00, 0x21, 0xf9, 0x04, 0x00, 0x0a, 0x00, 0xff, 0x00,
0x2c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x4c,
0x01, 0x00, 0x21, 0xf9, 0x04, 0x00, 0x0a, 0x00, 0xff, 0x00, 0x2c, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x54, 0x01, 0x00, 0x21,
0xf9, 0x04, 0x00, 0x0a, 0x00, 0xff, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x44, 0x01, 0x00, 0x3b,
];
......@@ -13,8 +13,6 @@ import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'image_data.dart';
class TestImage implements ui.Image {
TestImage(this.scale);
final double scale;
......@@ -106,7 +104,7 @@ class TestAssetImage extends AssetImage {
TestAssetImage(String name) : super(name);
@override
ImageStreamCompleter load(AssetBundleImageKey key, DecoderCallback decode) {
ImageStreamCompleter load(AssetBundleImageKey key) {
ImageInfo imageInfo;
key.bundle.load(key.name).then<void>((ByteData data) {
final TestByteData testData = data;
......@@ -152,30 +150,6 @@ Widget buildImageAtRatio(String image, Key key, double ratio, bool inferSize, [
);
}
Widget buildImageCacheResized(String name, Key key, int width, int height, int cacheWidth, int cacheHeight) {
return Center(
child: RepaintBoundary(
child: Container(
width: 250,
height: 250,
child: Center(
child: Image.memory(
Uint8List.fromList(kTransparentImage),
key: key,
excludeFromSemantics: true,
color: const Color(0xFF00FFFF),
colorBlendMode: BlendMode.plus,
width: width.toDouble(),
height: height.toDouble(),
cacheWidth: cacheWidth,
cacheHeight: cacheHeight,
),
),
),
),
);
}
RenderImage getRenderImage(WidgetTester tester, Key key) {
return tester.renderObject<RenderImage>(find.byKey(key));
}
......@@ -329,22 +303,4 @@ void main() {
expect(getTestImage(tester, key).scale, 10.0);
});
testWidgets('Image cache resize upscale display 5', (WidgetTester tester) async {
final Key key = GlobalKey();
await pumpTreeToLayout(tester, buildImageCacheResized(image, key, 5, 5, 20, 20));
expect(getRenderImage(tester, key).size, const Size(5.0, 5.0));
});
testWidgets('Image cache resize upscale display 50', (WidgetTester tester) async {
final Key key = GlobalKey();
await pumpTreeToLayout(tester, buildImageCacheResized(image, key, 50, 50, 20, 20));
expect(getRenderImage(tester, key).size, const Size(50.0, 50.0));
});
testWidgets('Image cache resize downscale display 5', (WidgetTester tester) async {
final Key key = GlobalKey();
await pumpTreeToLayout(tester, buildImageCacheResized(image, key, 5, 5, 1, 1));
expect(getRenderImage(tester, key).size, const Size(5.0, 5.0));
});
}
......@@ -19,7 +19,7 @@ class TestImageProvider extends ImageProvider<TestImageProvider> {
}
@override
ImageStreamCompleter load(TestImageProvider key, DecoderCallback decode) {
ImageStreamCompleter load(TestImageProvider key) {
return OneFrameImageStreamCompleter(
SynchronousFuture<ImageInfo>(ImageInfo(image: TestImage()))
);
......
......@@ -1199,7 +1199,7 @@ class TestImageProvider extends ImageProvider<TestImageProvider> {
}
@override
ImageStreamCompleter load(TestImageProvider key, DecoderCallback decode) => _streamCompleter;
ImageStreamCompleter load(TestImageProvider key) => _streamCompleter;
void complete() {
_completer.complete(ImageInfo(image: TestImage()));
......
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