[framework] use shader tiling instead of repeated calls to drawImage (#119495)
* [framework] use shader tiling instead of repeated calls to drawImage
* ++
* ++
* review and test updates
* ++
* Update decoration_image.dart
* Update decoration_image.dart
* ++
* ++
* ++
* ++
* ++
* Update painting.dart
* Update decoration_test.dart
diff --git a/packages/flutter/lib/src/painting/decoration_image.dart b/packages/flutter/lib/src/painting/decoration_image.dart
index 92cdecc..2b11996 100644
--- a/packages/flutter/lib/src/painting/decoration_image.dart
+++ b/packages/flutter/lib/src/painting/decoration_image.dart
@@ -8,6 +8,7 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/scheduler.dart';
+import 'package:vector_math/vector_math_64.dart';
import 'alignment.dart';
import 'basic_types.dart';
@@ -404,6 +405,61 @@
}());
}
+/// Information that describes how to tile an image for a given [ImageRepeat]
+/// enum.
+///
+/// Used with [createTilingInfo].
+@visibleForTesting
+@immutable
+class ImageTilingInfo {
+ /// Create a new [ImageTilingInfo] object.
+ const ImageTilingInfo({
+ required this.tmx,
+ required this.tmy,
+ required this.transform,
+ });
+
+ /// The tile mode for the x-axis.
+ final TileMode tmx;
+
+ /// The tile mode for the y-axis.
+ final TileMode tmy;
+
+ /// The transform to apply to the image shader.
+ final Matrix4 transform;
+
+ @override
+ String toString() {
+ if (!kDebugMode) {
+ return 'ImageTilingInfo';
+ }
+ return 'ImageTilingInfo($tmx, $tmy, $transform)';
+ }
+}
+
+/// Create the [ImageTilingInfo] for a given [ImageRepeat], canvas [rect],
+/// [destinationRect], and [sourceRect].
+@visibleForTesting
+ImageTilingInfo createTilingInfo(ImageRepeat repeat, Rect rect, Rect destinationRect, Rect sourceRect) {
+ assert(repeat != ImageRepeat.noRepeat);
+ final TileMode tmx = (repeat == ImageRepeat.repeatX || repeat == ImageRepeat.repeat)
+ ? TileMode.repeated
+ : TileMode.decal;
+ final TileMode tmy = (repeat == ImageRepeat.repeatY || repeat == ImageRepeat.repeat)
+ ? TileMode.repeated
+ : TileMode.decal;
+ final Rect data = _generateImageTileRects(rect, destinationRect, repeat).first;
+ final Matrix4 transform = Matrix4.identity()
+ ..scale(data.width / sourceRect.width, data.height / sourceRect.height)
+ ..setTranslationRaw(data.topLeft.dx, data.topLeft.dy, 0);
+
+ return ImageTilingInfo(
+ tmx: tmx,
+ tmy: tmy,
+ transform: transform,
+ );
+}
+
/// Paints an image into the given rectangle on the canvas.
///
/// The arguments have the following meanings:
@@ -626,7 +682,8 @@
if (needSave) {
canvas.save();
}
- if (repeat != ImageRepeat.noRepeat) {
+ if (repeat != ImageRepeat.noRepeat && centerSlice != null) {
+ // Don't clip if an image shader is used.
canvas.clipRect(rect);
}
if (flipHorizontally) {
@@ -642,9 +699,12 @@
if (repeat == ImageRepeat.noRepeat) {
canvas.drawImageRect(image, sourceRect, destinationRect, paint);
} else {
- for (final Rect tileRect in _generateImageTileRects(rect, destinationRect, repeat)) {
- canvas.drawImageRect(image, sourceRect, tileRect, paint);
- }
+ final ImageTilingInfo info = createTilingInfo(repeat, rect, destinationRect, sourceRect);
+ final ImageShader shader = ImageShader(image, info.tmx, info.tmy, info.transform.storage, filterQuality: filterQuality);
+ canvas.drawRect(
+ rect,
+ paint..shader = shader
+ );
}
} else {
canvas.scale(1 / scale);
@@ -665,7 +725,7 @@
}
}
-Iterable<Rect> _generateImageTileRects(Rect outputRect, Rect fundamentalRect, ImageRepeat repeat) {
+List<Rect> _generateImageTileRects(Rect outputRect, Rect fundamentalRect, ImageRepeat repeat) {
int startX = 0;
int startY = 0;
int stopX = 0;
diff --git a/packages/flutter/test/painting/decoration_test.dart b/packages/flutter/test/painting/decoration_test.dart
index 65026f6..de257e1 100644
--- a/packages/flutter/test/painting/decoration_test.dart
+++ b/packages/flutter/test/painting/decoration_test.dart
@@ -9,6 +9,7 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/painting.dart';
import 'package:flutter_test/flutter_test.dart';
+import 'package:vector_math/vector_math_64.dart';
import '../image_data.dart';
import '../painting/mocks_for_image_cache.dart';
@@ -672,7 +673,8 @@
final TestCanvas canvas = TestCanvas();
// Paint a square image into an output rect that is twice as wide as it is
- // tall. Two copies of the image should be painted, one next to the other.
+ // tall. One copy of the image should be painted, aligned so that a repeating
+ // tile mode causes it to appear twice.
const Rect outputRect = Rect.fromLTWH(30.0, 30.0, 400.0, 200.0);
final ui.Image image = await createTestImage(width: 100, height: 100);
@@ -685,34 +687,29 @@
repeat: ImageRepeat.repeatX,
);
- const Size imageSize = Size(100.0, 100.0);
+ final List<Invocation> calls = canvas.invocations.where((Invocation call) => call.memberName == #drawRect).toList();
- final List<Invocation> calls = canvas.invocations.where((Invocation call) => call.memberName == #drawImageRect).toList();
- final Set<Rect> tileRects = <Rect>{};
+ expect(calls, hasLength(1));
+ final Invocation call = calls[0];
+ expect(call.isMethod, isTrue);
+ expect(call.positionalArguments, hasLength(2));
- expect(calls, hasLength(2));
- for (final Invocation call in calls) {
- expect(call.isMethod, isTrue);
- expect(call.positionalArguments, hasLength(4));
+ // A tiled image is drawn as a rect with a shader.
+ expect(call.positionalArguments[0], isA<Rect>());
+ expect(call.positionalArguments[1], isA<Paint>());
- expect(call.positionalArguments[0], isA<ui.Image>());
+ final Paint paint = call.positionalArguments[1] as Paint;
- // sourceRect should contain all pixels of the source image
- expect(call.positionalArguments[1], Offset.zero & imageSize);
-
- tileRects.add(call.positionalArguments[2] as Rect);
-
- expect(call.positionalArguments[3], isA<Paint>());
- }
-
- expect(tileRects, <Rect>{const Rect.fromLTWH(30.0, 30.0, 200.0, 200.0), const Rect.fromLTWH(230.0, 30.0, 200.0, 200.0)});
+ expect(paint.shader, isA<ImageShader>());
+ expect(call.positionalArguments[0], outputRect);
});
test('paintImage with repeatY and fitWidth', () async {
final TestCanvas canvas = TestCanvas();
// Paint a square image into an output rect that is twice as tall as it is
- // wide. Two copies of the image should be painted, one above the other.
+ // wide. One copy of the image should be painted, aligned so that a repeating
+ // tile mode causes it to appear twice.
const Rect outputRect = Rect.fromLTWH(30.0, 30.0, 200.0, 400.0);
final ui.Image image = await createTestImage(width: 100, height: 100);
@@ -724,28 +721,21 @@
fit: BoxFit.fitWidth,
repeat: ImageRepeat.repeatY,
);
+ final List<Invocation> calls = canvas.invocations.where((Invocation call) => call.memberName == #drawRect).toList();
- const Size imageSize = Size(100.0, 100.0);
+ expect(calls, hasLength(1));
+ final Invocation call = calls[0];
+ expect(call.isMethod, isTrue);
+ expect(call.positionalArguments, hasLength(2));
- final List<Invocation> calls = canvas.invocations.where((Invocation call) => call.memberName == #drawImageRect).toList();
- final Set<Rect> tileRects = <Rect>{};
+ // A tiled image is drawn as a rect with a shader.
+ expect(call.positionalArguments[0], isA<Rect>());
+ expect(call.positionalArguments[1], isA<Paint>());
- expect(calls, hasLength(2));
- for (final Invocation call in calls) {
- expect(call.isMethod, isTrue);
- expect(call.positionalArguments, hasLength(4));
+ final Paint paint = call.positionalArguments[1] as Paint;
- expect(call.positionalArguments[0], isA<ui.Image>());
-
- // sourceRect should contain all pixels of the source image
- expect(call.positionalArguments[1], Offset.zero & imageSize);
-
- tileRects.add(call.positionalArguments[2] as Rect);
-
- expect(call.positionalArguments[3], isA<Paint>());
- }
-
- expect(tileRects, <Rect>{const Rect.fromLTWH(30.0, 30.0, 200.0, 200.0), const Rect.fromLTWH(30.0, 230.0, 200.0, 200.0)});
+ expect(paint.shader, isA<ImageShader>());
+ expect(call.positionalArguments[0], outputRect);
});
test('DecorationImage scale test', () async {
@@ -794,4 +784,52 @@
info.dispose();
}, skip: kIsWeb); // https://github.com/flutter/flutter/issues/87442
+
+ test('Compute image tiling', () {
+ expect(() => createTilingInfo(ImageRepeat.noRepeat, Rect.zero, Rect.zero, Rect.zero), throwsAssertionError);
+
+ // These tests draw a 16x9 image into a 100x50 container with a destination
+ // size of and make assertions based on observed behavior and the original
+ // rectangles from https://github.com/flutter/flutter/pull/119495/
+
+ final ImageTilingInfo repeatX = createTilingInfo(
+ ImageRepeat.repeatX,
+ const Rect.fromLTRB(0.0, 0.0, 100.0, 50.0),
+ const Rect.fromLTRB(84.0, 0.0, 100.0, 9.0),
+ const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0),
+ );
+
+ expect(repeatX.tmx, TileMode.repeated);
+ expect(repeatX.tmy, TileMode.decal);
+ expect(repeatX.transform, matrixMoreOrLessEquals(Matrix4.identity()
+ ..scale(1.0, 1.0)
+ ..setTranslationRaw(-12.0, 0.0, 0.0)
+ ));
+
+ final ImageTilingInfo repeatY = createTilingInfo(
+ ImageRepeat.repeatY,
+ const Rect.fromLTRB(0.0, 0.0, 100.0, 50.0),
+ const Rect.fromLTRB(84.0, 0.0, 100.0, 9.0),
+ const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0),
+ );
+ expect(repeatY.tmx, TileMode.decal);
+ expect(repeatY.tmy, TileMode.repeated);
+ expect(repeatY.transform, matrixMoreOrLessEquals(Matrix4.identity()
+ ..scale(1.0, 1.0)
+ ..setTranslationRaw(84.0, 0.0, 0.0)
+ ));
+
+ final ImageTilingInfo repeat = createTilingInfo(
+ ImageRepeat.repeat,
+ const Rect.fromLTRB(0.0, 0.0, 100.0, 50.0),
+ const Rect.fromLTRB(84.0, 0.0, 100.0, 9.0),
+ const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0),
+ );
+ expect(repeat.tmx, TileMode.repeated);
+ expect(repeat.tmy, TileMode.repeated);
+ expect(repeat.transform, matrixMoreOrLessEquals(Matrix4.identity()
+ ..scale(1.0, 1.0)
+ ..setTranslationRaw(-12.0, 0.0, 0.0)
+ ));
+ });
}
diff --git a/packages/flutter/test/rendering/mock_canvas.dart b/packages/flutter/test/rendering/mock_canvas.dart
index 3ae1388..2f53a8b 100644
--- a/packages/flutter/test/rendering/mock_canvas.dart
+++ b/packages/flutter/test/rendering/mock_canvas.dart
@@ -193,7 +193,7 @@
/// painting has completed, not at the time of the call. If the same [Paint]
/// object is reused multiple times, then this may not match the actual
/// arguments as they were seen by the method.
- void rect({ Rect? rect, Color? color, double? strokeWidth, bool? hasMaskFilter, PaintingStyle? style });
+ void rect({ Rect? rect, Color? color, double? strokeWidth, bool? hasMaskFilter, PaintingStyle? style, Matcher? shader });
/// Indicates that a rounded rectangle clip is expected next.
///
@@ -734,8 +734,8 @@
}
@override
- void rect({ Rect? rect, Color? color, double? strokeWidth, bool? hasMaskFilter, PaintingStyle? style }) {
- _predicates.add(_RectPaintPredicate(rect: rect, color: color, strokeWidth: strokeWidth, hasMaskFilter: hasMaskFilter, style: style));
+ void rect({ Rect? rect, Color? color, double? strokeWidth, bool? hasMaskFilter, PaintingStyle? style, Matcher? shader }) {
+ _predicates.add(_RectPaintPredicate(rect: rect, color: color, strokeWidth: strokeWidth, hasMaskFilter: hasMaskFilter, style: style, shader: shader));
}
@override
@@ -891,6 +891,7 @@
this.strokeWidth,
this.hasMaskFilter,
this.style,
+ this.shader,
});
final Symbol symbol;
@@ -901,6 +902,7 @@
final double? strokeWidth;
final bool? hasMaskFilter;
final PaintingStyle? style;
+ final Matcher? shader;
String get methodName => _symbolName(symbol);
@@ -935,6 +937,9 @@
if (style != null && paintArgument.style != style) {
throw 'It called $methodName with a paint whose style, ${paintArgument.style}, was not exactly the expected style ($style).';
}
+ if (shader != null && !shader!.matches(paintArgument.shader, <dynamic, dynamic>{})) {
+ throw 'It called $methodName with a paint whose shader, ${paintArgument.shader}, was not exactly the expected shader ($shader).';
+ }
}
@override
@@ -975,6 +980,7 @@
required double? strokeWidth,
required bool? hasMaskFilter,
required PaintingStyle? style,
+ Matcher? shader,
}) : super(
symbol,
name,
@@ -984,6 +990,7 @@
strokeWidth: strokeWidth,
hasMaskFilter: hasMaskFilter,
style: style,
+ shader: shader,
);
final T? expected;
@@ -1069,7 +1076,7 @@
}
class _RectPaintPredicate extends _OneParameterPaintPredicate<Rect> {
- _RectPaintPredicate({ Rect? rect, Color? color, double? strokeWidth, bool? hasMaskFilter, PaintingStyle? style }) : super(
+ _RectPaintPredicate({ Rect? rect, Color? color, double? strokeWidth, bool? hasMaskFilter, PaintingStyle? style, Matcher? shader }) : super(
#drawRect,
'a rectangle',
expected: rect,
@@ -1077,6 +1084,7 @@
strokeWidth: strokeWidth,
hasMaskFilter: hasMaskFilter,
style: style,
+ shader: shader,
);
}
diff --git a/packages/flutter/test/widgets/image_rtl_test.dart b/packages/flutter/test/widgets/image_rtl_test.dart
index 6d574d5..3b2572b 100644
--- a/packages/flutter/test/widgets/image_rtl_test.dart
+++ b/packages/flutter/test/widgets/image_rtl_test.dart
@@ -56,17 +56,10 @@
EnginePhase.layout, // so that we don't try to paint the fake images
);
expect(find.byType(Container), paints
- ..clipRect(rect: const Rect.fromLTRB(0.0, 0.0, 100.0, 50.0))
..translate(x: 50.0, y: 0.0)
..scale(x: -1.0, y: 1.0)
..translate(x: -50.0, y: 0.0)
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(-12.0, 0.0, 4.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(4.0, 0.0, 20.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(20.0, 0.0, 36.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(36.0, 0.0, 52.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(52.0, 0.0, 68.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(68.0, 0.0, 84.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(84.0, 0.0, 100.0, 9.0))
+ ..rect(rect: const Rect.fromLTRB(0.0, 0.0, 100.0, 50.0), shader: isA<ImageShader>())
..restore(),
);
expect(find.byType(Container), isNot(paints..scale()..scale()));
@@ -95,14 +88,7 @@
EnginePhase.layout, // so that we don't try to paint the fake images
);
expect(find.byType(Container), paints
- ..clipRect(rect: const Rect.fromLTRB(0.0, 0.0, 100.0, 50.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(-12.0, 0.0, 4.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(4.0, 0.0, 20.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(20.0, 0.0, 36.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(36.0, 0.0, 52.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(52.0, 0.0, 68.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(68.0, 0.0, 84.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(84.0, 0.0, 100.0, 9.0))
+ ..rect(rect: const Rect.fromLTRB(0.0, 0.0, 100.0, 50.0), shader: isA<ImageShader>())
..restore(),
);
expect(find.byType(Container), isNot(paints..scale()));
@@ -130,14 +116,7 @@
EnginePhase.layout, // so that we don't try to paint the fake images
);
expect(find.byType(Container), paints
- ..clipRect(rect: const Rect.fromLTRB(0.0, 0.0, 100.0, 50.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(16.0, 0.0, 32.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(32.0, 0.0, 48.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(48.0, 0.0, 64.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(64.0, 0.0, 80.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(80.0, 0.0, 96.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(96.0, 0.0, 112.0, 9.0))
+ ..rect(rect: const Rect.fromLTRB(0.0, 0.0, 100.0, 50.0), shader: isA<ImageShader>())
..restore(),
);
expect(find.byType(Container), isNot(paints..scale()));
@@ -165,14 +144,7 @@
EnginePhase.layout, // so that we don't try to paint the fake images
);
expect(find.byType(Container), paints
- ..clipRect(rect: const Rect.fromLTRB(0.0, 0.0, 100.0, 50.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(-12.0, 0.0, 4.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(4.0, 0.0, 20.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(20.0, 0.0, 36.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(36.0, 0.0, 52.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(52.0, 0.0, 68.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(68.0, 0.0, 84.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(84.0, 0.0, 100.0, 9.0))
+ ..rect(rect: const Rect.fromLTRB(0.0, 0.0, 100.0, 50.0), shader: isA<ImageShader>())
..restore(),
);
expect(find.byType(Container), isNot(paints..scale()));
@@ -314,17 +286,10 @@
EnginePhase.layout, // so that we don't try to paint the fake images
);
expect(find.byType(SizedBox), paints
- ..clipRect(rect: const Rect.fromLTRB(0.0, 0.0, 100.0, 50.0))
..translate(x: 50.0, y: 0.0)
..scale(x: -1.0, y: 1.0)
..translate(x: -50.0, y: 0.0)
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(-12.0, 0.0, 4.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(4.0, 0.0, 20.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(20.0, 0.0, 36.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(36.0, 0.0, 52.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(52.0, 0.0, 68.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(68.0, 0.0, 84.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(84.0, 0.0, 100.0, 9.0))
+ ..rect(rect: const Rect.fromLTRB(0.0, 0.0, 100.0, 50.0), shader: isA<ImageShader>())
..restore(),
);
expect(find.byType(SizedBox), isNot(paints..scale()..scale()));
@@ -351,14 +316,7 @@
EnginePhase.layout, // so that we don't try to paint the fake images
);
expect(find.byType(SizedBox), paints
- ..clipRect(rect: const Rect.fromLTRB(0.0, 0.0, 100.0, 50.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(-12.0, 0.0, 4.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(4.0, 0.0, 20.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(20.0, 0.0, 36.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(36.0, 0.0, 52.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(52.0, 0.0, 68.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(68.0, 0.0, 84.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(84.0, 0.0, 100.0, 9.0))
+ ..rect(rect: const Rect.fromLTRB(0.0, 0.0, 100.0, 50.0), shader: isA<ImageShader>())
..restore(),
);
expect(find.byType(SizedBox), isNot(paints..scale()));
@@ -384,14 +342,7 @@
EnginePhase.layout, // so that we don't try to paint the fake images
);
expect(find.byType(SizedBox), paints
- ..clipRect(rect: const Rect.fromLTRB(0.0, 0.0, 100.0, 50.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(16.0, 0.0, 32.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(32.0, 0.0, 48.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(48.0, 0.0, 64.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(64.0, 0.0, 80.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(80.0, 0.0, 96.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(96.0, 0.0, 112.0, 9.0))
+ ..rect(rect: const Rect.fromLTRB(0.0, 0.0, 100.0, 50.0), shader: isA<ImageShader>())
..restore(),
);
expect(find.byType(SizedBox), isNot(paints..scale()));
@@ -417,14 +368,7 @@
EnginePhase.layout, // so that we don't try to paint the fake images
);
expect(find.byType(SizedBox), paints
- ..clipRect(rect: const Rect.fromLTRB(0.0, 0.0, 100.0, 50.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(-12.0, 0.0, 4.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(4.0, 0.0, 20.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(20.0, 0.0, 36.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(36.0, 0.0, 52.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(52.0, 0.0, 68.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(68.0, 0.0, 84.0, 9.0))
- ..drawImageRect(source: const Rect.fromLTRB(0.0, 0.0, 16.0, 9.0), destination: const Rect.fromLTRB(84.0, 0.0, 100.0, 9.0))
+ ..rect(rect: const Rect.fromLTRB(0.0, 0.0, 100.0, 50.0), shader: isA<ImageShader>())
..restore(),
);
expect(find.byType(SizedBox), isNot(paints..scale()));