pix_engine/color/
ops.rs

1//! [Color] operation functions.
2//!
3//! Provides numeric operations and trait implementations:
4//!
5//! - [`LowerHex`]: Allows displaying as lowercase hexadecimal value.
6//! - [`UpperHex`]: Allows displaying as uppercase hexadecimal value.
7//! - [`Index`]: Allows indexing to retrieve RGBA values. (e.g. `color[0]` for the red
8//!   channel).
9//! - [`PartialEq`] and [Eq]: Allows comparison.
10//! - [`Hash`]: Allows hashing.
11//!
12//! Also implemented are [`Add`], [`Sub`], [`AddAssign`], and [`SubAssign`] with other `Color`s and u8
13//! values channel-wise. [`Deref`] is also implemented which returns `[u8; 4]`.
14
15use super::{
16    conversion::{calculate_channels, clamp_levels, convert_levels},
17    Color,
18};
19use std::{
20    fmt::{self, LowerHex, UpperHex},
21    hash::{Hash, Hasher},
22    ops::{Add, AddAssign, Deref, Div, DivAssign, Index, Mul, MulAssign, Sub, SubAssign},
23};
24
25impl LowerHex for Color {
26    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27        let [red, green, blue, alpha] = self.channels();
28        write!(f, "#{red:x}{green:x}{blue:x}{alpha:x}")
29    }
30}
31
32impl UpperHex for Color {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        let [red, green, blue, alpha] = self.channels();
35        write!(f, "#{red:X}{green:X}{blue:X}{alpha:X}")
36    }
37}
38
39impl Index<usize> for Color {
40    type Output = u8;
41    fn index(&self, idx: usize) -> &Self::Output {
42        &self.channels[idx]
43    }
44}
45
46impl PartialEq for Color {
47    fn eq(&self, other: &Self) -> bool {
48        self.channels.eq(&other.channels)
49    }
50}
51
52impl Eq for Color {}
53
54impl Hash for Color {
55    fn hash<H: Hasher>(&self, state: &mut H) {
56        self.channels.hash(state);
57    }
58}
59
60impl Add for Color {
61    type Output = Self;
62    fn add(self, other: Color) -> Self::Output {
63        if self.mode == other.mode {
64            let [r, g, b, a] = self.channels();
65            let [or, og, ob, _] = other.channels();
66            Self {
67                mode: self.mode,
68                channels: [
69                    r.saturating_add(or),
70                    g.saturating_add(og),
71                    b.saturating_add(ob),
72                    a,
73                ],
74            }
75        } else {
76            let [v1, v2, v3, a] = self.levels();
77            let [ov1, ov2, ov3, _] = convert_levels(other.levels(), other.mode, self.mode);
78            let levels = clamp_levels([v1 + ov1, v2 + ov2, v3 + ov3, a]);
79            Self {
80                mode: self.mode,
81                channels: calculate_channels(levels),
82            }
83        }
84    }
85}
86
87impl Add<u8> for Color {
88    type Output = Self;
89    fn add(self, val: u8) -> Self::Output {
90        let [r, g, b, _] = self.channels;
91        Self::rgb(
92            r.saturating_add(val),
93            g.saturating_add(val),
94            b.saturating_add(val),
95        )
96    }
97}
98
99impl AddAssign for Color {
100    fn add_assign(&mut self, other: Color) {
101        if self.mode == other.mode {
102            for (i, v) in self.channels.iter_mut().enumerate().take(3) {
103                *v = v.saturating_add(other[i]);
104            }
105        } else {
106            let [v1, v2, v3, a] = self.levels();
107            let [ov1, ov2, ov3, _] = convert_levels(other.levels(), other.mode, self.mode);
108            let levels = clamp_levels([v1 + ov1, v2 + ov2, v3 + ov3, a]);
109            self.update_channels(levels, self.mode);
110        }
111    }
112}
113
114impl AddAssign<u8> for Color {
115    fn add_assign(&mut self, val: u8) {
116        for v in &mut self.channels {
117            *v = v.saturating_add(val);
118        }
119    }
120}
121
122impl Sub for Color {
123    type Output = Self;
124    fn sub(self, other: Color) -> Self::Output {
125        if self.mode == other.mode {
126            let [r, g, b, a] = self.channels();
127            let [or, og, ob, _] = other.channels();
128            Self {
129                mode: self.mode,
130                channels: [
131                    r.saturating_sub(or),
132                    g.saturating_sub(og),
133                    b.saturating_sub(ob),
134                    a,
135                ],
136            }
137        } else {
138            let [v1, v2, v3, a] = self.levels();
139            let [ov1, ov2, ov3, _] = convert_levels(other.levels(), other.mode, self.mode);
140            let levels = clamp_levels([v1 - ov1, v2 - ov2, v3 - ov3, a]);
141            Self {
142                mode: self.mode,
143                channels: calculate_channels(levels),
144            }
145        }
146    }
147}
148
149impl Sub<u8> for Color {
150    type Output = Self;
151    fn sub(self, val: u8) -> Self::Output {
152        let [r, g, b, a] = self.channels;
153        Self::rgba(
154            r.saturating_sub(val),
155            g.saturating_sub(val),
156            b.saturating_sub(val),
157            a,
158        )
159    }
160}
161
162impl SubAssign for Color {
163    fn sub_assign(&mut self, other: Color) {
164        if self.mode == other.mode {
165            for (i, v) in self.channels.iter_mut().enumerate().take(3) {
166                *v = v.saturating_sub(other[i]);
167            }
168        } else {
169            let [v1, v2, v3, a] = self.levels();
170            let [ov1, ov2, ov3, _] = convert_levels(other.levels(), other.mode, self.mode);
171            let levels = clamp_levels([v1 - ov1, v2 - ov2, v3 - ov3, a]);
172            self.update_channels(levels, self.mode);
173        }
174    }
175}
176
177impl SubAssign<u8> for Color {
178    fn sub_assign(&mut self, val: u8) {
179        for v in self.channels.iter_mut().take(3) {
180            *v = v.saturating_sub(val);
181        }
182    }
183}
184
185impl Deref for Color {
186    type Target = [u8; 4];
187    /// Deref `Color` to `&[u8; 4]`.
188    fn deref(&self) -> &Self::Target {
189        &self.channels
190    }
191}
192
193macro_rules! impl_ops {
194    ($($target:ty),*) => {
195        $(
196            impl Mul<$target> for Color where $target: Into<f64> {
197                type Output = Self;
198                fn mul(self, s: $target) -> Self::Output {
199                    let [v1, v2, v3, a] = self.levels();
200                    let s = f64::from(s);
201                    let levels = clamp_levels([v1 * s, v2 * s, v3 * s, a]);
202                    Self {
203                        mode: self.mode,
204                        channels: calculate_channels(levels),
205                    }
206                }
207            }
208
209            impl Mul<Color> for $target where $target: Into<f64> {
210                type Output = Color;
211                fn mul(self, c: Color) -> Self::Output {
212                    let [v1, v2, v3, a] = c.levels();
213                    let s = f64::from(self);
214                    let levels = clamp_levels([v1 * s, v2 * s, v3 * s, a]);
215                    Color {
216                        mode: c.mode,
217                        channels: calculate_channels(levels),
218                    }
219                }
220            }
221
222            impl MulAssign<$target> for Color where $target: Into<f64> {
223                fn mul_assign(&mut self, s: $target) {
224                    let [v1, v2, v3, a] = self.levels();
225                    let s = f64::from(s);
226                    let levels = clamp_levels([v1 * s, v2 * s, v3 * s, a]);
227                    self.update_channels(levels, self.mode);
228                }
229            }
230
231            impl Div<$target> for Color where $target: Into<f64> {
232                type Output = Self;
233                fn div(self, s: $target) -> Self::Output {
234                    let [v1, v2, v3, a] = self.levels();
235                    let s = f64::from(s);
236                    let levels = clamp_levels([v1 / s, v2 / s, v3 / s, a]);
237                    Self {
238                        mode: self.mode,
239                        channels: calculate_channels(levels),
240                    }
241                }
242            }
243
244            impl DivAssign<$target> for Color where $target: Into<f64> {
245                fn div_assign(&mut self, s: $target) {
246                    let [v1, v2, v3, a] = self.levels();
247                    let s = f64::from(s);
248                    let levels = clamp_levels([v1 / s, v2 / s, v3 / s, a]);
249                    self.update_channels(levels, self.mode);
250                }
251            }
252        )*
253    };
254}
255
256macro_rules! impl_as_ops {
257    ($($target:ty),*) => {
258        $(
259            impl Mul<$target> for Color {
260                type Output = Self;
261                fn mul(self, s: $target) -> Self::Output {
262                    let [v1, v2, v3, a] = self.levels();
263                    let s = s as f64;
264                    let levels = clamp_levels([v1 * s, v2 * s, v3 * s, a]);
265                    Self {
266                        mode: self.mode,
267                        channels: calculate_channels(levels),
268                    }
269                }
270            }
271
272            impl Mul<Color> for $target {
273                type Output = Color;
274                fn mul(self, c: Color) -> Self::Output {
275                    let [v1, v2, v3, a] = c.levels();
276                    let s = self as f64;
277                    let levels = clamp_levels([v1 * s, v2 * s, v3 * s, a]);
278                    Color {
279                        mode: c.mode,
280                        channels: calculate_channels(levels),
281                    }
282                }
283            }
284
285            impl MulAssign<$target> for Color {
286                fn mul_assign(&mut self, s: $target) {
287                    let [v1, v2, v3, a] = self.levels();
288                    let s = s as f64;
289                    let levels = clamp_levels([v1 * s, v2 * s, v3 * s, a]);
290                    self.update_channels(levels, self.mode);
291                }
292            }
293
294            impl Div<$target> for Color {
295                type Output = Self;
296                fn div(self, s: $target) -> Self::Output {
297                    let [v1, v2, v3, a] = self.levels();
298                    let s = s as f64;
299                    let levels = clamp_levels([v1 / s, v2 / s, v3 / s, a]);
300                    Self {
301                        mode: self.mode,
302                        channels: calculate_channels(levels),
303                    }
304                }
305            }
306
307            impl DivAssign<$target> for Color {
308                fn div_assign(&mut self, s: $target) {
309                    let [v1, v2, v3, a] = self.levels();
310                    let s = s as f64;
311                    let levels = clamp_levels([v1 / s, v2 / s, v3 / s, a]);
312                    self.update_channels(levels, self.mode);
313                }
314            }
315        )*
316    }
317}
318
319impl_ops!(i8, u8, i16, u16, f32);
320impl_ops!(i32, u32, f64);
321impl_as_ops!(isize, usize, i64, u64, i128, u128);
322
323#[cfg(test)]
324mod tests {
325    use crate::prelude::*;
326
327    macro_rules! test_ops {
328        ($($val: expr),*) => {
329            $(
330                // Mul<T> for Color
331                let c = color!(200, 50, 10, 100) * $val;
332                assert_eq!(c.channels(), [255, 100, 20, 100]);
333
334                // Mul<Color> for T
335                let c: Color = $val * color!(200, 50, 10, 100);
336                assert_eq!(c.channels(), [255, 100, 20, 100]);
337
338                // MulAssign<T> for Color
339                let mut c = color!(200, 50, 10, 100);
340                c *= $val;
341                assert_eq!(c.channels(), [255, 100, 20, 100]);
342
343                // Div<T> for Color
344                let c: Color = color!(100, 255, 0, 100) / $val;
345                assert_eq!(c.channels(), [50, 128, 0, 100]);
346
347                // DivAssign<T> for Color
348                let mut c = color!(200, 50, 10, 100);
349                c /= $val;
350                assert_eq!(c.channels(), [100, 25, 5, 100]);
351            )*
352        };
353    }
354
355    #[test]
356    fn test_ops() {
357        // Add
358        let c1 = color!(200, 50, 10, 100);
359        let c2 = color!(100, 50, 10, 100);
360        let c3 = c1 + c2;
361        assert_eq!(c3.channels(), [255, 100, 20, 100]);
362
363        // AddAssign
364        let mut c1 = color!(200, 50, 10, 100);
365        let c2 = color!(100, 50, 10, 100);
366        c1 += c2;
367        assert_eq!(c1.channels(), [255, 100, 20, 100]);
368
369        // Sub
370        let c1 = color!(200, 100, 20, 200);
371        let c2 = color!(100, 50, 30, 100);
372        let c3 = c1 - c2;
373        assert_eq!(c3.channels(), [100, 50, 0, 200]);
374
375        // SubAssign
376        let mut c1 = color!(200, 100, 20, 200);
377        let c2 = color!(100, 50, 30, 100);
378        c1 -= c2;
379        assert_eq!(c1.channels(), [100, 50, 0, 200]);
380
381        test_ops!(2i8, 2u8, 2i16, 2u16, 2i32, 2u32, 2f32, 2f64);
382    }
383}