1
use std::fmt;
2

            
3
use crate::surface_utils::{
4
    iterators::Pixels,
5
    shared_surface::{SharedImageSurface, SurfaceType},
6
    ImageSurfaceDataExt, Pixel, PixelOps,
7
};
8

            
9
use rgb::{ColorComponentMap, ComponentMap, RGB};
10

            
11
pub enum BufferDiff {
12
    DifferentSizes,
13
    Diff(Diff),
14
}
15

            
16
pub struct Diff {
17
    pub num_pixels_changed: usize,
18
    pub max_diff: u8,
19
    pub surface: SharedImageSurface,
20
}
21

            
22
impl fmt::Display for BufferDiff {
23
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24
        match self {
25
            BufferDiff::DifferentSizes => write!(f, "different sizes"),
26
            BufferDiff::Diff(diff) => diff.fmt(f),
27
        }
28
    }
29
}
30

            
31
impl fmt::Display for Diff {
32
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33
        write!(
34
            f,
35
            "{} pixels are different, with a maximum difference of {}",
36
            self.num_pixels_changed, self.max_diff
37
        )
38
    }
39
}
40

            
41
#[inline]
42
9175670
fn emphasize(p: &Pixel) -> Pixel {
43
36702680
    let emphasize_component = |c| {
44
36702680
        // emphasize
45
36702680
        let mut c = c as u32 * 4;
46
36702680
        // make sure it's visible
47
36702680
        if c > 0 {
48
14238410
            c += 128;
49
22464270
        }
50
36702680
        c.min(255) as u8
51
36702680
    };
52
9175670
    p.map(emphasize_component)
53
9175670
}
54

            
55
15485
pub fn compare_surfaces(
56
15485
    surf_a: &SharedImageSurface,
57
15485
    surf_b: &SharedImageSurface,
58
15485
) -> Result<BufferDiff, cairo::Error> {
59
15485
    let a_width = surf_a.width();
60
15485
    let a_height = surf_a.height();
61
15485

            
62
15485
    let b_width = surf_b.width();
63
15485
    let b_height = surf_b.height();
64
15485

            
65
15485
    if a_width != b_width || a_height != b_height {
66
        return Ok(BufferDiff::DifferentSizes);
67
15485
    }
68

            
69
15485
    let mut surf_diff = cairo::ImageSurface::create(cairo::Format::ARgb32, a_width, a_height)?;
70
15485
    let diff_stride = surf_diff.stride() as usize;
71
15485

            
72
15485
    let mut num_pixels_changed = 0;
73
15485
    let mut max_diff = 0;
74
15485

            
75
15485
    let black = Pixel::default().with_alpha(255);
76
15485

            
77
15485
    {
78
15485
        let mut diff_data = surf_diff.data().unwrap();
79

            
80
964181239
        for ((xa, ya, pixel_a), (_, _, pixel_b)) in Pixels::new(surf_a).zip(Pixels::new(surf_b)) {
81
964181239
            let dest = if pixel_a != pixel_b {
82
9175670
                num_pixels_changed += 1;
83
9175670

            
84
9175670
                let pixel_diff = pixel_a.diff(&pixel_b);
85
9175670

            
86
36702680
                max_diff = pixel_diff.iter().fold(max_diff, |acc, c| acc.max(c));
87
9175670

            
88
9175670
                let pixel_diff = emphasize(&pixel_diff);
89
9175670

            
90
9175670
                if pixel_diff.rgb() == RGB::default() {
91
                    // alpha only difference; convert alpha to gray
92
83581
                    let a = pixel_diff.a;
93
250743
                    pixel_diff.map_colors(|_| a)
94
                } else {
95
9092089
                    pixel_diff.with_alpha(255)
96
                }
97
            } else {
98
955005569
                black
99
            };
100

            
101
964181239
            diff_data.set_pixel(diff_stride, dest, xa, ya);
102
        }
103
    }
104

            
105
15485
    let surface = SharedImageSurface::wrap(surf_diff, SurfaceType::SRgb)?;
106

            
107
15485
    Ok(BufferDiff::Diff(Diff {
108
15485
        num_pixels_changed,
109
15485
        max_diff,
110
15485
        surface,
111
15485
    }))
112
15485
}