Skip to main content

image_texel/image/
atomic.rs

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