icu_lib/endecoder/utils/
diff.rs1use crate::midata::MiData;
2use image::{Pixel, Rgba, RgbaImage};
3
4#[derive(Clone)]
5pub struct ImageDiffPixel {
6 pub pos: (u32, u32),
7 pub color_lhs: Rgba<u8>,
8 pub color_rhs: Rgba<u8>,
9 pub diff: [f32; 4],
10}
11
12impl ImageDiffPixel {
13 pub fn new(pos: (u32, u32), color_lhs: Rgba<u8>, color_rhs: Rgba<u8>) -> Self {
14 let mut s = Self {
15 pos,
16 color_lhs,
17 color_rhs,
18 diff: [0.; 4],
19 };
20
21 s.diff = s.calc_diff();
22 s
23 }
24
25 fn calc_diff(&self) -> [f32; 4] {
26 [
27 self.color_lhs[0] as f32 - self.color_rhs[0] as f32,
28 self.color_lhs[1] as f32 - self.color_rhs[1] as f32,
29 self.color_lhs[2] as f32 - self.color_rhs[2] as f32,
30 self.color_lhs[3] as f32 - self.color_rhs[3] as f32,
31 ]
32 }
33}
34
35#[derive(Clone)]
36pub struct ImageDiffResult {
37 size: (u32, u32),
38 diffs: Vec<ImageDiffPixel>,
39 min_diff: f32,
40 max_diff: f32,
41}
42
43impl Default for ImageDiffResult {
44 fn default() -> Self {
45 Self::new((0, 0), Vec::new(), f32::MIN, f32::MAX)
46 }
47}
48
49impl ImageDiffResult {
50 pub fn new(size: (u32, u32), diffs: Vec<ImageDiffPixel>, min_diff: f32, max_diff: f32) -> Self {
51 Self {
52 size,
53 diffs,
54 min_diff,
55 max_diff,
56 }
57 }
58 pub fn size(&self) -> (u32, u32) {
59 self.size
60 }
61
62 pub fn diffs(&self) -> &Vec<ImageDiffPixel> {
63 &self.diffs
64 }
65
66 pub fn diff_filter(&self, tolerance: f32) -> impl Iterator<Item = &ImageDiffPixel> + use<'_> {
67 self.diffs
68 .iter()
69 .filter(move |x| x.diff.iter().any(|&d| d.abs() >= tolerance))
70 }
71
72 pub fn max_diff(&self) -> f32 {
73 self.max_diff
74 }
75
76 pub fn min_diff(&self) -> f32 {
77 self.min_diff
78 }
79
80 pub fn render_diff_mask(&self, tolerance: f32, color: Rgba<u8>) -> RgbaImage {
81 let (width, height) = self.size();
82 let mut img = RgbaImage::new(width, height);
83 for pixel in img.pixels_mut() {
84 *pixel = Rgba([0, 0, 0, 0]);
85 }
86 for diff_pixel in self.diff_filter(tolerance) {
87 let (x, y) = diff_pixel.pos;
88 img.put_pixel(x, y, color);
89 }
90 img
91 }
92}
93
94pub fn blend_color32(
95 c1: &impl Pixel<Subpixel = u8>,
96 c2: &impl Pixel<Subpixel = u8>,
97 t: f32,
98) -> impl Pixel<Subpixel = u8> {
99 let t = t.clamp(0., 1.0);
100 let c1 = c1.to_rgba();
101 let c2 = c2.to_rgba();
102 Rgba::from([
103 (c1[0] as f32 * (1.0 - t) + c2[0] as f32 * t) as u8,
104 (c1[1] as f32 * (1.0 - t) + c2[1] as f32 * t) as u8,
105 (c1[2] as f32 * (1.0 - t) + c2[2] as f32 * t) as u8,
106 (c1[3] as f32 * (1.0 - t) + c2[3] as f32 * t) as u8,
107 ])
108}
109
110pub fn color_diff_f32(c1: &impl Pixel<Subpixel = u8>, c2: &impl Pixel<Subpixel = u8>) -> f32 {
121 let a32 = c1.to_rgba();
122 let b32 = c2.to_rgba();
123
124 let dr = a32[0].abs_diff(b32[0]);
125 let dg = a32[1].abs_diff(b32[1]);
126 let db = a32[2].abs_diff(b32[2]);
127 let da = a32[3].abs_diff(b32[3]);
128
129 dr.max(dg).min(db).max(da) as f32 / (4.0 * 255.0)
130}
131
132pub fn diff_image(img1: &MiData, img2: &MiData) -> Option<ImageDiffResult> {
133 match (img1, img2) {
134 (MiData::RGBA(img1), MiData::RGBA(img2)) => {
135 if img1 != img2 && img1.width() == img2.width() && img1.height() == img2.height() {
136 let mut diff_data = Vec::with_capacity(img1.pixels().len());
138 let mut min_diff = f32::MAX;
139 let mut max_diff = f32::MIN;
140 for ((p1_i, p1), p2) in img1.pixels().enumerate().zip(img2.pixels()) {
142 let diff_pixel = ImageDiffPixel::new(
143 (p1_i as u32 % img1.width(), p1_i as u32 / img1.width()),
144 *p1,
145 *p2,
146 );
147 let diff = diff_pixel.diff;
148 let d = diff
149 .into_iter()
150 .map(f32::abs)
151 .reduce(f32::max)
152 .unwrap_or(0.);
153 if d < min_diff {
154 min_diff = d;
155 }
156 if d > max_diff {
157 max_diff = d;
158 }
159 diff_data.push(diff_pixel);
160 }
161 Some(ImageDiffResult::new(
162 (img1.width(), img1.height()),
163 diff_data,
164 min_diff,
165 max_diff,
166 ))
167 } else {
168 None
169 }
170 }
171 _ => None,
172 }
173}