convolve2d/
subpixels.rs

1use core::ops::{Add, Mul};
2
3/// A collection of subpixels that should make working with multi-channeled images more convenient.
4///
5/// This struct implements both `Add` and `Mul`, so that it can be used as the data type for a
6/// [`Matrix`](crate::Matrix)
7///
8/// Instead of needing to divide the Red, Green, and Blue channels out so that each has its own
9/// image `Matrix`, using `SubPixels` gives you the ability to perform all three convolutions at
10/// once.
11///
12/// # Example
13/// ```
14/// # use convolve2d::SubPixels;
15/// let sp1 = SubPixels([1, 2, 3]);
16/// let sp2 = SubPixels([4, 5, 6]);
17///
18/// assert_eq!(sp1 * 2, SubPixels([2, 4, 6]));
19/// assert_eq!(sp1 + sp2, SubPixels([5, 7, 9]));
20/// ```
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub struct SubPixels<T: Copy, const N: usize>(pub [T; N]);
23
24impl<T: Copy, const N: usize> SubPixels<T, N> {
25    /// Perform an infallible type conversion
26    ///
27    /// # Example
28    /// ```
29    /// # use convolve2d::SubPixels;
30    /// let pixel: SubPixels<u8, 3> = SubPixels([1, 2, 3]);
31    /// assert_eq!(pixel.convert::<f64>(), SubPixels([1.0, 2.0, 3.0]));
32    /// ```
33    pub fn convert<U: From<T> + Copy + Default>(self) -> SubPixels<U, N> {
34        let mut arr = [U::default(); N];
35        for (a, n) in arr.iter_mut().zip(self.0) {
36            *a = U::from(n);
37        }
38        SubPixels(arr)
39    }
40
41    /// Perform a fallible type conversion
42    ///
43    /// # Example
44    /// ```
45    /// # use convolve2d::SubPixels;
46    /// let pixel: SubPixels<u32, 3> = SubPixels([1, 2, 3]);
47    /// assert_eq!(pixel.try_convert::<u8>(), Ok(SubPixels([1, 2, 3])));
48    /// ```
49    pub fn try_convert<U: TryFrom<T> + Copy + Default>(self) -> Result<SubPixels<U, N>, U::Error> {
50        let mut arr = [U::default(); N];
51        for (a, n) in arr.iter_mut().zip(self.0) {
52            *a = U::try_from(n)?;
53        }
54        Ok(SubPixels(arr))
55    }
56
57    /// Perform a map operation, applying the provided function to each subpixel.
58    ///
59    /// # Example
60    /// ```
61    /// # use convolve2d::SubPixels;
62    /// let pixel = SubPixels([1, 2, 3]);
63    /// assert_eq!(pixel.map(|x| x + 1), SubPixels([2, 3, 4]));
64    /// ```
65    pub fn map<F, O>(self, operation: F) -> SubPixels<O, N>
66    where
67        F: Fn(T) -> O,
68        O: Default + Copy,
69    {
70        let mut arr = [O::default(); N];
71        for (i, x) in self.0.into_iter().enumerate() {
72            arr[i] = operation(x);
73        }
74        SubPixels(arr)
75    }
76}
77
78impl<T: Copy, const N: usize> From<[T; N]> for SubPixels<T, N> {
79    fn from(other: [T; N]) -> Self {
80        Self(other)
81    }
82}
83
84impl<T: Add<Output = T> + Copy, const N: usize> Add for SubPixels<T, N> {
85    type Output = Self;
86
87    fn add(mut self, rhs: Self) -> Self::Output {
88        for (i, x) in rhs.0.into_iter().enumerate() {
89            self.0[i] = self.0[i] + x;
90        }
91        self
92    }
93}
94
95impl<T, C, O, const N: usize> Mul<C> for SubPixels<T, N>
96where
97    C: Copy,
98    T: Mul<C, Output = O> + Copy,
99    O: Default + Copy,
100{
101    type Output = SubPixels<O, N>;
102
103    fn mul(self, rhs: C) -> Self::Output {
104        let mut arr = [O::default(); N];
105        self.0
106            .into_iter()
107            .map(|a| a * rhs)
108            .enumerate()
109            .for_each(|(i, v)| arr[i] = v);
110        SubPixels(arr)
111    }
112}
113
114impl<T: Copy + Default, const N: usize> Default for SubPixels<T, N> {
115    fn default() -> Self {
116        Self([T::default(); N])
117    }
118}