| 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 | |