• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'dart:typed_data';
6import 'dart:ui';
7import 'dart:io';
8import 'package:image/image.dart' as dart_image;
9
10import 'package:path/path.dart' as path;
11import 'package:test/test.dart';
12
13typedef CanvasCallback = void Function(Canvas canvas);
14
15void testCanvas(CanvasCallback callback) {
16  try {
17    callback(Canvas(PictureRecorder(), const Rect.fromLTRB(0.0, 0.0, 0.0, 0.0)));
18  } catch (error) { } // ignore: empty_catches
19}
20
21void testNoCrashes() {
22  test('canvas APIs should not crash', () async {
23    final Paint paint = Paint();
24    const Rect rect = Rect.fromLTRB(double.nan, double.nan, double.nan, double.nan);
25    final RRect rrect = RRect.fromRectAndCorners(rect);
26    const Offset offset = Offset(double.nan, double.nan);
27    final Path path = Path();
28    const Color color = Color(0);
29    final Paragraph paragraph = ParagraphBuilder(ParagraphStyle()).build();
30
31    final PictureRecorder recorder = PictureRecorder();
32    final Canvas recorderCanvas = Canvas(recorder);
33    recorderCanvas.scale(1.0, 1.0);
34    final Picture picture = recorder.endRecording();
35    final Image image = await picture.toImage(1, 1);
36
37    try { Canvas(null, null); } catch (error) { } // ignore: empty_catches
38    try { Canvas(null, rect); } catch (error) { } // ignore: empty_catches
39    try { Canvas(PictureRecorder(), null); } catch (error) { } // ignore: empty_catches
40    try { Canvas(PictureRecorder(), rect); } catch (error) { } // ignore: empty_catches
41
42    try {
43      PictureRecorder()
44        ..endRecording()
45        ..endRecording()
46        ..endRecording();
47    } catch (error) { } // ignore: empty_catches
48
49    testCanvas((Canvas canvas) => canvas.clipPath(path));
50    testCanvas((Canvas canvas) => canvas.clipRect(rect));
51    testCanvas((Canvas canvas) => canvas.clipRRect(rrect));
52    testCanvas((Canvas canvas) => canvas.drawArc(rect, 0.0, 0.0, false, paint));
53    testCanvas((Canvas canvas) => canvas.drawAtlas(image, <RSTransform>[], <Rect>[], <Color>[], BlendMode.src, rect, paint));
54    testCanvas((Canvas canvas) => canvas.drawCircle(offset, double.nan, paint));
55    testCanvas((Canvas canvas) => canvas.drawColor(color, BlendMode.src));
56    testCanvas((Canvas canvas) => canvas.drawDRRect(rrect, rrect, paint));
57    testCanvas((Canvas canvas) => canvas.drawImage(image, offset, paint));
58    testCanvas((Canvas canvas) => canvas.drawImageNine(image, rect, rect, paint));
59    testCanvas((Canvas canvas) => canvas.drawImageRect(image, rect, rect, paint));
60    testCanvas((Canvas canvas) => canvas.drawLine(offset, offset, paint));
61    testCanvas((Canvas canvas) => canvas.drawOval(rect, paint));
62    testCanvas((Canvas canvas) => canvas.drawPaint(paint));
63    testCanvas((Canvas canvas) => canvas.drawParagraph(paragraph, offset));
64    testCanvas((Canvas canvas) => canvas.drawPath(path, paint));
65    testCanvas((Canvas canvas) => canvas.drawPicture(picture));
66    testCanvas((Canvas canvas) => canvas.drawPoints(PointMode.points, <Offset>[], paint));
67    testCanvas((Canvas canvas) => canvas.drawRawAtlas(image, Float32List(0), Float32List(0), Int32List(0), BlendMode.src, rect, paint));
68    testCanvas((Canvas canvas) => canvas.drawRawPoints(PointMode.points, Float32List(0), paint));
69    testCanvas((Canvas canvas) => canvas.drawRect(rect, paint));
70    testCanvas((Canvas canvas) => canvas.drawRRect(rrect, paint));
71    testCanvas((Canvas canvas) => canvas.drawShadow(path, color, double.nan, null));
72    testCanvas((Canvas canvas) => canvas.drawShadow(path, color, double.nan, false));
73    testCanvas((Canvas canvas) => canvas.drawShadow(path, color, double.nan, true));
74    testCanvas((Canvas canvas) => canvas.drawVertices(Vertices(VertexMode.triangles, <Offset>[]), null, paint));
75    testCanvas((Canvas canvas) => canvas.getSaveCount());
76    testCanvas((Canvas canvas) => canvas.restore());
77    testCanvas((Canvas canvas) => canvas.rotate(double.nan));
78    testCanvas((Canvas canvas) => canvas.save());
79    testCanvas((Canvas canvas) => canvas.saveLayer(rect, paint));
80    testCanvas((Canvas canvas) => canvas.saveLayer(null, null));
81    testCanvas((Canvas canvas) => canvas.scale(double.nan, double.nan));
82    testCanvas((Canvas canvas) => canvas.skew(double.nan, double.nan));
83    testCanvas((Canvas canvas) => canvas.transform(null));
84    testCanvas((Canvas canvas) => canvas.translate(double.nan, double.nan));
85  });
86}
87
88/// @returns true When the images are resonably similar.
89/// @todo Make the search actually fuzzy to a certain degree.
90Future<bool> fuzzyCompareImages(Image golden, Image img) async {
91  if (golden.width != img.width || golden.height != img.height) {
92    return false;
93  }
94  int getPixel(ByteData data, int x, int y) => data.getUint32((x + y * golden.width) * 4);
95  final ByteData goldenData = await golden.toByteData();
96  final ByteData imgData = await img.toByteData();
97  for (int y = 0; y < golden.height; y++) {
98    for (int x = 0; x < golden.width; x++) {
99      if (getPixel(goldenData, x, y) != getPixel(imgData, x, y)) {
100        return false;
101      }
102    }
103  }
104  return true;
105}
106
107/// @returns true When the images are resonably similar.
108Future<bool> fuzzyGoldenImageCompare(
109    Image image, String goldenImageName) async {
110  final String imagesPath = path.join('flutter', 'testing', 'resources');
111  final File file = File(path.join(imagesPath, goldenImageName));
112  final Uint8List goldenData = await file.readAsBytes();
113
114  final Codec codec = await instantiateImageCodec(goldenData);
115  final FrameInfo frame = await codec.getNextFrame();
116  expect(frame.image.height, equals(image.width));
117  expect(frame.image.width, equals(image.height));
118
119  final bool areEqual = await fuzzyCompareImages(frame.image, image);
120  if (!areEqual) {
121    final ByteData pngData = await image.toByteData();
122    final ByteBuffer buffer = pngData.buffer;
123    final dart_image.Image png = dart_image.Image.fromBytes(
124        image.width, image.height, buffer.asUint8List());
125    final String outPath = path.join(imagesPath, 'found_' + goldenImageName);
126    File(outPath)..writeAsBytesSync(dart_image.encodePng(png));
127    print('wrote: ' + outPath);
128  }
129  return areEqual;
130}
131
132void main() {
133  testNoCrashes();
134
135  test('Simple .toImage', () async {
136    final PictureRecorder recorder = PictureRecorder();
137    final Canvas canvas = Canvas(recorder);
138    final Path circlePath = Path()
139      ..addOval(
140          Rect.fromCircle(center: const Offset(40.0, 40.0), radius: 20.0));
141    final Paint paint = Paint()
142      ..isAntiAlias = false
143      ..style = PaintingStyle.fill;
144    canvas.drawPath(circlePath, paint);
145    final Picture picture = recorder.endRecording();
146    final Image image = await picture.toImage(100, 100);
147    expect(image.width, equals(100));
148    expect(image.height, equals(100));
149
150    final bool areEqual =
151        await fuzzyGoldenImageCompare(image, 'canvas_test_toImage.png');
152    expect(areEqual, true);
153  });
154}
155