Skip to content

Commit f35de0c

Browse files
authored
Adds wide gamut saveLayer integration test (#120131)
* Added wide gamut integration test that uses save layers. * updated the test to support bgra too * analysis errors * switched blend mode to multiply to avoid future optimizations
1 parent dff0955 commit f35de0c

File tree

2 files changed

+156
-32
lines changed

2 files changed

+156
-32
lines changed

dev/integration_tests/wide_gamut_test/integration_test/app_test.dart

Lines changed: 64 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -23,40 +23,80 @@ bool _isAlmost(double x, double y, double epsilon) {
2323
return (x - y).abs() < epsilon;
2424
}
2525

26+
bool _findDeepRedBGRA10(Uint8List bytes, int width, int height) {
27+
final ByteData byteData = ByteData.sublistView(bytes);
28+
expect(bytes.lengthInBytes, width * height * 8);
29+
expect(bytes.lengthInBytes, byteData.lengthInBytes);
30+
bool foundDeepRed = false;
31+
for (int i = 0; i < bytes.lengthInBytes; i += 8) {
32+
final int pixel = byteData.getUint64(i, Endian.host);
33+
final double blue = _decodeBGR10((pixel >> 6) & 0x3ff);
34+
final double green = _decodeBGR10((pixel >> 22) & 0x3ff);
35+
final double red = _decodeBGR10((pixel >> 38) & 0x3ff);
36+
if (_isAlmost(red, 1.0931, 0.01) &&
37+
_isAlmost(green, -0.2268, 0.01) &&
38+
_isAlmost(blue, -0.1501, 0.01)) {
39+
foundDeepRed = true;
40+
}
41+
}
42+
return foundDeepRed;
43+
}
44+
45+
bool _findDeepRedBGR10(Uint8List bytes, int width, int height) {
46+
final ByteData byteData = ByteData.sublistView(bytes);
47+
expect(bytes.lengthInBytes, width * height * 4);
48+
expect(bytes.lengthInBytes, byteData.lengthInBytes);
49+
bool foundDeepRed = false;
50+
for (int i = 0; i < bytes.lengthInBytes; i += 4) {
51+
final int pixel = byteData.getUint32(i, Endian.host);
52+
final double blue = _decodeBGR10(pixel & 0x3ff);
53+
final double green = _decodeBGR10((pixel >> 10) & 0x3ff);
54+
final double red = _decodeBGR10((pixel >> 20) & 0x3ff);
55+
if (_isAlmost(red, 1.0931, 0.01) &&
56+
_isAlmost(green, -0.2268, 0.01) &&
57+
_isAlmost(blue, -0.1501, 0.01)) {
58+
foundDeepRed = true;
59+
}
60+
}
61+
return foundDeepRed;
62+
}
63+
64+
bool _findDeepRed(List<Object?> result) {
65+
expect(result, isNotNull);
66+
expect(result.length, 4);
67+
final int width = (result[0] as int?)!;
68+
final int height = (result[1] as int?)!;
69+
final String format = (result[2] as String?)!;
70+
if (format == 'MTLPixelFormatBGR10_XR') {
71+
return _findDeepRedBGR10((result[3] as Uint8List?)!, width, height);
72+
} else if (format == 'MTLPixelFormatBGRA10_XR') {
73+
return _findDeepRedBGRA10((result[3] as Uint8List?)!, width, height);
74+
} else {
75+
fail('Unsupported pixel format: $format');
76+
}
77+
}
78+
2679
void main() {
2780
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
2881

2982
group('end-to-end test', () {
3083
testWidgets('look for display p3 deepest red', (WidgetTester tester) async {
31-
app.main();
84+
app.run(app.Setup.image);
85+
await tester.pumpAndSettle(const Duration(seconds: 2));
86+
87+
const MethodChannel channel = MethodChannel('flutter/screenshot');
88+
final List<Object?> result =
89+
await channel.invokeMethod('test') as List<Object?>;
90+
expect(_findDeepRed(result), isTrue);
91+
});
92+
testWidgets('look for display p3 deepest red', (WidgetTester tester) async {
93+
app.run(app.Setup.canvasSaveLayer);
3294
await tester.pumpAndSettle(const Duration(seconds: 2));
3395

3496
const MethodChannel channel = MethodChannel('flutter/screenshot');
3597
final List<Object?> result =
3698
await channel.invokeMethod('test') as List<Object?>;
37-
expect(result, isNotNull);
38-
expect(result.length, 4);
39-
final int width = (result[0] as int?)!;
40-
final int height = (result[1] as int?)!;
41-
final String format = (result[2] as String?)!;
42-
expect(format, 'MTLPixelFormatBGR10_XR');
43-
final Uint8List bytes = (result[3] as Uint8List?)!;
44-
final ByteData byteData = ByteData.sublistView(bytes);
45-
expect(bytes.lengthInBytes, width * height * 4);
46-
expect(bytes.lengthInBytes, byteData.lengthInBytes);
47-
bool foundDeepRed = false;
48-
for (int i = 0; i < bytes.lengthInBytes; i += 4) {
49-
final int pixel = byteData.getUint32(i, Endian.host);
50-
final double blue = _decodeBGR10(pixel & 0x3ff);
51-
final double green = _decodeBGR10((pixel >> 10) & 0x3ff);
52-
final double red = _decodeBGR10((pixel >> 20) & 0x3ff);
53-
if (_isAlmost(red, 1.0931, 0.01) &&
54-
_isAlmost(green, -0.2268, 0.01) &&
55-
_isAlmost(blue, -0.1501, 0.01)) {
56-
foundDeepRed = true;
57-
}
58-
}
59-
expect(foundDeepRed, isTrue);
99+
expect(_findDeepRed(result), isTrue);
60100
});
61101
});
62102
}

dev/integration_tests/wide_gamut_test/lib/main.dart

Lines changed: 92 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// found in the LICENSE file.
44

55
import 'dart:convert' show base64Decode;
6+
import 'dart:ui' as ui;
67
import 'package:flutter/material.dart';
78

89
/// A 100x100 png in Display P3 colorspace.
@@ -63,12 +64,21 @@ const String _displayP3Logo =
6364
'ElWKtuy2OXm9QtxYmoawGJUL3jcwHpiBNxagGJUL3jcwHpiBNxagGJUL3jcwHpiBNxagGJUL3j'
6465
'cwHpiBNx6gU/2fLWVmm7wQAAAABJRU5ErkJggg==';
6566

66-
void main() {
67-
runApp(const MyApp());
67+
void main() => run(Setup.canvasSaveLayer);
68+
69+
enum Setup {
70+
image,
71+
canvasSaveLayer,
72+
}
73+
74+
void run(Setup setup) {
75+
runApp(MyApp(setup));
6876
}
6977

7078
class MyApp extends StatelessWidget {
71-
const MyApp({super.key});
79+
const MyApp(this._setup, {super.key});
80+
81+
final Setup _setup;
7282

7383
@override
7484
Widget build(BuildContext context) {
@@ -77,27 +87,101 @@ class MyApp extends StatelessWidget {
7787
theme: ThemeData(
7888
primarySwatch: Colors.blue,
7989
),
80-
home: const MyHomePage(title: 'Wide Gamut Test'),
90+
home: MyHomePage(_setup, title: 'Wide Gamut Test'),
8191
);
8292
}
8393
}
8494

85-
class MyHomePage extends StatelessWidget {
86-
const MyHomePage({super.key, required this.title});
95+
class _SaveLayerDrawer extends CustomPainter {
96+
_SaveLayerDrawer(this._image);
97+
98+
final ui.Image? _image;
99+
100+
@override
101+
void paint(Canvas canvas, Size size) {
102+
if (_image != null) {
103+
final Rect imageRect = Rect.fromCenter(
104+
center: Offset.zero,
105+
width: _image!.width.toDouble(),
106+
height: _image!.height.toDouble());
107+
canvas.saveLayer(
108+
imageRect,
109+
Paint());
110+
canvas.drawRect(
111+
imageRect.inflate(-_image!.width.toDouble() / 4.0),
112+
Paint()
113+
..style = PaintingStyle.stroke
114+
..color = const Color(0xffffffff)
115+
..strokeWidth = 3);
116+
canvas.saveLayer(
117+
imageRect,
118+
Paint()..blendMode = BlendMode.multiply);
119+
canvas.drawImage(_image!,
120+
Offset(-_image!.width / 2.0, -_image!.height / 2.0), Paint());
121+
canvas.restore();
122+
canvas.restore();
123+
}
124+
}
125+
126+
@override
127+
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
128+
}
129+
130+
Future<ui.Image> _loadImage() async {
131+
final ui.ImmutableBuffer buffer =
132+
await ui.ImmutableBuffer.fromUint8List(base64Decode(_displayP3Logo));
133+
final ui.ImageDescriptor descriptor =
134+
await ui.ImageDescriptor.encoded(buffer);
135+
final ui.Codec codec = await descriptor.instantiateCodec();
136+
return (await codec.getNextFrame()).image;
137+
}
138+
139+
class MyHomePage extends StatefulWidget {
140+
const MyHomePage(this.setup, {super.key, required this.title});
87141

142+
final Setup setup;
88143
final String title;
89144

145+
@override
146+
State<StatefulWidget> createState() => _MyHomePageState();
147+
}
148+
149+
class _MyHomePageState extends State<MyHomePage> {
150+
ui.Image? _image;
151+
152+
@override
153+
void initState() {
154+
if (widget.setup == Setup.canvasSaveLayer) {
155+
_loadImage().then((ui.Image? value) {
156+
setState(() {
157+
_image = value;
158+
});
159+
});
160+
}
161+
super.initState();
162+
}
163+
90164
@override
91165
Widget build(BuildContext context) {
166+
late Widget imageWidget;
167+
switch (widget.setup) {
168+
case Setup.image:
169+
imageWidget = Image.memory(base64Decode(_displayP3Logo));
170+
break;
171+
case Setup.canvasSaveLayer:
172+
imageWidget = CustomPaint(painter: _SaveLayerDrawer(_image));
173+
break;
174+
}
175+
92176
return Scaffold(
93177
appBar: AppBar(
94-
title: Text(title),
178+
title: Text(widget.title),
95179
),
96180
body: Center(
97181
child: Column(
98182
mainAxisAlignment: MainAxisAlignment.center,
99183
children: <Widget>[
100-
Image.memory(base64Decode(_displayP3Logo)),
184+
imageWidget,
101185
],
102186
),
103187
),

0 commit comments

Comments
 (0)