1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
use core::ops::{Add, Mul};

/// A collection of subpixels that should make working with multi-channeled images more convenient.
///
/// This struct implements both `Add` and `Mul`, so that it can be used as the data type for a
/// [`Matrix`](crate::Matrix)
///
/// Instead of needing to divide the Red, Green, and Blue channels out so that each has its own
/// image `Matrix`, using `SubPixels` gives you the ability to perform all three convolutions at
/// once.
///
/// # Example
/// ```
/// # use convolve2d::SubPixels;
/// let sp1 = SubPixels([1, 2, 3]);
/// let sp2 = SubPixels([4, 5, 6]);
///
/// assert_eq!(sp1 * 2, SubPixels([2, 4, 6]));
/// assert_eq!(sp1 + sp2, SubPixels([5, 7, 9]));
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SubPixels<T: Copy, const N: usize>(pub [T; N]);

impl<T: Copy, const N: usize> SubPixels<T, N> {
    /// Perform an infallible type conversion
    ///
    /// # Example
    /// ```
    /// # use convolve2d::SubPixels;
    /// let pixel: SubPixels<u8, 3> = SubPixels([1, 2, 3]);
    /// assert_eq!(pixel.convert::<f64>(), SubPixels([1.0, 2.0, 3.0]));
    /// ```
    pub fn convert<U: From<T> + Copy + Default>(self) -> SubPixels<U, N> {
        let mut arr = [U::default(); N];
        for (a, n) in arr.iter_mut().zip(self.0) {
            *a = U::from(n);
        }
        SubPixels(arr)
    }

    /// Perform a fallible type conversion
    ///
    /// # Example
    /// ```
    /// # use convolve2d::SubPixels;
    /// let pixel: SubPixels<u32, 3> = SubPixels([1, 2, 3]);
    /// assert_eq!(pixel.try_convert::<u8>(), Ok(SubPixels([1, 2, 3])));
    /// ```
    pub fn try_convert<U: TryFrom<T> + Copy + Default>(self) -> Result<SubPixels<U, N>, U::Error> {
        let mut arr = [U::default(); N];
        for (a, n) in arr.iter_mut().zip(self.0) {
            *a = U::try_from(n)?;
        }
        Ok(SubPixels(arr))
    }

    /// Perform a map operation, applying the provided function to each subpixel.
    ///
    /// # Example
    /// ```
    /// # use convolve2d::SubPixels;
    /// let pixel = SubPixels([1, 2, 3]);
    /// assert_eq!(pixel.map(|x| x + 1), SubPixels([2, 3, 4]));
    /// ```
    pub fn map<F, O>(self, operation: F) -> SubPixels<O, N>
    where
        F: Fn(T) -> O,
        O: Default + Copy,
    {
        let mut arr = [O::default(); N];
        for (i, x) in self.0.into_iter().enumerate() {
            arr[i] = operation(x);
        }
        SubPixels(arr)
    }
}

impl<T: Copy, const N: usize> From<[T; N]> for SubPixels<T, N> {
    fn from(other: [T; N]) -> Self {
        Self(other)
    }
}

impl<T: Add<Output = T> + Copy, const N: usize> Add for SubPixels<T, N> {
    type Output = Self;

    fn add(mut self, rhs: Self) -> Self::Output {
        for (i, x) in rhs.0.into_iter().enumerate() {
            self.0[i] = self.0[i] + x;
        }
        self
    }
}

impl<T, C, O, const N: usize> Mul<C> for SubPixels<T, N>
where
    C: Copy,
    T: Mul<C, Output = O> + Copy,
    O: Default + Copy,
{
    type Output = SubPixels<O, N>;

    fn mul(self, rhs: C) -> Self::Output {
        let mut arr = [O::default(); N];
        self.0
            .into_iter()
            .map(|a| a * rhs)
            .enumerate()
            .for_each(|(i, v)| arr[i] = v);
        SubPixels(arr)
    }
}

impl<T: Copy + Default, const N: usize> Default for SubPixels<T, N> {
    fn default() -> Self {
        Self([T::default(); N])
    }
}