Thanks to visit codestin.com
Credit goes to github.com

Skip to content

remove single-view assumption from paintImage #117495

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions dev/tracing_tests/test/image_painting_event_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ void main() {
rect: const Rect.fromLTWH(50.0, 75.0, 200.0, 100.0),
image: image,
debugImageLabel: 'test.png',
devicePixelRatio: 3.0,
);

// Make sure that we don't report an identical image size info if we
Expand All @@ -65,6 +66,7 @@ void main() {
rect: const Rect.fromLTWH(50.0, 75.0, 200.0, 100.0),
image: image,
debugImageLabel: 'test.png',
devicePixelRatio: 3.0,
);
await binding.endOfFrame;

Expand All @@ -90,13 +92,15 @@ void main() {
rect: const Rect.fromLTWH(50.0, 75.0, 200.0, 100.0),
image: image,
debugImageLabel: 'test.png',
devicePixelRatio: 3.0,
);

paintImage(
canvas: canvas,
rect: const Rect.fromLTWH(50.0, 75.0, 300.0, 300.0),
image: image,
debugImageLabel: 'test.png',
devicePixelRatio: 3.0,
);
await binding.endOfFrame;

Expand Down
13 changes: 8 additions & 5 deletions packages/flutter/lib/src/painting/decoration_image.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import 'package:flutter/scheduler.dart';

import 'alignment.dart';
import 'basic_types.dart';
import 'binding.dart';
import 'borders.dart';
import 'box_fit.dart';
import 'debug.dart';
Expand Down Expand Up @@ -337,6 +336,7 @@ class DecorationImagePainter {
canvas: canvas,
rect: rect,
image: _image!.image,
devicePixelRatio: configuration.devicePixelRatio,
debugImageLabel: _image!.debugLabel,
scale: _details.scale * _image!.scale,
colorFilter: _details.colorFilter,
Expand Down Expand Up @@ -424,6 +424,10 @@ void debugFlushLastFrameImageSizeInfo() {
///
/// * `image`: The image to paint onto the canvas.
///
/// * `devicePixelRatio`: The device pixel ratio of the [FlutterView] into
/// which the image will be drawn. Can be obtained via
/// `View.of(context).devicePixelRation`.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note to self: mention MediaQuery.devicePixelRatioOf(context) as well

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// `View.of(context).devicePixelRation`.
/// `View.of(context).devicePixelRatio`.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this get defaulted somehow?

For example, just default it to the first view in the collection of views?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we default this, the assert check in paintImage becomes more or less meaningless, no? It needs to know the exact dpr in order to figure out whether the current image dimensions are overkill or not.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could just use the max device pixel ratio of all views as an upper bound, though. That will greatly simplify things.

///
/// * `scale`: The number of image pixels for each logical pixel.
///
/// * `opacity`: The opacity to paint the image onto the canvas with.
Expand Down Expand Up @@ -486,6 +490,7 @@ void paintImage({
required Canvas canvas,
required Rect rect,
required ui.Image image,
double? devicePixelRatio,
String? debugImageLabel,
double scale = 1.0,
double opacity = 1.0,
Expand Down Expand Up @@ -557,14 +562,12 @@ void paintImage({
// Set to true if we added a saveLayer to the canvas to invert/flip the image.
bool invertedCanvas = false;
// Output size and destination rect are fully calculated.
if (!kReleaseMode) {
if (!kReleaseMode && devicePixelRatio != null) {
final ImageSizeInfo sizeInfo = ImageSizeInfo(
// Some ImageProvider implementations may not have given this.
source: debugImageLabel ?? '<Unknown Image(${image.width}×${image.height})>',
imageSize: Size(image.width.toDouble(), image.height.toDouble()),
// 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,
displaySize: outputSize * devicePixelRatio,
);
assert(() {
if (debugInvertOversizedImages &&
Expand Down
17 changes: 16 additions & 1 deletion packages/flutter/lib/src/rendering/image.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class RenderImage extends RenderBox {
bool invertColors = false,
bool isAntiAlias = false,
FilterQuality filterQuality = FilterQuality.low,
double? devicePixelRatio,
}) : assert(scale != null),
assert(repeat != null),
assert(alignment != null),
Expand All @@ -65,7 +66,8 @@ class RenderImage extends RenderBox {
_invertColors = invertColors,
_textDirection = textDirection,
_isAntiAlias = isAntiAlias,
_filterQuality = filterQuality {
_filterQuality = filterQuality,
_devicePixelRatio = devicePixelRatio {
_updateColorFilter();
}

Expand Down Expand Up @@ -208,6 +210,18 @@ class RenderImage extends RenderBox {
markNeedsPaint();
}

/// The device pixel ratio of the [FlutterView] into which the image is
/// drawn.
double? get devicePixelRatio => _devicePixelRatio;
double? _devicePixelRatio;
set devicePixelRatio(double? value) {
assert(value != null);
if (value == _devicePixelRatio) {
return;
}
_devicePixelRatio = value;
markNeedsPaint();
}

/// Used to combine [color] with this image.
///
Expand Down Expand Up @@ -447,6 +461,7 @@ class RenderImage extends RenderBox {
canvas: context.canvas,
rect: offset & size,
image: _image!,
devicePixelRatio: devicePixelRatio,
debugImageLabel: debugImageLabel,
scale: _scale,
opacity: _opacity?.value ?? 1.0,
Expand Down
5 changes: 4 additions & 1 deletion packages/flutter/lib/src/widgets/basic.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import 'binding.dart';
import 'debug.dart';
import 'framework.dart';
import 'localizations.dart';
import 'view.dart';
import 'widget_span.dart';

export 'package:flutter/animation.dart';
Expand Down Expand Up @@ -5980,6 +5981,7 @@ class RawImage extends LeafRenderObjectWidget {
invertColors: invertColors,
isAntiAlias: isAntiAlias,
filterQuality: filterQuality,
devicePixelRatio: View.maybeOf(context)?.devicePixelRatio,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note to self: here and below use MediaQuery.maybeDevicePixelRatioOf(context) instead.

);
}

Expand Down Expand Up @@ -6007,7 +6009,8 @@ class RawImage extends LeafRenderObjectWidget {
..textDirection = matchTextDirection || alignment is! Alignment ? Directionality.of(context) : null
..invertColors = invertColors
..isAntiAlias = isAntiAlias
..filterQuality = filterQuality;
..filterQuality = filterQuality
..devicePixelRatio = View.maybeOf(context)?.devicePixelRatio;
}

@override
Expand Down
22 changes: 14 additions & 8 deletions packages/flutter/test/painting/decoration_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ void main() {
});

final TestCanvas canvas = TestCanvas();
const ImageConfiguration imageConfiguration = ImageConfiguration(size: Size.zero);
const ImageConfiguration imageConfiguration = ImageConfiguration(size: Size.zero, devicePixelRatio: 1.2);
boxPainter.paint(canvas, Offset.zero, imageConfiguration);

// The onChanged callback should not be invoked during the call to boxPainter.paint
Expand All @@ -189,7 +189,7 @@ void main() {
});

final TestCanvas canvas = TestCanvas();
const ImageConfiguration imageConfiguration = ImageConfiguration(size: Size.zero);
const ImageConfiguration imageConfiguration = ImageConfiguration(size: Size.zero, devicePixelRatio: 1.2);
boxPainter.paint(canvas, Offset.zero, imageConfiguration);

// The onChanged callback should be invoked asynchronously.
Expand All @@ -213,7 +213,7 @@ void main() {
});

final TestCanvas canvas = TestCanvas();
const ImageConfiguration imageConfiguration = ImageConfiguration(size: Size.zero);
const ImageConfiguration imageConfiguration = ImageConfiguration(size: Size.zero, devicePixelRatio: 1.2);
boxPainter.paint(canvas, Offset.zero, imageConfiguration);

// The onChanged callback should be invoked asynchronously.
Expand Down Expand Up @@ -310,7 +310,7 @@ void main() {
final BoxDecoration boxDecoration = BoxDecoration(image: backgroundImage);
final BoxPainter boxPainter = boxDecoration.createBoxPainter(() { assert(false); });
final TestCanvas canvas = TestCanvas();
boxPainter.paint(canvas, Offset.zero, const ImageConfiguration(size: Size(100.0, 100.0)));
boxPainter.paint(canvas, Offset.zero, const ImageConfiguration(size: Size(100.0, 100.0), devicePixelRatio: 1.2));

final Invocation call = canvas.invocations.singleWhere((Invocation call) => call.memberName == #drawImageNine);
expect(call.isMethod, isTrue);
Expand Down Expand Up @@ -352,6 +352,7 @@ void main() {
try {
boxPainter.paint(canvas, Offset.zero, const ImageConfiguration(
size: Size(100.0, 100.0),
devicePixelRatio: 1.2,
));
} on FlutterError catch (e) {
error = e;
Expand All @@ -374,7 +375,8 @@ void main() {
' match text direction, scale 0.5, opacity 0.5,\n'
' FilterQuality.low, invert colors, use anti-aliasing)\n'
' The ImageConfiguration was:\n'
' ImageConfiguration(size: Size(100.0, 100.0))\n',
' ImageConfiguration(devicePixelRatio: 1.2, size: Size(100.0,\n'
' 100.0))\n'
);
}, skip: kIsWeb); // https://github.com/flutter/flutter/issues/87364

Expand All @@ -391,7 +393,7 @@ void main() {
TestCanvas(),
Rect.largest,
Path(),
ImageConfiguration.empty,
const ImageConfiguration(devicePixelRatio: 1.2),
);
// Yield so that the exception callback gets called before we check it.
await null;
Expand Down Expand Up @@ -524,6 +526,7 @@ void main() {
scale: scale,
alignment: Alignment.bottomRight,
fit: BoxFit.none,
devicePixelRatio: 1.2,
);

const Size imageSize = Size(100.0, 100.0);
Expand Down Expand Up @@ -566,6 +569,7 @@ void main() {
scale: scale,
alignment: Alignment.bottomRight,
fit: BoxFit.scaleDown,
devicePixelRatio: 1.2,
);

const Size imageSize = Size(100.0, 100.0);
Expand Down Expand Up @@ -607,6 +611,7 @@ void main() {
scale: 2.0,
alignment: Alignment.bottomRight,
fit: BoxFit.scaleDown,
devicePixelRatio: 1.2,
);

const Size imageSize = Size(100.0, 100.0);
Expand Down Expand Up @@ -656,6 +661,7 @@ void main() {
image: image,
scale: 3.0,
fit: boxFit,
devicePixelRatio: 1.2,
);

final Invocation call = canvas.invocations.firstWhere((Invocation call) => call.memberName == #drawImageRect);
Expand All @@ -680,7 +686,7 @@ void main() {
final BoxDecoration boxDecoration = BoxDecoration(image: backgroundImage);
final BoxPainter boxPainter = boxDecoration.createBoxPainter(() { assert(false); });
final TestCanvas canvas = TestCanvas();
boxPainter.paint(canvas, Offset.zero, const ImageConfiguration(size: Size(100.0, 100.0)));
boxPainter.paint(canvas, Offset.zero, const ImageConfiguration(size: Size(100.0, 100.0), devicePixelRatio: 1.2));

final Invocation call = canvas.invocations.firstWhere((Invocation call) => call.memberName == #drawImageRect);
// The image should scale down to Size(25.0, 25.0) from Size(100.0, 100.0)
Expand All @@ -707,7 +713,7 @@ void main() {

final DecorationImagePainter painter = DecorationImage(image: provider).createPainter(() {});
final Canvas canvas = TestCanvas();
painter.paint(canvas, Rect.zero, Path(), ImageConfiguration.empty);
painter.paint(canvas, Rect.zero, Path(), const ImageConfiguration(devicePixelRatio: 1.2));

expect(info.image.debugGetOpenHandleStackTraces()!.length, baselineRefCount + 1);
painter.dispose();
Expand Down
18 changes: 13 additions & 5 deletions packages/flutter/test/painting/paint_image_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ void main() {
image: image300x300,
fit: BoxFit.cover,
alignment: Alignment.centerLeft,
devicePixelRatio: 1.2,
);

final Invocation command = canvas.invocations.firstWhere((Invocation invocation) {
Expand All @@ -52,7 +53,6 @@ void main() {

test('debugInvertOversizedImages', () async {
debugInvertOversizedImages = true;
expect(PaintingBinding.instance.window.devicePixelRatio != 1.0, true);
final FlutterExceptionHandler? oldFlutterError = FlutterError.onError;

final List<String> messages = <String>[];
Expand All @@ -69,6 +69,7 @@ void main() {
image: image300x300,
debugImageLabel: 'TestImage',
fit: BoxFit.fill,
devicePixelRatio: 3.0,
);

final List<Invocation> commands = canvas.invocations
Expand Down Expand Up @@ -131,6 +132,7 @@ void main() {
image: image300x300,
debugImageLabel: 'TestImage',
fit: BoxFit.fill,
devicePixelRatio: 1.2,
);

expect(messages, isEmpty);
Expand All @@ -148,6 +150,7 @@ void main() {
image: image300x300,
scale: 2.0,
centerSlice: const Rect.fromLTRB(50, 40, 250, 260),
devicePixelRatio: 1.2,
);

final Invocation command = canvas.invocations.firstWhere((Invocation invocation) {
Expand All @@ -174,13 +177,14 @@ void main() {
rect: const Rect.fromLTWH(50.0, 75.0, 200.0, 100.0),
image: image300x300,
debugImageLabel: 'test.png',
devicePixelRatio: 1.2,
);

expect(count, 1);
expect(imageSizeInfo, isNotNull);
expect(imageSizeInfo.source, 'test.png');
expect(imageSizeInfo.imageSize, const Size(300, 300));
expect(imageSizeInfo.displaySize, const Size(200, 100) * PaintingBinding.instance.window.devicePixelRatio);
expect(imageSizeInfo.displaySize, const Size(200, 100) * 1.2);

// Make sure that we don't report an identical image size info if we
// redraw in the next frame.
Expand All @@ -192,6 +196,7 @@ void main() {
rect: const Rect.fromLTWH(50.0, 75.0, 200.0, 100.0),
image: image300x300,
debugImageLabel: 'test.png',
devicePixelRatio: 1.2,
);

expect(count, 1);
Expand All @@ -213,13 +218,14 @@ void main() {
rect: const Rect.fromLTWH(50.0, 75.0, 200.0, 100.0),
image: image300x300,
debugImageLabel: 'test.png',
devicePixelRatio: 1.2,
);

expect(count, 1);
expect(imageSizeInfo, isNotNull);
expect(imageSizeInfo.source, 'test.png');
expect(imageSizeInfo.imageSize, const Size(300, 300));
expect(imageSizeInfo.displaySize, const Size(200, 100) * PaintingBinding.instance.window.devicePixelRatio);
expect(imageSizeInfo.displaySize, const Size(200, 100) * 1.2);

// Make sure that we don't report an identical image size info if we
// redraw in the next frame.
Expand All @@ -231,13 +237,14 @@ void main() {
rect: const Rect.fromLTWH(50.0, 75.0, 200.0, 150.0),
image: image300x300,
debugImageLabel: 'test.png',
devicePixelRatio: 1.2,
);

expect(count, 2);
expect(imageSizeInfo, isNotNull);
expect(imageSizeInfo.source, 'test.png');
expect(imageSizeInfo.imageSize, const Size(300, 300));
expect(imageSizeInfo.displaySize, const Size(200, 150) * PaintingBinding.instance.window.devicePixelRatio);
expect(imageSizeInfo.displaySize, const Size(200, 150) * 1.2);

debugOnPaintImage = null;
});
Expand All @@ -255,13 +262,14 @@ void main() {
canvas: canvas,
rect: const Rect.fromLTWH(50.0, 75.0, 200.0, 100.0),
image: image300x200,
devicePixelRatio: 1.2,
);

expect(count, 1);
expect(imageSizeInfo, isNotNull);
expect(imageSizeInfo.source, '<Unknown Image(300×200)>');
expect(imageSizeInfo.imageSize, const Size(300, 200));
expect(imageSizeInfo.displaySize, const Size(200, 100) * PaintingBinding.instance.window.devicePixelRatio);
expect(imageSizeInfo.displaySize, const Size(200, 100) * 1.2);

debugOnPaintImage = null;
});
Expand Down
Loading