use immutable buffer for loading asset images (#103496)
This commit is contained in:
parent
5cb9c2197f
commit
8d2978af64
@ -4,6 +4,7 @@
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:ui' as ui show Codec;
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@ -57,11 +58,11 @@ class DelayedBase64Image extends ImageProvider<int> {
|
||||
}
|
||||
|
||||
@override
|
||||
ImageStreamCompleter load(int key, DecoderCallback decode) {
|
||||
ImageStreamCompleter loadBuffer(int key, DecoderBufferCallback decode) {
|
||||
return MultiFrameImageStreamCompleter(
|
||||
codec: Future<ui.Codec>.delayed(
|
||||
delay,
|
||||
() => decode(base64.decode(data)),
|
||||
() async => decode(await ImmutableBuffer.fromUint8List(base64.decode(data))),
|
||||
),
|
||||
scale: 1.0,
|
||||
);
|
||||
|
@ -87,9 +87,7 @@ Future<void> main() async {
|
||||
for (int i = 0; i < 10; i += 1) {
|
||||
await Future.wait(<Future<ui.ImmutableBuffer>>[
|
||||
for (String asset in assets)
|
||||
rootBundle.load(asset).then((ByteData data) {
|
||||
return ui.ImmutableBuffer.fromUint8List(data.buffer.asUint8List());
|
||||
})
|
||||
rootBundle.loadBuffer(asset)
|
||||
]);
|
||||
}
|
||||
watch.stop();
|
||||
|
@ -46,7 +46,26 @@ class NetworkImage extends image_provider.ImageProvider<image_provider.NetworkIm
|
||||
final StreamController<ImageChunkEvent> chunkEvents = StreamController<ImageChunkEvent>();
|
||||
|
||||
return MultiFrameImageStreamCompleter(
|
||||
codec: _loadAsync(key as NetworkImage, chunkEvents, decode),
|
||||
codec: _loadAsync(key as NetworkImage, chunkEvents, null, decode),
|
||||
chunkEvents: chunkEvents.stream,
|
||||
scale: key.scale,
|
||||
debugLabel: key.url,
|
||||
informationCollector: () => <DiagnosticsNode>[
|
||||
DiagnosticsProperty<image_provider.ImageProvider>('Image provider', this),
|
||||
DiagnosticsProperty<image_provider.NetworkImage>('Image key', key),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
ImageStreamCompleter loadBuffer(image_provider.NetworkImage key, image_provider.DecoderBufferCallback decode) {
|
||||
// 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 as NetworkImage, chunkEvents, decode, null),
|
||||
chunkEvents: chunkEvents.stream,
|
||||
scale: key.scale,
|
||||
debugLabel: key.url,
|
||||
@ -77,7 +96,8 @@ class NetworkImage extends image_provider.ImageProvider<image_provider.NetworkIm
|
||||
Future<ui.Codec> _loadAsync(
|
||||
NetworkImage key,
|
||||
StreamController<ImageChunkEvent> chunkEvents,
|
||||
image_provider.DecoderCallback decode,
|
||||
image_provider.DecoderBufferCallback? decode,
|
||||
image_provider.DecoderCallback? decodeDepreacted,
|
||||
) async {
|
||||
try {
|
||||
assert(key == this);
|
||||
@ -111,7 +131,13 @@ class NetworkImage extends image_provider.ImageProvider<image_provider.NetworkIm
|
||||
throw Exception('NetworkImage is an empty file: $resolved');
|
||||
}
|
||||
|
||||
return decode(bytes);
|
||||
if (decode != null) {
|
||||
final ui.ImmutableBuffer buffer = await ui.ImmutableBuffer.fromUint8List(bytes);
|
||||
return decode(buffer);
|
||||
} else {
|
||||
assert(decodeDepreacted != null);
|
||||
return decodeDepreacted!(bytes);
|
||||
}
|
||||
} catch (e) {
|
||||
// Depending on where the exception was thrown, the image cache may not
|
||||
// have had a chance to track the key in the cache at all.
|
||||
|
@ -66,7 +66,24 @@ class NetworkImage
|
||||
|
||||
return MultiFrameImageStreamCompleter(
|
||||
chunkEvents: chunkEvents.stream,
|
||||
codec: _loadAsync(key as NetworkImage, decode, chunkEvents),
|
||||
codec: _loadAsync(key as NetworkImage, null, decode, chunkEvents),
|
||||
scale: key.scale,
|
||||
debugLabel: key.url,
|
||||
informationCollector: _imageStreamInformationCollector(key),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
ImageStreamCompleter loadBuffer(image_provider.NetworkImage key, image_provider.DecoderBufferCallback decode) {
|
||||
// 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(
|
||||
chunkEvents: chunkEvents.stream,
|
||||
codec: _loadAsync(key as NetworkImage, decode, null, chunkEvents),
|
||||
scale: key.scale,
|
||||
debugLabel: key.url,
|
||||
informationCollector: _imageStreamInformationCollector(key),
|
||||
@ -93,7 +110,8 @@ class NetworkImage
|
||||
// directly in place of the typical `instantiateImageCodec` method.
|
||||
Future<ui.Codec> _loadAsync(
|
||||
NetworkImage key,
|
||||
image_provider.DecoderCallback decode,
|
||||
image_provider.DecoderBufferCallback? decode,
|
||||
image_provider.DecoderCallback? decodeDepreacted,
|
||||
StreamController<ImageChunkEvent> chunkEvents,
|
||||
) async {
|
||||
assert(key == this);
|
||||
@ -144,7 +162,13 @@ class NetworkImage
|
||||
statusCode: request.status!, uri: resolved);
|
||||
}
|
||||
|
||||
return decode(bytes);
|
||||
if (decode != null) {
|
||||
final ui.ImmutableBuffer buffer = await ui.ImmutableBuffer.fromUint8List(bytes);
|
||||
return decode(buffer);
|
||||
} else {
|
||||
assert(decodeDepreacted != null);
|
||||
return decodeDepreacted!(bytes);
|
||||
}
|
||||
} else {
|
||||
// This API only exists in the web engine implementation and is not
|
||||
// contained in the analyzer summary for Flutter.
|
||||
|
@ -3,7 +3,7 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:typed_data' show Uint8List;
|
||||
import 'dart:ui' as ui show instantiateImageCodec, Codec;
|
||||
import 'dart:ui' as ui show instantiateImageCodec, instantiateImageCodecFromBuffer, Codec, ImmutableBuffer;
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart' show ServicesBinding;
|
||||
|
||||
@ -81,6 +81,9 @@ mixin PaintingBinding on BindingBase, ServicesBinding {
|
||||
|
||||
/// Calls through to [dart:ui.instantiateImageCodec] from [ImageCache].
|
||||
///
|
||||
/// This method is deprecated. use [instantiateImageCodecFromBuffer] with an
|
||||
/// [ImmutableBuffer] instance instead of this method.
|
||||
///
|
||||
/// The `cacheWidth` and `cacheHeight` parameters, when specified, indicate
|
||||
/// the size to decode the image to.
|
||||
///
|
||||
@ -97,6 +100,10 @@ mixin PaintingBinding on BindingBase, ServicesBinding {
|
||||
/// unnecessary memory usage for images. Callers that wish to display an image
|
||||
/// above its native resolution should prefer scaling the canvas the image is
|
||||
/// drawn into.
|
||||
@Deprecated(
|
||||
'Use instantiateImageCodecFromBuffer with an ImmutableBuffer instance instead. '
|
||||
'This feature was deprecated after v2.13.0-1.0.pre.',
|
||||
)
|
||||
Future<ui.Codec> instantiateImageCodec(
|
||||
Uint8List bytes, {
|
||||
int? cacheWidth,
|
||||
@ -114,6 +121,44 @@ mixin PaintingBinding on BindingBase, ServicesBinding {
|
||||
);
|
||||
}
|
||||
|
||||
/// Calls through to [dart:ui.instantiateImageCodecFromBuffer] from [ImageCache].
|
||||
///
|
||||
/// The [buffer] parameter should be an [ui.ImmutableBuffer] instance which can
|
||||
/// be acquired from [ui.ImmutableBuffer.fromUint8List] or [ui.ImmutableBuffer.fromAsset].
|
||||
///
|
||||
/// 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 be scaled to maintain the aspect ratio of the original
|
||||
/// dimensions. When both are null or omitted, the image will be decoded at
|
||||
/// its native resolution.
|
||||
///
|
||||
/// The [allowUpscaling] parameter determines whether the `cacheWidth` or
|
||||
/// [cacheHeight] parameters are clamped to the intrinsic width and height of
|
||||
/// the original image. By default, the dimensions are clamped to avoid
|
||||
/// unnecessary memory usage for images. Callers that wish to display an image
|
||||
/// above its native resolution should prefer scaling the canvas the image is
|
||||
/// drawn into.
|
||||
Future<ui.Codec> instantiateImageCodecFromBuffer(
|
||||
ui.ImmutableBuffer buffer, {
|
||||
int? cacheWidth,
|
||||
int? cacheHeight,
|
||||
bool allowUpscaling = false,
|
||||
}) {
|
||||
assert(cacheWidth == null || cacheWidth > 0);
|
||||
assert(cacheHeight == null || cacheHeight > 0);
|
||||
assert(allowUpscaling != null);
|
||||
return ui.instantiateImageCodecFromBuffer(
|
||||
buffer,
|
||||
targetWidth: cacheWidth,
|
||||
targetHeight: cacheHeight,
|
||||
allowUpscaling: allowUpscaling,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void evict(String asset) {
|
||||
super.evict(asset);
|
||||
|
@ -5,7 +5,7 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ui' as ui show Codec;
|
||||
import 'dart:ui' as ui show Codec, ImmutableBuffer;
|
||||
import 'dart:ui' show Size, Locale, TextDirection;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
@ -164,6 +164,9 @@ class ImageConfiguration {
|
||||
|
||||
/// Performs the decode process for use in [ImageProvider.load].
|
||||
///
|
||||
/// This typedef is deprecated. Use [DecoderBufferCallback] with
|
||||
/// [ImageProvider.loadBuffer] instead.
|
||||
///
|
||||
/// This callback allows decoupling of the `cacheWidth`, `cacheHeight`, and
|
||||
/// `allowUpscaling` parameters from implementations of [ImageProvider] that do
|
||||
/// not expose them.
|
||||
@ -172,7 +175,24 @@ class ImageConfiguration {
|
||||
///
|
||||
/// * [ResizeImage], which uses this to override the `cacheWidth`,
|
||||
/// `cacheHeight`, and `allowUpscaling` parameters.
|
||||
typedef DecoderCallback = Future<ui.Codec> Function(Uint8List bytes, {int? cacheWidth, int? cacheHeight, bool allowUpscaling});
|
||||
@Deprecated(
|
||||
'Use DecoderBufferCallback with ImageProvider.loadBuffer instead. '
|
||||
'This feature was deprecated after v2.13.0-1.0.pre.',
|
||||
)
|
||||
typedef DecoderCallback = Future<ui.Codec> Function(Uint8List buffer, {int? cacheWidth, int? cacheHeight, bool allowUpscaling});
|
||||
|
||||
|
||||
/// Performs the decode process for use in [ImageProvider.loadBuffer].
|
||||
///
|
||||
/// This callback allows decoupling of the `cacheWidth`, `cacheHeight`, and
|
||||
/// `allowUpscaling` parameters from implementations of [ImageProvider] that do
|
||||
/// not expose them.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [ResizeImage], which uses this to override the `cacheWidth`,
|
||||
/// `cacheHeight`, and `allowUpscaling` parameters.
|
||||
typedef DecoderBufferCallback = Future<ui.Codec> Function(ui.ImmutableBuffer buffer, {int? cacheWidth, int? cacheHeight, bool allowUpscaling});
|
||||
|
||||
/// 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
|
||||
@ -213,16 +233,16 @@ typedef DecoderCallback = Future<ui.Codec> Function(Uint8List bytes, {int? cache
|
||||
/// using that key. This is handled by [resolveStreamForKey]. That method
|
||||
/// may fizzle if it determines the image is no longer necessary, use the
|
||||
/// provided [ImageErrorListener] to report an error, set the completer
|
||||
/// from the cache if possible, or call [load] to fetch the encoded image
|
||||
/// from the cache if possible, or call [loadBuffer] to fetch the encoded image
|
||||
/// bytes and schedule decoding.
|
||||
/// 4. The [load] method is responsible for both fetching the encoded bytes
|
||||
/// 4. The [loadBuffer] method is responsible for both fetching the encoded bytes
|
||||
/// and decoding them using the provided [DecoderCallback]. It is called
|
||||
/// in a context that uses the [ImageErrorListener] to report errors back.
|
||||
///
|
||||
/// Subclasses normally only have to implement the [load] and [obtainKey]
|
||||
/// Subclasses normally only have to implement the [loadBuffer] and [obtainKey]
|
||||
/// methods. A subclass that needs finer grained control over the [ImageStream]
|
||||
/// type must override [createStream]. A subclass that needs finer grained
|
||||
/// control over the resolution, such as delaying calling [load], must override
|
||||
/// control over the resolution, such as delaying calling [loadBuffer], must override
|
||||
/// [resolveStreamForKey].
|
||||
///
|
||||
/// The [resolve] method is marked as [nonVirtual] so that [ImageProvider]s can
|
||||
@ -491,7 +511,7 @@ abstract class ImageProvider<T extends Object> {
|
||||
}
|
||||
final ImageStreamCompleter? completer = PaintingBinding.instance.imageCache.putIfAbsent(
|
||||
key,
|
||||
() => load(key, PaintingBinding.instance.instantiateImageCodec),
|
||||
() => loadBuffer(key, PaintingBinding.instance.instantiateImageCodecFromBuffer),
|
||||
onError: handleError,
|
||||
);
|
||||
if (completer != null) {
|
||||
@ -563,6 +583,10 @@ abstract class ImageProvider<T extends Object> {
|
||||
/// Converts a key into an [ImageStreamCompleter], and begins fetching the
|
||||
/// image.
|
||||
///
|
||||
/// This method is deprecated. Implement [loadBuffer] for faster image
|
||||
/// loading. Only one of [load] and [loadBuffer] must be implemented, and
|
||||
/// [loadBuffer] is preferred.
|
||||
///
|
||||
/// The [decode] callback provides the logic to obtain the codec for the
|
||||
/// image.
|
||||
///
|
||||
@ -570,7 +594,31 @@ abstract class ImageProvider<T extends Object> {
|
||||
///
|
||||
/// * [ResizeImage], for modifying the key to account for cache dimensions.
|
||||
@protected
|
||||
ImageStreamCompleter load(T key, DecoderCallback decode);
|
||||
@Deprecated(
|
||||
'Implement loadBuffer for faster image loading. '
|
||||
'This feature was deprecated after v2.13.0-1.0.pre.',
|
||||
)
|
||||
ImageStreamCompleter load(T key, DecoderCallback decode) {
|
||||
throw UnsupportedError('Implement loadBuffer for faster image loading');
|
||||
}
|
||||
|
||||
/// Converts a key into an [ImageStreamCompleter], and begins fetching the
|
||||
/// image.
|
||||
///
|
||||
/// For backwards-compatibility the default implementation of this method calls
|
||||
/// through to [ImageProvider.load]. However, implementors of this interface should
|
||||
/// only override this method and not [ImageProvider.load], which is deprecated.
|
||||
///
|
||||
/// 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 loadBuffer(T key, DecoderBufferCallback decode) {
|
||||
return load(key, PaintingBinding.instance.instantiateImageCodec);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => '${objectRuntimeType(this, 'ImageConfiguration')}()';
|
||||
@ -634,6 +682,24 @@ abstract class AssetBundleImageProvider extends ImageProvider<AssetBundleImageKe
|
||||
|
||||
/// Converts a key into an [ImageStreamCompleter], and begins fetching the
|
||||
/// image.
|
||||
@override
|
||||
ImageStreamCompleter loadBuffer(AssetBundleImageKey key, DecoderBufferCallback decode) {
|
||||
InformationCollector? collector;
|
||||
assert(() {
|
||||
collector = () => <DiagnosticsNode>[
|
||||
DiagnosticsProperty<ImageProvider>('Image provider', this),
|
||||
DiagnosticsProperty<AssetBundleImageKey>('Image key', key),
|
||||
];
|
||||
return true;
|
||||
}());
|
||||
return MultiFrameImageStreamCompleter(
|
||||
codec: _loadAsync(key, decode, null),
|
||||
scale: key.scale,
|
||||
debugLabel: key.name,
|
||||
informationCollector: collector,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
ImageStreamCompleter load(AssetBundleImageKey key, DecoderCallback decode) {
|
||||
InformationCollector? collector;
|
||||
@ -645,7 +711,7 @@ abstract class AssetBundleImageProvider extends ImageProvider<AssetBundleImageKe
|
||||
return true;
|
||||
}());
|
||||
return MultiFrameImageStreamCompleter(
|
||||
codec: _loadAsync(key, decode),
|
||||
codec: _loadAsync(key, null, decode),
|
||||
scale: key.scale,
|
||||
debugLabel: key.name,
|
||||
informationCollector: collector,
|
||||
@ -657,8 +723,24 @@ abstract class AssetBundleImageProvider extends ImageProvider<AssetBundleImageKe
|
||||
///
|
||||
/// This function is used by [load].
|
||||
@protected
|
||||
Future<ui.Codec> _loadAsync(AssetBundleImageKey key, DecoderCallback decode) async {
|
||||
ByteData? data;
|
||||
Future<ui.Codec> _loadAsync(AssetBundleImageKey key, DecoderBufferCallback? decode, DecoderCallback? decodeDepreacted) async {
|
||||
if (decode != null) {
|
||||
ui.ImmutableBuffer? buffer;
|
||||
// Hot reload/restart could change whether an asset bundle or key in a
|
||||
// bundle are available, or if it is a network backed bundle.
|
||||
try {
|
||||
buffer = await key.bundle.loadBuffer(key.name);
|
||||
} on FlutterError {
|
||||
PaintingBinding.instance.imageCache.evict(key);
|
||||
rethrow;
|
||||
}
|
||||
if (buffer == null) {
|
||||
PaintingBinding.instance.imageCache.evict(key);
|
||||
throw StateError('Unable to read data');
|
||||
}
|
||||
return decode(buffer);
|
||||
}
|
||||
ByteData data;
|
||||
// Hot reload/restart could change whether an asset bundle or key in a
|
||||
// bundle are available, or if it is a network backed bundle.
|
||||
try {
|
||||
@ -671,7 +753,7 @@ abstract class AssetBundleImageProvider extends ImageProvider<AssetBundleImageKe
|
||||
PaintingBinding.instance.imageCache.evict(key);
|
||||
throw StateError('Unable to read data');
|
||||
}
|
||||
return decode(data.buffer.asUint8List());
|
||||
return decodeDepreacted!(data.buffer.asUint8List());
|
||||
}
|
||||
}
|
||||
|
||||
@ -757,13 +839,13 @@ class ResizeImage extends ImageProvider<ResizeImageKey> {
|
||||
|
||||
@override
|
||||
ImageStreamCompleter load(ResizeImageKey key, DecoderCallback decode) {
|
||||
Future<ui.Codec> decodeResize(Uint8List bytes, {int? cacheWidth, int? cacheHeight, bool? allowUpscaling}) {
|
||||
Future<ui.Codec> decodeResize(Uint8List buffer, {int? cacheWidth, int? cacheHeight, bool? allowUpscaling}) {
|
||||
assert(
|
||||
cacheWidth == null && cacheHeight == null && allowUpscaling == null,
|
||||
'ResizeImage cannot be composed with another ImageProvider that applies '
|
||||
'cacheWidth, cacheHeight, or allowUpscaling.',
|
||||
);
|
||||
return decode(bytes, cacheWidth: width, cacheHeight: height, allowUpscaling: this.allowUpscaling);
|
||||
return decode(buffer, cacheWidth: width, cacheHeight: height, allowUpscaling: this.allowUpscaling);
|
||||
}
|
||||
final ImageStreamCompleter completer = imageProvider.load(key._providerCacheKey, decodeResize);
|
||||
if (!kReleaseMode) {
|
||||
@ -772,6 +854,23 @@ class ResizeImage extends ImageProvider<ResizeImageKey> {
|
||||
return completer;
|
||||
}
|
||||
|
||||
@override
|
||||
ImageStreamCompleter loadBuffer(ResizeImageKey key, DecoderBufferCallback decode) {
|
||||
Future<ui.Codec> decodeResize(ui.ImmutableBuffer buffer, {int? cacheWidth, int? cacheHeight, bool? allowUpscaling}) {
|
||||
assert(
|
||||
cacheWidth == null && cacheHeight == null && allowUpscaling == null,
|
||||
'ResizeImage cannot be composed with another ImageProvider that applies '
|
||||
'cacheWidth, cacheHeight, or allowUpscaling.',
|
||||
);
|
||||
return decode(buffer, cacheWidth: width, cacheHeight: height, allowUpscaling: this.allowUpscaling);
|
||||
}
|
||||
final ImageStreamCompleter completer = imageProvider.loadBuffer(key._providerCacheKey, decodeResize);
|
||||
if (!kReleaseMode) {
|
||||
completer.debugLabel = '${completer.debugLabel} - Resized(${key._width}×${key._height})';
|
||||
}
|
||||
return completer;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ResizeImageKey> obtainKey(ImageConfiguration configuration) {
|
||||
Completer<ResizeImageKey>? completer;
|
||||
@ -832,6 +931,9 @@ abstract class NetworkImage extends ImageProvider<NetworkImage> {
|
||||
|
||||
@override
|
||||
ImageStreamCompleter load(NetworkImage key, DecoderCallback decode);
|
||||
|
||||
@override
|
||||
ImageStreamCompleter loadBuffer(NetworkImage key, DecoderBufferCallback decode);
|
||||
}
|
||||
|
||||
/// Decodes the given [File] object as an image, associating it with the given
|
||||
@ -866,7 +968,7 @@ class FileImage extends ImageProvider<FileImage> {
|
||||
@override
|
||||
ImageStreamCompleter load(FileImage key, DecoderCallback decode) {
|
||||
return MultiFrameImageStreamCompleter(
|
||||
codec: _loadAsync(key, decode),
|
||||
codec: _loadAsync(key, null, decode),
|
||||
scale: key.scale,
|
||||
debugLabel: key.file.path,
|
||||
informationCollector: () => <DiagnosticsNode>[
|
||||
@ -875,18 +977,32 @@ class FileImage extends ImageProvider<FileImage> {
|
||||
);
|
||||
}
|
||||
|
||||
Future<ui.Codec> _loadAsync(FileImage key, DecoderCallback decode) async {
|
||||
@override
|
||||
ImageStreamCompleter loadBuffer(FileImage key, DecoderBufferCallback decode) {
|
||||
return MultiFrameImageStreamCompleter(
|
||||
codec: _loadAsync(key, decode, null),
|
||||
scale: key.scale,
|
||||
debugLabel: key.file.path,
|
||||
informationCollector: () => <DiagnosticsNode>[
|
||||
ErrorDescription('Path: ${file.path}'),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Future<ui.Codec> _loadAsync(FileImage key, DecoderBufferCallback? decode, DecoderCallback? decodeDeprecated) async {
|
||||
assert(key == this);
|
||||
|
||||
final Uint8List bytes = await file.readAsBytes();
|
||||
|
||||
if (bytes.lengthInBytes == 0) {
|
||||
// The file may become available later.
|
||||
PaintingBinding.instance.imageCache.evict(key);
|
||||
throw StateError('$file is empty and cannot be loaded as an image.');
|
||||
}
|
||||
|
||||
return decode(bytes);
|
||||
if (decode != null) {
|
||||
return decode(await ui.ImmutableBuffer.fromUint8List(bytes));
|
||||
}
|
||||
return decodeDeprecated!(bytes);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -953,16 +1069,28 @@ class MemoryImage extends ImageProvider<MemoryImage> {
|
||||
@override
|
||||
ImageStreamCompleter load(MemoryImage key, DecoderCallback decode) {
|
||||
return MultiFrameImageStreamCompleter(
|
||||
codec: _loadAsync(key, decode),
|
||||
codec: _loadAsync(key, null, decode),
|
||||
scale: key.scale,
|
||||
debugLabel: 'MemoryImage(${describeIdentity(key.bytes)})',
|
||||
);
|
||||
}
|
||||
|
||||
Future<ui.Codec> _loadAsync(MemoryImage key, DecoderCallback decode) {
|
||||
assert(key == this);
|
||||
@override
|
||||
ImageStreamCompleter loadBuffer(MemoryImage key, DecoderBufferCallback decode) {
|
||||
return MultiFrameImageStreamCompleter(
|
||||
codec: _loadAsync(key, decode, null),
|
||||
scale: key.scale,
|
||||
debugLabel: 'MemoryImage(${describeIdentity(key.bytes)})',
|
||||
);
|
||||
}
|
||||
|
||||
return decode(bytes);
|
||||
Future<ui.Codec> _loadAsync(MemoryImage key, DecoderBufferCallback? decode, DecoderCallback? decodeDepreacted) async {
|
||||
assert(key == this);
|
||||
if (decode != null) {
|
||||
final ui.ImmutableBuffer buffer = await ui.ImmutableBuffer.fromUint8List(bytes);
|
||||
return decode(buffer);
|
||||
}
|
||||
return decodeDepreacted!(bytes);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -2,11 +2,11 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
@ -55,6 +55,15 @@ abstract class AssetBundle {
|
||||
/// Throws an exception if the asset is not found.
|
||||
Future<ByteData> load(String key);
|
||||
|
||||
/// Retrieve a binary resource from the asset bundle as an immutable
|
||||
/// buffer.
|
||||
///
|
||||
/// Throws an exception if the asset is not found.
|
||||
Future<ui.ImmutableBuffer> loadBuffer(String key) async {
|
||||
final ByteData data = await load(key);
|
||||
return ui.ImmutableBuffer.fromUint8List(data.buffer.asUint8List());
|
||||
}
|
||||
|
||||
/// Retrieve a string from the asset bundle.
|
||||
///
|
||||
/// Throws an exception if the asset is not found.
|
||||
@ -228,6 +237,12 @@ abstract class CachingAssetBundle extends AssetBundle {
|
||||
_stringCache.clear();
|
||||
_structuredDataCache.clear();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ui.ImmutableBuffer> loadBuffer(String key) async {
|
||||
final ByteData data = await load(key);
|
||||
return ui.ImmutableBuffer.fromUint8List(data.buffer.asUint8List());
|
||||
}
|
||||
}
|
||||
|
||||
/// An [AssetBundle] that loads resources using platform messages.
|
||||
@ -242,6 +257,19 @@ class PlatformAssetBundle extends CachingAssetBundle {
|
||||
}
|
||||
return asset;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ui.ImmutableBuffer> loadBuffer(String key) async {
|
||||
if (kIsWeb) {
|
||||
final ByteData bytes = await load(key);
|
||||
return ui.ImmutableBuffer.fromUint8List(bytes.buffer.asUint8List());
|
||||
}
|
||||
try {
|
||||
return await ui.ImmutableBuffer.fromAsset(key);
|
||||
} on Exception {
|
||||
throw FlutterError('Unable to load asset: $key.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AssetBundle _initRootBundle() {
|
||||
|
@ -109,6 +109,9 @@ class ScrollAwareImageProvider<T extends Object> extends ImageProvider<T> {
|
||||
@override
|
||||
ImageStreamCompleter load(T key, DecoderCallback decode) => imageProvider.load(key, decode);
|
||||
|
||||
@override
|
||||
ImageStreamCompleter loadBuffer(T key, DecoderBufferCallback decode) => imageProvider.loadBuffer(key, decode);
|
||||
|
||||
@override
|
||||
Future<T> obtainKey(ImageConfiguration configuration) => imageProvider.obtainKey(configuration);
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
@ -136,15 +135,15 @@ void main() {
|
||||
});
|
||||
|
||||
test('Returns null if an error is caught resolving an image', () {
|
||||
Future<ui.Codec> basicDecoder(Uint8List bytes, {int? cacheWidth, int? cacheHeight, bool? allowUpscaling}) {
|
||||
return PaintingBinding.instance.instantiateImageCodec(bytes, cacheWidth: cacheWidth, cacheHeight: cacheHeight, allowUpscaling: allowUpscaling ?? false);
|
||||
Future<ui.Codec> basicDecoder(ui.ImmutableBuffer bytes, {int? cacheWidth, int? cacheHeight, bool? allowUpscaling}) {
|
||||
return PaintingBinding.instance.instantiateImageCodecFromBuffer(bytes, cacheWidth: cacheWidth, cacheHeight: cacheHeight, allowUpscaling: allowUpscaling ?? false);
|
||||
}
|
||||
final ErrorImageProvider errorImage = ErrorImageProvider();
|
||||
expect(() => imageCache.putIfAbsent(errorImage, () => errorImage.load(errorImage, basicDecoder)), throwsA(isA<Error>()));
|
||||
expect(() => imageCache.putIfAbsent(errorImage, () => errorImage.loadBuffer(errorImage, basicDecoder)), throwsA(isA<Error>()));
|
||||
bool caughtError = false;
|
||||
final ImageStreamCompleter? result = imageCache.putIfAbsent(
|
||||
errorImage,
|
||||
() => errorImage.load(errorImage, basicDecoder),
|
||||
() => errorImage.loadBuffer(errorImage, basicDecoder),
|
||||
onError: (dynamic error, StackTrace? stackTrace) {
|
||||
caughtError = true;
|
||||
},
|
||||
|
@ -17,8 +17,8 @@ import 'mocks_for_image_cache.dart';
|
||||
void main() {
|
||||
TestRenderingFlutterBinding.ensureInitialized();
|
||||
|
||||
Future<ui.Codec> basicDecoder(Uint8List bytes, {int? cacheWidth, int? cacheHeight, bool? allowUpscaling}) {
|
||||
return PaintingBinding.instance.instantiateImageCodec(bytes, cacheWidth: cacheWidth, cacheHeight: cacheHeight, allowUpscaling: allowUpscaling ?? false);
|
||||
Future<ui.Codec> basicDecoder(ui.ImmutableBuffer bytes, {int? cacheWidth, int? cacheHeight, bool? allowUpscaling}) {
|
||||
return PaintingBinding.instance.instantiateImageCodecFromBuffer(bytes, cacheWidth: cacheWidth, cacheHeight: cacheHeight, allowUpscaling: allowUpscaling ?? false);
|
||||
}
|
||||
|
||||
FlutterExceptionHandler? oldError;
|
||||
@ -76,7 +76,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.loadBuffer(imageProvider, basicDecoder),
|
||||
)!;
|
||||
final ImageStream stream = imageProvider.resolve(ImageConfiguration.empty);
|
||||
final Completer<void> completer = Completer<void>();
|
||||
|
@ -6,7 +6,7 @@ import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:math' as math;
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ui' show Codec, FrameInfo;
|
||||
import 'dart:ui' show Codec, FrameInfo, ImmutableBuffer;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/painting.dart';
|
||||
@ -18,8 +18,8 @@ import '../rendering/rendering_tester.dart';
|
||||
void main() {
|
||||
TestRenderingFlutterBinding.ensureInitialized();
|
||||
|
||||
Future<Codec> basicDecoder(Uint8List bytes, {int? cacheWidth, int? cacheHeight, bool? allowUpscaling}) {
|
||||
return PaintingBinding.instance.instantiateImageCodec(bytes, cacheWidth: cacheWidth, cacheHeight: cacheHeight, allowUpscaling: allowUpscaling ?? false);
|
||||
Future<Codec> basicDecoder(ImmutableBuffer buffer, {int? cacheWidth, int? cacheHeight, bool? allowUpscaling}) {
|
||||
return PaintingBinding.instance.instantiateImageCodecFromBuffer(buffer, cacheWidth: cacheWidth, cacheHeight: cacheHeight, allowUpscaling: allowUpscaling ?? false);
|
||||
}
|
||||
|
||||
late _FakeHttpClient httpClient;
|
||||
@ -77,7 +77,7 @@ void main() {
|
||||
|
||||
Future<void> loadNetworkImage() async {
|
||||
final NetworkImage networkImage = NetworkImage(nonconst('foo'));
|
||||
final ImageStreamCompleter completer = networkImage.load(networkImage, basicDecoder);
|
||||
final ImageStreamCompleter completer = networkImage.loadBuffer(networkImage, basicDecoder);
|
||||
completer.addListener(ImageStreamListener(
|
||||
(ImageInfo image, bool synchronousCall) { },
|
||||
onError: (dynamic error, StackTrace? stackTrace) {
|
||||
@ -189,7 +189,7 @@ void main() {
|
||||
debugNetworkImageHttpClientProvider = null;
|
||||
}, skip: isBrowser); // [intended] Browser does not resolve images this way.
|
||||
|
||||
Future<Codec> decoder(Uint8List bytes, {int? cacheWidth, int? cacheHeight, bool? allowUpscaling}) async {
|
||||
Future<Codec> decoder(ImmutableBuffer buffer, {int? cacheWidth, int? cacheHeight, bool? allowUpscaling}) async {
|
||||
return FakeCodec();
|
||||
}
|
||||
|
||||
@ -207,7 +207,7 @@ void main() {
|
||||
|
||||
const NetworkImage provider = NetworkImage(url);
|
||||
|
||||
final MultiFrameImageStreamCompleter completer = provider.load(provider, decoder) as MultiFrameImageStreamCompleter;
|
||||
final MultiFrameImageStreamCompleter completer = provider.loadBuffer(provider, decoder) as MultiFrameImageStreamCompleter;
|
||||
|
||||
expect(completer.debugLabel, url);
|
||||
});
|
||||
|
@ -99,14 +99,14 @@ void main() {
|
||||
final MemoryImage memoryImage = MemoryImage(bytes);
|
||||
final ResizeImage resizeImage = ResizeImage(memoryImage, width: 123, height: 321);
|
||||
|
||||
Future<ui.Codec> decode(Uint8List bytes, {int? cacheWidth, int? cacheHeight, bool allowUpscaling = false}) {
|
||||
Future<ui.Codec> decode(ui.ImmutableBuffer buffer, {int? cacheWidth, int? cacheHeight, bool allowUpscaling = false}) {
|
||||
expect(cacheWidth, 123);
|
||||
expect(cacheHeight, 321);
|
||||
expect(allowUpscaling, false);
|
||||
return PaintingBinding.instance.instantiateImageCodec(bytes, cacheWidth: cacheWidth, cacheHeight: cacheHeight, allowUpscaling: allowUpscaling);
|
||||
return PaintingBinding.instance.instantiateImageCodecFromBuffer(buffer, cacheWidth: cacheWidth, cacheHeight: cacheHeight, allowUpscaling: allowUpscaling);
|
||||
}
|
||||
|
||||
resizeImage.load(await resizeImage.obtainKey(ImageConfiguration.empty), decode);
|
||||
resizeImage.loadBuffer(await resizeImage.obtainKey(ImageConfiguration.empty), decode);
|
||||
});
|
||||
|
||||
test('ResizeImage handles sync obtainKey', () async {
|
||||
|
@ -88,14 +88,14 @@ void main() {
|
||||
final File file = fs.file('/empty.png')..createSync(recursive: true);
|
||||
final FileImage provider = FileImage(file);
|
||||
|
||||
expect(provider.load(provider, (Uint8List bytes, {int? cacheWidth, int? cacheHeight, bool? allowUpscaling}) async {
|
||||
expect(provider.loadBuffer(provider, (ImmutableBuffer buffer, {int? cacheWidth, int? cacheHeight, bool? allowUpscaling}) async {
|
||||
return Future<Codec>.value(FakeCodec());
|
||||
}), isA<MultiFrameImageStreamCompleter>());
|
||||
|
||||
expect(await error.future, isStateError);
|
||||
});
|
||||
|
||||
Future<Codec> decoder(Uint8List bytes, {int? cacheWidth, int? cacheHeight, bool? allowUpscaling}) async {
|
||||
Future<Codec> decoder(ImmutableBuffer buffer, {int? cacheWidth, int? cacheHeight, bool? allowUpscaling}) async {
|
||||
return FakeCodec();
|
||||
}
|
||||
|
||||
@ -104,7 +104,7 @@ void main() {
|
||||
final File file = fs.file('/blue.png')..createSync(recursive: true)..writeAsBytesSync(kBlueSquarePng);
|
||||
final FileImage provider = FileImage(file);
|
||||
|
||||
final MultiFrameImageStreamCompleter completer = provider.load(provider, decoder) as MultiFrameImageStreamCompleter;
|
||||
final MultiFrameImageStreamCompleter completer = provider.loadBuffer(provider, decoder) as MultiFrameImageStreamCompleter;
|
||||
|
||||
expect(completer.debugLabel, file.path);
|
||||
});
|
||||
@ -113,7 +113,7 @@ void main() {
|
||||
final Uint8List bytes = Uint8List.fromList(kBlueSquarePng);
|
||||
final MemoryImage provider = MemoryImage(bytes);
|
||||
|
||||
final MultiFrameImageStreamCompleter completer = provider.load(provider, decoder) as MultiFrameImageStreamCompleter;
|
||||
final MultiFrameImageStreamCompleter completer = provider.loadBuffer(provider, decoder) as MultiFrameImageStreamCompleter;
|
||||
|
||||
expect(completer.debugLabel, 'MemoryImage(${describeIdentity(bytes)})');
|
||||
});
|
||||
@ -122,7 +122,7 @@ void main() {
|
||||
const String asset = 'images/blue.png';
|
||||
final ExactAssetImage provider = ExactAssetImage(asset, bundle: _TestAssetBundle());
|
||||
final AssetBundleImageKey key = await provider.obtainKey(ImageConfiguration.empty);
|
||||
final MultiFrameImageStreamCompleter completer = provider.load(key, decoder) as MultiFrameImageStreamCompleter;
|
||||
final MultiFrameImageStreamCompleter completer = provider.loadBuffer(key, decoder) as MultiFrameImageStreamCompleter;
|
||||
|
||||
expect(completer.debugLabel, asset);
|
||||
});
|
||||
@ -130,7 +130,7 @@ void main() {
|
||||
test('Resize image sets tag', () async {
|
||||
final Uint8List bytes = Uint8List.fromList(kBlueSquarePng);
|
||||
final ResizeImage provider = ResizeImage(MemoryImage(bytes), width: 40, height: 40);
|
||||
final MultiFrameImageStreamCompleter completer = provider.load(
|
||||
final MultiFrameImageStreamCompleter completer = provider.loadBuffer(
|
||||
await provider.obtainKey(ImageConfiguration.empty),
|
||||
decoder,
|
||||
) as MultiFrameImageStreamCompleter;
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/painting.dart';
|
||||
@ -34,6 +35,12 @@ class TestAssetBundle extends CachingAssetBundle {
|
||||
}
|
||||
throw FlutterError('key not found');
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ui.ImmutableBuffer> loadBuffer(String key) async {
|
||||
final ByteData data = await load(key);
|
||||
return ui.ImmutableBuffer.fromUint8List(data.buffer.asUint8List());
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
|
@ -30,6 +30,11 @@ class TestImageProvider extends ImageProvider<TestImageProvider> {
|
||||
|
||||
@override
|
||||
ImageStreamCompleter load(TestImageProvider key, DecoderCallback decode) {
|
||||
throw UnsupportedError('Use ImageProvider.loadBuffer instead.');
|
||||
}
|
||||
|
||||
@override
|
||||
ImageStreamCompleter loadBuffer(TestImageProvider key, DecoderBufferCallback decode) {
|
||||
loadCallCount += 1;
|
||||
return OneFrameImageStreamCompleter(_completer.future);
|
||||
}
|
||||
|
@ -86,6 +86,11 @@ Future<ImageInfo> extractOneFrame(ImageStream stream) {
|
||||
}
|
||||
|
||||
class ErrorImageProvider extends ImageProvider<ErrorImageProvider> {
|
||||
@override
|
||||
ImageStreamCompleter loadBuffer(ErrorImageProvider key, DecoderBufferCallback decode) {
|
||||
throw Error();
|
||||
}
|
||||
|
||||
@override
|
||||
ImageStreamCompleter load(ErrorImageProvider key, DecoderCallback decode) {
|
||||
throw Error();
|
||||
@ -99,7 +104,7 @@ class ErrorImageProvider extends ImageProvider<ErrorImageProvider> {
|
||||
|
||||
class ObtainKeyErrorImageProvider extends ImageProvider<ObtainKeyErrorImageProvider> {
|
||||
@override
|
||||
ImageStreamCompleter load(ObtainKeyErrorImageProvider key, DecoderCallback decode) {
|
||||
ImageStreamCompleter loadBuffer(ObtainKeyErrorImageProvider key, DecoderBufferCallback decode) {
|
||||
throw Error();
|
||||
}
|
||||
|
||||
@ -107,11 +112,16 @@ class ObtainKeyErrorImageProvider extends ImageProvider<ObtainKeyErrorImageProvi
|
||||
Future<ObtainKeyErrorImageProvider> obtainKey(ImageConfiguration configuration) {
|
||||
throw Error();
|
||||
}
|
||||
|
||||
@override
|
||||
ImageStreamCompleter load(ObtainKeyErrorImageProvider key, DecoderCallback decode) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
|
||||
class LoadErrorImageProvider extends ImageProvider<LoadErrorImageProvider> {
|
||||
@override
|
||||
ImageStreamCompleter load(LoadErrorImageProvider key, DecoderCallback decode) {
|
||||
ImageStreamCompleter loadBuffer(LoadErrorImageProvider key, DecoderBufferCallback decode) {
|
||||
throw Error();
|
||||
}
|
||||
|
||||
@ -119,6 +129,11 @@ class LoadErrorImageProvider extends ImageProvider<LoadErrorImageProvider> {
|
||||
Future<LoadErrorImageProvider> obtainKey(ImageConfiguration configuration) {
|
||||
return SynchronousFuture<LoadErrorImageProvider>(this);
|
||||
}
|
||||
|
||||
@override
|
||||
ImageStreamCompleter load(LoadErrorImageProvider key, DecoderCallback decode) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
|
||||
class LoadErrorCompleterImageProvider extends ImageProvider<LoadErrorCompleterImageProvider> {
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
@ -58,8 +59,8 @@ class LoadTestImageProvider extends ImageProvider<Object> {
|
||||
|
||||
final ImageProvider provider;
|
||||
|
||||
ImageStreamCompleter testLoad(Object key, DecoderCallback decode) {
|
||||
return provider.load(key, decode);
|
||||
ImageStreamCompleter testLoad(Object key, DecoderBufferCallback decode) {
|
||||
return provider.loadBuffer(key, decode);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -339,7 +340,7 @@ Future<void> main() async {
|
||||
|
||||
group('ImageProvider', () {
|
||||
|
||||
testWidgets('memory placeholder cacheWidth and cacheHeight is passed through', (WidgetTester tester) async {
|
||||
test('memory placeholder cacheWidth and cacheHeight is passed through', () async {
|
||||
final Uint8List testBytes = Uint8List.fromList(kTransparentImage);
|
||||
final FadeInImage image = FadeInImage.memoryNetwork(
|
||||
placeholder: testBytes,
|
||||
@ -351,22 +352,29 @@ Future<void> main() async {
|
||||
);
|
||||
|
||||
bool called = false;
|
||||
Future<ui.Codec> decode(Uint8List bytes, {int? cacheWidth, int? cacheHeight, bool allowUpscaling = false}) {
|
||||
Future<ui.Codec> decode(ui.ImmutableBuffer buffer, {int? cacheWidth, int? cacheHeight, bool allowUpscaling = false}) {
|
||||
expect(cacheWidth, 20);
|
||||
expect(cacheHeight, 30);
|
||||
expect(allowUpscaling, false);
|
||||
called = true;
|
||||
return PaintingBinding.instance.instantiateImageCodec(bytes, cacheWidth: cacheWidth, cacheHeight: cacheHeight, allowUpscaling: allowUpscaling);
|
||||
return PaintingBinding.instance.instantiateImageCodecFromBuffer(buffer, cacheWidth: cacheWidth, cacheHeight: cacheHeight, allowUpscaling: allowUpscaling);
|
||||
}
|
||||
final ImageProvider resizeImage = image.placeholder;
|
||||
expect(image.placeholder, isA<ResizeImage>());
|
||||
expect(called, false);
|
||||
final LoadTestImageProvider testProvider = LoadTestImageProvider(image.placeholder);
|
||||
testProvider.testLoad(await resizeImage.obtainKey(ImageConfiguration.empty), decode);
|
||||
final ImageStreamCompleter streamCompleter = testProvider.testLoad(await resizeImage.obtainKey(ImageConfiguration.empty), decode);
|
||||
|
||||
final Completer<void> completer = Completer<void>();
|
||||
streamCompleter.addListener(ImageStreamListener((ImageInfo imageInfo, bool syncCall) {
|
||||
completer.complete();
|
||||
}));
|
||||
await completer.future;
|
||||
|
||||
expect(called, true);
|
||||
});
|
||||
|
||||
testWidgets('do not resize when null cache dimensions', (WidgetTester tester) async {
|
||||
test('do not resize when null cache dimensions', () async {
|
||||
final Uint8List testBytes = Uint8List.fromList(kTransparentImage);
|
||||
final FadeInImage image = FadeInImage.memoryNetwork(
|
||||
placeholder: testBytes,
|
||||
@ -374,19 +382,26 @@ Future<void> main() async {
|
||||
);
|
||||
|
||||
bool called = false;
|
||||
Future<ui.Codec> decode(Uint8List bytes, {int? cacheWidth, int? cacheHeight, bool allowUpscaling = false}) {
|
||||
Future<ui.Codec> decode(ui.ImmutableBuffer buffer, {int? cacheWidth, int? cacheHeight, bool allowUpscaling = false}) {
|
||||
expect(cacheWidth, null);
|
||||
expect(cacheHeight, null);
|
||||
expect(allowUpscaling, false);
|
||||
called = true;
|
||||
return PaintingBinding.instance.instantiateImageCodec(bytes, cacheWidth: cacheWidth, cacheHeight: cacheHeight);
|
||||
return PaintingBinding.instance.instantiateImageCodecFromBuffer(buffer, cacheWidth: cacheWidth, cacheHeight: cacheHeight);
|
||||
}
|
||||
// image.placeholder should be an instance of MemoryImage instead of ResizeImage
|
||||
final ImageProvider memoryImage = image.placeholder;
|
||||
expect(image.placeholder, isA<MemoryImage>());
|
||||
expect(called, false);
|
||||
final LoadTestImageProvider testProvider = LoadTestImageProvider(image.placeholder);
|
||||
testProvider.testLoad(await memoryImage.obtainKey(ImageConfiguration.empty), decode);
|
||||
final ImageStreamCompleter streamCompleter = testProvider.testLoad(await memoryImage.obtainKey(ImageConfiguration.empty), decode);
|
||||
|
||||
final Completer<void> completer = Completer<void>();
|
||||
streamCompleter.addListener(ImageStreamListener((ImageInfo imageInfo, bool syncCall) {
|
||||
completer.complete();
|
||||
}));
|
||||
await completer.future;
|
||||
|
||||
expect(called, true);
|
||||
});
|
||||
});
|
||||
|
@ -84,7 +84,7 @@ class TestAssetImage extends AssetImage {
|
||||
final Map<double, ui.Image> images;
|
||||
|
||||
@override
|
||||
ImageStreamCompleter load(AssetBundleImageKey key, DecoderCallback decode) {
|
||||
ImageStreamCompleter loadBuffer(AssetBundleImageKey key, DecoderBufferCallback decode) {
|
||||
late ImageInfo imageInfo;
|
||||
key.bundle.load(key.name).then<void>((ByteData data) {
|
||||
final ui.Image image = images[scaleOf(data)]!;
|
||||
|
@ -2122,7 +2122,7 @@ class _DebouncingImageProvider extends ImageProvider<Object> {
|
||||
Future<Object> obtainKey(ImageConfiguration configuration) => imageProvider.obtainKey(configuration);
|
||||
|
||||
@override
|
||||
ImageStreamCompleter load(Object key, DecoderCallback decode) => imageProvider.load(key, decode);
|
||||
ImageStreamCompleter loadBuffer(Object key, DecoderBufferCallback decode) => imageProvider.loadBuffer(key, decode);
|
||||
}
|
||||
|
||||
class _FailingImageProvider extends ImageProvider<int> {
|
||||
|
@ -322,7 +322,7 @@ void main() {
|
||||
// If we miss the early return, we will fail.
|
||||
testImageProvider.complete();
|
||||
|
||||
imageCache.putIfAbsent(testImageProvider, () => testImageProvider.load(testImageProvider, PaintingBinding.instance.instantiateImageCodec));
|
||||
imageCache.putIfAbsent(testImageProvider, () => testImageProvider.loadBuffer(testImageProvider, PaintingBinding.instance.instantiateImageCodecFromBuffer));
|
||||
// We've stopped scrolling fast.
|
||||
physics.recommendDeferredLoadingValue = false;
|
||||
await tester.idle();
|
||||
@ -377,7 +377,7 @@ void main() {
|
||||
|
||||
// Complete the original image while we're still scrolling fast.
|
||||
testImageProvider.complete();
|
||||
stream.setCompleter(testImageProvider.load(testImageProvider, PaintingBinding.instance.instantiateImageCodec));
|
||||
stream.setCompleter(testImageProvider.loadBuffer(testImageProvider, PaintingBinding.instance.instantiateImageCodecFromBuffer));
|
||||
|
||||
// Verify that this hasn't changed the cache state yet
|
||||
expect(imageCache.containsKey(testImageProvider), false);
|
||||
|
Loading…
x
Reference in New Issue
Block a user