1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
//! Image comparison (diffing) for computer graphics renderer test cases.
//!
//! The algorithm implemented in this library is intended to allow comparing images
//! of the same scene which were rendered using different algorithms, or different
//! hardware, causing small “rounding errors” in either color or spatial position.
//!
//! To use it, call [`diff()`] on your images, and test the result against a [`Threshold`].
//!
//! ## When to use this library
//!
//! `rendiff` is *not* a “perceptual” difference algorithm; it does not attempt to calculate
//! how visible a difference is to the eye.
//! Rather, it is intended to allow for various kinds of numerical error or arbitrary
//! choice in rendering, which happen to also be perceptually insignificant:
//!
//! * Spatial: A line or curve being considered to fall on one side or another of a pixel center.
//! * Color: A color value being rounded up or down.
//!
//! This algorithm will always ignore spatial errors which meet the following criteria:
//!
//! * the spatial offset is at most 1 pixel,
//! * there are no 1-pixel-sized shapes that vanish entirely
//! (e.g. imagine a very narrow, pointy, non-axis-aligned triangle;
//! its sharp end turns into a series of disconnected dots), and
//! * there isn't antialiasing which introduces miscellaneous intermediate shades.
//!
//! Therefore, `rendiff` is ideal for comparing non-antialiased renderings of “vector” graphics.
//! In other situations, and for color rounding differences, you must tune the
//! [`Threshold`] for allowed differences.
//!
//! `rendiff` is unsuitable for comparing images which have strong noise (e.g. halftone)
//! or spatial displacements of more than one pixel.
//!
//! ## Example output
//!
//! These two cartoon robot head images are the inputs.
//! The third image is the visual output of the diff function;
//! red comes from the input and cyan marks differences.
//!
//! <div style="background: gray; text-align: center; padding: 0.7em;">
//!
//! ![robot-exp]
//! ![robot-actual]
//! ![robot-diff]
//!
//! </div>
//!
//! Note that the eyes’ shapes differ slightly, and this is ignored, but the gaps at the bottom
//! and between the teeth are highlighted, because they are places where colors are present in
//! one image but entirely absent in the other.
//! These are examples of the type of rendering bug which `rendiff` is designed to catch.
//!
//! ## Example usage
//!
//! ```
//! use rendiff::Threshold;
//!
//! // In a real application, you would load the expected image from disk and
//! // the actual image from the output of your renderer or other image processor.
//! // For this example, we'll embed some very simple images as text.
//!
//! fn ascii_image(s: &str) -> Vec<[u8; 4]> {
//! s.chars().map(|ch| {
//! let gray = u8::try_from(ch.to_digit(10).unwrap()).unwrap();
//! [gray, gray, gray, 255]
//! }).collect()
//! }
//!
//! let expected_image = imgref::ImgVec::new(
//! ascii_image("\
//! 00000000\
//! 00000000\
//! 00229900\
//! 00229900\
//! 00229900\
//! 00000000\
//! 00000000\
//! "),
//! 8, 6
//! );
//! let actual_image = imgref::ImgVec::new(
//! ascii_image("\
//! 00000000\
//! 00000000\
//! 00449990\
//! 00449990\
//! 00449990\
//! 00000000\
//! 00000000\
//! "),
//! 8, 6
//! );
//!
//! let difference = rendiff::diff(actual_image.as_ref(), expected_image.as_ref());
//!
//! // `difference` describes the differences found but does not define success or failure.
//! // To do that, you must use a `Threshold`, or examine the `histogram()` yourself.
//!
//! assert!(Threshold::no_bigger_than(2).allows(difference.histogram()));
//! assert!(!Threshold::no_bigger_than(1).allows(difference.histogram()));
//!
//! let diff_image = difference.diff_image();
//! // You can put `diff_image` in your test report.
//! ```
//!
//! ## Principle of operation
//!
//! Suppose we are comparing two images, A and B.
//! For each pixel in A (except for the perimeter),
//! a neighborhood around the corresponding pixel in B is compared, and the _smallest_
//! color difference is taken to be the difference value for that pixel in A.
//! Then, the same process is repeated, swapping the roles of the two images, and the
//! final difference value for each pixel is the maximum of those two.
//!
//! The pixel differences are then compiled into a difference image (for user viewing)
//! and a histogram (for pass/fail conditions).
//!
//! The effect of this strategy is that any feature in the image, such as the edge of a
//! shape, can be displaced by up to the neighborhood size (currently fixed to 1 pixel
//! radius, i.e. a 3×3 neighborhood) in any direction, thus
//! tolerating different choices of rounding into the pixel grid, as long as the color is
//! the same.
//!
//! This algorithm does not inherently accept differences in antialiased images, because
//! depending on how an edge lands with respect to the pixel grid, the color may be
//! different. A future version of this library may solve that problem by accepting any
//! color which is a blend of colors found in the neighborhood.
//!
//!
// This list is sorted.
/// `rendiff` uses image types from `imgref`.
pub use imgref;
type RgbaPixel = ;
pub use *;
pub use *;
pub use *;