rgb/legacy/internal/
ops.rs

1use crate::formats::gray::Gray_v08 as Gray;
2use super::pixel::ComponentMap;
3use crate::alt::GrayAlpha;
4use crate::alt::ARGB;
5use crate::alt::GRB;
6use crate::{RGB, RGBA};
7use core::iter::Sum;
8use core::ops::*;
9
10#[cfg(feature = "checked_fns")]
11macro_rules! impl_struct_checked {
12    ($ty:ident, $field_ty:ident, => $($field:tt)+) => {
13        impl $ty<$field_ty>
14        {
15            /// `px.checked_add(px)`
16            #[inline(always)]
17            pub fn checked_add(self, rhs: $ty<$field_ty>) -> Option<Self> {
18                Some($ty {
19                    $(
20                        $field: self.$field.checked_add(rhs.$field)?,
21                    )+
22                })
23            }
24
25            /// `px.checked_sub(px)`
26            #[inline(always)]
27            pub fn checked_sub(self, rhs: $ty<$field_ty>) -> Option<Self> {
28                Some($ty {
29                    $(
30                        $field: self.$field.checked_sub(rhs.$field)?,
31                    )+
32                })
33            }
34        }
35    }
36}
37
38#[cfg(not(feature = "checked_fns"))]
39macro_rules! impl_struct_checked {
40    ($ty:ident, $field_ty:ident, => $($field:tt)+) => {};
41}
42
43macro_rules! impl_struct_ops_opaque {
44    ($ty:ident => $($field:tt)+) => {
45        /// `px + px`
46        impl<T: Add> Add for $ty<T> {
47            type Output = $ty<<T as Add>::Output>;
48
49            #[inline(always)]
50            fn add(self, other: $ty<T>) -> Self::Output {
51                $ty {
52                    $(
53                        $field: self.$field + other.$field,
54                    )+
55                }
56            }
57        }
58
59        /// `px + px`
60        impl<T> AddAssign for $ty<T> where
61            T: Add<Output = T> + Copy
62        {
63            #[inline(always)]
64            fn add_assign(&mut self, other: $ty<T>) {
65                *self = Self {
66                    $(
67                        $field: self.$field + other.$field,
68                    )+
69                };
70            }
71        }
72
73        /// `px * px`
74        impl<T: Mul> Mul for $ty<T> {
75            type Output = $ty<<T as Mul>::Output>;
76
77            #[inline(always)]
78            fn mul(self, other: $ty<T>) -> Self::Output {
79                $ty {
80                    $(
81                        $field: self.$field * other.$field,
82                    )+
83                }
84            }
85        }
86
87        /// `px * px`
88        impl<T> MulAssign for $ty<T> where
89            T: Mul<Output = T> + Copy
90        {
91            #[inline(always)]
92            fn mul_assign(&mut self, other: $ty<T>) {
93                *self = Self {
94                    $(
95                        $field: self.$field * other.$field,
96                    )+
97                };
98            }
99        }
100
101        /// `px - px`
102        impl<T: Sub> Sub for $ty<T> {
103            type Output = $ty<<T as Sub>::Output>;
104
105            #[inline(always)]
106            fn sub(self, other: $ty<T>) -> Self::Output {
107                $ty {
108                    $(
109                        $field: self.$field - other.$field,
110                    )+
111                }
112            }
113        }
114
115        /// `px - px`
116        impl<T> SubAssign for $ty<T> where
117            T: Sub<Output = T> + Copy
118        {
119            #[inline(always)]
120            fn sub_assign(&mut self, other: $ty<T>) {
121                *self = Self {
122                    $(
123                        $field: self.$field - other.$field,
124                    )+
125                };
126            }
127        }
128
129        impl<T> Sum<$ty<T>> for $ty<T> where T: Default + Add<Output=T> {
130            #[inline(always)]
131            fn sum<I: Iterator<Item=Self>>(iter: I) -> Self {
132                iter.fold($ty::default(), Add::add)
133            }
134        }
135
136        impl_struct_checked!($ty, u8, => $($field)+);
137        impl_struct_checked!($ty, u16, => $($field)+);
138        impl_struct_checked!($ty, u32, => $($field)+);
139        impl_struct_checked!($ty, u64, => $($field)+);
140        impl_struct_checked!($ty, i8, => $($field)+);
141        impl_struct_checked!($ty, i16, => $($field)+);
142        impl_struct_checked!($ty, i32, => $($field)+);
143        impl_struct_checked!($ty, i64, => $($field)+);
144    };
145}
146
147macro_rules! impl_struct_ops_alpha {
148    ($ty:ident => $($field:tt)+) => {
149        /// `px + px`
150        impl<T: Add, A: Add> Add for $ty<T, A> {
151            type Output = $ty<<T as Add>::Output, <A as Add>::Output>;
152
153            #[inline(always)]
154            fn add(self, other: $ty<T, A>) -> Self::Output {
155                $ty {
156                    $(
157                        $field: self.$field + other.$field,
158                    )+
159                }
160            }
161        }
162
163        /// `px + px`
164        impl<T, A> AddAssign for $ty<T, A> where
165            T: Add<Output = T> + Copy,
166            A: Add<Output = A> + Copy
167        {
168            #[inline(always)]
169            fn add_assign(&mut self, other: $ty<T, A>) {
170                *self = Self {
171                    $(
172                        $field: self.$field + other.$field,
173                    )+
174                };
175            }
176        }
177
178        /// `px - px`
179        impl<T: Sub, A: Sub> Sub for $ty<T, A> {
180            type Output = $ty<<T as Sub>::Output, <A as Sub>::Output>;
181
182            #[inline(always)]
183            fn sub(self, other: $ty<T, A>) -> Self::Output {
184                $ty {
185                    $(
186                        $field: self.$field - other.$field,
187                    )+
188                }
189            }
190        }
191
192        /// `px - px`
193        impl<T, A> SubAssign for $ty<T, A> where
194            T: Sub<Output = T> + Copy,
195            A: Sub<Output = A> + Copy
196        {
197            #[inline(always)]
198            fn sub_assign(&mut self, other: $ty<T, A>) {
199                *self = Self {
200                    $(
201                        $field: self.$field - other.$field,
202                    )+
203                };
204            }
205        }
206
207        impl<T, A> Sum<$ty<T, A>> for $ty<T, A> where T: Default + Add<Output=T>, A: Default + Add<Output=A> {
208            #[inline(always)]
209            fn sum<I: Iterator<Item=Self>>(iter: I) -> Self {
210                iter.fold($ty::default(), Add::add)
211            }
212        }
213
214        impl_struct_checked!($ty, u8, => $($field)+);
215        impl_struct_checked!($ty, u16, => $($field)+);
216        impl_struct_checked!($ty, u32, => $($field)+);
217        impl_struct_checked!($ty, u64, => $($field)+);
218        impl_struct_checked!($ty, i8, => $($field)+);
219        impl_struct_checked!($ty, i16, => $($field)+);
220        impl_struct_checked!($ty, i32, => $($field)+);
221        impl_struct_checked!($ty, i64, => $($field)+);
222    };
223}
224
225macro_rules! impl_scalar {
226    ($ty:ident) => {
227        /// `px - 1`
228        impl<T> Sub<T> for $ty<T>
229        where T: Copy + Sub<Output = T>
230        {
231            type Output = $ty<<T as Sub>::Output>;
232
233            #[inline(always)]
234            fn sub(self, r: T) -> Self::Output {
235                self.map(|l| l - r)
236            }
237        }
238
239        /// `px - 1`
240        impl<T> SubAssign<T> for $ty<T>
241        where T: Copy + Sub<Output = T>
242        {
243            #[inline(always)]
244            fn sub_assign(&mut self, r: T) {
245                *self = self.map(|l| l - r);
246            }
247        }
248
249        /// `px + 1`
250        impl<T> Add<T> for $ty<T>
251        where T: Copy + Add<Output = T>
252        {
253            type Output = $ty<T>;
254
255            #[inline(always)]
256            fn add(self, r: T) -> Self::Output {
257                self.map(|l| l + r)
258            }
259        }
260
261        /// `px + 1`
262        impl<T> AddAssign<T> for $ty<T>
263        where T: Copy + Add<Output = T>
264        {
265            #[inline(always)]
266            fn add_assign(&mut self, r: T) {
267                *self = self.map(|l| l + r);
268            }
269        }
270
271        /// `px * 1`
272        impl<T> Mul<T> for $ty<T>
273        where T: Copy + Mul<Output = T>
274        {
275            type Output = $ty<T>;
276
277            #[inline(always)]
278            fn mul(self, r: T) -> Self::Output {
279                self.map(|l| l * r)
280            }
281        }
282
283        /// `px * 1`
284        impl<T> MulAssign<T> for $ty<T>
285        where T: Copy + Mul<Output = T>
286        {
287            #[inline(always)]
288            fn mul_assign(&mut self, r: T) {
289                *self = self.map(|l| l * r);
290            }
291        }
292
293        /// `px / 1`
294        impl<T> Div<T> for $ty<T>
295        where T: Copy + Div<Output = T>
296        {
297            type Output = $ty<T>;
298
299            #[inline(always)]
300            fn div(self, r: T) -> Self::Output {
301                self.map(|l| l / r)
302            }
303        }
304
305        /// `px * 1`
306        impl<T> DivAssign<T> for $ty<T>
307        where T: Copy + Div<Output = T>
308        {
309            #[inline(always)]
310            fn div_assign(&mut self, r: T) {
311                *self = self.map(|l| l / r);
312            }
313        }
314    };
315}
316
317impl_scalar! {RGB}
318impl_scalar! {RGBA}
319impl_scalar! {ARGB}
320impl_scalar! {GRB}
321impl_scalar! {Gray}
322impl_scalar! {GrayAlpha}
323
324impl_struct_ops_opaque! {RGB => r g b}
325impl_struct_ops_opaque! {GRB => g r b}
326impl_struct_ops_opaque! {Gray => 0}
327
328impl_struct_ops_alpha! {RGBA => r g b a}
329impl_struct_ops_alpha! {ARGB => a r g b}
330impl_struct_ops_alpha! {GrayAlpha => 0 1}
331
332#[cfg(test)]
333mod test {
334    use super::*;
335    use core::num::Wrapping;
336    const WHITE_RGB: RGB<u8> = RGB::new(255, 255, 255);
337    const BLACK_RGB: RGB<u8> = RGB::new(0, 0, 0);
338    const RED_RGB: RGB<u8> = RGB::new(255, 0, 0);
339    const GREEN_RGB: RGB<u8> = RGB::new(0, 255, 0);
340    const BLUE_RGB: RGB<u8> = RGB::new(0, 0, 255);
341
342    const WHITE_RGBA: RGBA<u8> = RGBA::new(255, 255, 255, 255);
343    const BLACK_RGBA: RGBA<u8> = RGBA::new(0, 0, 0, 0);
344    const RED_RGBA: RGBA<u8> = RGBA::new(255, 0, 0, 255);
345    const GREEN_RGBA: RGBA<u8> = RGBA::new(0, 255, 0, 0);
346    const BLUE_RGBA: RGBA<u8> = RGBA::new(0, 0, 255, 255);
347
348    #[test]
349    fn test_add() {
350        assert_eq!(RGB::new(2,4,6), RGB::new(1,2,3) + RGB{r:1,g:2,b:3});
351        assert_eq!(RGB::new(2.,4.,6.), RGB::new(1.,3.,5.) + 1.);
352
353        assert_eq!(RGBA::new_alpha(2f32,4.,6.,8u32), RGBA::new_alpha(1f32,2.,3.,4u32) + RGBA{r:1f32,g:2.0,b:3.0,a:4u32});
354        assert_eq!(RGBA::new(2i16,4,6,8), RGBA::new(1,3,5,7) + 1);
355
356        assert_eq!(RGB::new(255, 255, 0), RED_RGB+GREEN_RGB);
357        assert_eq!(RGB::new(255, 0, 0), RED_RGB+RGB::new(0, 0, 0));
358        assert_eq!(WHITE_RGB, BLACK_RGB + 255);
359
360        assert_eq!(RGBA::new(255, 255, 0, 255), RED_RGBA+GREEN_RGBA);
361        assert_eq!(RGBA::new(255, 0, 0, 255), RED_RGBA+RGBA::new(0, 0, 0, 0));
362        assert_eq!(WHITE_RGBA, BLACK_RGBA + 255);
363    }
364
365    #[test]
366    #[cfg(feature = "checked_fns")]
367    fn test_checked_add() {
368        assert_eq!(WHITE_RGB.checked_add(WHITE_RGB), None);
369        assert_eq!(RGB::<u8>::new(255, 255, 255).checked_add(RGB::<u8>::new(255, 0, 0)), None);
370        assert_eq!(RGB::<u8>::new(255, 255, 255).checked_add(RGB::<u8>::new(0, 255, 0)), None);
371        assert_eq!(RGB::<u8>::new(255, 255, 255).checked_add(RGB::<u8>::new(0, 0, 255)), None);
372        assert_eq!(WHITE_RGBA.checked_add(BLACK_RGBA), Some(WHITE_RGBA));
373
374        assert_eq!(RGB::<i8>::new(-128, 2, 3).checked_add(RGB::<i8>::new(-1, 0, 0)), None);
375        assert_eq!(RGB::<i8>::new(2, -128, 3).checked_add(RGB::<i8>::new(0, -1, 0)), None);
376        assert_eq!(RGB::<i8>::new(2, 2, -128).checked_add(RGB::<i8>::new(0, 0, -1)), None);
377        assert_eq!(RGB::<i8>::new(2, 2, -128).checked_add(RGB::<i8>::new(0, 0, 1)), Some(RGB::<i8>::new(2, 2, -127)));
378    }
379
380    #[test]
381    #[should_panic]
382    #[cfg(debug_assertions)]
383    fn test_add_overflow() {
384        assert_ne!(RGBA::new(255u8, 255, 0, 0), RED_RGBA + BLUE_RGBA);
385    }
386
387    #[test]
388    fn test_sub() {
389        assert_eq!(RED_RGB, (WHITE_RGB - GREEN_RGB) - BLUE_RGB);
390        assert_eq!(BLACK_RGB, WHITE_RGB - 255);
391
392        assert_eq!(RGBA::new(255, 255, 0, 0), WHITE_RGBA - BLUE_RGBA);
393        assert_eq!(BLACK_RGBA, WHITE_RGBA - 255);
394    }
395
396    #[test]
397    #[cfg(feature = "checked_fns")]
398    fn test_checked_sub() {
399        assert_eq!(RGBA::<u8>::new(2,4,6,111).checked_sub(RGBA::<u8>::new(3,4,6,0)), None);
400        assert_eq!(RGB::<u8>::new(2,4,6).checked_sub(RGB::<u8>::new(2,5,6)), None);
401        assert_eq!(RGB::<u8>::new(2,4,6).checked_sub(RGB::<u8>::new(2,4,7)), None);
402        assert_eq!(RGB::<u8>::new(2,4,6).checked_sub(RGB::<u8>::new(2,4,6)), Some(BLACK_RGB));
403
404        assert_eq!(RGB::<i8>::new(-128,4,6).checked_sub(RGB::<i8>::new(1,4,7)), None);
405        assert_eq!(RGB::<i8>::new(2,-128,6).checked_sub(RGB::<i8>::new(2,1,7)), None);
406        assert_eq!(RGB::<i8>::new(2,4,-128).checked_sub(RGB::<i8>::new(2,4,1)), None);
407        assert_eq!(RGB::<i8>::new(2,4,6).checked_sub(RGB::<i8>::new(-2,4,6)), Some(RGB::<i8>::new(4,0,0)));
408    }
409
410    #[test]
411    fn test_add_assign() {
412        let mut green_rgb = RGB::new(0, 255, 0);
413        green_rgb += RGB::new(255, 0, 255);
414        assert_eq!(WHITE_RGB, green_rgb);
415
416        let mut black_rgb = RGB::new(0, 0, 0);
417        black_rgb += 255;
418        assert_eq!(WHITE_RGB, black_rgb);
419
420        let mut green_rgba = RGBA::new(0, 255, 0, 0);
421        green_rgba += RGBA::new(255, 0, 255, 255);
422        assert_eq!(WHITE_RGBA, green_rgba);
423
424        let mut black_rgba = RGBA::new(0, 0, 0, 0);
425        black_rgba += 255;
426        assert_eq!(WHITE_RGBA, black_rgba);
427    }
428
429    #[test]
430    fn test_sub_assign() {
431        let mut green_rgb = RGB::new(0, 255, 0);
432        green_rgb -= RGB::new(0, 255, 0);
433        assert_eq!(BLACK_RGB, green_rgb);
434
435        let mut white_rgb = RGB::new(255, 255, 255);
436        white_rgb -= 255;
437        assert_eq!(BLACK_RGB, white_rgb);
438
439        let mut green_rgba = RGBA::new(0, 255, 0, 0);
440        green_rgba -= RGBA::new(0, 255, 0, 0);
441        assert_eq!(BLACK_RGBA, green_rgba);
442
443        let mut white_rgba = RGBA::new(255, 255, 255, 255);
444        white_rgba -= 255;
445        assert_eq!(BLACK_RGBA, white_rgba);
446    }
447
448    #[test]
449    fn test_mult() {
450        assert_eq!(RGB::new(0.5,1.5,2.5), RGB::new(1.,3.,5.) * 0.5);
451        assert_eq!(RGBA::new(2,4,6,8), RGBA::new(1,2,3,4) * 2);
452        assert_eq!(RGB::new(0.5,1.5,2.5) * RGB::new(1.,3.,5.),
453        RGB::new(0.5,4.5,12.5));
454    }
455
456    #[test]
457    fn test_mult_assign() {
458        let mut green_rgb = RGB::new(0u16, 255, 0);
459        green_rgb *= 1;
460        assert_eq!(RGB::new(0, 255, 0), green_rgb);
461        green_rgb *= 2;
462        assert_eq!(RGB::new(0, 255*2, 0), green_rgb);
463
464        let mut rgb = RGB::new(0.5,1.5,2.5);
465        rgb *= RGB::new(1.,3.,5.);
466        assert_eq!(rgb, RGB::new(0.5,4.5,12.5));
467
468        let mut green_rgba = RGBA::new(0u16, 255, 0, 0);
469        green_rgba *= 1;
470        assert_eq!(RGBA::new(0, 255, 0, 0), green_rgba);
471        green_rgba *= 2;
472        assert_eq!(RGBA::new(0, 255*2, 0, 0), green_rgba);
473    }
474
475    #[test]
476    fn sum() {
477        let s1 = [RGB::new(1u8,1,1), RGB::new(2,3,4)].iter().copied().sum::<RGB<u8>>();
478        let s2 = [RGB::new(1u16,1,1), RGB::new(2,3,4)].iter().copied().sum::<RGB<u16>>();
479        let s3 = [RGBA::new_alpha(1u16,1,1,Wrapping(1u16)), RGBA::new_alpha(2,3,4,Wrapping(5))].iter().copied().sum::<RGBA<u16, Wrapping<u16>>>();
480        let s4 = [RGBA::new_alpha(1u16,1,1,1u16), RGBA::new_alpha(2,3,4,5)].iter().copied().sum::<RGBA<u16, u16>>();
481        assert_eq!(s1, RGB::new(3, 4, 5));
482        assert_eq!(s2, RGB::new(3, 4, 5));
483        assert_eq!(s3, RGBA::new_alpha(3, 4, 5, Wrapping(6)));
484        assert_eq!(s4, RGBA::new_alpha(3, 4, 5, 6));
485    }
486}