Skip to main content

image_texel/image/
cell.rs

1//! Defines the containers operating on `!Sync` shared bytes.
2//!
3//! Re-exported at its super `image` module.
4use crate::buf::{cell_buf, CellBuffer};
5use crate::image::{raw::RawImage, Image, IntoPlanesError};
6use crate::layout::{Bytes, Decay, Layout, Mend, PlaneOf, Relocate, SliceLayout, Take, TryMend};
7use crate::texel::{constants::U8, MAX_ALIGN};
8use crate::{BufferReuseError, Texel, TexelBuffer};
9use core::cell::Cell;
10
11/// A container of allocated bytes, parameterized over the layout.
12///
13/// This is a unsynchronized, shared equivalent to [`Image`][`crate::image::Image`]. That is the
14/// buffer of bytes of this container is shared between clones of this value but can not be sent
15/// between threads. In particular the same buffer may be owned and viewed with different layouts.
16///
17/// # Examples
18///
19/// As a type with shared ownership over the underling buffer, this type can be cloned very
20/// cheaply. Such duplicates refer to the same buffer, making changes in one visible to the other.
21///
22/// ```
23/// use image_texel::{image::CellImage, layout::Matrix};
24/// let matrix = Matrix::<u8>::width_and_height(16, 16).unwrap();
25/// let image: CellImage<_> = CellImage::new(matrix);
26///
27/// let another_reference = image.clone();
28/// assert!(CellImage::ptr_eq(&image, &another_reference));
29///
30/// another_reference.as_slice().as_slice_of_cells()[0].set(0xff);
31/// let value = image.as_slice().as_slice_of_cells()[0].get();
32/// assert_eq!(value, 0xff);
33/// ```
34#[derive(Clone, PartialEq, Eq)]
35pub struct CellImage<Layout = Bytes> {
36    pub(super) inner: RawImage<CellBuffer, Layout>,
37}
38
39/// A partial view of an atomic image.
40///
41/// Note that this requires its underlying buffer to be highly aligned! For that reason it is not
42/// possible to take a reference at an arbitrary number of bytes. Values of this type are created
43/// by calling [`CellImage::as_ref`] or [`CellImage::checked_to_ref`].
44#[derive(Clone, PartialEq, Eq)]
45pub struct CellImageRef<'buf, Layout = &'buf Bytes> {
46    pub(super) inner: RawImage<&'buf cell_buf, Layout>,
47}
48
49/// Image methods for all layouts.
50impl<L: Layout> CellImage<L> {
51    /// Create a new image for a specific layout.
52    pub fn new(layout: L) -> Self {
53        RawImage::<CellBuffer, L>::new(layout).into()
54    }
55
56    /// Create a new image with initial byte content.
57    pub fn with_bytes(layout: L, bytes: &[u8]) -> Self {
58        RawImage::with_contents(bytes, layout).into()
59    }
60
61    /// Create a new image with initial texel contents.
62    ///
63    /// The memory is reused as much as possible. If the layout is too large for the buffer then
64    /// the remainder is filled up with zeroed bytes.
65    pub fn with_buffer<T>(layout: L, bytes: TexelBuffer<T>) -> Self {
66        let (buffer, layout) = RawImage::from_buffer(Bytes(0), bytes.into_inner())
67            .with_layout(layout)
68            .into_parts();
69        RawImage::from_buffer(layout, CellBuffer::from(buffer)).into()
70    }
71
72    /// Change the layer of the image.
73    ///
74    /// Call [`CellImage::fits`] to check if this will work beforehand. Returns an `Err` with the
75    /// original image if the buffer does not fit the new layout. Returns `Ok` with the new image
76    /// if the buffer does fit. Never reallocates the buffer, the new image will always alias any
77    /// other image sharing the buffer.
78    ///
79    /// This returns a [`BufferReuseError`] with information about the exceeded limits. If you need
80    /// the prior value then you can make a [`CellImage::clone`]` of it, it is cheap.
81    pub fn try_with_layout<M>(self, layout: M) -> Result<CellImage<M>, BufferReuseError>
82    where
83        M: Layout,
84    {
85        let requested = layout.byte_len();
86        match self.inner.try_reinterpret(layout) {
87            Ok(raw) => Ok(raw.into()),
88            Err(err) => Err(BufferReuseError {
89                capacity: err.as_capacity_cell_buf().len(),
90                requested: Some(requested),
91            }),
92        }
93    }
94
95    /// Attempt to modify the layout to a new value, without modifying its type.
96    ///
97    /// Returns an `Err` if the layout does not fit the underlying buffer. Otherwise returns `Ok`
98    /// and overwrites the layout accordingly.
99    ///
100    /// TODO: public name and provide a `set_capacity` for `L = Bytes`?
101    pub(crate) fn try_set_layout(&mut self, layout: L) -> Result<(), BufferReuseError>
102    where
103        L: Layout,
104    {
105        self.inner.try_reuse(layout)
106    }
107
108    /// Decay into a image with less specific layout.
109    ///
110    /// See the [`Decay`] trait for an explanation of this operation.
111    ///
112    /// # Example
113    ///
114    /// The common layouts define ways to decay into a dynamically typed variant.
115    ///
116    /// ```
117    /// # use image_texel::{image::CellImage, layout::Matrix, layout};
118    /// let matrix = Matrix::<u8>::width_and_height(32, 32).unwrap();
119    /// let image: CellImage<layout::Matrix<u8>> = CellImage::new(matrix);
120    ///
121    /// // to turn hide the `u8` type but keep width, height, texel layout
122    /// let as_bytes: CellImage<layout::MatrixBytes> = image.clone().decay();
123    /// assert_eq!(as_bytes.layout().width(), 32);
124    /// assert_eq!(as_bytes.layout().height(), 32);
125    /// ```
126    ///
127    /// See also [`CellImage::mend`] and [`CellImage::try_mend`] for operations that reverse
128    /// the effects.
129    ///
130    /// Can also be used to forget specifics of the layout, turning the image into a more general
131    /// container type. For example, to use a uniform type as an allocated buffer waiting on reuse.
132    ///
133    /// ```
134    /// # use image_texel::{image::CellImage, layout::Matrix, layout};
135    /// let matrix = Matrix::<u8>::width_and_height(32, 32).unwrap();
136    ///
137    /// // Can always decay to a byte buffer.
138    /// let bytes: CellImage = CellImage::new(matrix).decay();
139    /// let _: &layout::Bytes = bytes.layout();
140    /// ```
141    ///
142    /// [`Decay`]: ../layout/trait.Decay.html
143    pub fn decay<M>(self) -> CellImage<M>
144    where
145        M: Decay<L>,
146        M: Layout,
147    {
148        self.inner
149            .checked_decay()
150            .unwrap_or_else(super::decay_failed)
151            .into()
152    }
153
154    /// Like [`Self::decay`]` but returns `None` rather than panicking. While this is strictly
155    /// speaking a violation of the trait contract, you may want to handle this yourself.
156    pub fn checked_decay<M>(self) -> Option<CellImage<M>>
157    where
158        M: Decay<L>,
159        M: Layout,
160    {
161        Some(self.inner.checked_decay()?.into())
162    }
163
164    /// Copy all bytes to a newly allocated image.
165    ///
166    /// Note this will allocate a buffer according to the capacity length of this reference, not
167    /// merely the layout. When this is not the intention, consider first adjusting the buffer by
168    /// reference with [`Self::as_ref`].
169    ///
170    /// # Examples
171    ///
172    /// Here we make an independent copy of a pixel matrix image.
173    ///
174    /// ```
175    /// use image_texel::image::{CellImage, Image};
176    /// use image_texel::layout::{PlaneMatrices, Matrix};
177    /// use image_texel::texels::U8;
178    ///
179    /// let matrix = Matrix::from_width_height(U8, 8, 8).unwrap();
180    /// let buffer = CellImage::new(matrix);
181    ///
182    /// // … some code to initialize those planes.
183    /// # let mut buffer = buffer;
184    /// # let data = &buffer.as_cell_buf()[U8.to_range(0..8).unwrap()];
185    /// # U8.store_cell_slice(data, b"not zero");
186    /// # let buffer = buffer;
187    ///
188    /// let clone_of: Image<_> = buffer.clone().into_owned();
189    ///
190    /// assert!(clone_of.as_bytes() == buffer.as_cell_buf());
191    /// ```
192    pub fn into_owned(self) -> Image<L> {
193        self.inner.into_owned().into()
194    }
195
196    /// Move the bytes into a new image.
197    ///
198    /// Afterwards, `self` will refer to an empty but unique new buffer.
199    pub fn take(&mut self) -> CellImage<L>
200    where
201        L: Take,
202    {
203        self.inner.take().into()
204    }
205
206    /// Strengthen the layout of the image.
207    ///
208    /// See the [`Mend`] trait for an explanation of this operation.
209    ///
210    /// [`Mend`]: ../layout/trait.Mend.html
211    pub fn mend<Item>(self, mend: Item) -> CellImage<Item::Into>
212    where
213        Item: Mend<L>,
214        L: Take,
215    {
216        let new_layout = mend.mend(self.inner.layout());
217        self.inner.mogrify_layout(|_| new_layout).into()
218    }
219
220    /// Strengthen the layout of the image.
221    ///
222    /// See the [`Mend`] trait for an explanation of this operation.
223    ///
224    /// This is a fallible operation. In case of success returns `Ok` and the byte buffer of the
225    /// image is moved into the result. When mending fails this method returns `Err` and the buffer
226    /// is kept by this image.
227    ///
228    /// [`Mend`]: ../layout/trait.Mend.html
229    pub fn try_mend<Item>(&mut self, mend: Item) -> Result<CellImage<Item::Into>, Item::Err>
230    where
231        Item: TryMend<L>,
232        L: Take,
233    {
234        let new_layout = mend.try_mend(self.inner.layout())?;
235        Ok(self.inner.take().mogrify_layout(|_| new_layout).into())
236    }
237}
238
239/// Image methods that do not require a layout.
240impl<L> CellImage<L> {
241    /// Check if the buffer could accommodate another layout without reallocating.
242    pub fn fits(&self, layout: &impl Layout) -> bool {
243        self.inner.fits(layout)
244    }
245
246    /// Check if two images refer to the same buffer.
247    ///
248    /// Note that two buffers can use different layout types to describe their share of the data or
249    /// even to refer to the same data in different ways.
250    pub fn ptr_eq<O>(&self, other: &CellImage<O>) -> bool {
251        CellBuffer::ptr_eq(self.inner.get(), other.inner.get())
252    }
253
254    /// Get a reference to the underlying buffer.
255    pub fn as_cell_buf(&self) -> &cell_buf
256    where
257        L: Layout,
258    {
259        self.inner.as_cell_buf()
260    }
261
262    /// Get a reference to the aligned unstructured bytes of the image.
263    ///
264    /// Note that this may return more bytes than required for the specific layout for various
265    /// reasons. See also [`Self::make_mut`].
266    pub fn as_capacity_cell_buf(&self) -> &cell_buf {
267        self.inner.as_capacity_cell_buf()
268    }
269
270    /// Get a mutable reference to all allocated bytes if this image does not alias any other.
271    ///
272    /// # Example
273    ///
274    /// ```
275    /// use image_texel::{image::CellImage, layout::Matrix};
276    ///
277    /// let layout = Matrix::<[u8; 4]>::width_and_height(10, 10).unwrap();
278    /// let mut image = CellImage::new(layout);
279    /// assert!(image.get_mut().is_some());
280    ///
281    /// let mut clone_of = image.clone();
282    /// assert!(image.get_mut().is_none());
283    /// ```
284    pub fn get_mut(&mut self) -> Option<&mut cell_buf> {
285        self.inner.get_mut().get_mut()
286    }
287
288    /// Ensure this image does not alias any other.
289    ///
290    /// Then returns a mutable reference to all the bytes allocated in the buffer.
291    ///
292    /// # Example
293    ///
294    /// ```
295    /// use image_texel::{image::CellImage, layout::Matrix, texels::U8};
296    /// let texel = U8.array::<4>();
297    ///
298    /// let layout = Matrix::<[u8; 4]>::width_and_height(10, 10).unwrap();
299    /// let image = CellImage::new(layout);
300    ///
301    /// let mut clone_of = image.clone();
302    /// let atomic_mut_buf = clone_of.make_mut();
303    ///
304    /// // Now these are independent buffers.
305    /// atomic_mut_buf.as_texels(texel).as_slice_of_cells()[0].set([0xff; 4]);
306    /// assert_ne!(image.as_slice().as_slice_of_cells()[0].get(), [0xff; 4]);
307    ///
308    /// // With mutable reference we initialized the new buffer.
309    /// assert_eq!(clone_of.as_slice().as_slice_of_cells()[0].get(), [0xff; 4]);
310    /// ```
311    pub fn make_mut(&mut self) -> &mut cell_buf {
312        self.inner.get_mut().make_mut()
313    }
314
315    /// View this buffer as a slice of texels.
316    ///
317    /// This reinterprets the bytes of the buffer. It can be used to view the buffer as any kind of
318    /// pixel, regardless of its association with the layout. Use it with care.
319    ///
320    /// An alternative way to get a slice of texels when a layout has an inherent texel type is
321    /// [`Self::as_slice`].
322    pub fn as_texels<P>(&self, texel: Texel<P>) -> &'_ Cell<[P]>
323    where
324        L: Layout,
325    {
326        self.as_ref().into_texels(texel)
327    }
328
329    /// View this buffer as a slice of its inherent pixels.
330    pub fn as_slice(&self) -> &'_ Cell<[L::Sample]>
331    where
332        L: SliceLayout,
333    {
334        self.as_ref().into_slice()
335    }
336
337    /// Get a reference to the layout.
338    pub fn layout(&self) -> &L {
339        self.inner.layout()
340    }
341
342    /// Get a mutable reference to the layout.
343    ///
344    /// Be mindful not to modify the layout to exceed the allocated size. This does not cause any
345    /// unsoundness but might lead to panics when calling other methods.
346    pub fn layout_mut_unguarded(&mut self) -> &mut L {
347        self.inner.layout_mut_unguarded()
348    }
349
350    /// Get a view of this image.
351    pub fn as_ref(&self) -> CellImageRef<'_, &'_ L> {
352        self.inner.as_deref().into()
353    }
354
355    /// Get a view of this image, if the alternate layout fits.
356    pub fn checked_to_ref<M: Layout>(&self, layout: M) -> Option<CellImageRef<'_, M>> {
357        self.as_ref().checked_with_layout(layout)
358    }
359
360    /*
361    /// Get a single texel from a raster image.
362    #[deprecated = "Do not use yet"]
363    pub fn get_texel<P>(&self, _: Coord) -> Option<P>
364    where
365        L: Raster<P>,
366    {
367        todo!("Failure of the Raster trait");
368    }
369
370    /// Put a single texel to a raster image.
371    #[deprecated = "Do not use yet"]
372    pub fn put_texel<P>(&mut self, _: Coord, _: P)
373    where
374        L: RasterMut<P>,
375    {
376        todo!("Failure of the Raster trait");
377    }
378
379    /// Call a function on each texel of this raster image.
380    ///
381    /// The order of evaluation is _not_ defined although certain layouts may offer more specific
382    /// guarantees. In general, one can expect that layouts call the function in a cache-efficient
383    /// manner if they are aware of a better iteration strategy.
384    pub fn shade<P>(&mut self, _: impl FnMut(u32, u32, &mut P))
385    where
386        L: RasterMut<P>,
387    {
388        todo!()
389    }
390    */
391}
392
393impl<'data, L> CellImageRef<'data, L> {
394    /// Get a reference to the underlying buffer.
395    pub fn as_cell_buf(&self) -> &cell_buf
396    where
397        L: Layout,
398    {
399        self.inner.as_cell_buf()
400    }
401
402    /// Get a reference to the complete underlying buffer, ignoring the layout.
403    pub fn as_capacity_cell_buf(&self) -> &cell_buf {
404        self.inner.get()
405    }
406
407    pub fn layout(&self) -> &L {
408        self.inner.layout()
409    }
410
411    /// Get a view of this image.
412    pub fn as_ref(&self) -> CellImageRef<'_, &'_ L> {
413        self.inner.as_deref().into()
414    }
415
416    /// Check if a call to [`CellImageRef::checked_with_layout`] would succeed.
417    pub fn fits(&self, other: &impl Layout) -> bool {
418        self.inner.fits(other)
419    }
420
421    /// Change this view to a different layout.
422    ///
423    /// This returns `Some` if the layout fits the underlying data, and `None` otherwise. Use
424    /// [`CellImageRef::fits`] to check this property in a separate call. Note that the new layout
425    /// need not be related to the old layout in any other way.
426    ///
427    /// # Usage
428    ///
429    /// ```rust
430    /// # fn not_main() -> Option<()> {
431    /// use image_texel::{image::CellImage, layout::Matrix, layout::Bytes};
432    ///
433    /// let layout = Matrix::<[u8; 4]>::width_and_height(10, 10).unwrap();
434    /// let image = CellImage::new(layout);
435    ///
436    /// let reference = image.as_ref();
437    ///
438    /// let as_bytes = reference.checked_with_layout(Bytes(400))?;
439    /// assert!(matches!(as_bytes.layout(), Bytes(400)));
440    ///
441    /// // But not if we request too much.
442    /// assert!(as_bytes.checked_with_layout(Bytes(500)).is_none());
443    ///
444    /// # Some(()) }
445    /// # fn main() { not_main(); }
446    /// ```
447    pub fn checked_with_layout<M>(self, layout: M) -> Option<CellImageRef<'data, M>>
448    where
449        M: Layout,
450    {
451        Some(self.inner.try_reinterpret(layout).ok()?.into())
452    }
453
454    /// Attempt to modify the layout to a new value, without modifying its type.
455    ///
456    /// Returns an `Err` if the layout does not fit the underlying buffer. Otherwise returns `Ok`
457    /// and overwrites the layout accordingly.
458    ///
459    /// TODO: public name and provide a `set_capacity` for `L = Bytes`?
460    pub(crate) fn try_set_layout(&mut self, layout: L) -> Result<(), BufferReuseError>
461    where
462        L: Layout,
463    {
464        self.inner.try_reuse(layout)
465    }
466
467    /// Decay into a image with less specific layout.
468    ///
469    /// See [`CellImage::decay`].
470    pub fn decay<M>(self) -> CellImageRef<'data, M>
471    where
472        M: Decay<L>,
473        M: Layout,
474    {
475        self.inner
476            .checked_decay()
477            .unwrap_or_else(super::decay_failed)
478            .into()
479    }
480
481    /// Decay into a image with less specific layout.
482    ///
483    /// See [`CellImage::checked_decay`].
484    pub fn checked_decay<M>(self) -> Option<CellImageRef<'data, M>>
485    where
486        M: Decay<L>,
487        M: Layout,
488    {
489        Some(self.inner.checked_decay()?.into())
490    }
491
492    /// Copy all bytes to a newly allocated image.
493    ///
494    /// Note this will allocate a buffer according to the capacity length of this reference, not
495    /// merely the layout. When this is not the intention, consider calling [`Self::split_layout`]
496    /// or [`Self::truncate_layout`] respectively.
497    ///
498    /// # Examples
499    ///
500    /// Here we make an independent copy of a pixel matrix image.
501    ///
502    /// ```
503    /// use image_texel::image::{CellImage, Image};
504    /// use image_texel::layout::{PlaneMatrices, Matrix};
505    /// use image_texel::texels::U8;
506    ///
507    /// let matrix = Matrix::from_width_height(U8, 8, 8).unwrap();
508    /// let buffer = CellImage::new(PlaneMatrices::<_, 2>::from_repeated(matrix));
509    ///
510    /// // … some code to initialize those planes.
511    /// # let [plane] = buffer.as_ref().into_planes([1]).unwrap();
512    /// # let data = &plane.as_cell_buf()[U8.to_range(0..8).unwrap()];
513    /// # U8.store_cell_slice(data, b"not zero");
514    ///
515    /// let [plane1] = buffer.as_ref().into_planes([1]).unwrap();
516    /// let clone_of: Image<_> = plane1.clone().into_owned();
517    ///
518    /// assert!(clone_of.as_bytes() == plane1.as_cell_buf());
519    /// ```
520    pub fn into_owned(self) -> Image<L> {
521        self.inner.into_owned().into()
522    }
523
524    /// Get a slice of the individual samples in the layout.
525    pub fn as_slice(&self) -> &'_ Cell<[L::Sample]>
526    where
527        L: SliceLayout,
528    {
529        self.as_texels(self.inner.layout().sample())
530    }
531
532    /// View this buffer as a slice of pixels.
533    ///
534    /// This reinterprets the bytes of the buffer. It can be used to view the buffer as any kind of
535    /// pixel, regardless of its association with the layout. Use it with care.
536    ///
537    /// An alternative way to get a slice of texels when a layout has an inherent texel type is
538    /// [`Self::as_slice`].
539    pub fn as_texels<P>(&self, pixel: Texel<P>) -> &'_ Cell<[P]>
540    where
541        L: Layout,
542    {
543        self.inner.as_cell_buf().as_texels(pixel)
544    }
545
546    /// Turn into a slice of the individual samples in the layout.
547    ///
548    /// This preserves the lifetime with which the layout is borrowed from the underlying image,
549    /// and the `ImageMut` need not stay alive.
550    pub fn into_bytes(self) -> alloc::vec::Vec<u8>
551    where
552        L: Layout,
553    {
554        let (buffer, layout) = self.inner.into_parts();
555        let len = layout.byte_len();
556        let mut target = alloc::vec![0; len];
557        let source = buffer.truncate(len).as_texels(U8);
558        U8.cell_memory_copy(
559            source.as_slice_of_cells(),
560            Cell::from_mut(&mut target[..]).as_slice_of_cells(),
561        );
562        target
563    }
564
565    /// Turn into a slice of the individual samples in the layout.
566    ///
567    /// This preserves the lifetime with which the layout is borrowed from the underlying image,
568    /// and the `ImageMut` need not stay alive.
569    pub fn into_slice(self) -> &'data Cell<[L::Sample]>
570    where
571        L: SliceLayout,
572    {
573        let sample = self.inner.layout().sample();
574        self.into_texels(sample)
575    }
576
577    /// View this buffer as a slice of pixels.
578    ///
579    /// This reinterprets the bytes of the buffer. It can be used to view the buffer as any kind of
580    /// pixel, regardless of its association with the layout. Use it with care.
581    ///
582    /// An alternative way to get a slice of texels when a layout has an inherent texel type is
583    /// [`Self::as_texels`].
584    pub fn into_texels<P>(self, pixel: Texel<P>) -> &'data Cell<[P]>
585    where
586        L: Layout,
587    {
588        let (buffer, layout) = self.inner.into_parts();
589        let byte_len = layout.byte_len();
590        buffer.truncate(byte_len).as_texels(pixel)
591    }
592
593    /*
594    /// Retrieve a single texel from a raster image.
595    #[deprecated = "Do not use yet"]
596    pub fn get_texel<P>(&self, _: Coord) -> Option<P>
597    where
598        L: Raster<P>,
599    {
600        todo!("Failure of the Raster trait");
601    }
602
603    /// Retrieve a single texel from a raster image.
604    #[deprecated = "Do not use yet"]
605    pub fn put_texel<P>(&self, _: Coord) -> Option<P>
606    where
607        L: Raster<P>,
608    {
609        todo!("Failure of the Raster trait");
610    }
611    */
612
613    /// Split off all unused bytes at the tail of the layout.
614    pub fn split_layout(&mut self) -> CellImageRef<'data, Bytes>
615    where
616        L: Layout,
617    {
618        // Need to roundup to correct alignment.
619        let size = self.inner.layout().byte_len();
620        let round_up = size.next_multiple_of(MAX_ALIGN);
621        let buffer = self.inner.get_mut();
622
623        if round_up > buffer.len() {
624            return RawImage::from_buffer(Bytes(0), cell_buf::new(&[])).into();
625        }
626
627        let (initial, next) = buffer.split_at(round_up);
628        *buffer = initial;
629
630        RawImage::from_buffer(Bytes(next.len()), next).into()
631    }
632
633    /// Remove all past-the-layout bytes.
634    ///
635    /// This is a utility to combine with pipelining. It is equivalent to calling
636    /// [`Self::split_layout`] and discarding that result.
637    pub fn truncate_layout(mut self) -> Self
638    where
639        L: Layout,
640    {
641        let _ = self.split_layout();
642        self
643    }
644
645    /// Split this reference into independent planes.
646    ///
647    /// If any plane fails their indexing operation or would not be aligned to the required
648    /// alignment or any plane layouts would overlap, an error is returned. The planes are returned
649    /// in the order of the descriptors.
650    ///
651    /// FIXME: the layout type is not what we want. For instance, with `PlaneMatrices` we get a
652    /// plane type of `Relocated<Matrix<_>>` but when we relocate that to `0` then we would really
653    /// prefer having a simple `Matrix<_>` as the layout type.
654    ///
655    /// # Examples
656    ///
657    /// A layout describing a matrix array can be split:
658    ///
659    /// ```
660    /// use image_texel::image::{CellImage, CellImageRef};
661    /// use image_texel::layout::{PlaneMatrices, Matrix};
662    /// use image_texel::texels::U8;
663    ///
664    /// let mat = Matrix::from_width_height(U8, 8, 8).unwrap();
665    /// let buffer = CellImage::new(PlaneMatrices::<_, 2>::from_repeated(mat));
666    /// let image: CellImageRef<'_, _> = buffer.as_ref();
667    ///
668    /// let [p0, p1] = buffer.as_ref().into_planes([0, 1]).unwrap();
669    /// ```
670    ///
671    /// You may select the same plane twice:
672    ///
673    pub fn into_planes<const N: usize, D>(
674        self,
675        descriptors: [D; N],
676    ) -> Result<[CellImageRef<'data, D::Plane>; N], IntoPlanesError>
677    where
678        D: PlaneOf<L>,
679        D::Plane: Relocate,
680    {
681        let layout = self.layout();
682        let mut planes = descriptors.map(|d| {
683            let plane = <D as PlaneOf<L>>::get_plane(d, layout);
684            let empty_buf = cell_buf::new(&[]);
685            (plane, empty_buf)
686        });
687
688        let (mut buffer, _) = self.inner.into_parts();
689
690        for plane in &mut planes {
691            let Some(layout) = &mut plane.0 else {
692                continue;
693            };
694
695            let skip_by = layout.byte_offset();
696
697            // FIXME: do we want failure reasons?
698            if skip_by % MAX_ALIGN != 0 {
699                plane.0 = None;
700                continue;
701            }
702
703            if buffer.len() < skip_by {
704                plane.0 = None;
705                continue;
706            }
707
708            layout.relocate(Default::default());
709            let len = layout.byte_len().div_ceil(MAX_ALIGN) * MAX_ALIGN;
710
711            // Check this before we consume the buffer. This way the tail can still be used by
712            // following layouts, we ignore this.
713            if buffer.len() - skip_by < len {
714                plane.0 = None;
715                continue;
716            }
717
718            let (_pre, tail) = buffer.split_at(skip_by);
719            let (img_buf, _post) = tail.split_at(len);
720
721            plane.1 = img_buf;
722            buffer = tail;
723        }
724
725        let planes = IntoPlanesError::from_array(planes)?;
726        Ok(planes.map(|(layout, buffer)| RawImage::from_buffer(layout, buffer).into()))
727    }
728}
729
730impl<L> From<RawImage<CellBuffer, L>> for CellImage<L> {
731    fn from(image: RawImage<CellBuffer, L>) -> Self {
732        CellImage { inner: image }
733    }
734}
735
736impl<'lt, L> From<RawImage<&'lt cell_buf, L>> for CellImageRef<'lt, L> {
737    fn from(image: RawImage<&'lt cell_buf, L>) -> Self {
738        CellImageRef { inner: image }
739    }
740}