image_effects/filter/
raw.rs1use palette::{Srgb, FromColor, Lch, SetHue, Lighten, Darken, ShiftHue, LabHue};
2
3use crate::colour::utils;
4
5pub const CHROMA_BOUND: f32 = 128.0;
7
8#[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
25pub 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
69fn _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}