blob: 87af30ab6e6afbef519e0837c42860748c7366d5 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:typed_data';
import 'dart:ui';
import 'package:litetest/litetest.dart';
const Color red = Color(0xFFAA0000);
const Color green = Color(0xFF00AA00);
const int greenCenterBlurred = 0x1C001300;
const int greenSideBlurred = 0x15000E00;
const int greenCornerBlurred = 0x10000A00;
const int greenCenterScaled = 0xFF00AA00;
const int greenSideScaled = 0x80005500;
const int greenCornerScaled = 0x40002B00;
const List<double> grayscaleColorMatrix = <double>[
0.2126, 0.7152, 0.0722, 0, 0,
0.2126, 0.7152, 0.0722, 0, 0,
0.2126, 0.7152, 0.0722, 0, 0,
0, 0, 0, 1, 0,
];
const List<double> identityColorMatrix = <double>[
1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0,
];
const List<double> constValueColorMatrix = <double>[
0, 0, 0, 0, 2,
0, 0, 0, 0, 2,
0, 0, 0, 0, 2,
0, 0, 0, 0, 255,
];
const List<double> halvesBrightnessColorMatrix = <double>[
0.5, 0, 0, 0, 0,
0, 0.5, 0, 0, 0,
0, 0, 0.5, 0, 0,
0, 0, 0, 1, 0,
];
void main() {
Future<Uint32List> getBytesForPaint(Paint paint, {int width = 3, int height = 3}) async {
final PictureRecorder recorder = PictureRecorder();
final Canvas recorderCanvas = Canvas(recorder);
recorderCanvas.drawRect(const Rect.fromLTRB(1.0, 1.0, 2.0, 2.0), paint);
final Picture picture = recorder.endRecording();
final Image image = await picture.toImage(width, height);
final ByteData bytes = (await image.toByteData())!;
expect(bytes.lengthInBytes, equals(width * height * 4));
return bytes.buffer.asUint32List();
}
Future<Uint32List> getBytesForColorPaint(Paint paint, {int width = 1, int height = 1}) async {
final PictureRecorder recorder = PictureRecorder();
final Canvas recorderCanvas = Canvas(recorder);
recorderCanvas.drawPaint(paint);
final Picture picture = recorder.endRecording();
final Image image = await picture.toImage(width, height);
final ByteData bytes = (await image.toByteData())!;
expect(bytes.lengthInBytes, width * height * 4);
return bytes.buffer.asUint32List();
}
ImageFilter makeBlur(double sigmaX, double sigmaY, [TileMode tileMode = TileMode.clamp]) =>
ImageFilter.blur(sigmaX: sigmaX, sigmaY: sigmaY, tileMode: tileMode);
ImageFilter makeScale(double scX, double scY,
[double trX = 0.0, double trY = 0.0,
FilterQuality quality = FilterQuality.low]) {
trX *= 1.0 - scX;
trY *= 1.0 - scY;
return ImageFilter.matrix(Float64List.fromList(<double>[
scX, 0.0, 0.0, 0.0,
0.0, scY, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
trX, trY, 0.0, 1.0,
]), filterQuality: quality);
}
List<ColorFilter> colorFilters() {
// Create new color filter instances on each invocation.
return <ColorFilter> [ // ignore: prefer_const_constructors
ColorFilter.mode(green, BlendMode.color), // ignore: prefer_const_constructors
ColorFilter.mode(red, BlendMode.color), // ignore: prefer_const_constructors
ColorFilter.mode(red, BlendMode.screen), // ignore: prefer_const_constructors
ColorFilter.matrix(grayscaleColorMatrix), // ignore: prefer_const_constructors
ColorFilter.linearToSrgbGamma(), // ignore: prefer_const_constructors
ColorFilter.srgbToLinearGamma(), // ignore: prefer_const_constructors
];
}
List<ImageFilter> makeList() {
return <ImageFilter>[
makeBlur(10.0, 10.0),
makeBlur(10.0, 10.0, TileMode.decal),
makeBlur(10.0, 20.0),
makeBlur(20.0, 20.0),
makeScale(10.0, 10.0),
makeScale(10.0, 20.0),
makeScale(20.0, 10.0),
makeScale(10.0, 10.0, 1.0, 1.0),
makeScale(10.0, 10.0, 0.0, 0.0, FilterQuality.medium),
makeScale(10.0, 10.0, 0.0, 0.0, FilterQuality.high),
makeScale(10.0, 10.0, 0.0, 0.0, FilterQuality.none),
...colorFilters(),
];
}
void checkEquality(List<ImageFilter> a, List<ImageFilter> b) {
for (int i = 0; i < a.length; i++) {
for(int j = 0; j < a.length; j++) {
if (i == j) {
expect(a[i], equals(b[j]));
expect(a[i].hashCode, equals(b[j].hashCode));
expect(a[i].toString(), equals(b[j].toString()));
} else {
expect(a[i], notEquals(b[j]));
// No expectations on hashCode if objects are not equal
expect(a[i].toString(), notEquals(b[j].toString()));
}
}
}
}
List<ImageFilter> composed(List<ImageFilter> a, List<ImageFilter> b) {
return <ImageFilter>[for (final ImageFilter x in a) for (final ImageFilter y in b) ImageFilter.compose(outer: x, inner: y)];
}
test('ImageFilter - equals', () async {
final List<ImageFilter> A = makeList();
final List<ImageFilter> B = makeList();
checkEquality(A, A);
checkEquality(A, B);
checkEquality(B, B);
checkEquality(composed(A, A), composed(A, A));
checkEquality(composed(A, B), composed(B, A));
checkEquality(composed(B, B), composed(B, B));
});
void checkBytes(Uint32List bytes, int center, int side, int corner) {
expect(bytes[0], equals(corner));
expect(bytes[1], equals(side));
expect(bytes[2], equals(corner));
expect(bytes[3], equals(side));
expect(bytes[4], equals(center));
expect(bytes[5], equals(side));
expect(bytes[6], equals(corner));
expect(bytes[7], equals(side));
expect(bytes[8], equals(corner));
}
test('ImageFilter - blur', () async {
final Paint paint = Paint()
..color = green
..imageFilter = makeBlur(1.0, 1.0);
final Uint32List bytes = await getBytesForPaint(paint);
checkBytes(bytes, greenCenterBlurred, greenSideBlurred, greenCornerBlurred);
});
test('ImageFilter - matrix', () async {
final Paint paint = Paint()
..color = green
..imageFilter = makeScale(2.0, 2.0, 1.5, 1.5);
final Uint32List bytes = await getBytesForPaint(paint);
checkBytes(bytes, greenCenterScaled, greenSideScaled, greenCornerScaled);
});
test('ImageFilter - matrix: copies the list', () async {
final Float64List matrix = Float64List.fromList(<double>[
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0,
]);
final ImageFilter filter = ImageFilter.matrix(matrix);
final String originalDescription = filter.toString();
// Modify the matrix.
matrix[0] = 12345;
expect(filter.toString(), contains('[1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]'));
expect(filter.toString(), originalDescription);
});
test('ImageFilter - from color filters', () async {
final Paint paint = Paint()
..color = green
..imageFilter = const ColorFilter.matrix(constValueColorMatrix);
final Uint32List bytes = await getBytesForColorPaint(paint);
expect(bytes[0], 0xFF020202);
});
test('ImageFilter - color filter composition', () async {
final ImageFilter compOrder1 = ImageFilter.compose(
outer: const ColorFilter.matrix(halvesBrightnessColorMatrix),
inner: const ColorFilter.matrix(constValueColorMatrix),
);
final ImageFilter compOrder2 = ImageFilter.compose(
outer: const ColorFilter.matrix(constValueColorMatrix),
inner: const ColorFilter.matrix(halvesBrightnessColorMatrix),
);
final Paint paint = Paint()
..color = green
..imageFilter = compOrder1;
Uint32List bytes = await getBytesForColorPaint(paint);
expect(bytes[0], 0xFF010101);
paint
..color = green
..imageFilter = compOrder2;
bytes = await getBytesForColorPaint(paint);
expect(bytes[0], 0xFF020202);
});
test('Composite ImageFilter toString', () {
expect(
ImageFilter.compose(outer: makeBlur(20.0, 20.0, TileMode.decal), inner: makeBlur(10.0, 10.0)).toString(),
contains('blur(10.0, 10.0, clamp) -> blur(20.0, 20.0, decal)'),
);
// Produces a flat list of filters
expect(
ImageFilter.compose(
outer: ImageFilter.compose(outer: makeBlur(30.0, 30.0, TileMode.mirror), inner: makeBlur(20.0, 20.0, TileMode.repeated)),
inner: ImageFilter.compose(
outer: const ColorFilter.mode(Color(0xFFABCDEF), BlendMode.color),
inner: makeScale(10.0, 10.0),
),
).toString(),
contains(
'matrix([10.0, 0.0, 0.0, 0.0, 0.0, 10.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -0.0, -0.0, 0.0, 1.0], FilterQuality.low) -> '
'ColorFilter.mode(Color(0xffabcdef), BlendMode.color) -> '
'blur(20.0, 20.0, repeated) -> '
'blur(30.0, 30.0, mirror)'
),
);
});
}