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}