1 | use crate::formats::gray::Gray_v08 as Gray; |
2 | use super::pixel::ComponentMap; |
3 | use crate::alt::GrayAlpha; |
4 | use crate::alt::ARGB; |
5 | use crate::alt::GRB; |
6 | use crate::{RGB, RGBA}; |
7 | use core::iter::Sum; |
8 | use core::ops::*; |
9 | |
10 | #[cfg (feature = "checked_fns" )] |
11 | macro_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" ))] |
39 | macro_rules! impl_struct_checked { |
40 | ($ty:ident, $field_ty:ident, => $($field:tt)+) => {}; |
41 | } |
42 | |
43 | macro_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 | |
147 | macro_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 | |
225 | macro_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 | |
317 | impl_scalar! {RGB} |
318 | impl_scalar! {RGBA} |
319 | impl_scalar! {ARGB} |
320 | impl_scalar! {GRB} |
321 | impl_scalar! {Gray} |
322 | impl_scalar! {GrayAlpha} |
323 | |
324 | impl_struct_ops_opaque! {RGB => r g b} |
325 | impl_struct_ops_opaque! {GRB => g r b} |
326 | impl_struct_ops_opaque! {Gray => 0} |
327 | |
328 | impl_struct_ops_alpha! {RGBA => r g b a} |
329 | impl_struct_ops_alpha! {ARGB => a r g b} |
330 | impl_struct_ops_alpha! {GrayAlpha => 0 1} |
331 | |
332 | #[cfg (test)] |
333 | mod 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 | } |
487 | |