Unverified Commit 791edc23 authored by Yegor's avatar Yegor Committed by GitHub

always pass filterQuality specified in the Image widget to canvas (#74854)

parent dbb1958c
...@@ -473,9 +473,7 @@ void paintImage({ ...@@ -473,9 +473,7 @@ void paintImage({
final Paint paint = Paint()..isAntiAlias = isAntiAlias; final Paint paint = Paint()..isAntiAlias = isAntiAlias;
if (colorFilter != null) if (colorFilter != null)
paint.colorFilter = colorFilter; paint.colorFilter = colorFilter;
if (sourceSize != destinationSize) { paint.filterQuality = filterQuality;
paint.filterQuality = filterQuality;
}
paint.invertColors = invertColors; paint.invertColors = invertColors;
final double halfWidthDelta = (outputSize.width - destinationSize.width) / 2.0; final double halfWidthDelta = (outputSize.width - destinationSize.width) / 2.0;
final double halfHeightDelta = (outputSize.height - destinationSize.height) / 2.0; final double halfHeightDelta = (outputSize.height - destinationSize.height) / 2.0;
......
...@@ -312,10 +312,9 @@ class Image extends StatefulWidget { ...@@ -312,10 +312,9 @@ class Image extends StatefulWidget {
/// Otherwise, the image dimensions will change as the image is loaded, which /// Otherwise, the image dimensions will change as the image is loaded, which
/// will result in ugly layout changes. /// will result in ugly layout changes.
/// ///
/// Use [filterQuality] to change the quality when scaling an image. /// {@template flutter.widgets.image.filterQualityParameter}
/// Use the [FilterQuality.low] quality setting to scale the image, /// Use [filterQuality] to specify the rendering quality of the image.
/// which corresponds to bilinear interpolation, rather than the default /// {@endtemplate}
/// [FilterQuality.none] which corresponds to nearest-neighbor.
/// ///
/// If [excludeFromSemantics] is true, then [semanticLabel] will be ignored. /// If [excludeFromSemantics] is true, then [semanticLabel] will be ignored.
const Image({ const Image({
...@@ -360,10 +359,7 @@ class Image extends StatefulWidget { ...@@ -360,10 +359,7 @@ class Image extends StatefulWidget {
/// An optional [headers] argument can be used to send custom HTTP headers /// An optional [headers] argument can be used to send custom HTTP headers
/// with the image request. /// with the image request.
/// ///
/// Use [filterQuality] to change the quality when scaling an image. /// {@macro flutter.widgets.image.filterQualityParameter}
/// Use the [FilterQuality.low] quality setting to scale the image,
/// which corresponds to bilinear interpolation, rather than the default
/// [FilterQuality.none] which corresponds to nearest-neighbor.
/// ///
/// If [excludeFromSemantics] is true, then [semanticLabel] will be ignored. /// If [excludeFromSemantics] is true, then [semanticLabel] will be ignored.
/// ///
...@@ -424,10 +420,7 @@ class Image extends StatefulWidget { ...@@ -424,10 +420,7 @@ class Image extends StatefulWidget {
/// On Android, this may require the /// On Android, this may require the
/// `android.permission.READ_EXTERNAL_STORAGE` permission. /// `android.permission.READ_EXTERNAL_STORAGE` permission.
/// ///
/// Use [filterQuality] to change the quality when scaling an image. /// {@macro flutter.widgets.image.filterQualityParameter}
/// Use the [FilterQuality.low] quality setting to scale the image,
/// which corresponds to bilinear interpolation, rather than the default
/// [FilterQuality.none] which corresponds to nearest-neighbor.
/// ///
/// If [excludeFromSemantics] is true, then [semanticLabel] will be ignored. /// If [excludeFromSemantics] is true, then [semanticLabel] will be ignored.
/// ///
...@@ -520,10 +513,7 @@ class Image extends StatefulWidget { ...@@ -520,10 +513,7 @@ class Image extends StatefulWidget {
/// Otherwise, the image dimensions will change as the image is loaded, which /// Otherwise, the image dimensions will change as the image is loaded, which
/// will result in ugly layout changes. /// will result in ugly layout changes.
/// ///
/// Use [filterQuality] to change the quality when scaling an image. /// {@macro flutter.widgets.image.filterQualityParameter}
/// Use the [FilterQuality.low] quality setting to scale the image,
/// which corresponds to bilinear interpolation, rather than the default
/// [FilterQuality.none] which corresponds to nearest-neighbor.
/// ///
/// {@tool snippet} /// {@tool snippet}
/// ///
...@@ -667,10 +657,7 @@ class Image extends StatefulWidget { ...@@ -667,10 +657,7 @@ class Image extends StatefulWidget {
/// Otherwise, the image dimensions will change as the image is loaded, which /// Otherwise, the image dimensions will change as the image is loaded, which
/// will result in ugly layout changes. /// will result in ugly layout changes.
/// ///
/// Use [filterQuality] to change the quality when scaling an image. /// {@macro flutter.widgets.image.filterQualityParameter}
/// Use the [FilterQuality.low] quality setting to scale the image,
/// which corresponds to bilinear interpolation, rather than the default
/// [FilterQuality.none] which corresponds to nearest-neighbor.
/// ///
/// If [excludeFromSemantics] is true, then [semanticLabel] will be ignored. /// If [excludeFromSemantics] is true, then [semanticLabel] will be ignored.
/// ///
...@@ -930,11 +917,22 @@ class Image extends StatefulWidget { ...@@ -930,11 +917,22 @@ class Image extends StatefulWidget {
/// If non-null, this color is blended with each image pixel using [colorBlendMode]. /// If non-null, this color is blended with each image pixel using [colorBlendMode].
final Color? color; final Color? color;
/// Used to set the [FilterQuality] of the image. /// The rendering quality of the image.
///
/// If the image is of a high quality and its pixels are perfectly aligned
/// with the physical screen pixels, extra quality enhancement may not be
/// necessary. If so, then [FilterQuality.none] would be the most efficient.
///
/// If the pixels are not perfectly aligned with the screen pixels, or if the
/// image itself is of a low quality, [FilterQuality.none] may produce
/// undesirable artifacts. Consider using other [FilterQuality] values to
/// improve the rendered image quality in this case. Pixels may be misaligned
/// with the screen pixels as a result of transforms or scaling.
///
/// See also:
/// ///
/// Use the [FilterQuality.low] quality setting to scale the image with /// * [FilterQuality], the enum containing all possible filter quality
/// bilinear interpolation, or the [FilterQuality.none] which corresponds /// options.
/// to nearest-neighbor.
final FilterQuality filterQuality; final FilterQuality filterQuality;
/// Used to combine [color] with this image. /// Used to combine [color] with this image.
......
...@@ -1877,6 +1877,61 @@ void main() { ...@@ -1877,6 +1877,61 @@ void main() {
expect(tester.widget<Padding>(find.byType(Padding)).child, isA<RawImage>()); expect(tester.widget<Padding>(find.byType(Padding)).child, isA<RawImage>());
expect(find.byKey(errorKey), findsNothing); expect(find.byKey(errorKey), findsNothing);
}); });
testWidgets('Image at default filterQuality', (WidgetTester tester) async {
await testImageQuality(tester, null);
});
testWidgets('Image at high filterQuality', (WidgetTester tester) async {
await testImageQuality(tester, ui.FilterQuality.high);
});
testWidgets('Image at none filterQuality', (WidgetTester tester) async {
await testImageQuality(tester, ui.FilterQuality.none);
});
}
Future<void> testImageQuality(WidgetTester tester, ui.FilterQuality? quality) async {
await tester.binding.setSurfaceSize(const ui.Size(3, 3));
// A 3x3 image encoded as PNG with white background and black pixels on the diagonal:
// ┌──────┐
// │▓▓ │
// │ ▓▓ │
// │ ▓▓│
// └──────┘
// At different levels of quality these pixels are blurred differently.
final Uint8List test3x3Image = Uint8List.fromList(<int>[
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03,
0x08, 0x02, 0x00, 0x00, 0x00, 0xd9, 0x4a, 0x22, 0xe8, 0x00, 0x00, 0x00,
0x1b, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0x64, 0x60, 0x60, 0xf8,
0xff, 0xff, 0x3f, 0x03, 0x9c, 0xfa, 0xff, 0xff, 0x3f, 0xc3, 0xff, 0xff,
0xff, 0x21, 0x1c, 0x00, 0xcb, 0x70, 0x0e, 0xf3, 0x5d, 0x11, 0xc2, 0xf8,
0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
]);
final ui.Image image = (await tester.runAsync(() async {
final ui.Codec codec = await ui.instantiateImageCodec(test3x3Image);
return (await codec.getNextFrame()).image;
}))!;
expect(image.width, 3);
expect(image.height, 3);
final TestImageStreamCompleter streamCompleter = TestImageStreamCompleter();
streamCompleter.setData(imageInfo: ImageInfo(image: image));
final TestImageProvider imageProvider = TestImageProvider(streamCompleter: streamCompleter);
await tester.pumpWidget(
quality == null
? Image(image: imageProvider)
: Image(
image: imageProvider,
filterQuality: quality,
),
);
await expectLater(
find.byType(Image),
matchesGoldenFile('image_quality_${quality ?? 'default'}.png'),
);
} }
class ImagePainter extends CustomPainter { class ImagePainter extends CustomPainter {
......
...@@ -169,6 +169,9 @@ class AnimationSheetBuilder { ...@@ -169,6 +169,9 @@ class AnimationSheetBuilder {
image: image.clone(), image: image.clone(),
width: frameSize.width, width: frameSize.width,
height: frameSize.height, height: frameSize.height,
// Disable quality enhancement because the point of this class is to
// precisely record what the widget looks like.
filterQuality: ui.FilterQuality.none,
)).toList(), )).toList(),
); );
} }
......
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