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