expectation/extensions/
image.rs

1use super::super::provider::{Provider, WriteRequester};
2use super::super::*;
3use expectation_shared::filesystem::ReadSeek;
4
5use std::io::{BufReader, Result as IoResult};
6use std::path::Path;
7
8use image::*;
9
10pub trait ImageDiffExtension {
11    fn png_writer<N>(&self, filename: N) -> Writer
12    where
13        N: AsRef<Path>;
14
15    fn rgb_image<N>(&self, filename: N, image: RgbImage) -> IoResult<()>
16    where
17        N: AsRef<Path>,
18    {
19        let mut w = self.png_writer(filename);
20        let dyn_image = DynamicImage::ImageRgb8(image);
21        dyn_image.write_to(&mut w, ImageOutputFormat::PNG).unwrap();
22        Ok(())
23    }
24
25    fn rgba_image<N>(&self, filename: N, image: RgbaImage) -> IoResult<()>
26    where
27        N: AsRef<Path>,
28    {
29        let mut w = self.png_writer(filename);
30        let dyn_image = DynamicImage::ImageRgba8(image);
31        dyn_image.write_to(&mut w, ImageOutputFormat::PNG).unwrap();
32        Ok(())
33    }
34}
35
36impl ImageDiffExtension for Provider {
37    fn png_writer<S>(&self, filename: S) -> Writer
38    where
39        S: AsRef<Path>,
40    {
41        self.custom_test(
42            filename,
43            |a, b| image_eq(a, b),
44            |a, b, c, d| image_diff(a, b, c, d),
45        )
46    }
47}
48
49fn image_eq<R1: ReadSeek, R2: ReadSeek>(r1: R1, r2: R2) -> IoResult<bool> {
50    let mut r1 = BufReader::new(r1);
51    let mut r2 = BufReader::new(r2);
52
53    let i1 = load(&mut r1, ImageFormat::PNG).unwrap();
54    let i2 = load(&mut r2, ImageFormat::PNG).unwrap();
55
56    match (i1, i2) {
57        (DynamicImage::ImageRgb8(i1), DynamicImage::ImageRgb8(i2)) => {
58            if i1.width() != i2.width() || i1.height() != i2.height() {
59                return Ok(false);
60            }
61            for x in 0..i1.width() {
62                for y in 0..i1.height() {
63                    if i1.get_pixel(x, y) != i2.get_pixel(x, y) {
64                        return Ok(false);
65                    }
66                }
67            }
68        }
69        (DynamicImage::ImageRgba8(i1), DynamicImage::ImageRgba8(i2)) => {
70            if i1.width() != i2.width() || i1.height() != i2.height() {
71                return Ok(false);
72            }
73            for x in 0..i1.width() {
74                for y in 0..i1.height() {
75                    if i1.get_pixel(x, y) != i2.get_pixel(x, y) {
76                        return Ok(false);
77                    }
78                }
79            }
80        }
81        (_, _) => return Ok(false),
82    }
83
84    Ok(true)
85}
86
87fn _add_extension(p: &Path, new_ext: &str) -> PathBuf {
88    let old_ext = match p.extension() {
89        Some(e) => e.to_string_lossy().into_owned(),
90        None => "".to_owned(),
91    };
92    p.with_extension(format!("{}{}", old_ext, new_ext))
93}
94
95fn image_diff<R1: ReadSeek, R2: ReadSeek>(
96    r1: R1,
97    r2: R2,
98    path: &Path,
99    write_requester: &mut WriteRequester,
100) -> IoResult<()> {
101    let mut r1 = BufReader::new(r1);
102    let mut r2 = BufReader::new(r2);
103
104    let i1 = load(&mut r1, ImageFormat::PNG).unwrap();
105    let i2 = load(&mut r2, ImageFormat::PNG).unwrap();
106
107    match (i1, i2) {
108        (DynamicImage::ImageRgb8(i1), DynamicImage::ImageRgb8(i2)) => {
109            if i1.width() != i2.width() || i1.height() != i2.height() {
110                return write_requester.request(path.join("img-size.txt"), |w| {
111                    writeln!(w, "image dimensions are different")?;
112                    writeln!(w, "actual:   width: {} height: {}", i1.width(), i1.height())?;
113                    writeln!(w, "expected: width: {} height: {}", i2.width(), i2.height())?;
114                    Ok(())
115                });
116            }
117            // TODO: implement image diffing
118            Ok(())
119            //panic!("images arent pixel-by-pixel equal");
120        }
121        (DynamicImage::ImageRgba8(i1), DynamicImage::ImageRgba8(i2)) => {
122            if i1.width() != i2.width() || i1.height() != i2.height() {
123                return write_requester.request(path.join("img-size.txt"), |w| {
124                    writeln!(w, "image dimensions are different")?;
125                    writeln!(w, "actual:   width: {} height: {}", i1.width(), i1.height())?;
126                    writeln!(w, "expected: width: {} height: {}", i2.width(), i2.height())?;
127                    Ok(())
128                });
129            }
130            // TODO: implement image diffing
131            Ok(())
132            //panic!("images arent pixel-by-pixel equal");
133        }
134        (DynamicImage::ImageRgb8(_), DynamicImage::ImageRgba8(_)) => {
135            return write_requester.request(path.join("img-format.txt"), |w| {
136                writeln!(w, "image formats are different");
137                writeln!(w, "actual:   RGB8");
138                writeln!(w, "expected: RGBA8 (Alpha)");
139                Ok(())
140            });
141        }
142        (DynamicImage::ImageRgba8(_), DynamicImage::ImageRgb8(_)) => {
143            return write_requester.request(path.join("img-format.txt"), |w| {
144                writeln!(w, "image formats are different");
145                writeln!(w, "actual:   RGBA8 (Alpha)");
146                writeln!(w, "expected: RGB8");
147                Ok(())
148            });
149        }
150        (_, _) => panic!(),
151    }
152}