Unverified Commit c91a2982 authored by Dan Field's avatar Dan Field Committed by GitHub

Take DPR into account for image inversion (#88309)

parent a562b3cb
...@@ -4,4 +4,4 @@ The tests in this folder must be run with `flutter test --enable-vmservice`, ...@@ -4,4 +4,4 @@ The tests in this folder must be run with `flutter test --enable-vmservice`,
since they test that trace data is written to the timeline by connecting to since they test that trace data is written to the timeline by connecting to
the observatory. the observatory.
These tests will fail if run without this flag. These tests will fail if run without this flag.
\ No newline at end of file
...@@ -72,7 +72,7 @@ void main() { ...@@ -72,7 +72,7 @@ void main() {
expect(event.extensionKind, 'Flutter.ImageSizesForFrame'); expect(event.extensionKind, 'Flutter.ImageSizesForFrame');
expect( expect(
jsonEncode(event.extensionData!.data), jsonEncode(event.extensionData!.data),
'{"test.png":{"source":"test.png","displaySize":{"width":200.0,"height":100.0},"imageSize":{"width":300.0,"height":300.0},"displaySizeInBytes":106666,"decodedSizeInBytes":480000}}', '{"test.png":{"source":"test.png","displaySize":{"width":600.0,"height":300.0},"imageSize":{"width":300.0,"height":300.0},"displaySizeInBytes":960000,"decodedSizeInBytes":480000}}',
); );
}, skip: isBrowser); // [intended] uses dart:isolate and io. }, skip: isBrowser); // [intended] uses dart:isolate and io.
...@@ -104,7 +104,7 @@ void main() { ...@@ -104,7 +104,7 @@ void main() {
expect(event.extensionKind, 'Flutter.ImageSizesForFrame'); expect(event.extensionKind, 'Flutter.ImageSizesForFrame');
expect( expect(
jsonEncode(event.extensionData!.data), jsonEncode(event.extensionData!.data),
'{"test.png":{"source":"test.png","displaySize":{"width":300.0,"height":300.0},"imageSize":{"width":300.0,"height":300.0},"displaySizeInBytes":480000,"decodedSizeInBytes":480000}}', '{"test.png":{"source":"test.png","displaySize":{"width":900.0,"height":900.0},"imageSize":{"width":300.0,"height":300.0},"displaySizeInBytes":4320000,"decodedSizeInBytes":480000}}',
); );
}, skip: isBrowser); // [intended] uses dart:isolate and io. }, skip: isBrowser); // [intended] uses dart:isolate and io.
} }
......
...@@ -54,20 +54,20 @@ class ImageSizeInfo { ...@@ -54,20 +54,20 @@ class ImageSizeInfo {
/// This class is used by the framework when it paints an image to a canvas /// This class is used by the framework when it paints an image to a canvas
/// to report to `dart:developer`'s [postEvent], as well as to the /// to report to `dart:developer`'s [postEvent], as well as to the
/// [debugOnPaintImage] callback if it is set. /// [debugOnPaintImage] callback if it is set.
const ImageSizeInfo({this.source, this.displaySize, required this.imageSize}); const ImageSizeInfo({this.source, required this.displaySize, required this.imageSize});
/// A unique identifier for this image, for example its asset path or network /// A unique identifier for this image, for example its asset path or network
/// URL. /// URL.
final String? source; final String? source;
/// The size of the area the image will be rendered in. /// The size of the area the image will be rendered in.
final Size? displaySize; final Size displaySize;
/// The size the image has been decoded to. /// The size the image has been decoded to.
final Size imageSize; final Size imageSize;
/// The number of bytes needed to render the image without scaling it. /// The number of bytes needed to render the image without scaling it.
int get displaySizeInBytes => _sizeToBytes(displaySize!); int get displaySizeInBytes => _sizeToBytes(displaySize);
/// The number of bytes used by the image in memory. /// The number of bytes used by the image in memory.
int get decodedSizeInBytes => _sizeToBytes(imageSize); int get decodedSizeInBytes => _sizeToBytes(imageSize);
...@@ -82,11 +82,10 @@ class ImageSizeInfo { ...@@ -82,11 +82,10 @@ class ImageSizeInfo {
Map<String, Object?> toJson() { Map<String, Object?> toJson() {
return <String, Object?>{ return <String, Object?>{
'source': source, 'source': source,
if (displaySize != null) 'displaySize': <String, Object?>{
'displaySize': <String, Object?>{ 'width': displaySize.width,
'width': displaySize!.width, 'height': displaySize.height,
'height': displaySize!.height, },
},
'imageSize': <String, Object?>{ 'imageSize': <String, Object?>{
'width': imageSize.width, 'width': imageSize.width,
'height': imageSize.height, 'height': imageSize.height,
......
...@@ -11,6 +11,7 @@ import 'package:flutter/scheduler.dart'; ...@@ -11,6 +11,7 @@ import 'package:flutter/scheduler.dart';
import 'alignment.dart'; import 'alignment.dart';
import 'basic_types.dart'; import 'basic_types.dart';
import 'binding.dart';
import 'borders.dart'; import 'borders.dart';
import 'box_fit.dart'; import 'box_fit.dart';
import 'debug.dart'; import 'debug.dart';
...@@ -494,14 +495,16 @@ void paintImage({ ...@@ -494,14 +495,16 @@ void paintImage({
// Some ImageProvider implementations may not have given this. // Some ImageProvider implementations may not have given this.
source: debugImageLabel ?? '<Unknown Image(${image.width}×${image.height})>', source: debugImageLabel ?? '<Unknown Image(${image.width}×${image.height})>',
imageSize: Size(image.width.toDouble(), image.height.toDouble()), imageSize: Size(image.width.toDouble(), image.height.toDouble()),
displaySize: outputSize, // It's ok to use this instead of a MediaQuery because if this changes,
// whatever is aware of the MediaQuery will be repainting the image anyway.
displaySize: outputSize * PaintingBinding.instance!.window.devicePixelRatio,
); );
assert(() { assert(() {
if (debugInvertOversizedImages && if (debugInvertOversizedImages &&
sizeInfo.decodedSizeInBytes > sizeInfo.displaySizeInBytes + debugImageOverheadAllowance) { sizeInfo.decodedSizeInBytes > sizeInfo.displaySizeInBytes + debugImageOverheadAllowance) {
final int overheadInKilobytes = (sizeInfo.decodedSizeInBytes - sizeInfo.displaySizeInBytes) ~/ 1024; final int overheadInKilobytes = (sizeInfo.decodedSizeInBytes - sizeInfo.displaySizeInBytes) ~/ 1024;
final int outputWidth = outputSize.width.toInt(); final int outputWidth = sizeInfo.displaySize.width.toInt();
final int outputHeight = outputSize.height.toInt(); final int outputHeight = sizeInfo.displaySize.height.toInt();
FlutterError.reportError(FlutterErrorDetails( FlutterError.reportError(FlutterErrorDetails(
exception: 'Image $debugImageLabel has a display size of ' exception: 'Image $debugImageLabel has a display size of '
'$outputWidth×$outputHeight but a decode size of ' '$outputWidth×$outputHeight but a decode size of '
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/painting.dart'; import 'package:flutter/painting.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
...@@ -52,6 +53,7 @@ void main() { ...@@ -52,6 +53,7 @@ void main() {
test('debugInvertOversizedImages', () async { test('debugInvertOversizedImages', () async {
debugInvertOversizedImages = true; debugInvertOversizedImages = true;
expect(PaintingBinding.instance!.window.devicePixelRatio != 1.0, true);
final FlutterExceptionHandler? oldFlutterError = FlutterError.onError; final FlutterExceptionHandler? oldFlutterError = FlutterError.onError;
final List<String> messages = <String>[]; final List<String> messages = <String>[];
...@@ -60,7 +62,7 @@ void main() { ...@@ -60,7 +62,7 @@ void main() {
}; };
final TestCanvas canvas = TestCanvas(); final TestCanvas canvas = TestCanvas();
const Rect rect = Rect.fromLTWH(50.0, 50.0, 200.0, 100.0); const Rect rect = Rect.fromLTWH(50.0, 50.0, 100.0, 50.0);
paintImage( paintImage(
canvas: canvas, canvas: canvas,
...@@ -88,7 +90,7 @@ void main() { ...@@ -88,7 +90,7 @@ void main() {
); );
expect(commands[1].memberName, #translate); expect(commands[1].memberName, #translate);
expect(commands[1].positionalArguments[0], 0.0); expect(commands[1].positionalArguments[0], 0.0);
expect(commands[1].positionalArguments[1], 100.0); expect(commands[1].positionalArguments[1], 75.0);
expect(commands[2].memberName, #scale); expect(commands[2].memberName, #scale);
expect(commands[2].positionalArguments[0], 1.0); expect(commands[2].positionalArguments[0], 1.0);
...@@ -97,12 +99,12 @@ void main() { ...@@ -97,12 +99,12 @@ void main() {
expect(commands[3].memberName, #translate); expect(commands[3].memberName, #translate);
expect(commands[3].positionalArguments[0], 0.0); expect(commands[3].positionalArguments[0], 0.0);
expect(commands[3].positionalArguments[1], -100.0); expect(commands[3].positionalArguments[1], -75.0);
expect( expect(
messages.single, messages.single,
'Image TestImage has a display size of 200×100 but a decode size of 300×300, which uses an additional 364KB.\n\n' 'Image TestImage has a display size of 300×150 but a decode size of 300×300, which uses an additional 234KB.\n\n'
'Consider resizing the asset ahead of time, supplying a cacheWidth parameter of 200, a cacheHeight parameter of 100, or using a ResizeImage.', 'Consider resizing the asset ahead of time, supplying a cacheWidth parameter of 300, a cacheHeight parameter of 150, or using a ResizeImage.',
); );
debugInvertOversizedImages = false; debugInvertOversizedImages = false;
...@@ -179,7 +181,7 @@ void main() { ...@@ -179,7 +181,7 @@ void main() {
expect(imageSizeInfo, isNotNull); expect(imageSizeInfo, isNotNull);
expect(imageSizeInfo.source, 'test.png'); expect(imageSizeInfo.source, 'test.png');
expect(imageSizeInfo.imageSize, const Size(300, 300)); expect(imageSizeInfo.imageSize, const Size(300, 300));
expect(imageSizeInfo.displaySize, const Size(200, 100)); expect(imageSizeInfo.displaySize, const Size(200, 100) * PaintingBinding.instance!.window.devicePixelRatio);
// Make sure that we don't report an identical image size info if we // Make sure that we don't report an identical image size info if we
// redraw in the next frame. // redraw in the next frame.
...@@ -218,7 +220,7 @@ void main() { ...@@ -218,7 +220,7 @@ void main() {
expect(imageSizeInfo, isNotNull); expect(imageSizeInfo, isNotNull);
expect(imageSizeInfo.source, 'test.png'); expect(imageSizeInfo.source, 'test.png');
expect(imageSizeInfo.imageSize, const Size(300, 300)); expect(imageSizeInfo.imageSize, const Size(300, 300));
expect(imageSizeInfo.displaySize, const Size(200, 100)); expect(imageSizeInfo.displaySize, const Size(200, 100) * PaintingBinding.instance!.window.devicePixelRatio);
// Make sure that we don't report an identical image size info if we // Make sure that we don't report an identical image size info if we
// redraw in the next frame. // redraw in the next frame.
...@@ -236,7 +238,7 @@ void main() { ...@@ -236,7 +238,7 @@ void main() {
expect(imageSizeInfo, isNotNull); expect(imageSizeInfo, isNotNull);
expect(imageSizeInfo.source, 'test.png'); expect(imageSizeInfo.source, 'test.png');
expect(imageSizeInfo.imageSize, const Size(300, 300)); expect(imageSizeInfo.imageSize, const Size(300, 300));
expect(imageSizeInfo.displaySize, const Size(200, 150)); expect(imageSizeInfo.displaySize, const Size(200, 150) * PaintingBinding.instance!.window.devicePixelRatio);
debugOnPaintImage = null; debugOnPaintImage = null;
}); });
...@@ -260,7 +262,7 @@ void main() { ...@@ -260,7 +262,7 @@ void main() {
expect(imageSizeInfo, isNotNull); expect(imageSizeInfo, isNotNull);
expect(imageSizeInfo.source, '<Unknown Image(300×200)>'); expect(imageSizeInfo.source, '<Unknown Image(300×200)>');
expect(imageSizeInfo.imageSize, const Size(300, 200)); expect(imageSizeInfo.imageSize, const Size(300, 200));
expect(imageSizeInfo.displaySize, const Size(200, 100)); expect(imageSizeInfo.displaySize, const Size(200, 100) * PaintingBinding.instance!.window.devicePixelRatio);
debugOnPaintImage = null; debugOnPaintImage = null;
}); });
......
...@@ -1819,7 +1819,7 @@ void main() { ...@@ -1819,7 +1819,7 @@ void main() {
const ImageSizeInfo( const ImageSizeInfo(
source: 'test.png', source: 'test.png',
imageSize: Size(100, 100), imageSize: Size(100, 100),
displaySize: Size(50, 50), displaySize: Size(150, 150),
), ),
); );
......
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