| // 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 makeDilate(double radiusX, double radiusY) => |
| ImageFilter.dilate(radiusX: radiusX, radiusY: radiusY); |
| |
| ImageFilter makeErode(double radiusX, double radiusY) => |
| ImageFilter.erode(radiusX: radiusX, radiusY: radiusY); |
| |
| 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), |
| makeDilate(10.0, 20.0), |
| makeDilate(20.0, 20.0), |
| makeDilate(20.0, 10.0), |
| makeErode(10.0, 20.0), |
| makeErode(20.0, 20.0), |
| makeErode(20.0, 10.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 - dilate', () async { |
| final Paint paint = Paint() |
| ..color = green |
| ..imageFilter = makeDilate(1.0, 1.0); |
| |
| final Uint32List bytes = await getBytesForPaint(paint); |
| checkBytes(bytes, green.value, green.value, green.value); |
| }); |
| |
| test('ImageFilter - erode', () async { |
| final Paint paint = Paint() |
| ..color = green |
| ..imageFilter = makeErode(1.0, 1.0); |
| |
| final Uint32List bytes = await getBytesForPaint(paint); |
| checkBytes(bytes, 0, 0, 0); |
| }); |
| |
| 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)' |
| ), |
| ); |
| }); |
| } |