agent_image_diff/
color.rs1const MAX_YIQ_DELTA_SQ: f64 = 35215.0;
4
5pub type Rgba = [u8; 4];
6
7pub fn color_delta(px1: Rgba, px2: Rgba, y_only: bool) -> f64 {
11 let (r1, g1, b1) = blend(px1);
12 let (r2, g2, b2) = blend(px2);
13
14 let dr = r1 - r2;
15 let dg = g1 - g2;
16 let db = b1 - b2;
17
18 let y = 0.29889531 * dr + 0.58662247 * dg + 0.11448223 * db;
20
21 if y_only {
22 return y;
23 }
24
25 let i = 0.59597799 * dr - 0.27417610 * dg - 0.32180189 * db;
27 let q = 0.21147017 * dr - 0.52261711 * dg + 0.31114694 * db;
28
29 let delta_sq = 0.5053 * y * y + 0.299 * i * i + 0.1957 * q * q;
30
31 (delta_sq / MAX_YIQ_DELTA_SQ).sqrt()
32}
33
34fn blend(px: Rgba) -> (f64, f64, f64) {
36 let a = px[3] as f64 / 255.0;
37 if (a - 1.0).abs() < f64::EPSILON {
38 return (px[0] as f64, px[1] as f64, px[2] as f64);
39 }
40 let bg = 255.0;
41 (
42 bg * (1.0 - a) + px[0] as f64 * a,
43 bg * (1.0 - a) + px[1] as f64 * a,
44 bg * (1.0 - a) + px[2] as f64 * a,
45 )
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51
52 #[test]
53 fn identical_pixels_have_zero_delta() {
54 let px = [128, 64, 200, 255];
55 assert_eq!(color_delta(px, px, false), 0.0);
56 }
57
58 #[test]
59 fn black_vs_white_is_high_delta() {
60 let black = [0, 0, 0, 255];
61 let white = [255, 255, 255, 255];
62 let delta = color_delta(black, white, false);
63 assert!(delta > 0.9, "black vs white delta should be near 1.0, got {delta}");
64 }
65
66 #[test]
67 fn similar_colors_have_small_delta() {
68 let a = [100, 100, 100, 255];
69 let b = [102, 100, 101, 255];
70 let delta = color_delta(a, b, false);
71 assert!(delta < 0.02, "similar colors should have small delta, got {delta}");
72 }
73
74 #[test]
75 fn transparent_vs_opaque_blends_against_white() {
76 let transparent = [0, 0, 0, 0]; let white = [255, 255, 255, 255];
78 let delta = color_delta(transparent, white, false);
79 assert!(delta < 0.001, "transparent pixel should blend to white background, got {delta}");
80 }
81
82 #[test]
83 fn y_only_returns_luminance_delta() {
84 let a = [100, 100, 100, 255];
85 let b = [200, 100, 100, 255];
86 let y_delta = color_delta(a, b, true);
87 let full_delta = color_delta(a, b, false);
88 assert!(y_delta.abs() < full_delta.abs() + 50.0);
90 }
91}