image_effects/filter/
algorithms.rs

1
2use palette::Srgb;
3
4use crate::{utils::image::RgbPixelRepr, effect::Effect};
5
6use super::raw::{contrast, gradient_map, quantize_hue, brighten, saturate, shift_hue, multiply_hue};
7
8/// Rotates the hue based on the amount of degrees passed.
9pub struct HueRotate(
10    /// Amount of degrees to rotate the hue by.
11    pub f32
12);
13
14/// Modifies the contrast of the image.
15pub struct Contrast(
16    /// A general factor to apply contrast.
17    /// 
18    /// - Anything higher than `1.0` will add contrast.
19    /// - Anything between `0.0` and `1.0` will decrease the contrast.
20    /// - Anything below `0.0` will start inverting the image - with `-1.0` being a 
21    ///   total inversion while preserving contrast
22    pub f32
23);
24
25/// Modifies the brightness of the image.
26pub struct Brighten(
27    /// Factor to change brightness by.
28    /// 
29    /// This value can range from `-1.0`, which turns all pixels black, and `1.0`, which makes
30    /// all pixels white.
31    pub f32
32);
33
34/// Modifies the saturation of the image.
35pub struct Saturate(
36    /// Factor to affect saturation by.
37    /// 
38    /// This value can range from `-1.0`, which removes all saturation, and `1.0`, which maximizes
39    /// all saturation.
40    /// 
41    /// Internally, `Saturate(1.0)` would mean setting each pixel to `128.0 chroma` in LCH terms -
42    /// despite Chroma being technically unbounded.
43    /// 
44    /// This may change in the future.
45    pub f32
46);
47
48/// Applies a gradient map to the image.
49///
50/// The gradient map is defined as a slice of tuples containing the *colour* and its threshold.
51/// Each pixel in the image will be mapped to the gradient using its luminance value.
52///
53/// The threshold must be between `0.0` and `1.0` - you can technically use other values but the results
54/// may be a bit weird.
55///
56/// As an example, to turn an image grayscale you could pass the colour black at `0.0` and the colour
57/// white at `1.0`.
58pub struct GradientMap {
59    map: Vec<(Srgb, f32)>
60}
61
62impl GradientMap {
63    pub fn new() -> Self {
64        Self { map: Vec::new() }
65    }
66
67    /// Create a new gradient map from an existing map.
68    pub fn with_map(map: Vec<(Srgb, f32)>) -> Self {
69        Self { map }
70    }
71
72    /// Add an entry into the gradient map.
73    pub fn add_entry(&mut self, colour: Srgb, luminance: f32) -> &mut Self {
74        self.map.push((colour, luminance));
75        self
76    }
77}
78
79/// Quantizes the hue of each pixel to one of the hues passed.
80///
81/// This *only* changes the hue - useful for defining a colour
82/// scheme without losing luminance/saturation detail.
83pub struct QuantizeHue {
84    hues: Vec<f32>
85}
86
87impl QuantizeHue {
88    pub fn new() -> Self {
89        Self { hues: Vec::new() }
90    }
91
92    /// Create a `QuantizeHue` effect with the given hues.
93    pub fn with_hues(hues: Vec<f32>) -> Self {
94        Self { hues }
95    }
96
97    /// Add a hue to the list.
98    pub fn add_hue(&mut self, hue: f32) -> &mut Self {
99        self.hues.push(hue);
100        self
101    }
102}
103
104/// Multiplies the hue of each pixel by the factor passed.
105pub struct MultiplyHue(pub f32);
106
107impl Effect<RgbPixelRepr> for HueRotate {
108    fn affect(&self, item: RgbPixelRepr) -> RgbPixelRepr {
109        shift_hue(item, self.0)
110    }
111}
112
113impl Effect<RgbPixelRepr> for Contrast {
114    fn affect(&self, item: RgbPixelRepr) -> RgbPixelRepr {
115        contrast(item, self.0)
116    }
117}
118
119impl Effect<RgbPixelRepr> for Brighten {
120    fn affect(&self, item: RgbPixelRepr) -> RgbPixelRepr {
121        brighten(item, self.0)
122    }
123}
124
125impl Effect<RgbPixelRepr> for Saturate {
126    fn affect(&self, item: RgbPixelRepr) -> RgbPixelRepr {
127        saturate(item, self.0)
128    }
129}
130
131impl Effect<RgbPixelRepr> for QuantizeHue {
132    fn affect(&self, item: RgbPixelRepr) -> RgbPixelRepr {
133        quantize_hue(item, &self.hues)
134    }
135}
136
137impl Effect<RgbPixelRepr> for GradientMap {
138    fn affect(&self, item: RgbPixelRepr) -> RgbPixelRepr {
139        gradient_map(item, &self.map).map_or(item, |colour| colour.into_format().into())
140    }
141}
142
143impl Effect<RgbPixelRepr> for MultiplyHue {
144    fn affect(&self, item: RgbPixelRepr) -> RgbPixelRepr {
145        multiply_hue(item, self.0)
146    }
147}