-
Notifications
You must be signed in to change notification settings - Fork 6k
Add more flexible image loading API #38905
Changes from 1 commit
759c980
b095f72
e6371f3
ca4dc78
1a026b3
5c7d830
cb7cf84
d31fa58
3d79727
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
This adds a new `instantiateImageCodecWithSize` method, which can be used to decode an image into a size, where the target size isn't known until the caller is allowed to inspect the image descriptor. This enables the use case of loading an image whose aspect ratio isn't known at load time, and which needs to be resized to fit within a bounding box while also maintaining its original aspect ratio. flutter/flutter#118543
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2105,7 +2105,7 @@ Future<Codec> instantiateImageCodec( | |
| bool allowUpscaling = true, | ||
| }) async { | ||
| final ImmutableBuffer buffer = await ImmutableBuffer.fromUint8List(list); | ||
| return instantiateImageCodecFromBuffer( | ||
| return await instantiateImageCodecFromBuffer( | ||
| buffer, | ||
| targetWidth: targetWidth, | ||
| targetHeight: targetHeight, | ||
|
|
@@ -2124,6 +2124,10 @@ Future<Codec> instantiateImageCodec( | |
| /// The data can be for either static or animated images. The following image | ||
| /// formats are supported: {@macro dart.ui.imageFormats} | ||
| /// | ||
| /// The [buffer] will be disposed by this method once the codec has been created, | ||
| /// so the caller must relinquish ownership of the [buffer] when they call this | ||
| /// method. | ||
| /// | ||
| /// The [targetWidth] and [targetHeight] arguments specify the size of the | ||
| /// output image, in image pixels. If they are not equal to the intrinsic | ||
| /// dimensions of the image, then the image will be scaled after being decoded. | ||
|
|
@@ -2146,21 +2150,119 @@ Future<Codec> instantiateImageCodecFromBuffer( | |
| int? targetWidth, | ||
| int? targetHeight, | ||
| bool allowUpscaling = true, | ||
| }) { | ||
| return instantiateImageCodecWithSize( | ||
| buffer, | ||
| getTargetSize: (ImageDescriptor descriptor) { | ||
| if (!allowUpscaling) { | ||
| if (targetWidth != null && targetWidth! > descriptor.width) { | ||
| targetWidth = descriptor.width; | ||
| } | ||
| if (targetHeight != null && targetHeight! > descriptor.height) { | ||
| targetHeight = descriptor.height; | ||
| } | ||
| } | ||
| return TargetImageSize(width: targetWidth, height: targetHeight); | ||
| }, | ||
| ); | ||
| } | ||
|
|
||
| /// Instantiates an image [Codec]. | ||
| /// | ||
| /// This method is a convenience wrapper around the [ImageDescriptor] API. | ||
| /// | ||
| /// The [buffer] parameter is the binary image data (e.g a PNG or GIF binary data). | ||
| /// The data can be for either static or animated images. The following image | ||
| /// formats are supported: {@macro dart.ui.imageFormats} | ||
| /// | ||
| /// The [buffer] will be disposed by this method once the codec has been created, | ||
| /// so the caller must relinquish ownership of the [buffer] when they call this | ||
| /// method. | ||
| /// | ||
| /// The [getTargetSize] parameter, when specified, will be invoked and passed | ||
| /// the image's [ImageDescriptor] to determine the size to decode the image to. | ||
| /// The width and the height of the size it returns must be positive values | ||
| /// greater than or equal to 1, or null. It is valid to return a [TargetImageSize] | ||
| /// that specifies only one of `width` and `height` 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 (as will be the case if | ||
| /// the [getTargetSize] parameter is omitted). | ||
| /// | ||
| /// Scaling the image to larger than its intrinsic size should usually be | ||
| /// avoided, since it causes the image to use more memory than necessary. | ||
| /// Instead, prefer scaling the [Canvas] transform. | ||
| /// | ||
| /// The returned future can complete with an error if the image decoding has | ||
| /// failed. | ||
| Future<Codec> instantiateImageCodecWithSize( | ||
| ImmutableBuffer buffer, { | ||
| TargetImageSizeProducer? getTargetSize, | ||
| }) async { | ||
| getTargetSize ??= (ImageDescriptor descriptor) => const TargetImageSize(); | ||
|
||
| final ImageDescriptor descriptor = await ImageDescriptor.encoded(buffer); | ||
| if (!allowUpscaling) { | ||
| if (targetWidth != null && targetWidth > descriptor.width) { | ||
| targetWidth = descriptor.width; | ||
| } | ||
| if (targetHeight != null && targetHeight > descriptor.height) { | ||
| targetHeight = descriptor.height; | ||
| } | ||
| try { | ||
| final TargetImageSize targetSize = getTargetSize(descriptor); | ||
| assert(targetSize.width == null || targetSize.width! > 0); | ||
| assert(targetSize.height == null || targetSize.height! > 0); | ||
| return descriptor.instantiateCodec( | ||
| targetWidth: targetSize.width, | ||
| targetHeight: targetSize.height, | ||
| ); | ||
| } finally { | ||
| buffer.dispose(); | ||
| } | ||
| buffer.dispose(); | ||
| return descriptor.instantiateCodec( | ||
| targetWidth: targetWidth, | ||
| targetHeight: targetHeight, | ||
| ); | ||
| } | ||
|
|
||
| /// Signature for a callback that determines the size to which an image should | ||
| /// be decoded given its [ImageDescriptor]. | ||
| /// | ||
| /// See also: | ||
| /// | ||
| /// * [instantiateImageCodecWithSize], which used this signature for its | ||
| /// `getTargetSize` argument. | ||
| typedef TargetImageSizeProducer = TargetImageSize Function(ImageDescriptor descriptor); | ||
|
||
|
|
||
| /// A specification of the size to which an image should be decoded. | ||
| /// | ||
| /// See also: | ||
| /// | ||
| /// * [TargetImageSizeProducer], a callback that returns instances of this | ||
| /// class when consulted by image decoding methods such as | ||
| /// [instantiateImageCodecWithSize]. | ||
| class TargetImageSize { | ||
| /// Creates a new instance of this class. | ||
| /// | ||
| /// The `width` and `height` may both be null, but if they're non-null, they | ||
| /// must be positive. | ||
| const TargetImageSize({this.width, this.height}) | ||
| : assert(width == null || width > 0), | ||
| assert(width == null || width > 0); | ||
|
||
|
|
||
| /// The width into which to load the image. | ||
| /// | ||
| /// If this is non-null, the image will be decoded into the specified width. | ||
| /// If this is null and [height] is also null, the image will be decoded into | ||
| /// its intrinsic size. If this is null and [height] is non-null, the image | ||
| /// will be decoded into a width that maintains its intrinsic aspect ratio | ||
| /// while respecting the [height] value. | ||
| /// | ||
| /// If this value is non-null, it must be positive. | ||
| final int? width; | ||
|
|
||
| /// The height into which to load the image. | ||
| /// | ||
| /// If this is non-null, the image will be decoded into the specified height. | ||
| /// If this is null and [width] is also null, the image will be decoded into | ||
| /// its intrinsic size. If this is null and [width] is non-null, the image | ||
| /// will be decoded into a height that maintains its intrinsic aspect ratio | ||
| /// while respecting the [width] value. | ||
| /// | ||
| /// If this value is non-null, it must be positive. | ||
| final int? height; | ||
|
|
||
| @override | ||
| String toString() => 'TargetImageSize($width x $height)'; | ||
| } | ||
|
|
||
| /// Loads a single image frame from a byte array into an [Image] object. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe note here that image resizing capabilities are only available with the canvaskit renderer and not html renderer (we should probably update the api docs for
instantiateImageCodecto note this too)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done