Skip to main content

agent_image_diff/
antialias.rs

1use image::RgbaImage;
2
3use crate::color;
4
5/// Check if a pixel at (x, y) is anti-aliased by examining its neighbors.
6///
7/// A pixel is considered anti-aliased if:
8/// 1. It has fewer than 3 identical neighbors (not part of a solid region)
9/// 2. It sits between a darker and brighter neighbor (gradient transition)
10/// 3. The darkest/brightest neighbors have >= 3 identical neighbors in the other image
11///
12/// Based on Vysniauskas (2009) as adapted by pixelmatch.
13pub fn is_antialiased(
14    img: &RgbaImage,
15    other: &RgbaImage,
16    x: u32,
17    y: u32,
18) -> bool {
19    let (width, height) = img.dimensions();
20    let center = img.get_pixel(x, y).0;
21
22    let mut identical_count = 0;
23    let mut min_y_delta = 0.0_f64;
24    let mut max_y_delta = 0.0_f64;
25    let mut min_x = x;
26    let mut min_y_coord = y;
27    let mut max_x = x;
28    let mut max_y_coord = y;
29
30    // Examine 8 neighbors
31    for dy in -1i32..=1 {
32        for dx in -1i32..=1 {
33            if dx == 0 && dy == 0 {
34                continue;
35            }
36            let nx = x as i32 + dx;
37            let ny = y as i32 + dy;
38            if nx < 0 || ny < 0 || nx >= width as i32 || ny >= height as i32 {
39                continue;
40            }
41            let nx = nx as u32;
42            let ny = ny as u32;
43            let neighbor = img.get_pixel(nx, ny).0;
44
45            // Check if identical
46            if center == neighbor {
47                identical_count += 1;
48                if identical_count >= 3 {
49                    return false; // Part of a solid region, not AA
50                }
51                continue;
52            }
53
54            // Compute luminance delta (Y-only)
55            let y_delta = color::color_delta(center, neighbor, true);
56
57            if y_delta < min_y_delta {
58                min_y_delta = y_delta;
59                min_x = nx;
60                min_y_coord = ny;
61            }
62            if y_delta > max_y_delta {
63                max_y_delta = y_delta;
64                max_x = nx;
65                max_y_coord = ny;
66            }
67        }
68    }
69
70    // Must have both darker and brighter neighbors
71    if min_y_delta >= 0.0 || max_y_delta <= 0.0 {
72        return false;
73    }
74
75    // Check if the darkest and brightest neighbors are solid in the other image
76    has_many_identical_neighbors(other, min_x, min_y_coord)
77        && has_many_identical_neighbors(other, max_x, max_y_coord)
78}
79
80/// Check if a pixel has >= 3 identical neighbors in the given image.
81fn has_many_identical_neighbors(img: &RgbaImage, x: u32, y: u32) -> bool {
82    let (width, height) = img.dimensions();
83    if x >= width || y >= height {
84        return false;
85    }
86    let center = img.get_pixel(x, y).0;
87    let mut count = 0;
88
89    for dy in -1i32..=1 {
90        for dx in -1i32..=1 {
91            if dx == 0 && dy == 0 {
92                continue;
93            }
94            let nx = x as i32 + dx;
95            let ny = y as i32 + dy;
96            if nx < 0 || ny < 0 || nx >= width as i32 || ny >= height as i32 {
97                continue;
98            }
99            if img.get_pixel(nx as u32, ny as u32).0 == center {
100                count += 1;
101                if count >= 3 {
102                    return true;
103                }
104            }
105        }
106    }
107    false
108}