use crate::chunk::ImageHeader;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum FilterStrategy {
Zero,
MinSum,
Entropy,
BruteForce,
}
pub(crate) fn paeth_predictor(a: i16, b: i16, c: i16) -> u8 {
let pa = (b - c).abs();
let pb = (a - c).abs();
let pc = (a + b - c - c).abs();
if pc < pa && pc < pb {
c as u8
} else if pb < pa {
b as u8
} else {
a as u8
}
}
fn filter_scanline(
out: &mut [u8],
scanline: &[u8],
prevline: Option<&[u8]>,
length: usize,
bytewidth: usize,
filter_type: u8,
) {
match filter_type {
0 => {
out[..length].clone_from_slice(&scanline[..length]);
}
1 => {
out[..bytewidth].clone_from_slice(&scanline[..bytewidth]);
for i in bytewidth..length {
out[i] = scanline[i].wrapping_sub(scanline[i - bytewidth]);
}
}
2 => {
if let Some(prevline) = prevline {
for i in 0..length {
out[i] = scanline[i].wrapping_sub(prevline[i]);
}
} else {
out[..length].clone_from_slice(&scanline[..length]);
}
}
3 => {
if let Some(prevline) = prevline {
for i in 0..bytewidth {
out[i] = scanline[i].wrapping_sub(prevline[i] >> 1);
}
for i in bytewidth..length {
let s = scanline[i - bytewidth] as u16 + prevline[i] as u16;
out[i] = scanline[i].wrapping_sub((s >> 1) as u8);
}
} else {
out[..bytewidth].clone_from_slice(&scanline[..bytewidth]);
for i in bytewidth..length {
out[i] =
scanline[i].wrapping_sub(scanline[i - bytewidth] >> 1);
}
}
}
4 => {
if let Some(prevline) = prevline {
for i in 0..bytewidth {
out[i] = scanline[i].wrapping_sub(prevline[i]);
}
for i in bytewidth..length {
out[i] = scanline[i].wrapping_sub(paeth_predictor(
scanline[i - bytewidth].into(),
prevline[i].into(),
prevline[i - bytewidth].into(),
));
}
} else {
out[..bytewidth].clone_from_slice(&scanline[..bytewidth]);
for i in bytewidth..length {
out[i] = scanline[i].wrapping_sub(scanline[i - bytewidth]);
}
}
}
_ => {}
};
}
pub(super) fn filter(
out: &mut [u8],
inp: &[u8],
w: usize,
h: usize,
header: &ImageHeader,
) {
let color_type = header.color_type;
let bit_depth = header.bit_depth;
let bpp = color_type.bpp(bit_depth) as usize;
let linebytes = (w * bpp + 7) / 8;
let bytewidth = (bpp + 7) / 8;
let mut prevline = None;
for y in 0..h {
let outindex = (1 + linebytes) * y;
let inindex = linebytes * y;
out[outindex] = 0u8;
filter_scanline(
&mut out[(outindex + 1)..],
&inp[inindex..],
prevline,
linebytes,
bytewidth,
0u8,
);
prevline = Some(&inp[inindex..]);
}
}
#[cfg(test)]
mod tests {
}