Skip to content

Commit 253bd3d

Browse files
[framework] lerp images in a save layer. (flutter#131703)
Without a saveLayer, the BlendMode.plus will add itself to the backdrop and not just the previous image. Pushing without tests to see if existing goldens fail, but otherwise I have some good examples locally. This is necessary uncondtionally, and lerping lerped images has the same issue. Fixes flutter#131617 ### Before ![flutter_02](https://github.com/flutter/flutter/assets/8975114/1e783285-2fc2-429f-9fd8-6d04d4a155e1) ### After ![flutter_03](https://github.com/flutter/flutter/assets/8975114/3d08b187-26aa-4471-926d-e9ed5946a206)
1 parent 5720dea commit 253bd3d

File tree

2 files changed

+165
-0
lines changed

2 files changed

+165
-0
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -815,8 +815,10 @@ class _BlendedDecorationImagePainter implements DecorationImagePainter {
815815

816816
@override
817817
void paint(Canvas canvas, Rect rect, Path? clipPath, ImageConfiguration configuration, { double blend = 1.0, BlendMode blendMode = BlendMode.srcOver }) {
818+
canvas.saveLayer(null, Paint());
818819
a?.paint(canvas, rect, clipPath, configuration, blend: blend * (1.0 - t), blendMode: blendMode);
819820
b?.paint(canvas, rect, clipPath, configuration, blend: blend * t, blendMode: a != null ? BlendMode.plus : blendMode);
821+
canvas.restore();
820822
}
821823

822824
@override

packages/flutter/test/painting/decoration_image_lerp_test.dart

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,169 @@ void main() {
405405
expect(getPixelFromBlock(19, 19, 19), const Color(0xFF000000));
406406
}
407407
}, skip: kIsWeb); // TODO(ianh): https://github.com/flutter/flutter/issues/130612, https://github.com/flutter/flutter/issues/130609
408+
409+
testWidgets('ImageDecoration.lerp with colored background', (WidgetTester tester) async {
410+
final MemoryImage cmyk = MemoryImage(Uint8List.fromList(<int>[
411+
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
412+
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x02, 0x03, 0x00, 0x00, 0x00, 0xd4, 0x9f, 0x76,
413+
0xed, 0x00, 0x00, 0x00, 0x0c, 0x50, 0x4c, 0x54, 0x45, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff,
414+
0xff, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x4c, 0x59, 0x13, 0x00, 0x00, 0x00, 0x0e, 0x49, 0x44, 0x41,
415+
0x54, 0x08, 0xd7, 0x63, 0x60, 0x05, 0xc2, 0xf5, 0x0c, 0xeb, 0x01, 0x03, 0x00, 0x01, 0x69, 0x19,
416+
0xea, 0x34, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
417+
]));
418+
final MemoryImage wrgb = MemoryImage(Uint8List.fromList(<int>[
419+
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
420+
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x02, 0x03, 0x00, 0x00, 0x00, 0xd4, 0x9f, 0x76,
421+
0xed, 0x00, 0x00, 0x00, 0x0c, 0x50, 0x4c, 0x54, 0x45, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00,
422+
0xff, 0x00, 0xff, 0x00, 0x00, 0x1e, 0x46, 0xbb, 0x1c, 0x00, 0x00, 0x00, 0x0e, 0x49, 0x44, 0x41,
423+
0x54, 0x08, 0xd7, 0x63, 0xe0, 0x07, 0xc2, 0xa5, 0x0c, 0x4b, 0x01, 0x03, 0x50, 0x01, 0x69, 0x4a,
424+
0x78, 0x1d, 0x41, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
425+
]));
426+
427+
await tester.runAsync(() async {
428+
await load(cmyk);
429+
await load(wrgb);
430+
});
431+
432+
await tester.pumpWidget(
433+
ColoredBox(
434+
color: Colors.pink,
435+
child: Align(
436+
alignment: Alignment.topLeft,
437+
child: Wrap(
438+
textDirection: TextDirection.ltr,
439+
children: <Widget>[
440+
TestImage(DecorationImage.lerp(
441+
DecorationImage(image: wrgb, fit: BoxFit.contain),
442+
DecorationImage(image: cmyk, fit: BoxFit.contain),
443+
0.0,
444+
)),
445+
TestImage(DecorationImage.lerp(
446+
DecorationImage(image: wrgb, fit: BoxFit.contain),
447+
DecorationImage(image: cmyk, fit: BoxFit.contain),
448+
0.1,
449+
)),
450+
TestImage(DecorationImage.lerp(
451+
DecorationImage(image: wrgb, fit: BoxFit.contain),
452+
DecorationImage(image: cmyk, fit: BoxFit.contain),
453+
0.2,
454+
)),
455+
TestImage(DecorationImage.lerp(
456+
DecorationImage(image: wrgb, fit: BoxFit.contain),
457+
DecorationImage(image: cmyk, fit: BoxFit.contain),
458+
0.5,
459+
)),
460+
TestImage(DecorationImage.lerp(
461+
DecorationImage(image: wrgb, fit: BoxFit.contain),
462+
DecorationImage(image: cmyk, fit: BoxFit.contain),
463+
0.8,
464+
)),
465+
TestImage(DecorationImage.lerp(
466+
DecorationImage(image: wrgb, fit: BoxFit.contain),
467+
DecorationImage(image: cmyk, fit: BoxFit.contain),
468+
0.9,
469+
)),
470+
TestImage(DecorationImage.lerp(
471+
DecorationImage(image: wrgb, fit: BoxFit.contain),
472+
DecorationImage(image: cmyk, fit: BoxFit.contain),
473+
1.0,
474+
)),
475+
TestImage(DecorationImage.lerp(
476+
DecorationImage(image: wrgb, fit: BoxFit.cover),
477+
DecorationImage(image: cmyk, repeat: ImageRepeat.repeat),
478+
0.5,
479+
)),
480+
TestImage(DecorationImage.lerp(
481+
DecorationImage(image: wrgb, repeat: ImageRepeat.repeat),
482+
DecorationImage(image: cmyk, repeat: ImageRepeat.repeatY),
483+
0.5,
484+
)),
485+
TestImage(DecorationImage.lerp(
486+
DecorationImage(image: wrgb, repeat: ImageRepeat.repeatX),
487+
DecorationImage(image: cmyk, repeat: ImageRepeat.repeat),
488+
0.5,
489+
)),
490+
TestImage(DecorationImage.lerp(
491+
DecorationImage(image: wrgb, repeat: ImageRepeat.repeat, opacity: 0.2),
492+
DecorationImage(image: cmyk, repeat: ImageRepeat.repeat, opacity: 0.2),
493+
0.25,
494+
)),
495+
TestImage(DecorationImage.lerp(
496+
DecorationImage(image: wrgb, repeat: ImageRepeat.repeat, opacity: 0.2),
497+
DecorationImage(image: cmyk, repeat: ImageRepeat.repeat, opacity: 0.2),
498+
0.5,
499+
)),
500+
TestImage(DecorationImage.lerp(
501+
DecorationImage(image: wrgb, repeat: ImageRepeat.repeat, opacity: 0.2),
502+
DecorationImage(image: cmyk, repeat: ImageRepeat.repeat, opacity: 0.2),
503+
0.75,
504+
)),
505+
TestImage(DecorationImage.lerp(
506+
DecorationImage(image: wrgb, scale: 0.5, repeat: ImageRepeat.repeatX),
507+
DecorationImage(image: cmyk, scale: 0.25, repeat: ImageRepeat.repeatY),
508+
0.5,
509+
)),
510+
TestImage(DecorationImage.lerp(
511+
DecorationImage(image: cmyk, centerSlice: const Rect.fromLTWH(2.0, 2.0, 1.0, 1.0)),
512+
DecorationImage(image: cmyk, centerSlice: const Rect.fromLTWH(2.0, 2.0, 1.0, 1.0)),
513+
0.0,
514+
)),
515+
TestImage(DecorationImage.lerp(
516+
DecorationImage(image: cmyk, centerSlice: const Rect.fromLTWH(2.0, 2.0, 1.0, 1.0)),
517+
DecorationImage(image: cmyk, centerSlice: const Rect.fromLTWH(2.0, 2.0, 1.0, 1.0)),
518+
0.25,
519+
)),
520+
TestImage(DecorationImage.lerp(
521+
DecorationImage(image: cmyk, centerSlice: const Rect.fromLTWH(2.0, 2.0, 1.0, 1.0)),
522+
DecorationImage(image: cmyk, centerSlice: const Rect.fromLTWH(2.0, 2.0, 1.0, 1.0)),
523+
0.5,
524+
)),
525+
TestImage(DecorationImage.lerp(
526+
DecorationImage(image: cmyk, centerSlice: const Rect.fromLTWH(2.0, 2.0, 1.0, 1.0)),
527+
DecorationImage(image: cmyk, centerSlice: const Rect.fromLTWH(2.0, 2.0, 1.0, 1.0)),
528+
0.75,
529+
)),
530+
TestImage(DecorationImage.lerp(
531+
DecorationImage(image: cmyk, centerSlice: const Rect.fromLTWH(2.0, 2.0, 1.0, 1.0)),
532+
DecorationImage(image: cmyk, centerSlice: const Rect.fromLTWH(2.0, 2.0, 1.0, 1.0)),
533+
1.0,
534+
)),
535+
TestImage(DecorationImage.lerp(
536+
DecorationImage(image: cmyk, centerSlice: const Rect.fromLTWH(0.0, 0.0, 1.0, 1.0)),
537+
DecorationImage(image: cmyk, centerSlice: const Rect.fromLTWH(2.0, 2.0, 1.0, 1.0)),
538+
0.0,
539+
)),
540+
TestImage(DecorationImage.lerp(
541+
DecorationImage(image: cmyk, centerSlice: const Rect.fromLTWH(0.0, 0.0, 1.0, 1.0)),
542+
DecorationImage(image: cmyk, centerSlice: const Rect.fromLTWH(2.0, 2.0, 1.0, 1.0)),
543+
0.25,
544+
)),
545+
TestImage(DecorationImage.lerp(
546+
DecorationImage(image: cmyk, centerSlice: const Rect.fromLTWH(0.0, 0.0, 1.0, 1.0)),
547+
DecorationImage(image: cmyk, centerSlice: const Rect.fromLTWH(2.0, 2.0, 1.0, 1.0)),
548+
0.5,
549+
)),
550+
TestImage(DecorationImage.lerp(
551+
DecorationImage(image: cmyk, centerSlice: const Rect.fromLTWH(0.0, 0.0, 1.0, 1.0)),
552+
DecorationImage(image: cmyk, centerSlice: const Rect.fromLTWH(2.0, 2.0, 1.0, 1.0)),
553+
0.75,
554+
)),
555+
TestImage(DecorationImage.lerp(
556+
DecorationImage(image: cmyk, centerSlice: const Rect.fromLTWH(0.0, 0.0, 1.0, 1.0)),
557+
DecorationImage(image: cmyk, centerSlice: const Rect.fromLTWH(2.0, 2.0, 1.0, 1.0)),
558+
1.0,
559+
)),
560+
],
561+
),
562+
),
563+
),
564+
);
565+
566+
await expectLater(
567+
find.byType(Wrap),
568+
matchesGoldenFile('decoration_image.lerp.2.png'),
569+
);
570+
}, skip: kIsWeb); // TODO(ianh): https://github.com/flutter/flutter/issues/130612, https://github.com/flutter/flutter/issues/130609
408571
}
409572

410573
Future<void> load(MemoryImage image) {

0 commit comments

Comments
 (0)