primitives/foundation/colorspace/
adjust.rs1use super::prelude::*;
2
3use super::*;
4
5pub trait GetHue {
7 fn get_hue(self) -> Float;
9}
10
11pub trait SetHue {
13 fn set_hue(&mut self, hue: Float) -> Self;
15}
16
17pub trait HasHue: Clone + Copy + GetHue + SetHue {}
19
20
21pub trait HasSaturation: Clone + Copy {
23 fn get_saturation(self) -> Float;
25 fn set_saturation(&mut self, saturation: Float) -> Self;
27}
28
29pub trait GetRadialSaturation:
31 FromColor<HslColor> + IntoColor<HslColor> + FromColor<HsvColor> + IntoColor<HsvColor>
32{
33 fn get_hsl_saturation(self) -> Float;
35 fn get_hsv_saturation(self) -> Float;
37}
38
39pub trait SetRadialSaturation:
41 FromColor<HslColor> + IntoColor<HslColor> + FromColor<HsvColor> + IntoColor<HsvColor>
42{
43 fn set_hsl_saturation(&mut self, saturation: Float) -> Self;
45 fn set_hsv_saturation(&mut self, saturation: Float) -> Self;
47}
48
49pub trait Lighten: Sized {
51 fn lighten(self, delta: Float) -> Self;
53 fn darken(self, delta: Float) -> Self {
55 self.lighten(-delta)
56 }
57}
58
59pub trait AdjustHue: Sized {
61 fn adjust_hue(self, delta: Float) -> Self;
63 fn complement(self) -> Self {
65 self.adjust_hue(180.)
66 }
67}
68
69pub trait Saturate: Sized {
71 fn saturate(self, delta: Float) -> Self;
73 fn desaturate(self, delta: Float) -> Self {
75 self.saturate(-delta)
76 }
77}
78
79pub trait Grayscale: Saturate {
81 fn grayscale(self) -> Self {
83 self.saturate(-100.)
84 }
85}
86impl<C: Saturate> Grayscale for C {}
87
88pub trait Invert: Sized {
90 fn invert(self) -> Self;
92}
93
94pub trait Adjust: Lighten + AdjustHue + Saturate + Grayscale {}
96impl<C: Lighten + AdjustHue + Saturate + Grayscale> Adjust for C {}
97
98impl GetHue for HslColor {
99 fn get_hue(self) -> Float {
100 self.hue
101 }
102}
103impl SetHue for HslColor {
104 fn set_hue(&mut self, hue: Float) -> Self {
105 self.hue = hue_bound(hue);
106 *self
107 }
108}
109impl GetHue for HsvColor {
110 fn get_hue(self) -> Float {
111 self.hue
112 }
113}
114impl SetHue for HsvColor {
115 fn set_hue(&mut self, hue: Float) -> Self {
116 self.hue = hue_bound(hue);
117 *self
118 }
119}
120
121impl HasHue for HslColor {}
122impl HasHue for HsvColor {}
123
124impl HasSaturation for HslColor {
125 fn get_saturation(self) -> Float {
126 self.saturation
127 }
128 fn set_saturation(&mut self, saturation: Float) -> Self {
129 self.saturation = clamp(saturation, 0., 100.);
130 *self
131 }
132}
133impl HasSaturation for HsvColor {
134 fn get_saturation(self) -> Float {
135 self.saturation
136 }
137 fn set_saturation(&mut self, saturation: Float) -> Self {
138 self.saturation = clamp(saturation, 0., 100.);
139 *self
140 }
141}
142
143impl<C: NonRadialSpace> GetHue for C {
144 fn get_hue(self) -> Float {
145 let hsv: HsvColor = self.into_color();
146 hsv.get_hue()
147 }
148}
149impl<C: NonRadialSpace> SetHue for C {
150 fn set_hue(&mut self, hue: Float) -> Self {
151 let mut hsv: HsvColor = (*self).into_color();
152 hsv.set_hue(hue);
153 *self = hsv.into_color();
154 *self
155 }
156}
157
158impl<C> GetRadialSaturation for C
159where
160 C: Clone
161 + Copy
162 + FromColor<HslColor>
163 + IntoColor<HslColor>
164 + FromColor<HsvColor>
165 + IntoColor<HsvColor>,
166{
167 fn get_hsl_saturation(self) -> Float {
168 let hsl: HslColor = self.into_color();
169 hsl.get_saturation()
170 }
171 fn get_hsv_saturation(self) -> Float {
172 let hsv: HsvColor = self.into_color();
173 hsv.get_saturation()
174 }
175}
176impl<C> SetRadialSaturation for C
177where
178 C: Clone
179 + Copy
180 + FromColor<HslColor>
181 + IntoColor<HslColor>
182 + FromColor<HsvColor>
183 + IntoColor<HsvColor>,
184{
185 fn set_hsl_saturation(&mut self, saturation: Float) -> Self {
186 let mut hsl: HslColor = (*self).into_color();
187 *self = hsl.set_saturation(saturation).into_color();
188 *self
189 }
190 fn set_hsv_saturation(&mut self, saturation: Float) -> Self {
191 let mut hsv: HsvColor = (*self).into_color();
192 *self = hsv.set_saturation(saturation).into_color();
193 *self
194 }
195}
196
197impl<C: FromColor<HslColor> + IntoColor<HslColor>> Lighten for C {
198 fn lighten(self, delta: Float) -> Self {
199 let hsl: HslColor = self.into_color();
200 let lightness = hsl.lightness + utils::clamp(delta, -100., 100.);
201 C::from_color(HslColor {
202 hue: hsl.hue,
203 saturation: hsl.saturation,
204 lightness: utils::clamp(lightness, 0., 100.),
205 })
206 }
207}
208
209impl<C: Clone + GetHue + SetHue> AdjustHue for C {
210 fn adjust_hue(self, delta: Float) -> Self {
211 self.clone().set_hue(self.get_hue() + delta)
212 }
213}
214
215impl<C: Clone + GetRadialSaturation + SetRadialSaturation> Saturate for C {
216 fn saturate(self, delta: Float) -> Self {
217 self.clone()
218 .set_hsl_saturation(self.get_hsl_saturation() + clamp(delta, -100., 100.))
219 }
220}
221
222impl<C: ColorTransition> Invert for C {
223 fn invert(self) -> Self {
224 let Color {
225 red,
226 green,
227 blue,
228 alpha,
229 } = self.into();
230 C::from(Color {
231 red: 1. - red,
232 green: 1. - green,
233 blue: 1. - blue,
234 alpha,
235 })
236 }
237}
238
239impl<C: Lighten + ColorSpace> Lighten for Alpha<C> {
241 fn lighten(self, delta: Float) -> Self {
242 let (color, alpha) = self.split();
243 Alpha::new(color.lighten(delta), alpha)
244 }
245}
246impl<C: AdjustHue + ColorSpace> AdjustHue for Alpha<C> {
247 fn adjust_hue(self, delta: Float) -> Self {
248 let (color, alpha) = self.split();
249 Alpha::new(color.adjust_hue(delta), alpha)
250 }
251}
252impl<C: Saturate + ColorSpace> Saturate for Alpha<C> {
253 fn saturate(self, delta: Float) -> Self {
254 let (color, alpha) = self.split();
255 Alpha::new(color.saturate(delta), alpha)
256 }
257}
258impl<C: Invert + ColorSpace> Invert for Alpha<C> {
259 fn invert(self) -> Self {
260 let (color, alpha) = self.split();
261 Alpha::new(color.invert(), alpha)
262 }
263}
264
265#[cfg(test)]
266mod test {
267 use super::super::prelude::*;
268
269 use super::super::*;
270 #[test]
279 fn adjust_hue_for_rgb() {
280 let red_rgb = RgbColor::new(255, 0, 0);
281 let green_rgb = red_rgb.adjust_hue(120.);
282 assert_eq!(
283 green_rgb.red, 0,
284 "wrong red cmp of green color: {}",
285 green_rgb
286 );
287 assert_eq!(
288 green_rgb.green, 255,
289 "wrong green cmp of green color: {}",
290 green_rgb
291 );
292 assert_eq!(
293 green_rgb.blue, 0,
294 "wrong blue cmp of green color: {}",
295 green_rgb
296 );
297 }
298
299 #[test]
300 fn adjust_hue_for_hsv() {
301 let red_hsv: HsvColor = RgbColor::new(255, 0, 0).into_color();
302 let green_hsv = red_hsv.adjust_hue(120.);
303 assert_eq!(red_hsv.hue, 0.);
304 assert_eq!(red_hsv.saturation, 100.);
305 assert_eq!(red_hsv.value, 100.);
306 assert_eq!(green_hsv.hue, 120.);
307 assert_eq!(green_hsv.saturation, 100.);
308 assert_eq!(green_hsv.value, 100.);
309 let green_rgb: RgbColor = green_hsv.into();
310 assert_eq!(green_rgb.red, 0, "wrong red: {}", green_rgb);
311 assert_eq!(green_rgb.green, 255, "wrong green: {}", green_rgb);
312 assert_eq!(green_rgb.blue, 0, "wrong blue: {}", green_rgb);
313 }
314
315 #[test]
338 fn saturate() {
339 let base = RgbColor::new(127, 63, 191); let saturated = base.saturate(20.); let desaturated = base.desaturate(20.); assert_eq!(base.get_hsl_saturation().round(), 50.);
343 assert_eq!(saturated.get_hsl_saturation().round(), 70.);
344 assert_eq!(desaturated.get_hsl_saturation().round(), 31.);
345 }
346
347 #[test]
348 fn grayscale() {
349 let base = RgbColor::new(102, 61, 142); let grayscale = base.grayscale();
351 assert_eq!(grayscale.red, 102);
352 assert_eq!(grayscale.green, 102);
353 assert_eq!(grayscale.blue, 102);
354 }
355
356 #[test]
357 fn get_saturation() {
358 let color = RgbColor::new(102, 61, 142); assert_eq!(color.get_hsl_saturation().round(), 40.);
360 assert_eq!(color.get_hsv_saturation().round(), 57.);
361 }
362
363 #[test]
364 fn complement() {
365 let base: CmykColor = HslColor::new(60., 50., 50.).into();
366 assert_eq!(base.get_hue(), 60.);
367 let base_rgb = RgbColor::from(base);
368 assert_eq!(base_rgb.red, 191);
369 assert_eq!(base_rgb.green, 191);
370 assert_eq!(base_rgb.blue, 63);
371
372 let complemented = base.complement();
373 assert_eq!(complemented.get_hue(), 240.);
374 let complemented_rgb: RgbColor = complemented.into();
375 assert_eq!(complemented_rgb.red, 63);
376 assert_eq!(complemented_rgb.green, 63);
377 assert_eq!(complemented_rgb.blue, 191);
378 }
379
380 #[test]
381 fn invert() {
382 let color = RgbColor::new(100, 60, 255);
383 let inverted = color.invert();
384 assert_eq!(inverted.red, 155);
385 assert_eq!(inverted.green, 195);
386 assert_eq!(inverted.blue, 0);
387 }
388}