image_effects/filter/
raw.rs

1use palette::{Srgb, FromColor, Lch, SetHue, Lighten, Darken, ShiftHue, LabHue};
2
3use crate::colour::utils;
4
5// consts
6pub const CHROMA_BOUND: f32 = 128.0;
7
8// utils
9#[inline] pub fn rgb_to_srgb(rgb: [u8; 3]) -> [f32; 3] {
10    [
11        rgb[0] as f32 / 255.0,
12        rgb[1] as f32 / 255.0,
13        rgb[2] as f32 / 255.0,
14    ]
15}
16
17#[inline] pub fn srgb_to_rgb(srgb: [f32; 3]) -> [u8; 3] {
18    [
19        (srgb[0] * 255.0) as u8,
20        (srgb[1] * 255.0) as u8,
21        (srgb[2] * 255.0) as u8,
22    ]
23}
24
25// PUBLIC API
26pub fn contrast<T>(rgb: T, amount: f32) -> T where 
27    T: Into<[u8; 3]> + From<[u8; 3]> 
28{
29    T::from(_contrast_u8(rgb.into(), amount))
30}
31
32pub fn gradient_map<T, U>(rgb: T, gradient: &[(U, f32)]) -> Option<U>where 
33    T: Into<[u8; 3]> + From<[u8; 3]>,
34    U: Copy + Clone + Into<Srgb> + From<Srgb>
35{
36    _gradient_map_u8(rgb.into(), gradient)
37}
38
39pub fn quantize_hue<T>(rgb: T, hues: &[f32]) -> T where 
40    T: Into<[u8; 3]> + From<[u8; 3]> 
41{
42    T::from(_quantize_hue_u8(rgb.into(), hues))
43}
44
45pub fn brighten<T>(rgb: T, factor: f32) -> T where 
46    T: Into<[u8; 3]> + From<[u8; 3]> 
47{
48    T::from(_brighten_u8(rgb.into(), factor))
49}
50
51pub fn saturate<T>(rgb: T, factor: f32) -> T where 
52    T: Into<[u8; 3]> + From<[u8; 3]> 
53{
54    T::from(_saturate_u8(rgb.into(), factor))
55}
56
57pub fn shift_hue<T>(rgb: T, degrees: f32) -> T where 
58    T: Into<[u8; 3]> + From<[u8; 3]> 
59{
60    T::from(_shift_hue_u8(rgb.into(), degrees))
61}
62
63pub fn multiply_hue<T>(rgb: T, factor: f32) -> T where
64    T: Into<[u8; 3]> + From<[u8; 3]>
65{
66    T::from(_multiply_hue_u8(rgb.into(), factor))
67}
68
69// PRIVATE API
70fn _contrast_u8(rgb: [u8; 3], amount: f32) -> [u8; 3] {
71    let mut color = Srgb::from(rgb).into_format::<f32>();
72    color.red = (((color.red - 0.5) * amount) + 0.5).clamp(0.0, 1.0);
73    color.blue = (((color.blue - 0.5) * amount) + 0.5).clamp(0.0, 1.0);
74    color.green = (((color.green - 0.5) * amount) + 0.5).clamp(0.0, 1.0);
75    Srgb::from_color(color).into_format().into()
76}
77
78fn _gradient_map_u8<U>(rgb: [u8; 3], gradient: &[(U, f32)]) -> Option<U> 
79    where U: Copy + Clone + Into<Srgb> + From<Srgb>
80{
81    let color = Srgb::from(rgb).into_format::<f32>();
82    let color = Lch::from_color(color);
83    let l = color.l / 100.0;
84
85    let mut gradient = Vec::from(gradient.clone());
86    gradient.sort_by(|a, b|
87        a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
88
89    let index = gradient.iter().position(|(_, threshold)| l < *threshold);
90
91    if index.is_none() { return None };
92
93    let index = index.unwrap();
94
95    let prev_col = gradient.get(index - 1);
96    let curr_col = gradient.get(index);
97
98    if prev_col.and(curr_col).is_some() {
99        let (c_col, c_th) = curr_col.unwrap();
100        let (p_col, p_th) = prev_col.unwrap();
101
102        let c_col: Srgb = (*c_col).into();
103        let p_col: Srgb = (*p_col).into();
104
105        let c_dist = c_th - l;
106        let p_dist = l - p_th;
107
108        let c_ratio = 1.0 - (c_dist / (c_dist + p_dist));
109        let p_ratio = 1.0 - (p_dist / (c_dist + p_dist));
110
111        let (c_r, c_g, c_b) = c_col.into_components();
112        let (p_r, p_g, p_b) = p_col.into_components();
113
114        let (new_r, new_g, new_b) = (
115            (c_ratio * c_r + p_ratio * p_r),
116            (c_ratio * c_g + p_ratio * p_g),
117            (c_ratio * c_b + p_ratio * p_b),
118        );
119
120        Some(U::from(Srgb::from_components((new_r, new_g, new_b,))))
121
122    } else if curr_col.is_some() {
123        curr_col.map(|tup| tup.0)
124    } else {
125        None
126    }
127}
128
129pub fn _quantize_hue_u8(rgb: [u8; 3], hues: &[f32]) -> [u8; 3] {
130    let color = Srgb::from(rgb).into_format::<f32>();
131    let mut color = Lch::from_color(color);
132    color.set_hue(utils::quantize_hue(color.hue.into_degrees(), hues));
133    Srgb::from_color(color).into_format().into()
134}
135
136pub fn _brighten_u8(rgb: [u8; 3], factor: f32) -> [u8; 3] {
137    let color = Srgb::from(rgb).into_format::<f32>();
138    let mut color = Lch::from_color(color);
139
140    if factor >= 0.0 {
141        color = color.lighten(factor);
142    } else {
143        color = color.darken(factor.abs());
144    };
145
146    Srgb::from_color(color).into_format().into()
147}
148
149pub fn _saturate_u8(rgb: [u8; 3], factor: f32) -> [u8; 3] {
150    let color = Srgb::from(rgb).into_format::<f32>();
151    let mut color = Lch::from_color(color);
152
153    color.chroma = if factor >= 0.0 {
154        color.chroma + (CHROMA_BOUND - color.chroma) * factor
155    } else {
156        color.chroma + (color.chroma) * factor
157    };
158    Srgb::from_color(color).into_format().into()
159}
160
161pub fn _shift_hue_u8(rgb: [u8; 3], hue: f32) -> [u8; 3] {
162    let color = Srgb::from(rgb).into_format::<f32>();
163    let mut color = Lch::from_color(color);
164    color = color.shift_hue(hue);
165    Srgb::from_color(color).into_format().into()
166}
167
168pub fn _multiply_hue_u8(rgb: [u8; 3], factor: f32) -> [u8; 3] {
169    let color = Srgb::from(rgb).into_format::<f32>();
170    let mut color = Lch::from_color(color);
171    color.hue = LabHue::new(color.hue.into_degrees() * factor);
172    Srgb::from_color(color).into_format().into()
173}