@@ -2124,6 +2124,10 @@ Future<Codec> instantiateImageCodec(
21242124/// The data can be for either static or animated images. The following image
21252125/// formats are supported: {@macro dart.ui.imageFormats}
21262126///
2127+ /// The [buffer] will be disposed by this method once the codec has been created,
2128+ /// so the caller must relinquish ownership of the [buffer] when they call this
2129+ /// method.
2130+ ///
21272131/// The [targetWidth] and [targetHeight] arguments specify the size of the
21282132/// output image, in image pixels. If they are not equal to the intrinsic
21292133/// dimensions of the image, then the image will be scaled after being decoded.
@@ -2141,26 +2145,145 @@ Future<Codec> instantiateImageCodec(
21412145///
21422146/// The returned future can complete with an error if the image decoding has
21432147/// failed.
2148+ ///
2149+ /// ## Compatibility note on the web
2150+ ///
2151+ /// When running Flutter on the web, only the CanvasKit renderer supports image
2152+ /// resizing capabilities (not the HTML renderer). So if image resizing is
2153+ /// critical to your use case, and you're deploying to the web, you should
2154+ /// build using the CanvasKit renderer.
21442155Future <Codec > instantiateImageCodecFromBuffer (
21452156 ImmutableBuffer buffer, {
21462157 int ? targetWidth,
21472158 int ? targetHeight,
21482159 bool allowUpscaling = true ,
2160+ }) {
2161+ return instantiateImageCodecWithSize (
2162+ buffer,
2163+ getTargetSize: (int intrinsicWidth, int intrinsicHeight) {
2164+ if (! allowUpscaling) {
2165+ if (targetWidth != null && targetWidth! > intrinsicWidth) {
2166+ targetWidth = intrinsicWidth;
2167+ }
2168+ if (targetHeight != null && targetHeight! > intrinsicHeight) {
2169+ targetHeight = intrinsicHeight;
2170+ }
2171+ }
2172+ return TargetImageSize (width: targetWidth, height: targetHeight);
2173+ },
2174+ );
2175+ }
2176+
2177+ /// Instantiates an image [Codec] .
2178+ ///
2179+ /// This method is a convenience wrapper around the [ImageDescriptor] API.
2180+ ///
2181+ /// The [buffer] parameter is the binary image data (e.g a PNG or GIF binary
2182+ /// data). The data can be for either static or animated images. The following
2183+ /// image formats are supported: {@macro dart.ui.imageFormats}
2184+ ///
2185+ /// The [buffer] will be disposed by this method once the codec has been
2186+ /// created, so the caller must relinquish ownership of the [buffer] when they
2187+ /// call this method.
2188+ ///
2189+ /// The [getTargetSize] parameter, when specified, will be invoked and passed
2190+ /// the image's intrinsic size to determine the size to decode the image to.
2191+ /// The width and the height of the size it returns must be positive values
2192+ /// greater than or equal to 1, or null. It is valid to return a
2193+ /// [TargetImageSize] that specifies only one of `width` and `height` with the
2194+ /// other remaining null, in which case the omitted dimension will be scaled to
2195+ /// maintain the aspect ratio of the original dimensions. When both are null or
2196+ /// omitted, the image will be decoded at its native resolution (as will be the
2197+ /// case if the [getTargetSize] parameter is omitted).
2198+ ///
2199+ /// Scaling the image to larger than its intrinsic size should usually be
2200+ /// avoided, since it causes the image to use more memory than necessary.
2201+ /// Instead, prefer scaling the [Canvas] transform.
2202+ ///
2203+ /// The returned future can complete with an error if the image decoding has
2204+ /// failed.
2205+ ///
2206+ /// ## Compatibility note on the web
2207+ ///
2208+ /// When running Flutter on the web, only the CanvasKit renderer supports image
2209+ /// resizing capabilities (not the HTML renderer). So if image resizing is
2210+ /// critical to your use case, and you're deploying to the web, you should
2211+ /// build using the CanvasKit renderer.
2212+ Future <Codec > instantiateImageCodecWithSize (
2213+ ImmutableBuffer buffer, {
2214+ TargetImageSizeCallback ? getTargetSize,
21492215}) async {
2216+ getTargetSize ?? = _getDefaultImageSize;
21502217 final ImageDescriptor descriptor = await ImageDescriptor .encoded (buffer);
2151- if (! allowUpscaling) {
2152- if (targetWidth != null && targetWidth > descriptor.width) {
2153- targetWidth = descriptor.width;
2154- }
2155- if (targetHeight != null && targetHeight > descriptor.height) {
2156- targetHeight = descriptor.height;
2157- }
2218+ try {
2219+ final TargetImageSize targetSize = getTargetSize (descriptor.width, descriptor.height);
2220+ assert (targetSize.width == null || targetSize.width! > 0 );
2221+ assert (targetSize.height == null || targetSize.height! > 0 );
2222+ return descriptor.instantiateCodec (
2223+ targetWidth: targetSize.width,
2224+ targetHeight: targetSize.height,
2225+ );
2226+ } finally {
2227+ buffer.dispose ();
21582228 }
2159- buffer.dispose ();
2160- return descriptor.instantiateCodec (
2161- targetWidth: targetWidth,
2162- targetHeight: targetHeight,
2163- );
2229+ }
2230+
2231+ TargetImageSize _getDefaultImageSize (int intrinsicWidth, int intrinsicHeight) {
2232+ return const TargetImageSize ();
2233+ }
2234+
2235+ /// Signature for a callback that determines the size to which an image should
2236+ /// be decoded given its intrinsic size.
2237+ ///
2238+ /// See also:
2239+ ///
2240+ /// * [instantiateImageCodecWithSize] , which used this signature for its
2241+ /// `getTargetSize` argument.
2242+ typedef TargetImageSizeCallback = TargetImageSize Function (
2243+ int intrinsicWidth,
2244+ int intrinsicHeight,
2245+ );
2246+
2247+ /// A specification of the size to which an image should be decoded.
2248+ ///
2249+ /// See also:
2250+ ///
2251+ /// * [TargetImageSizeCallback] , a callback that returns instances of this
2252+ /// class when consulted by image decoding methods such as
2253+ /// [instantiateImageCodecWithSize].
2254+ class TargetImageSize {
2255+ /// Creates a new instance of this class.
2256+ ///
2257+ /// The `width` and `height` may both be null, but if they're non-null, they
2258+ /// must be positive.
2259+ const TargetImageSize ({this .width, this .height})
2260+ : assert (width == null || width > 0 ),
2261+ assert (height == null || height > 0 );
2262+
2263+ /// The width into which to load the image.
2264+ ///
2265+ /// If this is non-null, the image will be decoded into the specified width.
2266+ /// If this is null and [height] is also null, the image will be decoded into
2267+ /// its intrinsic size. If this is null and [height] is non-null, the image
2268+ /// will be decoded into a width that maintains its intrinsic aspect ratio
2269+ /// while respecting the [height] value.
2270+ ///
2271+ /// If this value is non-null, it must be positive.
2272+ final int ? width;
2273+
2274+ /// The height into which to load the image.
2275+ ///
2276+ /// If this is non-null, the image will be decoded into the specified height.
2277+ /// If this is null and [width] is also null, the image will be decoded into
2278+ /// its intrinsic size. If this is null and [width] is non-null, the image
2279+ /// will be decoded into a height that maintains its intrinsic aspect ratio
2280+ /// while respecting the [width] value.
2281+ ///
2282+ /// If this value is non-null, it must be positive.
2283+ final int ? height;
2284+
2285+ @override
2286+ String toString () => 'TargetImageSize($width x $height )' ;
21642287}
21652288
21662289/// Loads a single image frame from a byte array into an [Image] object.
0 commit comments