Skip to content

Commit b7b4d9f

Browse files
author
jnickg
committed
Add missing map2/apply2 functions to Pixel trait
* Account for alpha channel to simplify some image math operations where naively operating on all pixel components won't work (such as summing images, where usually picking one alpha value from `self` or `other` makes more sense)
1 parent 1207aca commit b7b4d9f

File tree

2 files changed

+117
-0
lines changed

2 files changed

+117
-0
lines changed

src/color.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,37 @@ impl<T: $($bound+)*> Pixel for $ident<T> {
364364
}
365365
}
366366

367+
fn map2_with_alpha<F, G>(&self, other: &Self, f: F, g: G) -> $ident<T> where F: FnMut(T, T) -> T, G: FnMut(T, T) -> T {
368+
let mut this = (*self).clone();
369+
this.apply2_with_alpha(other, f, g);
370+
this
371+
}
372+
373+
fn apply2_with_alpha<F, G>(&mut self, other: &$ident<T>, mut f: F, mut g: G) where F: FnMut(T, T) -> T, G: FnMut(T, T) -> T {
374+
const ALPHA: usize = $channels - $alphas;
375+
for (a, &b) in self.0[..ALPHA].iter_mut().zip(other.0[..ALPHA].iter()) {
376+
*a = f(*a, b)
377+
}
378+
// The branch of this match is `const`. This way ensures that no subexpression fails the
379+
// `const_err` lint (the expression `self.0[ALPHA]` would).
380+
if let (Some(a), Some(b)) = (self.0.get_mut(ALPHA), other.0.get(ALPHA)) {
381+
*a = g(*a, *b)
382+
}
383+
}
384+
385+
fn map2_without_alpha<F>(&self, other: &Self, f: F) -> $ident<T> where F: FnMut(T, T) -> T {
386+
let mut this = (*self).clone();
387+
this.apply2_without_alpha(other, f);
388+
this
389+
}
390+
391+
fn apply2_without_alpha<F>(&mut self, other: &$ident<T>, mut f: F) where F: FnMut(T, T) -> T {
392+
const ALPHA: usize = $channels - $alphas;
393+
for (a, &b) in self.0[..ALPHA].iter_mut().zip(other.0[..ALPHA].iter()) {
394+
*a = f(*a, b)
395+
}
396+
}
397+
367398
fn invert(&mut self) {
368399
Invert::invert(self)
369400
}
@@ -924,6 +955,63 @@ mod tests {
924955
assert_eq!(rgb, Rgb([0, 0, 0]));
925956
}
926957

958+
#[test]
959+
fn test_apply2_with_alpha_rgba() {
960+
let mut rgba = Rgba([1, 2, 3, 64]);
961+
rgba.apply2_with_alpha(&Rgba([4, 5, 6, 128]), |s, o| s + o, |s, o| (s + o) / 2);
962+
assert_eq!(rgba, Rgba([5, 7, 9, 96]));
963+
}
964+
965+
#[test]
966+
fn test_apply2_with_alpha_rgb() {
967+
let mut rgb = Rgb([1, 2, 3]);
968+
rgb.apply2_with_alpha(&Rgb([4, 5, 6]), |s, o| s + o, |_s, _o| panic!("bug"));
969+
assert_eq!(rgb, Rgb([5, 7, 9]));
970+
}
971+
972+
#[test]
973+
fn test_map2_with_alpha_rgba() {
974+
let rgba = Rgba([1, 2, 3, 64]).map2_with_alpha(
975+
&Rgba([4, 5, 6, 128]),
976+
|s, o| s + o,
977+
|s, o| (s + o) / 2,
978+
);
979+
assert_eq!(rgba, Rgba([5, 7, 9, 96]));
980+
}
981+
982+
#[test]
983+
fn test_map2_with_alpha_rgb() {
984+
let rgb =
985+
Rgb([1, 2, 3]).map2_with_alpha(&Rgb([4, 5, 6]), |s, o| s + o, |_s, _o| panic!("bug"));
986+
assert_eq!(rgb, Rgb([5, 7, 9]));
987+
}
988+
989+
#[test]
990+
fn test_apply2_without_alpha_rgba() {
991+
let mut rgba = Rgba([1, 2, 3, 64]);
992+
rgba.apply2_without_alpha(&Rgba([4, 5, 6, 128]), |s, o| s + o);
993+
assert_eq!(rgba, Rgba([5, 7, 9, 64]));
994+
}
995+
996+
#[test]
997+
fn test_apply2_without_alpha_rgb() {
998+
let mut rgb = Rgb([1, 2, 3]);
999+
rgb.apply2_without_alpha(&Rgb([4, 5, 6]), |s, o| s + o);
1000+
assert_eq!(rgb, Rgb([5, 7, 9]));
1001+
}
1002+
1003+
#[test]
1004+
fn test_map2_without_alpha_rgba() {
1005+
let rgba = Rgba([1, 2, 3, 64]).map2_without_alpha(&Rgba([4, 5, 6, 128]), |s, o| s + o);
1006+
assert_eq!(rgba, Rgba([5, 7, 9, 64]));
1007+
}
1008+
1009+
#[test]
1010+
fn test_map2_without_alpha_rgb() {
1011+
let rgb = Rgb([1, 2, 3]).map2_without_alpha(&Rgb([4, 5, 6]), |s, o| s + o);
1012+
assert_eq!(rgb, Rgb([5, 7, 9]));
1013+
}
1014+
9271015
#[test]
9281016
fn test_blend_luma_alpha() {
9291017
let a = &mut LumaA([255_u8, 255]);

src/traits.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,35 @@ pub trait Pixel: Copy + Clone {
356356
where
357357
F: FnMut(Self::Subpixel, Self::Subpixel) -> Self::Subpixel;
358358

359+
/// Apply the function ```f``` to each channel except alpha channel.
360+
/// Apply the function ```g``` to the alpha channel.
361+
fn map2_with_alpha<F, G>(&self, other: &Self, f: F, g: G) -> Self
362+
where
363+
F: FnMut(Self::Subpixel, Self::Subpixel) -> Self::Subpixel,
364+
G: FnMut(Self::Subpixel, Self::Subpixel) -> Self::Subpixel;
365+
366+
/// Apply the function ```f``` to each channel (except alpha) of this
367+
/// pixel and ```other``` pairwise. Then apply the function ```g``` to
368+
/// the alpha channel of this pixel and ```other``` pairwise.
369+
///
370+
/// Works in place.
371+
fn apply2_with_alpha<F, G>(&mut self, other: &Self, f: F, g: G)
372+
where
373+
F: FnMut(Self::Subpixel, Self::Subpixel) -> Self::Subpixel,
374+
G: FnMut(Self::Subpixel, Self::Subpixel) -> Self::Subpixel;
375+
376+
/// Apply the function ```f``` to each channel except the alpha channel,
377+
/// of this pixel and ```other``` pairwise.
378+
fn map2_without_alpha<F>(&self, other: &Self, f: F) -> Self
379+
where
380+
F: FnMut(Self::Subpixel, Self::Subpixel) -> Self::Subpixel;
381+
382+
/// Apply the function ```f``` to each channel except the alpha channel,
383+
/// of this pixel and ```other``` pairwise. Works in place.
384+
fn apply2_without_alpha<F>(&mut self, other: &Self, f: F)
385+
where
386+
F: FnMut(Self::Subpixel, Self::Subpixel) -> Self::Subpixel;
387+
359388
/// Invert this pixel
360389
fn invert(&mut self);
361390

0 commit comments

Comments
 (0)