use std::error::Error;
use image::Rgba;
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum BleedStage {
Unprocessed,
Staged,
Processed,
}
const NEIGHBORS: [(i32, i32); 8] = [
( 1, 0),
( 1, -1),
( 0, -1),
(-1, -1),
(-1, 0),
(-1, 1),
( 0, 1),
( 1, 1),
];
fn neighbors(x: u32, y: u32, w: u32, h: u32) -> impl Iterator<Item = (u32, u32)> {
return NEIGHBORS.iter()
.filter(move |(u, v)| {
let x1 = x as i32 + *u;
let y1 = y as i32 + *v;
x1 >= 0 && y1 >= 0 && x1 < (w as i32) && y1 < (h as i32)
})
.map(move |(u, v)| {
(x.wrapping_add_signed(*u), y.wrapping_add_signed(*v))
});
}
pub fn set_alpha(img: &mut image::RgbaImage, a: u8) {
for pixel in img.pixels_mut() {
let Rgba([r, g, b, _]) = *pixel;
*pixel = Rgba([r, g, b, a]);
}
}
pub fn fix_alpha(img: &mut image::RgbaImage) -> Result<(), Box<dyn Error>> {
let (width, height) = (img.width(), img.height());
let mut queue0: Vec<(u32, u32)> = Vec::new();
let mut queue1: Vec<(u32, u32)> = Vec::new();
let mut stages: Vec<BleedStage> = vec![BleedStage::Unprocessed; (width * height) as usize];
for y in 0..width {
for x in 0..height {
let index = (y * width + x) as usize;
let Rgba([_, _, _, a]) = img[(x as u32, y as u32)];
if a > 0 {
stages[index] = BleedStage::Processed;
}
else {
stages[index] = BleedStage::Unprocessed;
}
}
}
for y in 0..width {
for x in 0..height {
let index = (y * width + x) as usize;
let stage0 = stages[index];
if stage0 != BleedStage::Processed {
continue;
}
for (x1, y1) in neighbors(x, y, width, height) {
let index1 = (y1 * width + x1) as usize;
if stages[index1] == BleedStage::Unprocessed {
queue0.push((x1, y1));
stages[index1] = BleedStage::Staged;
break;
}
}
}
}
while !queue0.is_empty() {
for (x, y) in queue0.iter().cloned() {
let (x, y) = (x, y);
let mut c: u32 = 0;
let mut r: u32 = 0; let mut g: u32 = 0; let mut b: u32 = 0;
for (x1, y1) in neighbors(x, y, width, height) {
let stage_index = (y1 * width + x1) as usize;
let stage = stages[stage_index];
if stage == BleedStage::Processed {
let pixel = img.get_pixel(x1, y1);
let Rgba([r1, g1, b1, _]) = *pixel;
c += 1;
r += r1 as u32;
g += g1 as u32;
b += b1 as u32;
}
else if stage == BleedStage::Unprocessed {
stages[stage_index] = BleedStage::Staged;
queue1.push((x1, y1));
}
}
if c > 0 {
r /= c; g /= c; b /= c;
let pixel = img.get_pixel_mut(x, y);
*pixel = Rgba([r as u8, g as u8, b as u8, 0 ]);
}
}
for (x, y) in queue0.iter().cloned() {
let index = (y * width + x) as usize;
stages[index] = BleedStage::Processed;
}
queue0.clear();
std::mem::swap(&mut queue0, &mut queue1);
}
Ok(())
}
pub fn open_image_file(path: impl AsRef<std::path::Path>) -> Result<image::DynamicImage, Box<dyn Error>> {
Ok(image::io::Reader::open(path)?.decode()?)
}