image-effects 0.1.0

An assortment of effects that can be applied to an image with the help of `image`.
Documentation
use palette::{Srgb, FromColor, Lch};

use super::comparisons::rgb_weighted_euclidean;

#[inline] pub fn collapse_angle(angle: f32) -> f32 {
    ((angle % 360.0) + 360.0) % 360.0
}

pub fn quantize_hue(original_hue: f32, hues: &[f32]) -> f32 {
    let mut closest_dist = f32::MAX;
    let pixel_hue = collapse_angle(original_hue);
    let mut current_hue = pixel_hue;

    for hue in hues.iter() {
        let normalized = collapse_angle(*hue);
        let distance = (normalized - pixel_hue).abs();
        if distance < closest_dist {
            closest_dist = distance;
            current_hue = normalized;
        }
    }

    current_hue
}

pub const ONE_BIT: &'static [Srgb] = &[
    Srgb::new(0.0, 0.0, 0.0),
    Srgb::new(1.0, 1.0, 1.0),
];

fn quantize_colour(
    original: (f32, f32, f32),
    palette: &[(f32, f32, f32)],
    distance_fn: fn((f32, f32, f32), (f32, f32, f32)) -> f32,
) -> (f32, f32, f32) {
    let mut closest_distance = f32::MAX;
    let mut current_colour = &original;

    for colour in palette.iter() {
        let distance = distance_fn(
            original,
            *colour,
        );
        if distance < closest_distance {
            current_colour = colour;
            closest_distance = distance;
        };
    }

    *current_colour
}

pub fn quantize_rgb(original_rgb: Srgb, palette: &[Srgb]) -> Srgb {
    let srgb = quantize_colour(
        original_rgb.into_components(),
        &palette.into_iter().map(|&col| col.into_components()).collect::<Vec<_>>(),
        rgb_weighted_euclidean
    );

    Srgb::from_components(srgb)
}

pub fn compute_rgb_error(main: Srgb, other: Srgb) -> (f32, f32, f32) {
    (main - other).into_components()
}

pub fn grayscale_rgb(rgb: Srgb) -> Srgb {
    let mut lch = Lch::from_color(rgb);
    lch.chroma = 0.0;
    Srgb::from_color(lch)
}

pub fn hexcode_to_srgb(value: &str) -> Srgb {
    let r = u8::from_str_radix(&value[0..=1], 16);
    let g = u8::from_str_radix(&value[2..=3], 16);
    let b = u8::from_str_radix(&value[4..=5], 16);

    if let (Ok(r), Ok(g), Ok(b)) = (r, g, b) {
        let (r, g, b) = (
            r as f32 / 255.0,
            g as f32 / 255.0,
            b as f32 / 255.0,
        );
        Srgb::new(r, g, b)
    } else {
        println!(
            "WARNING! Couldn't convert {} into an RGB value. Returning black.",
            value
        );
        Srgb::new(0.0, 0.0, 0.0)
    }
}