Skip to main content

image_texel/layout/
planar.rs

1use crate::{
2    layout::{
3        AlignedOffset, Decay, Layout, Matrix, MatrixBytes, MismatchedPixelError, PlaneOf, Relocate,
4        SliceLayout, TexelLayout, TryMend,
5    },
6    texel::Texel,
7};
8
9use super::relocated::Relocated;
10
11/// A collection of planes.
12///
13/// Note that the constructors and [`Layout`] implementations depend on the type parameter.
14#[derive(Clone)]
15pub struct Planes<Storage: ?Sized> {
16    inner: Storage,
17}
18
19impl<Storage> Planes<Storage> {
20    pub fn into_inner(self) -> Storage {
21        self.inner
22    }
23}
24
25impl<Pl, const N: usize> Planes<[Pl; N]>
26where
27    Pl: Layout,
28{
29    pub fn new(inner: [Pl; N]) -> Self {
30        Self { inner }
31    }
32
33    pub fn as_ref(&self) -> Planes<[&'_ Pl; N]> {
34        Planes {
35            inner: self.inner.each_ref(),
36        }
37    }
38
39    pub fn as_mut(&mut self) -> Planes<[&'_ mut Pl; N]> {
40        Planes {
41            inner: self.inner.each_mut(),
42        }
43    }
44}
45
46impl<Pl, const N: usize> Layout for Planes<[Pl; N]>
47where
48    Pl: Layout,
49{
50    fn byte_len(&self) -> usize {
51        let lengths: [usize; N] = self.inner.each_ref().map(|p| p.byte_len());
52        lengths.iter().copied().max().unwrap_or(0)
53    }
54}
55
56impl<Pl> Layout for Planes<[Pl]>
57where
58    Pl: Layout,
59{
60    fn byte_len(&self) -> usize {
61        let lengths = self.inner.iter().map(|p| p.byte_len());
62        lengths.max().unwrap_or(0)
63    }
64}
65
66impl<Pl, const N: usize> PlaneOf<Planes<[Pl; N]>> for usize
67where
68    Pl: Layout + Clone,
69{
70    type Plane = Pl;
71
72    fn get_plane(self, layout: &Planes<[Pl; N]>) -> Option<Self::Plane> {
73        layout.inner.get(self).cloned()
74    }
75}
76
77impl<Pl> PlaneOf<Planes<[Pl]>> for usize
78where
79    Pl: Layout + Clone,
80{
81    type Plane = Pl;
82
83    fn get_plane(self, layout: &Planes<[Pl]>) -> Option<Self::Plane> {
84        layout.inner.get(self).cloned()
85    }
86}
87
88/// An array of byte matrices.
89///
90/// This type is optimized for the concrete layout type.
91///
92/// ```
93/// use image_texel::texels::{U8, F32};
94/// use image_texel::layout::{Layout, PlaneBytes, MatrixBytes};
95///
96/// let m0 = MatrixBytes::from_width_height(U8.into(), 4, 4).unwrap();
97/// let m1 = MatrixBytes::from_width_height(F32.into(), 16, 16).unwrap();
98///
99/// let planar = PlaneBytes::new([m0, m1]);
100/// let ref_to_m0 = planar.plane_ref(0).unwrap();
101/// let ref_to_m1 = planar.plane_ref(1).unwrap();
102///
103/// assert!(ref_to_m0.byte_len() <= ref_to_m1.offset.get());
104/// assert!(ref_to_m1.byte_len() == planar.byte_len());
105/// ```
106#[derive(Clone)]
107pub struct PlaneBytes<const N: usize> {
108    planes: Planes<[Relocated<MatrixBytes>; N]>,
109}
110
111impl<const N: usize> PlaneBytes<N> {
112    /// Construct from separate matrices.
113    ///
114    /// Relocates each consecutive matrix such that they do not overlap.
115    ///
116    /// # Panics
117    ///
118    /// This method panics if the overall layout length would exceed `isize::MAX`.
119    pub fn new(inner: [MatrixBytes; N]) -> Self {
120        let mut inner = inner.map(Relocated::new);
121        let mut offset = AlignedOffset::default();
122
123        for plane in inner.iter_mut() {
124            plane.relocate(offset);
125            offset = plane.next_aligned_offset().expect("layout too large");
126        }
127
128        PlaneBytes {
129            planes: Planes::new(inner),
130        }
131    }
132
133    pub fn from_repeated(matrix: MatrixBytes) -> Self {
134        Self::new([matrix; N])
135    }
136
137    /// Return a reference to one relocated matrix layout.
138    pub fn plane_ref(&self, idx: usize) -> Option<&Relocated<MatrixBytes>> {
139        self.planes.inner.get(idx)
140    }
141
142    /// Return a layout where only planes with a matching texel layout are preserved.
143    ///
144    /// All other planes are rewritten to be empty matrices of that layout. All offsets are
145    /// preserved.
146    ///
147    /// ```
148    ///
149    /// use image_texel::texels::{U8, F32};
150    /// use image_texel::layout::{Layout, PlaneBytes, MatrixBytes};
151    ///
152    /// let m0 = MatrixBytes::from_width_height(U8.into(), 4, 4).unwrap();
153    /// let m1 = MatrixBytes::from_width_height(F32.into(), 16, 16).unwrap();
154    ///
155    /// let planar = PlaneBytes::new([m0, m1]);
156    /// let only_u8 = planar.retain_coefficients_like(U8.into());
157    ///
158    /// assert_eq!(planar.plane_ref(0), only_u8.plane_ref(0));
159    /// assert_ne!(planar.plane_ref(1), only_u8.plane_ref(1));
160    ///
161    /// use image_texel::layout::Relocate;
162    /// // That second plane is still offset, but empty
163    /// assert!(only_u8.plane_ref(1).unwrap().byte_len() > 0);
164    /// assert_eq!(only_u8.plane_ref(1).unwrap().byte_range().len(), 0);
165    /// ```
166    #[must_use]
167    pub fn retain_coefficients_like(&self, texel: TexelLayout) -> Self {
168        let matrices = self.planes.inner.clone();
169        let inner = matrices.map(|plane| {
170            if plane.inner.element() == texel {
171                plane
172            } else {
173                Relocated {
174                    offset: plane.offset,
175                    inner: MatrixBytes::empty(texel),
176                }
177            }
178        });
179
180        PlaneBytes {
181            planes: Planes::new(inner),
182        }
183    }
184}
185
186impl<const N: usize> Layout for PlaneBytes<N> {
187    fn byte_len(&self) -> usize {
188        if N == 0 {
189            0
190        } else {
191            // We made sure that planes are sorted!
192            self.planes.inner[N - 1].byte_len()
193        }
194    }
195}
196
197impl<const N: usize> Relocate for PlaneBytes<N> {
198    fn byte_offset(&self) -> usize {
199        if N == 0 {
200            0
201        } else {
202            self.planes.inner[0].byte_len()
203        }
204    }
205
206    fn relocate(&mut self, mut offset: AlignedOffset) {
207        for plane in self.planes.inner.iter_mut() {
208            plane.relocate(offset);
209            offset = plane.next_aligned_offset().expect("layout too large");
210        }
211    }
212}
213
214impl<const N: usize> PlaneOf<PlaneBytes<N>> for usize {
215    type Plane = Relocated<MatrixBytes>;
216
217    fn get_plane(self, layout: &PlaneBytes<N>) -> Option<Self::Plane> {
218        layout.planes.inner.get(self).copied()
219    }
220}
221
222/// Upgrade to a collection of planes of the same texel.
223impl<T, const N: usize> TryMend<PlaneBytes<N>> for Texel<T> {
224    type Into = PlaneMatrices<T, N>;
225
226    type Err = MismatchedPixelError;
227
228    fn try_mend(self, from: &PlaneBytes<N>) -> Result<Self::Into, Self::Err> {
229        let planes = from.planes.inner.each_ref();
230
231        // FIXME: use `try_map` once stable.
232        let mut results: [Result<_, MismatchedPixelError>; N] = planes.map(|plane| {
233            let matrix = self.try_mend(&plane.inner)?;
234            Ok(Relocated {
235                offset: plane.offset,
236                inner: matrix,
237            })
238        });
239
240        if let Some(err) = results.iter().position(|e| e.is_err()) {
241            let mut replacement = Ok(Relocated::new(Matrix::empty(self)));
242            core::mem::swap(&mut results[err], &mut replacement);
243
244            return Err(match replacement {
245                Err(err) => err,
246                Ok(_) => unreachable!(),
247            });
248        }
249
250        // FIXME: `try_map` until here.
251        let inner = results.map(|res| res.unwrap());
252
253        Ok(PlaneMatrices {
254            planes: Planes::new(inner),
255            texel: self,
256        })
257    }
258}
259
260/// An array of byte matrices.
261///
262/// This type is optimized for the concrete layout type of matrix planes.
263///
264/// # Examples
265///
266/// ```
267/// use image_texel::image::Image;
268/// use image_texel::layout::{PlaneMatrices, Matrix};
269/// use image_texel::texels::U8;
270///
271/// // Imagine a JPEG with progressive DCT coefficient planes.
272/// let rough = Matrix::from_width_height(U8, 8, 8).unwrap();
273/// let dense = Matrix::from_width_height(U8, 64, 64).unwrap();
274///
275/// // The assembled layout can be used to access the disjoint planes.
276/// let matrices = PlaneMatrices::new(U8, [rough, dense]);
277/// let rough = matrices.plane_ref(0).unwrap();
278/// let dense = matrices.plane_ref(1).unwrap();
279///
280/// let buffer = Image::new(&matrices);
281/// let rough_coeffs = &buffer.as_buf()[rough.texel_range()];
282/// assert_eq!(rough_coeffs.len(), 8 * 8);
283///
284/// let dense_coeffs = &buffer.as_buf()[dense.texel_range()];
285/// assert_eq!(dense_coeffs.len(), 64 * 64);
286///
287/// // The coefficient planes are disjoint and well-ordered.
288/// assert!(rough_coeffs.as_ptr_range().end <= dense_coeffs.as_ptr());
289/// ```
290#[derive(Clone)]
291pub struct PlaneMatrices<T, const N: usize> {
292    planes: Planes<[Relocated<Matrix<T>>; N]>,
293    texel: Texel<T>,
294}
295
296impl<T, const N: usize> PlaneMatrices<T, N> {
297    /// Construct from separate matrices.
298    ///
299    /// Relocates each consecutive matrix such that they do not overlap.
300    ///
301    /// # Panics
302    ///
303    /// This method panics if the overall layout length would exceed `isize::MAX`.
304    pub fn new(texel: Texel<T>, inner: [Matrix<T>; N]) -> Self {
305        use crate::layout::{Decay, TryMend};
306        let bytes = inner.each_ref().map(|m| MatrixBytes::decay(m));
307        let planes = PlaneBytes::new(bytes);
308        texel
309            .try_mend(&planes)
310            .expect("input matrices have this texel")
311    }
312
313    pub fn from_repeated(matrix: Matrix<T>) -> Self {
314        let texel = matrix.pixel();
315        Self::new(texel, [matrix; N])
316    }
317
318    /// Return a reference to one relocated matrix layout.
319    pub fn plane_ref(&self, idx: usize) -> Option<&Relocated<Matrix<T>>> {
320        self.planes.inner.get(idx)
321    }
322}
323
324impl<T, const N: usize> PlaneOf<PlaneMatrices<T, N>> for usize {
325    type Plane = Relocated<Matrix<T>>;
326
327    fn get_plane(self, layout: &PlaneMatrices<T, N>) -> Option<Self::Plane> {
328        layout.planes.inner.get(self).copied()
329    }
330}
331
332impl<T, const N: usize> Layout for PlaneMatrices<T, N> {
333    fn byte_len(&self) -> usize {
334        if N == 0 {
335            0
336        } else {
337            // We made sure that planes are sorted!
338            self.planes.inner[N - 1].byte_len()
339        }
340    }
341}
342
343impl<T, const N: usize> SliceLayout for PlaneMatrices<T, N> {
344    type Sample = T;
345
346    fn sample(&self) -> Texel<Self::Sample> {
347        self.texel
348    }
349}
350
351impl<T, const N: usize> Decay<PlaneMatrices<T, N>> for PlaneBytes<N> {
352    fn decay(from: PlaneMatrices<T, N>) -> PlaneBytes<N> {
353        let bytes = from.planes.inner.each_ref().map(|rel| Relocated {
354            offset: rel.offset,
355            inner: MatrixBytes::decay(&rel.inner),
356        });
357
358        PlaneBytes {
359            planes: Planes::new(bytes),
360        }
361    }
362}