image_texel/
layout.rs

1//! A module for different pixel layouts.
2//!
3//! The `*Layout` traits define generic standard layouts with a normal form. Other traits provide
4//! operations to convert between layouts, operations on the underlying image bytes, etc.
5use crate::texels::{MaxAligned, TexelRange};
6use crate::{AsTexel, Texel};
7use ::alloc::boxed::Box;
8use core::{alloc, cmp};
9
10mod matrix;
11mod planar;
12mod relocated;
13
14use crate::image::{Coord, ImageMut, ImageRef};
15pub use crate::stride::{BadStrideError, StrideSpec, StridedBytes, StridedLayout, Strides};
16pub use matrix::{Matrix, MatrixBytes, MatrixLayout};
17pub use planar::{PlaneBytes, PlaneMatrices, Planes};
18pub use relocated::Relocated;
19
20/// A byte layout that only describes the user bytes.
21///
22/// This is a minimal implementation of the basic `Layout` trait. It does not provide any
23/// additional semantics for the buffer bytes described by it. All other layouts may be converted
24/// into this layout.
25#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
26pub struct Bytes(pub usize);
27
28/// Describes the byte layout of an texture element, untyped.
29///
30/// This is not so different from `Texel` and `Layout` but is a combination of both. It has the
31/// same invariants on alignment as the former which being untyped like the latter. The alignment
32/// of an element must be at most that of [`MaxAligned`] and the size must be a multiple of its
33/// alignment.
34///
35/// This type is a lower semi lattice. That is, given two elements the type formed by taking the
36/// minimum of size and alignment individually will always form another valid element. This
37/// operation is implemented in the [`Self::infimum`] method.
38#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, Hash)]
39pub struct TexelLayout {
40    size: usize,
41    align: usize,
42}
43
44/// A descriptor of the layout of image bytes.
45///
46/// There is no color space and no strict type interpretation here, just some mapping to required
47/// bytes for such a fixed buffer and a width and height of the described image. This means that
48/// the byte usage for a particular buffer needs to be independent of the content, in particular
49/// can not be based on compressibility.
50///
51/// There is one more thing that differentiates an image from an encoded format. It is expected
52/// that the image can be unfolded into some matrix of independent pixels (with potentially
53/// multiple channels) without any arithmetic or conversion function. Independent here means that,
54/// when supplied with the missing color space and type information, there should exist an
55/// `Fn(U) -> T` that can map these pixels independently into some linear color space.
56///
57/// This property holds for any packed, strided or planar RGB/YCbCr/HSV format as well as chroma
58/// subsampled YUV images and even raw Bayer filtered images.
59pub trait Layout {
60    fn byte_len(&self) -> usize;
61}
62
63impl dyn Layout + '_ {
64    #[inline]
65    pub fn fits_buf(&self, bytes: &crate::buf::buf) -> bool {
66        self.byte_len() <= bytes.as_bytes().len()
67    }
68
69    #[inline]
70    pub fn fits_atomic_buf(&self, bytes: &crate::buf::atomic_buf) -> bool {
71        self.byte_len() <= bytes.len()
72    }
73
74    #[inline]
75    pub fn fits_cell_buf(&self, bytes: &crate::buf::cell_buf) -> bool {
76        self.byte_len() <= bytes.len()
77    }
78
79    #[inline]
80    pub fn fits_data(&self, len: &impl ?Sized) -> bool {
81        self.byte_len() <= core::mem::size_of_val(len)
82    }
83}
84
85/// Convert one layout to a less strict one.
86///
87/// In contrast to `From`/`Into` which is mostly assumed to model a lossless conversion the
88/// conversion here may generalize but need not be lossless. For example, the `Bytes` layout is the
89/// least descriptive layout that exists and any layout can decay into it. However, it should be
90/// clear that this conversion is never lossless.
91///
92/// In general, a layout `L` should implement `Decay<T>` if any image with layouts of type `T` is
93/// also valid for some layout of type `L`. A common example would be if a crate strictly adds more
94/// information to a predefined layout, then it should also decay to that layout. It is invalid to
95/// decay to a layout that somehow expands outside the initial layout, you must weaken the buffer
96/// required.
97///
98/// Also note that this trait is not reflexive, in contrast to `From` and `Into` which are. This
99/// avoids collisions in impls. In particular, it allows adding blanket impls of the form
100///
101/// ```ignore
102/// struct Local;
103///
104/// impl Trait for Local { /* … */ }
105///
106/// impl<T: Trait> Decay<T> for Local { /* … */ }
107/// ```
108///
109/// Otherwise, the instantiation `T = U` would collide with the reflexive impl.
110///
111/// ## Design
112///
113/// We consider re-rebalanced coherence rules ([RFC2451]) in this design especially to define the
114/// receiver type and the type parameter. Note that adding a blanket impl is a breaking change
115/// except under a special rule allowed in that RFC. To quote it here:
116///
117/// > RFC #1023 is amended to state that adding a new impl to an existing trait is considered a
118/// > breaking change unless, given impl<P1..=Pn> Trait<T1..=Tn> for T0:
119/// > * At least one of the types T0..=Tn must be a local type, added in this revision. Let Ti be
120/// >   the first such type.
121/// > * No uncovered type parameters P1..=Pn appear in T0..Ti (excluding Ti)
122/// >
123/// > [...]
124/// >
125/// > However, the following impls would not be considered a breaking change: [...]
126/// > * `impl<T> OldTrait<T> for NewType`
127///
128/// Let's say we want to introduce a new desciptor trait for matrix-like layouts. Then we can ship
129/// a new type representing the canonical form of this matrix trait and in the same revision define
130/// a blanket impl that allows other layouts to decay to it. This wouldn't be possible if the
131/// parameters were swapped. We can then let this specific type (it may contain covered type
132/// parameters) decay to any other previously defined layout to provide interoperability with older
133/// code.
134///
135/// [RFC2451]: https://rust-lang.github.io/rfcs/2451-re-rebalancing-coherence.html
136///
137pub trait Decay<T>: Layout {
138    fn decay(from: T) -> Self;
139}
140
141impl<T: Layout> Decay<T> for Bytes {
142    fn decay(from: T) -> Bytes {
143        Bytes(from.byte_len())
144    }
145}
146
147/// Convert a layout to a stricter one.
148///
149/// ## Design
150///
151/// A comment on the design space available for this trait.
152///
153/// (TODO: wrong) We require that the trait is
154/// implemented for the type that is _returned_. If we required that the trait be implemented for
155/// the receiver then this would restrict third-parties from using it to its full potential. In
156/// particular, since `Mend` is a foreign trait the coherence rules make it impossible to specify:
157///
158/// TODO Terminology: <https://rust-lang.github.io/rfcs/2451-re-rebalancing-coherence.html>
159///
160/// ```ignore
161/// impl<T> Mend<LocalType> for T {}
162/// ```
163///
164/// TODO: rewrite this...
165///
166/// ```ignore
167/// impl<T> Mend<T> for LocalType {}
168/// ```
169///
170/// The forms of evolution that we want to keep open:
171/// * Introduce a new form of mending between existing layouts. For example, a new color space
172///   transformation should be able to translate between existing types. Note that we will assume
173///   that in such a case the type parameters do not appear uncovered in the target or the source
174///   so that having either as the trait receiver (T0) allows this.
175/// * An *upgrader* type should be able to mend a <T: LocalOrForeignTrait> into a chosen layout.
176/// * TODO: When add a new layout type which mender types and targets do we want?
177///
178/// The exact form thus simply depends on expected use and the allow evolution for this crate.
179/// Consider in particular this coherence/SemVer rule:
180///
181/// > Adding any impl with an uncovered type parameter is considered a major breaking change.
182///
183/// TODO
184///
185/// TODO: comment and consider `&self`.
186///
187pub trait Mend<From> {
188    type Into: Layout;
189    fn mend(self, from: &From) -> Self::Into;
190}
191
192/// Try to convert a layout to a stricter one.
193pub trait TryMend<From> {
194    type Into: Layout;
195    type Err;
196    fn try_mend(self, from: &From) -> Result<Self::Into, Self::Err>;
197}
198
199/// A layout that can be emptied.
200///
201/// This trait contains all layout types from which we can steal their memory buffer. This is
202/// incredibly useful for fallible operations that change the _type_ of a buffers layout. Instead
203/// of being required to take the buffer by value and return the original in case of an error they
204/// can use the much natural signature:
205///
206/// * `fn mutate(&mut self) -> Result<Converted, Err>`
207///
208/// where semantics are that the buffer is unchanged in case of error but has been moved to the
209/// type `Converted` in case of success. This is very similar to the method `Vec::take` and others.
210///
211/// It is expected that the `byte_len` is `0` after the operation.
212///
213/// This trait is _not_ simply a clone of `Default`. While we expect that the described image
214/// contains no bytes after the operation other data such as channel count, color space
215/// information, image plane order, alpha interpretation should be retained.
216pub trait Take: Layout {
217    fn take(&mut self) -> Self;
218}
219
220/// A layout that is a slice of samples.
221///
222/// These layouts are represented with a slice of a _single_ type of samples. In particular these
223/// can be addressed and mutated independently.
224pub trait SliceLayout: Layout {
225    /// The sample type itself.
226    type Sample;
227
228    /// Get the sample description.
229    fn sample(&self) -> Texel<Self::Sample>;
230
231    /// The number of samples.
232    ///
233    /// A slice with the returned length should have the byte length returned in `byte_len`.
234    fn len(&self) -> usize {
235        self.byte_len() / self.sample().size()
236    }
237
238    fn as_index(&self) -> TexelRange<Self::Sample> {
239        self.sample()
240            .to_range(0..self.len())
241            .expect("A layout should fit into memory")
242    }
243}
244
245// Just assert that `dyn SliceLayout<Sample = T>` is a valid type.
246impl<T> dyn SliceLayout<Sample = T> {}
247
248/// A layout of individually addressable raster elements.
249///
250/// Often referred to as 'pixels', this is a special form of texels that represent a single group
251/// of color channels that form one color impression.
252///
253/// Note that it does not prescribe any particular order of arrangement of these channels. Indeed,
254/// they could be in column major format, in row major format, ordered according to some space
255/// filling curve, etc. Also, multiple pixels may form one group of subsampled channels.
256pub trait Raster<Pixel>: Layout + Sized {
257    fn dimensions(&self) -> Coord;
258    fn get(from: ImageRef<&Self>, at: Coord) -> Option<Pixel>;
259}
260
261/// A raster layout where one can change pixel values independently.
262///
263/// In other words, requires that texels are actually one-by-one blocks of pixels.
264///
265/// Note that it does not prescribe any particular order of arrangement of these texels. Indeed,
266/// they could be in column major format, in row major format, ordered according to some space
267/// filling curve, etc. but subsampled images are not easily possible as pixels can not be written
268/// to independently.
269pub trait RasterMut<Pixel>: Raster<Pixel> {
270    fn put(into: ImageMut<&mut Self>, at: Coord, val: Pixel);
271
272    /// Evaluate a function on each texel of the raster image.
273    fn shade(mut image: ImageMut<&mut Self>, mut f: impl FnMut(u32, u32, &mut Pixel)) {
274        let Coord(bx, by) = image.layout().dimensions();
275        for y in 0..by {
276            for x in 0..bx {
277                let mut pixel = Self::get(image.as_ref().as_deref(), Coord(x, y)).unwrap();
278                f(x, y, &mut pixel);
279                Self::put(image.as_mut().as_deref_mut(), Coord(x, y), pixel);
280            }
281        }
282    }
283}
284
285/// An index type that identifies a 'plane' of an image layout.
286///
287/// The byte offset of all planes must adhere to alignment requirements as set by [`MaxAligned`],
288/// each individually. This ensures that each plane can be addressed equally.
289///
290/// Planes may be any part of the image that can be addressed independently. This could be that
291/// the component matrices of different channels are stored after each other, or interleaved ways
292/// of storing different quantization levels, optimizations for cache oblivious layouts etc. While
293/// planes usually constructed to be non-overlapping this requirement is not inherent in the trait.
294/// However, consider that mutable access to overlapping planes is not possible.
295///
296/// There may be multiple different ways of indexing into the same layout. Similar to the standard
297/// libraries [Index](`core::ops::Index`) trait, this trait can be implemented to provide an index
298/// into a layout defined in a different crate.
299///
300/// # Examples
301///
302/// The `PlaneMatrices` wrapper stores an array of matrix layouts. This trait allows accessing them
303/// by a `usize` index, conveniently interacting with the `ImageRef` and `ImageMut` types.
304///
305/// ```
306/// use image_texel::image::Image;
307/// use image_texel::layout::{PlaneMatrices, Matrix};
308/// use image_texel::texels::U8;
309///
310/// // Imagine a JPEG with progressive DCT coefficient planes.
311/// let rough = Matrix::from_width_height(U8, 8, 8).unwrap();
312/// let dense = Matrix::from_width_height(U8, 64, 64).unwrap();
313///
314/// // The assembled layout can be used to access the disjoint planes.
315/// let matrices = PlaneMatrices::new(U8, [rough, dense]);
316///
317/// let buffer = Image::new(&matrices);
318/// let [p0, p1] = buffer.as_ref().into_planes([0, 1]).unwrap();
319///
320/// let rough_coeffs = p0.into_bytes();
321/// let dense_coeffs = p1.into_bytes();
322///
323/// assert_eq!(rough_coeffs.len(), 8 * 8);
324/// assert_eq!(dense_coeffs.len(), 64 * 64);
325/// ```
326pub trait PlaneOf<L: ?Sized> {
327    type Plane: Layout;
328
329    /// Get the layout describing the plane.
330    fn get_plane(self, layout: &L) -> Option<Self::Plane>;
331}
332
333/// A layout that supports being moved in memory.
334///
335/// Such a layout only occupies a smaller but contiguous range of its full buffer length. One can
336/// query that offset and modify it. Note that a relocatable layout should still report its
337/// [`Layout::byte_len`] as the past-the-end of its last relevant byte.
338pub trait Relocate: Layout {
339    /// The offset of the first relevant byte of this layout.
340    ///
341    /// This should be smaller or equal to the length.
342    fn byte_offset(&self) -> usize;
343
344    /// Move the layout to another aligned offset.
345    ///
346    /// The length of the layout should implicitly be modified by this operation, that is the range
347    /// between the start offset and its apparent length should remain the same.
348    ///
349    /// Moving to an aligned offset must work.
350    ///
351    /// # Panics
352    ///
353    /// Implementations are encouraged to panic if the newly chosen offset would make the total
354    /// length overflow `isize::MAX`, i.e. possible allocation size.
355    fn relocate(&mut self, offset: AlignedOffset);
356
357    /// Attempt to relocate the offset to another start offset, in bytes.
358    ///
359    /// This method should return `false` if the new offset is not suitable for the layout. The
360    /// default implementation requires an aligned offset. Implementations may work for additional
361    /// offsets.
362    fn relocate_to_byte(&mut self, offset: usize) -> bool {
363        if let Some(aligned_offset) = AlignedOffset::new(offset) {
364            self.relocate(aligned_offset);
365            true
366        } else {
367            false
368        }
369    }
370
371    /// Retrieve the contiguous byte range occupied by this layout.
372    fn byte_range(&self) -> core::ops::Range<usize> {
373        let start = self.byte_offset();
374        let end = self.byte_len();
375
376        debug_assert!(
377            start <= end,
378            "The length calculation of this layout is buggy"
379        );
380
381        start..end.min(start)
382    }
383
384    /// Get an index addressing all samples covered by the range of this relocated layout.
385    fn texel_range(&self) -> TexelRange<Self::Sample>
386    where
387        Self: SliceLayout + Sized,
388    {
389        TexelRange::from_byte_range(self.sample(), self.byte_offset()..self.byte_len())
390            .expect("A layout should fit into memory")
391    }
392}
393
394impl dyn Relocate {}
395
396/// An unsigned offset that is maximally aligned.
397#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
398pub struct AlignedOffset(usize);
399
400impl AlignedOffset {
401    const ALIGN: usize = core::mem::align_of::<MaxAligned>();
402
403    /// Try to construct an aligned offset.
404    pub fn new(offset: usize) -> Option<Self> {
405        if offset % Self::ALIGN == 0 && offset <= isize::MAX as usize {
406            Some(AlignedOffset(offset))
407        } else {
408            None
409        }
410    }
411
412    /// Get the wrapped offset as a `usize`.
413    pub fn get(self) -> usize {
414        self.0
415    }
416
417    /// Get the next valid offset after adding `bytes` to this.
418    ///
419    /// ```
420    /// use image_texel::layout::AlignedOffset;
421    ///
422    /// let zero = AlignedOffset::default();
423    ///
424    /// assert_eq!(zero.next_up(0), Some(zero));
425    /// assert_eq!(zero.next_up(1), zero.next_up(2));
426    ///
427    /// assert!(zero.next_up(usize::MAX).is_none());
428    /// assert!(zero.next_up(isize::MAX as usize).is_none());
429    /// ```
430    #[must_use]
431    pub fn next_up(self, bytes: usize) -> Option<Self> {
432        let range_start = self.0.checked_add(bytes)?;
433        let new_offset = range_start.checked_next_multiple_of(Self::ALIGN)?;
434
435        if new_offset > isize::MAX as usize {
436            return None;
437        }
438
439        AlignedOffset::new(new_offset)
440    }
441}
442
443/// An error indicating that mending failed due to mismatching pixel attributes.
444///
445/// This struct is used when a layout with dynamic pixel information should be mended into another
446/// layout with static information or a more restrictive combination of layouts. One example is the
447/// conversion of a dynamic matrix into a statically typed layout.
448#[derive(Debug, Default, PartialEq, Eq, Hash)]
449pub struct MismatchedPixelError {
450    _private: (),
451}
452
453impl Bytes {
454    /// Forget all layout semantics except the number of bytes used.
455    pub fn from_layout(layout: impl Layout) -> Self {
456        Bytes(layout.byte_len())
457    }
458}
459
460impl TexelLayout {
461    /// Construct an element from a self-evident pixel.
462    pub fn from_pixel<P: AsTexel>() -> Self {
463        let pix = P::texel();
464        TexelLayout {
465            size: pix.size(),
466            align: pix.align(),
467        }
468    }
469
470    /// An element with maximum size and no alignment requirements.
471    ///
472    /// This constructor is mainly useful for the purpose of using it as a modifier. When used with
473    /// [`Self::infimum`] it will only shrink the alignment and keep the size unchanged.
474    pub const MAX_SIZE: Self = {
475        TexelLayout {
476            size: isize::MAX as usize,
477            align: 1,
478        }
479    };
480
481    /// Create an element for a fictional type with specific layout.
482    ///
483    /// It's up to the caller to define or use an actual type with that same layout later. This
484    /// skips the check that such a type must not contain any padding and only performs the layout
485    /// related checks.
486    pub fn with_layout(layout: alloc::Layout) -> Option<Self> {
487        if layout.align() > MaxAligned::texel().align() {
488            return None;
489        }
490
491        if layout.size() % layout.align() != 0 {
492            return None;
493        }
494
495        Some(TexelLayout {
496            size: layout.size(),
497            align: layout.align(),
498        })
499    }
500
501    /// Convert this into a type layout.
502    ///
503    /// This can never fail as `TexelLayout` refines the standard library layout type.
504    pub fn layout(self) -> alloc::Layout {
505        alloc::Layout::from_size_align(self.size, self.align).expect("Valid layout")
506    }
507
508    /// Reduce the alignment of the element.
509    ///
510    /// This will perform the same modification as `repr(packed)` on the element's type.
511    ///
512    /// # Panics
513    ///
514    /// This method panics if `align` is not a valid alignment.
515    #[must_use = "This does not modify `self`."]
516    pub fn packed(self, align: usize) -> TexelLayout {
517        assert!(align.is_power_of_two());
518        let align = self.align.min(align);
519        TexelLayout { align, ..self }
520    }
521
522    /// Create an element having the smaller of both sizes and alignments.
523    #[must_use = "This does not modify `self`."]
524    pub fn infimum(self, other: Self) -> TexelLayout {
525        // We still have size divisible by align. Whatever the smaller of both, it's divisible by
526        // its align and thus also by the min of both alignments.
527        TexelLayout {
528            size: self.size.min(other.size),
529            align: self.align.min(other.align),
530        }
531    }
532
533    /// Get the size of the element.
534    pub const fn size(self) -> usize {
535        self.size
536    }
537
538    /// Get the minimum required alignment of the element.
539    pub const fn align(self) -> usize {
540        self.size
541    }
542
543    pub const fn superset_of(&self, other: TexelLayout) -> bool {
544        self.size >= other.size && self.align >= other.align
545    }
546}
547
548/// Convert a pixel to an element, discarding the exact type information.
549impl<T> From<Texel<T>> for TexelLayout {
550    fn from(texel: Texel<T>) -> Self {
551        TexelLayout {
552            size: texel.size(),
553            align: texel.align(),
554        }
555    }
556}
557
558impl Layout for Bytes {
559    fn byte_len(&self) -> usize {
560        self.0
561    }
562}
563
564impl<'lt, T: Layout + ?Sized> Layout for &'lt T {
565    fn byte_len(&self) -> usize {
566        (**self).byte_len()
567    }
568}
569
570impl<'lt, T: Layout + ?Sized> Layout for &'lt mut T {
571    fn byte_len(&self) -> usize {
572        (**self).byte_len()
573    }
574}
575
576impl Take for Bytes {
577    fn take(&mut self) -> Self {
578        Bytes(core::mem::take(&mut self.0))
579    }
580}
581
582impl<T> SliceLayout for &'_ T
583where
584    T: SliceLayout,
585{
586    type Sample = T::Sample;
587
588    fn sample(&self) -> Texel<Self::Sample> {
589        (**self).sample()
590    }
591}
592
593impl<T> SliceLayout for &'_ mut T
594where
595    T: SliceLayout,
596{
597    type Sample = T::Sample;
598
599    fn sample(&self) -> Texel<Self::Sample> {
600        (**self).sample()
601    }
602}
603
604impl<L: Layout + ?Sized> Layout for Box<L> {
605    fn byte_len(&self) -> usize {
606        (**self).byte_len()
607    }
608}
609
610impl<L: Layout> Decay<L> for Box<L> {
611    fn decay(from: L) -> Box<L> {
612        Box::new(from)
613    }
614}
615
616/// The partial order of elements is defined by comparing size and alignment.
617///
618/// This turns it into a semi-lattice structure, with infimum implementing the meet operation. For
619/// example, the following comparison all hold:
620///
621/// ```
622/// # use image_texel::texels::{U8, U16};
623/// # use image_texel::layout::TexelLayout;
624/// let u8 = TexelLayout::from(U8);
625/// let u8x2 = TexelLayout::from(U8.array::<2>());
626/// let u8x3 = TexelLayout::from(U8.array::<3>());
627/// let u16 = TexelLayout::from(U16);
628///
629/// assert!(u8 < u16, "due to size and alignment");
630/// assert!(u8x2 < u16, "due to its alignment");
631/// assert!(!(u8x3 < u16) && !(u16 < u8x3), "not comparable");
632///
633/// let meet = u8x3.infimum(u16);
634/// assert!(meet <= u8x3);
635/// assert!(meet <= u16);
636/// assert!(meet == u16.packed(1), "We know it precisely here {:?}", meet);
637/// ```
638impl cmp::PartialOrd for TexelLayout {
639    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
640        if self.size == other.size && self.align == other.align {
641            Some(cmp::Ordering::Equal)
642        } else if self.size <= other.size && self.align <= other.align {
643            Some(cmp::Ordering::Less)
644        } else if self.size >= other.size && self.align >= other.align {
645            Some(cmp::Ordering::Greater)
646        } else {
647            None
648        }
649    }
650}
651
652macro_rules! bytes_from_layout {
653    ($layout:path) => {
654        impl From<$layout> for Bytes {
655            fn from(layout: $layout) -> Self {
656                Bytes::from_layout(layout)
657            }
658        }
659    };
660    (<$($bound:ident),*> $layout:ident) => {
661        impl<$($bound),*> From<$layout <$($bound),*>> for Bytes {
662            fn from(layout: $layout <$($bound),*>) -> Self {
663                Bytes::from_layout(layout)
664            }
665        }
666    };
667}
668
669bytes_from_layout!(MatrixBytes);
670bytes_from_layout!(<P> Matrix);
671
672impl<P, L> PlaneOf<&'_ L> for P
673where
674    P: PlaneOf<L>,
675{
676    type Plane = <P as PlaneOf<L>>::Plane;
677
678    fn get_plane(self, layout: &&L) -> Option<Self::Plane> {
679        <P as PlaneOf<L>>::get_plane(self, *layout)
680    }
681}
682
683impl<P, L> PlaneOf<&'_ mut L> for P
684where
685    P: PlaneOf<L>,
686{
687    type Plane = <P as PlaneOf<L>>::Plane;
688
689    fn get_plane(self, layout: &&mut L) -> Option<Self::Plane> {
690        <P as PlaneOf<L>>::get_plane(self, *layout)
691    }
692}