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}