undither 1.0.8

Smart filter to remove Floyd-Steinberg dithering from paletted images
Documentation
use imgref::{Img, ImgRef, ImgVec};
use loop9::{loop9, Triple};
use rgb::{ComponentMap, RGB8, RGBA8};

pub trait ToGray {
    fn to_gray(self) -> i16;
}
impl ToGray for RGBA8 {
    fn to_gray(self) -> i16 {
        self.rgb().to_gray()
    }
}
impl ToGray for RGB8 {
    fn to_gray(self) -> i16 {
        let px = self.map(i16::from);
        px.r + px.g + px.g + px.b
    }
}

pub fn prewitt_squared_img<T: ToGray + Copy>(input: ImgRef<'_, T>) -> ImgVec<u16> {
    let gray: Vec<_> = input.pixels().map(|px| px.to_gray()).collect();
    let gray = Img::new(gray, input.width(), input.height());

    let mut prew = Vec::with_capacity(gray.width() * gray.height());
    loop9(gray.as_ref(), 0,0, gray.width(), gray.height(), |_x,_y,top,mid,bot|{
        prew.push(prewitt_squared3(top, mid, bot));
    });

    ImgVec::new(prew, gray.width(), gray.height())
}

#[inline]
pub fn prewitt_squared3<T: Into<i16>>(top: Triple<T>, mid: Triple<T>, bot: Triple<T>) -> u16 {
    prewitt_squared(top.prev, top.curr, top.next, mid.prev, mid.next, bot.prev, bot.curr, bot.next)
}

#[inline]
pub fn prewitt_squared<T: Into<i16>>(top_prev: T, top_curr: T, top_next: T, mid_prev: T, mid_next: T, bot_prev: T, bot_curr: T, bot_next: T) -> u16 {
    let top_prev = top_prev.into();
    let top_curr = top_curr.into();
    let top_next = top_next.into();
    let mid_prev = mid_prev.into();
    let mid_next = mid_next.into();
    let bot_prev = bot_prev.into();
    let bot_curr = bot_curr.into();
    let bot_next = bot_next.into();

    let gx = i32::from(
        top_next - top_prev +
        mid_next - mid_prev +
        bot_next - bot_prev);

    let gy = i32::from(
        bot_prev + bot_curr + bot_next -
        top_prev - top_curr - top_next);

    ((gx*gx + gy*gy) / 256) as u16
}