Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit b319938

Browse files
authored
Add more flexible image API (#118966)
This updates the framework to provide higher level wrappers around ui.instantiateImageCodecWithSize(). Functionally, this doesn't change anything (other than deprecating the older loadBuffer() method in favor of loadImage()), but it sets the stage for a simpler change that will allow us to provide a more flexible way to load sized images. #118543
1 parent 202e902 commit b319938

12 files changed

+323
-57
lines changed

dev/benchmarks/macrobenchmarks/lib/src/animated_placeholder.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class DelayedBase64Image extends ImageProvider<int> {
5858
}
5959

6060
@override
61-
ImageStreamCompleter loadBuffer(int key, DecoderBufferCallback decode) {
61+
ImageStreamCompleter loadImage(int key, ImageDecoderCallback decode) {
6262
return MultiFrameImageStreamCompleter(
6363
codec: Future<ui.Codec>.delayed(
6464
delay,

dev/integration_tests/web_e2e_tests/test_driver/cache_width_cache_height_integration.dart

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ import 'package:flutter/material.dart';
99
import 'package:flutter_test/flutter_test.dart';
1010
import 'package:integration_test/integration_test.dart';
1111

12-
// This class allows loadBuffer, a protected method, to be called with a custom
13-
// DecoderBufferCallback function.
12+
// This class allows loadImage, a protected method, to be called with a custom
13+
// ImageDecoderCallback function.
1414
class LoadTestImageProvider extends ImageProvider<Object> {
1515
LoadTestImageProvider(this.provider);
1616

1717
final ImageProvider provider;
1818

19-
ImageStreamCompleter testLoad(Object key, DecoderBufferCallback decode) {
20-
return provider.loadBuffer(key, decode);
19+
ImageStreamCompleter testLoad(Object key, ImageDecoderCallback decode) {
20+
return provider.loadImage(key, decode);
2121
}
2222

2323
@override
@@ -26,7 +26,7 @@ class LoadTestImageProvider extends ImageProvider<Object> {
2626
}
2727

2828
@override
29-
ImageStreamCompleter loadBuffer(Object key, DecoderBufferCallback decode) {
29+
ImageStreamCompleter loadImage(Object key, ImageDecoderCallback decode) {
3030
throw UnimplementedError();
3131
}
3232
}
@@ -47,12 +47,15 @@ void main() {
4747

4848
bool called = false;
4949

50-
Future<ui.Codec> decode(ui.ImmutableBuffer buffer, {int? cacheWidth, int? cacheHeight, bool allowUpscaling = false}) {
51-
expect(cacheHeight, expectedCacheHeight);
52-
expect(cacheWidth, expectedCacheWidth);
53-
expect(allowUpscaling, false);
54-
called = true;
55-
return PaintingBinding.instance.instantiateImageCodecFromBuffer(buffer, cacheWidth: cacheWidth, cacheHeight: cacheHeight, allowUpscaling: allowUpscaling);
50+
Future<ui.Codec> decode(ui.ImmutableBuffer buffer, {ui.TargetImageSizeCallback? getTargetSize}) {
51+
return PaintingBinding.instance.instantiateImageCodecWithSize(buffer, getTargetSize: (int intrinsicWidth, int intrinsicHeight) {
52+
expect(getTargetSize, isNotNull);
53+
final ui.TargetImageSize targetSize = getTargetSize!(intrinsicWidth, intrinsicHeight);
54+
expect(targetSize.width, expectedCacheWidth);
55+
expect(targetSize.height, expectedCacheHeight);
56+
called = true;
57+
return targetSize;
58+
});
5659
}
5760

5861
final ImageProvider resizeImage = image.image;

packages/flutter/lib/src/painting/_network_image_io.dart

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class NetworkImage extends image_provider.ImageProvider<image_provider.NetworkIm
4343
final StreamController<ImageChunkEvent> chunkEvents = StreamController<ImageChunkEvent>();
4444

4545
return MultiFrameImageStreamCompleter(
46-
codec: _loadAsync(key as NetworkImage, chunkEvents, null, decode),
46+
codec: _loadAsync(key as NetworkImage, chunkEvents, decodeDeprecated: decode),
4747
chunkEvents: chunkEvents.stream,
4848
scale: key.scale,
4949
debugLabel: key.url,
@@ -62,7 +62,26 @@ class NetworkImage extends image_provider.ImageProvider<image_provider.NetworkIm
6262
final StreamController<ImageChunkEvent> chunkEvents = StreamController<ImageChunkEvent>();
6363

6464
return MultiFrameImageStreamCompleter(
65-
codec: _loadAsync(key as NetworkImage, chunkEvents, decode, null),
65+
codec: _loadAsync(key as NetworkImage, chunkEvents, decodeBufferDeprecated: decode),
66+
chunkEvents: chunkEvents.stream,
67+
scale: key.scale,
68+
debugLabel: key.url,
69+
informationCollector: () => <DiagnosticsNode>[
70+
DiagnosticsProperty<image_provider.ImageProvider>('Image provider', this),
71+
DiagnosticsProperty<image_provider.NetworkImage>('Image key', key),
72+
],
73+
);
74+
}
75+
76+
@override
77+
ImageStreamCompleter loadImage(image_provider.NetworkImage key, image_provider.ImageDecoderCallback decode) {
78+
// Ownership of this controller is handed off to [_loadAsync]; it is that
79+
// method's responsibility to close the controller's stream when the image
80+
// has been loaded or an error is thrown.
81+
final StreamController<ImageChunkEvent> chunkEvents = StreamController<ImageChunkEvent>();
82+
83+
return MultiFrameImageStreamCompleter(
84+
codec: _loadAsync(key as NetworkImage, chunkEvents, decode: decode),
6685
chunkEvents: chunkEvents.stream,
6786
scale: key.scale,
6887
debugLabel: key.url,
@@ -92,10 +111,11 @@ class NetworkImage extends image_provider.ImageProvider<image_provider.NetworkIm
92111

93112
Future<ui.Codec> _loadAsync(
94113
NetworkImage key,
95-
StreamController<ImageChunkEvent> chunkEvents,
96-
image_provider.DecoderBufferCallback? decode,
97-
image_provider.DecoderCallback? decodeDepreacted,
98-
) async {
114+
StreamController<ImageChunkEvent> chunkEvents, {
115+
image_provider.ImageDecoderCallback? decode,
116+
image_provider.DecoderBufferCallback? decodeBufferDeprecated,
117+
image_provider.DecoderCallback? decodeDeprecated,
118+
}) async {
99119
try {
100120
assert(key == this);
101121

@@ -131,9 +151,12 @@ class NetworkImage extends image_provider.ImageProvider<image_provider.NetworkIm
131151
if (decode != null) {
132152
final ui.ImmutableBuffer buffer = await ui.ImmutableBuffer.fromUint8List(bytes);
133153
return decode(buffer);
154+
} else if (decodeBufferDeprecated != null) {
155+
final ui.ImmutableBuffer buffer = await ui.ImmutableBuffer.fromUint8List(bytes);
156+
return decodeBufferDeprecated(buffer);
134157
} else {
135-
assert(decodeDepreacted != null);
136-
return decodeDepreacted!(bytes);
158+
assert(decodeDeprecated != null);
159+
return decodeDeprecated!(bytes);
137160
}
138161
} catch (e) {
139162
// Depending on where the exception was thrown, the image cache may not

packages/flutter/lib/src/painting/_network_image_web.dart

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ class NetworkImage
6565

6666
return MultiFrameImageStreamCompleter(
6767
chunkEvents: chunkEvents.stream,
68-
codec: _loadAsync(key as NetworkImage, null, decode, chunkEvents),
68+
codec: _loadAsync(key as NetworkImage, null, null, decode, chunkEvents),
6969
scale: key.scale,
7070
debugLabel: key.url,
7171
informationCollector: _imageStreamInformationCollector(key),
@@ -82,7 +82,23 @@ class NetworkImage
8282

8383
return MultiFrameImageStreamCompleter(
8484
chunkEvents: chunkEvents.stream,
85-
codec: _loadAsync(key as NetworkImage, decode, null, chunkEvents),
85+
codec: _loadAsync(key as NetworkImage, null, decode, null, chunkEvents),
86+
scale: key.scale,
87+
debugLabel: key.url,
88+
informationCollector: _imageStreamInformationCollector(key),
89+
);
90+
}
91+
92+
@override
93+
ImageStreamCompleter loadImage(image_provider.NetworkImage key, image_provider.ImageDecoderCallback decode) {
94+
// Ownership of this controller is handed off to [_loadAsync]; it is that
95+
// method's responsibility to close the controller's stream when the image
96+
// has been loaded or an error is thrown.
97+
final StreamController<ImageChunkEvent> chunkEvents = StreamController<ImageChunkEvent>();
98+
99+
return MultiFrameImageStreamCompleter(
100+
chunkEvents: chunkEvents.stream,
101+
codec: _loadAsync(key as NetworkImage, decode, null, null, chunkEvents),
86102
scale: key.scale,
87103
debugLabel: key.url,
88104
informationCollector: _imageStreamInformationCollector(key),
@@ -106,8 +122,9 @@ class NetworkImage
106122
// directly in place of the typical `instantiateImageCodec` method.
107123
Future<ui.Codec> _loadAsync(
108124
NetworkImage key,
109-
image_provider.DecoderBufferCallback? decode,
110-
image_provider.DecoderCallback? decodeDepreacted,
125+
image_provider.ImageDecoderCallback? decode,
126+
image_provider.DecoderBufferCallback? decodeBufferDeprecated,
127+
image_provider.DecoderCallback? decodeDeprecated,
111128
StreamController<ImageChunkEvent> chunkEvents,
112129
) async {
113130
assert(key == this);
@@ -165,9 +182,12 @@ class NetworkImage
165182
if (decode != null) {
166183
final ui.ImmutableBuffer buffer = await ui.ImmutableBuffer.fromUint8List(bytes);
167184
return decode(buffer);
185+
} else if (decodeBufferDeprecated != null) {
186+
final ui.ImmutableBuffer buffer = await ui.ImmutableBuffer.fromUint8List(bytes);
187+
return decodeBufferDeprecated(buffer);
168188
} else {
169-
assert(decodeDepreacted != null);
170-
return decodeDepreacted!(bytes);
189+
assert(decodeDeprecated != null);
190+
return decodeDeprecated!(bytes);
171191
}
172192
} else {
173193
// This API only exists in the web engine implementation and is not

packages/flutter/lib/src/painting/binding.dart

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
import 'dart:ui' as ui show Codec, ImmutableBuffer, instantiateImageCodec, instantiateImageCodecFromBuffer;
5+
import 'dart:ui' as ui;
66
import 'package:flutter/foundation.dart';
77
import 'package:flutter/services.dart' show ServicesBinding;
88

@@ -100,7 +100,7 @@ mixin PaintingBinding on BindingBase, ServicesBinding {
100100
/// above its native resolution should prefer scaling the canvas the image is
101101
/// drawn into.
102102
@Deprecated(
103-
'Use instantiateImageCodecFromBuffer with an ImmutableBuffer instance instead. '
103+
'Use instantiateImageCodecWithSize with an ImmutableBuffer instance instead. '
104104
'This feature was deprecated after v2.13.0-1.0.pre.',
105105
)
106106
Future<ui.Codec> instantiateImageCodec(
@@ -140,6 +140,10 @@ mixin PaintingBinding on BindingBase, ServicesBinding {
140140
/// unnecessary memory usage for images. Callers that wish to display an image
141141
/// above its native resolution should prefer scaling the canvas the image is
142142
/// drawn into.
143+
@Deprecated(
144+
'Use instantiateImageCodecWithSize instead. '
145+
'This feature was deprecated after v3.7.0-1.4.pre.',
146+
)
143147
Future<ui.Codec> instantiateImageCodecFromBuffer(
144148
ui.ImmutableBuffer buffer, {
145149
int? cacheWidth,
@@ -156,6 +160,28 @@ mixin PaintingBinding on BindingBase, ServicesBinding {
156160
);
157161
}
158162

163+
/// Calls through to [dart:ui.instantiateImageCodecWithSize] from [ImageCache].
164+
///
165+
/// The [buffer] parameter should be an [ui.ImmutableBuffer] instance which can
166+
/// be acquired from [ui.ImmutableBuffer.fromUint8List] or
167+
/// [ui.ImmutableBuffer.fromAsset].
168+
///
169+
/// The [getTargetSize] parameter, when specified, will be invoked and passed
170+
/// the image's intrinsic size to determine the size to decode the image to.
171+
/// The width and the height of the size it returns must be positive values
172+
/// greater than or equal to 1, or null. It is valid to return a [TargetImageSize]
173+
/// that specifies only one of `width` and `height` with the other remaining
174+
/// null, in which case the omitted dimension will be scaled to maintain the
175+
/// aspect ratio of the original dimensions. When both are null or omitted,
176+
/// the image will be decoded at its native resolution (as will be the case if
177+
/// the [getTargetSize] parameter is omitted).
178+
Future<ui.Codec> instantiateImageCodecWithSize(
179+
ui.ImmutableBuffer buffer, {
180+
ui.TargetImageSizeCallback? getTargetSize,
181+
}) {
182+
return ui.instantiateImageCodecWithSize(buffer, getTargetSize: getTargetSize);
183+
}
184+
159185
@override
160186
void evict(String asset) {
161187
super.evict(asset);

0 commit comments

Comments
 (0)