Skip to main content

image_texel/layout/
matrix.rs

1//! Different styles of matrices.
2use crate::image::{ImageMut, ImageRef};
3use crate::layout::{
4    Coord, Decay, Layout, MismatchedPixelError, Raster, RasterMut, SliceLayout, Take, TexelLayout,
5    TryMend,
6};
7
8use crate::{AsTexel, Texel};
9
10/// A matrix of packed texels (channel groups).
11///
12/// This is a simple layout of exactly width·height homogeneous pixels.
13#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
14pub struct MatrixBytes {
15    pub(crate) element: TexelLayout,
16    pub(crate) first_dim: usize,
17    pub(crate) second_dim: usize,
18}
19
20/// A layout that's a matrix of elements.
21pub trait MatrixLayout: Layout {
22    /// The valid matrix specification of this layout.
23    ///
24    /// This call should not fail, or panic. Otherwise, prefer an optional getter for the
25    /// [`StridedBytes`][`crate::layout::StridedBytes`] and have the caller decay their own buffer.
26    fn matrix(&self) -> MatrixBytes;
27}
28
29impl<L> MatrixLayout for &'_ L
30where
31    L: MatrixLayout,
32{
33    fn matrix(&self) -> MatrixBytes {
34        L::matrix(*self)
35    }
36}
37
38impl<L> MatrixLayout for &'_ mut L
39where
40    L: MatrixLayout,
41{
42    fn matrix(&self) -> MatrixBytes {
43        L::matrix(*self)
44    }
45}
46
47impl MatrixBytes {
48    pub fn empty(element: TexelLayout) -> Self {
49        MatrixBytes {
50            element,
51            first_dim: 0,
52            second_dim: 0,
53        }
54    }
55
56    pub fn from_width_height(
57        element: TexelLayout,
58        first_dim: usize,
59        second_dim: usize,
60    ) -> Option<Self> {
61        let max_index = first_dim.checked_mul(second_dim)?;
62        let _ = max_index.checked_mul(element.size)?;
63
64        Some(MatrixBytes {
65            element,
66            first_dim,
67            second_dim,
68        })
69    }
70
71    /// Get the element type of this matrix.
72    pub const fn element(&self) -> TexelLayout {
73        self.element
74    }
75
76    /// Get the width of this matrix.
77    pub const fn width(&self) -> usize {
78        self.first_dim
79    }
80
81    /// Get the height of this matrix.
82    pub const fn height(&self) -> usize {
83        self.second_dim
84    }
85
86    /// Get the required bytes for this layout.
87    pub const fn byte_len(self) -> usize {
88        // Exactly this does not overflow due to construction.
89        self.element.size * self.len()
90    }
91
92    /// The number of pixels in this layout
93    pub const fn len(self) -> usize {
94        self.first_dim * self.second_dim
95    }
96}
97
98impl Layout for MatrixBytes {
99    fn byte_len(&self) -> usize {
100        MatrixBytes::byte_len(*self)
101    }
102}
103
104impl Take for MatrixBytes {
105    fn take(&mut self) -> Self {
106        core::mem::replace(self, MatrixBytes::empty(self.element))
107    }
108}
109
110/// A matrix of packed texels (channel groups).
111///
112/// The underlying buffer may have more data allocated than this region and cause the overhead to
113/// be reused when resizing the image. All ways to construct this already check that all pixels
114/// within the resulting image can be addressed via an index.
115pub struct Matrix<P> {
116    pub(crate) width: usize,
117    pub(crate) height: usize,
118    pub(crate) pixel: Texel<P>,
119}
120
121impl<P> Matrix<P> {
122    pub fn from_width_height(pixel: Texel<P>, width: usize, height: usize) -> Option<Self> {
123        let max_index = Self::max_index(width, height)?;
124        let _ = max_index.checked_mul(pixel.size())?;
125
126        Some(Matrix {
127            width,
128            height,
129            pixel,
130        })
131    }
132
133    pub fn width_and_height(width: usize, height: usize) -> Option<Self>
134    where
135        P: AsTexel,
136    {
137        Self::from_width_height(P::texel(), width, height)
138    }
139
140    pub const fn empty(pixel: Texel<P>) -> Self {
141        Matrix {
142            pixel,
143            width: 0,
144            height: 0,
145        }
146    }
147
148    pub fn into_matrix_bytes(self) -> MatrixBytes {
149        MatrixBytes {
150            element: self.pixel.into(),
151            first_dim: self.width,
152            second_dim: self.height,
153        }
154    }
155
156    /// Get the required bytes for this layout.
157    pub fn byte_len(self) -> usize {
158        // Exactly this does not overflow due to construction.
159        self.pixel.size() * self.width * self.height
160    }
161
162    /// The number of pixels in this layout
163    pub fn len(self) -> usize {
164        self.width * self.height
165    }
166
167    pub fn width(self) -> usize {
168        self.width
169    }
170
171    pub fn height(self) -> usize {
172        self.height
173    }
174
175    pub fn pixel(self) -> Texel<P> {
176        self.pixel
177    }
178
179    /// Reinterpret to another, same size pixel type.
180    ///
181    /// See `transmute_to` for details.
182    pub fn transmute<Q: AsTexel>(self) -> Matrix<Q> {
183        self.transmute_to(Q::texel())
184    }
185
186    /// Reinterpret to another, same size pixel type.
187    ///
188    /// # Panics
189    /// Like `std::mem::transmute`, the size of the two types need to be equal. This ensures that
190    /// all indices are valid in both directions.
191    pub fn transmute_to<Q>(self, pixel: Texel<Q>) -> Matrix<Q> {
192        assert!(
193            self.pixel.size() == pixel.size(),
194            "{} vs {}",
195            self.pixel.size(),
196            pixel.size()
197        );
198
199        Matrix {
200            width: self.width,
201            height: self.height,
202            pixel,
203        }
204    }
205
206    /// Utility method to change the pixel type without changing the dimensions.
207    pub fn map<Q: AsTexel>(self) -> Option<Matrix<Q>> {
208        self.map_to(Q::texel())
209    }
210
211    /// Utility method to change the pixel type without changing the dimensions.
212    pub fn map_to<Q>(self, pixel: Texel<Q>) -> Option<Matrix<Q>> {
213        Matrix::from_width_height(pixel, self.width, self.height)
214    }
215}
216
217impl<P> MatrixLayout for Matrix<P> {
218    fn matrix(&self) -> MatrixBytes {
219        self.into_matrix_bytes()
220    }
221}
222
223/// Remove the strong typing for dynamic channel type information.
224impl<L: MatrixLayout> Decay<L> for MatrixBytes {
225    fn decay(from: L) -> MatrixBytes {
226        from.matrix()
227    }
228}
229
230/// Try to use the matrix with a specific pixel type.
231impl<P> TryMend<MatrixBytes> for Texel<P> {
232    type Into = Matrix<P>;
233    type Err = MismatchedPixelError;
234
235    fn try_mend(self, matrix: &MatrixBytes) -> Result<Matrix<P>, Self::Err> {
236        Matrix::with_matrix(self, *matrix).ok_or_else(MismatchedPixelError::default)
237    }
238}
239
240impl<P> From<Matrix<P>> for MatrixBytes {
241    fn from(mat: Matrix<P>) -> Self {
242        MatrixBytes {
243            element: mat.pixel().into(),
244            first_dim: mat.width(),
245            second_dim: mat.height(),
246        }
247    }
248}
249
250/// Note: on 64-bit targets only the first `u32::MAX` dimensions appear accessible.
251impl<P> Raster<P> for Matrix<P> {
252    fn dimensions(&self) -> Coord {
253        use core::convert::TryFrom;
254        let width = u32::try_from(self.width()).unwrap_or(u32::MAX);
255        let height = u32::try_from(self.height()).unwrap_or(u32::MAX);
256        Coord(width, height)
257    }
258
259    fn get(from: ImageRef<&Self>, Coord(x, y): Coord) -> Option<P> {
260        if from.layout().in_bounds(x as usize, y as usize) {
261            let index = from.layout().index_of(x as usize, y as usize);
262            let texel = from.layout().sample();
263            from.as_slice().get(index).map(|v| texel.copy_val(v))
264        } else {
265            None
266        }
267    }
268}
269
270impl<P> RasterMut<P> for Matrix<P> {
271    fn put(into: ImageMut<&mut Self>, Coord(x, y): Coord, val: P) {
272        if into.layout().in_bounds(x as usize, y as usize) {
273            let index = into.layout().index_of(x as usize, y as usize);
274            if let Some(dst) = into.into_mut_slice().get_mut(index) {
275                *dst = val;
276            }
277        }
278    }
279}