image_canvas/
layout.rs

1//! Defines layout and buffer of our images.
2mod upsampling;
3
4use alloc::boxed::Box;
5
6use image_texel::image::{Coord, ImageRef};
7use image_texel::layout::{
8    AlignedOffset, Decay, Layout as ImageLayout, MatrixBytes, PlaneOf, Raster, Relocate,
9    SliceLayout, StrideSpec, StridedBytes, Strides, TexelLayout,
10};
11
12use crate::color::{Color, ColorChannel, ColorChannelModel};
13use crate::shader::ChunkSpec;
14
15/// The byte layout of a buffer.
16///
17/// An inner invariant is that the layout fits in memory, and in particular into a `usize`, while
18/// at the same time also fitting inside a `u64` of bytes.
19#[derive(Clone, Debug, PartialEq, Eq, Hash)]
20pub(crate) struct ByteLayout {
21    /// The number of pixels along our width.
22    pub(crate) width: u32,
23    /// The number of pixels along our height.
24    pub(crate) height: u32,
25    /// The number of bytes per row.
26    /// This is a u32 for compatibility with `wgpu`.
27    pub(crate) bytes_per_row: u32,
28}
29
30/// The layout of a full frame, with all planes and color.
31#[derive(Clone, Debug, PartialEq)]
32pub struct CanvasLayout {
33    // TODO: planarity..
34    /// The primary layout descriptor of the image itself.
35    /// When no explicit planes are given then this describes the sole plane as well.
36    pub(crate) bytes: ByteLayout,
37    /// The texel representing merged layers.
38    /// When no explicit planes are given then this describes the sole plane as well.
39    pub(crate) texel: Texel,
40    /// The offset of the first plane.
41    pub(crate) offset: usize,
42    /// How the numbers relate to physical quantities, important for conversion.
43    pub(crate) color: Option<Color>,
44    /// Additional color planes, if any.
45    pub(crate) planes: Box<[Plane]>,
46}
47
48/// …
49#[derive(Clone, Copy, Debug)]
50pub(crate) struct PlaneIdx(pub(crate) u8);
51
52/// The layout of a single color plane, internal.
53///
54/// This isn't a full descriptor as width and height in numbers of texels can be derived from the
55/// underlying byte layout.
56#[derive(Clone, Debug, PartialEq, Hash)]
57pub(crate) struct Plane {
58    pub(crate) bytes_per_row: u32,
59    /// Representation of the partial texel of the full frame.
60    pub(crate) texel: Texel,
61}
62
63/// The strides of uniformly spaced (color) channels.
64#[derive(Clone, Debug, PartialEq, Hash)]
65pub struct ChannelSpec {
66    pub channels: u8,
67    pub channel_stride: usize,
68    pub height: u32,
69    pub height_stride: usize,
70    pub width: u32,
71    pub width_stride: usize,
72}
73
74/// A layout with uniformly spaced (color) channels.
75#[derive(Clone, PartialEq)]
76pub struct ChannelBytes {
77    /// Based on a strided layout of the texel matrix.
78    pub(crate) inner: StridedBytes,
79    /// The texel associated with the layout.
80    pub(crate) texel: Texel,
81    /// Channels also have a uniform stride.
82    pub(crate) channel_stride: usize,
83    /// Provides the number of channels.
84    /// Assume that `channels * channel_stride` is at most the with stride of the underlying layout.
85    pub(crate) channels: u8,
86}
87
88/// A typed layout with uniform spaced (color) channels.
89#[derive(Clone, PartialEq)]
90pub struct ChannelLayout<T> {
91    pub(crate) channel: image_texel::Texel<T>,
92    pub(crate) inner: ChannelBytes,
93}
94
95/// The byte matrix layout of a single plane of the image.
96#[derive(Clone, PartialEq)]
97pub struct PlaneBytes {
98    /// The texel in this plane.
99    pub(crate) texel: Texel,
100    // FIXME: we could store merely the diff to the block-width.
101    /// The actual pixel width of this plane.
102    pub(crate) width: u32,
103    /// The actual pixel height of this plane.
104    pub(crate) height: u32,
105    /// The matrix descriptor of this plane.
106    pub(crate) matrix: StridedBytes,
107}
108
109/// The typed matrix layout of a single plane of the image.
110///
111/// Note that this is _not_ fully public like the related [`PlaneBytes`] as we have invariants
112/// relating the texel type to alignment and offset of the matrix within the image.
113#[derive(Clone, PartialEq)]
114pub struct PlanarLayout<T> {
115    /// The texel in this plane.
116    texel: Texel,
117    /// The matrix descriptor of this plane.
118    matrix: Strides<T>,
119}
120
121/// Describe a row-major rectangular matrix layout.
122///
123/// This is only concerned with byte-buffer compatibility and not type or color space semantics of
124/// texels. It assumes a row-major layout without space between texels of a row as that is the most
125/// efficient and common such layout.
126///
127/// For usage as an actual image buffer, to convert it to a `CanvasLayout` by calling
128/// [`CanvasLayout::with_row_layout`].
129#[derive(Clone, Debug, PartialEq, Hash)]
130pub struct RowLayoutDescription {
131    pub width: u32,
132    pub height: u32,
133    pub row_stride: u64,
134    pub texel: Texel,
135}
136
137/// One Unit of bytes in a texture.
138#[derive(Clone, Debug, PartialEq, Eq, Hash)]
139pub struct Texel {
140    /// Which part of the image a single texel refers to.
141    pub block: Block,
142    /// How the values are encoded as bits in the bytes.
143    pub bits: SampleBits,
144    /// Which values are encoded, which controls the applicable color spaces.
145    pub parts: SampleParts,
146}
147
148/// How many pixels are described by a single texel unit.
149///
150/// Also each pixel in a block to order of channels, i.e. provides the link between SampleParts and
151/// SampleBits. Note that some block layouts may have _less_ channel than the sample if channels
152/// are not encoded separately, for example block compressed layouts.
153#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
154#[non_exhaustive]
155#[repr(u8)]
156pub enum Block {
157    /// Each texel is a single pixel.
158    Pixel = 0,
159    /// Each texel refers to two pixels across width.
160    Sub1x2 = 1,
161    /// Each texel refers to four pixels across width.
162    Sub1x4 = 2,
163    /// Each texel refers to a two-by-two block.
164    Sub2x2 = 3,
165    /// Each texel refers to a two-by-four block.
166    Sub2x4 = 4,
167    /// Each texel refers to a four-by-four block.
168    Sub4x4 = 5,
169    /// Each texel contains channels for two pixels, consecutively.
170    Pack1x2,
171    /// Each texel contains channels for four pixels, consecutively.
172    Pack1x4,
173    /// Each texel contains channels for eight pixels, consecutively.
174    Pack1x8,
175    /* Special block layouts */
176    Yuv422,
177    /// Yuv422 with different order of channels.
178    Yuy2,
179    Yuv411,
180    /* TODO: Bc1, Astc, EAC */
181}
182
183/// Describes which values are present in a texel.
184///
185/// This is some set of channels that describe the color point precisely, given a color space.
186/// Depending on the chosen color there may be multiple ways in which case this names which of the
187/// canonical encodings to use. For example, `CIELAB` may be represented as `Lab` (Lightness,
188/// red/green, blue/yellow) or `LCh` (Lightness, Chroma, Hue; the polar cooordinate form of the
189/// previous).
190///
191/// FIXME(color): describe YUV, ASTC and BC block formats? Other? We surely can handle planar data
192/// properly?
193#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
194pub struct SampleParts {
195    pub(crate) parts: [Option<ColorChannel>; 4],
196    /// The position of each channel as a 2-bit number.
197    /// This is the index into which the channel is written.
198    pub(crate) color_index: u8,
199}
200
201macro_rules! sample_parts {
202    ( $($(#[$attr:meta])* $color:ident: $name:ident = $($ch:path),+;)* ) => {
203        $(sample_parts! { @$color: $(#[$attr])* $name = $($ch),* })*
204
205        impl SampleParts {
206            $(sample_parts! { @$color: $(#[$attr])* $name = $($ch),* })*
207        }
208    };
209    (@$color:ident: $(#[$attr:meta])* $name:ident = $ch0:path) => {
210        $(#[$attr])*
211        pub const $name: SampleParts = SampleParts {
212            parts: [Some($ch0), None, None, None],
213            color_index: (
214                $ch0.canonical_index_in_surely(ColorChannelModel::$color)
215            ),
216        };
217    };
218    (@$color:ident: $(#[$attr:meta])* $name:ident = $ch0:path,$ch1:path) => {
219        $(#[$attr])*
220        pub const $name: SampleParts = SampleParts {
221            parts: [Some($ch0), Some($ch1), None, None],
222            color_index: (
223                $ch0.canonical_index_in_surely(ColorChannelModel::$color)
224                | $ch1.canonical_index_in_surely(ColorChannelModel::$color) << 2
225            ),
226        };
227    };
228    (@$color:ident: $(#[$attr:meta])* $name:ident = $ch0:path,$ch1:path,$ch2:path) => {
229        $(#[$attr])*
230        pub const $name: SampleParts = SampleParts {
231            parts: [Some($ch0), Some($ch1), Some($ch2), None],
232            color_index: (
233                $ch0.canonical_index_in_surely(ColorChannelModel::$color)
234                | $ch1.canonical_index_in_surely(ColorChannelModel::$color) << 2
235                | $ch2.canonical_index_in_surely(ColorChannelModel::$color) << 4
236            ),
237        };
238    };
239    (@$color:ident: $(#[$attr:meta])* $name:ident = $ch0:path,$ch1:path,$ch2:path,$ch3:path) => {
240        $(#[$attr])*
241        pub const $name: SampleParts = SampleParts {
242            parts: [Some($ch0), Some($ch1), Some($ch2), Some($ch3)],
243            color_index: (
244                $ch0.canonical_index_in_surely(ColorChannelModel::$color)
245                | $ch1.canonical_index_in_surely(ColorChannelModel::$color) << 2
246                | $ch2.canonical_index_in_surely(ColorChannelModel::$color) << 4
247                | $ch3.canonical_index_in_surely(ColorChannelModel::$color) << 6
248            ),
249        };
250    };
251}
252
253#[allow(non_upper_case_globals)]
254// We use items here just as a glob-import.
255// They are duplicated as constants to the struct then.
256#[allow(unused)]
257mod sample_parts {
258    type Cc = super::ColorChannel;
259    use super::ColorChannelModel;
260    use super::SampleParts;
261
262    sample_parts! {
263        /// A pure alpha part.
264        Rgb: A = Cc::Alpha;
265        /// A pure red part.
266        Rgb: R = Cc::R;
267        Rgb: G = Cc::G;
268        Rgb: B = Cc::B;
269        Yuv: Luma = Cc::Luma;
270        Yuv: LumaA = Cc::Luma,Cc::Alpha;
271        Rgb: Rgb = Cc::R,Cc::G,Cc::B;
272        Rgb: RgbA = Cc::R,Cc::G,Cc::B,Cc::Alpha;
273        Rgb: ARgb = Cc::Alpha,Cc::R,Cc::G,Cc::B;
274        Rgb: Bgr = Cc::B,Cc::G,Cc::R;
275        Rgb: BgrA = Cc::B,Cc::G,Cc::R,Cc::Alpha;
276        Rgb: ABgr = Cc::Alpha,Cc::B,Cc::G,Cc::R;
277        Yuv: Yuv = Cc::Luma,Cc::Cb,Cc::Cr;
278        Yuv: YuvA = Cc::Luma,Cc::Cb,Cc::Cr,Cc::Alpha;
279        Lab: Lab = Cc::L,Cc::LABa,Cc::LABb;
280        Lab: LabA = Cc::L,Cc::LABa,Cc::LABb,Cc::Alpha;
281        Lab: Lch = Cc::L,Cc::C,Cc::LABh;
282        Lab: LchA = Cc::L,Cc::C,Cc::LABh,Cc::Alpha;
283    }
284
285    /*
286    impl SampleParts {
287        Rgb_ = 9,
288        Bgr_ = 11,
289        _Rgb = 13,
290        _Bgr = 15,
291    }
292    */
293}
294
295/// The bit-placement of samples within a texel.
296///
297/// We start with low-order bits in a little-endian representation of the surrounding numbers. So,
298/// for example, Int332 has the first sample in the three lowest bits of a u8 (byte-order
299/// independent) and a Int565 has its first channel in the first 5 low-order bits of a u16 little
300/// endian interpretation of the bytes.
301#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
302#[non_exhaustive]
303#[allow(non_camel_case_types)]
304#[repr(u8)]
305pub enum SampleBits {
306    /// A single 8-bit signed integer.
307    Int8,
308    /// A single 8-bit integer.
309    UInt8,
310    /// Eight 1-bit integer.
311    UInt1x8,
312    /// Four 2-bit integer.
313    UInt2x4,
314    /// Three packed integer.
315    UInt332,
316    /// Three packed integer.
317    UInt233,
318    /// A single 8-bit signed integer.
319    Int16,
320    /// A single 16-bit integer.
321    UInt16,
322    /// Two packed integer.
323    UInt4x2,
324    /// Four packed integer.
325    UInt4x4,
326    /// Six packed integer.
327    UInt4x6,
328    /// Four packed integer, one component ignored.
329    UInt_444,
330    /// Four packed integer, one component ignored.
331    UInt444_,
332    /// Three packed integer.
333    UInt565,
334    /// Two 8-bit integers.
335    UInt8x2,
336    /// Three 8-bit integer.
337    UInt8x3,
338    /// Four 8-bit integer.
339    UInt8x4,
340    /// Six 8-bit integer.
341    UInt8x6,
342    /// Two 8-bit integers.
343    Int8x2,
344    /// Three 8-bit integer.
345    Int8x3,
346    /// Four 8-bit integer.
347    Int8x4,
348    /// Two 16-bit integers.
349    UInt16x2,
350    /// Three 16-bit integer.
351    UInt16x3,
352    /// Four 16-bit integer.
353    UInt16x4,
354    /// Two 16-bit signed integers.
355    Int16x2,
356    /// Three 16-bit integer.
357    Int16x3,
358    /// Four 16-bit integer.
359    Int16x4,
360    /// Six 16-bit integer.
361    UInt16x6,
362    /// Four packed integer.
363    UInt1010102,
364    /// Four packed integer.
365    UInt2101010,
366    /// Three packed integer, one component ignored.
367    UInt101010_,
368    /// Three packed integer, one component ignored.
369    UInt_101010,
370    /// Four half-floats.
371    Float16x4,
372    /// A single floating-point channel.
373    Float32,
374    /// Two float channels.
375    Float32x2,
376    /// Three float channels.
377    Float32x3,
378    /// Four floats.
379    Float32x4,
380    /// Six floats.
381    Float32x6,
382}
383
384#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
385pub(crate) enum BitEncoding {
386    UInt,
387    Int,
388    Float,
389}
390
391/// Error that occurs when constructing a layout.
392#[derive(Clone, Copy, Debug, PartialEq, Eq)]
393pub struct LayoutError {
394    inner: LayoutErrorInner,
395}
396
397#[derive(Clone, Copy, Debug, PartialEq, Eq)]
398enum LayoutErrorInner {
399    NoInfo,
400    NoPlanes,
401    NoModel,
402    TooManyPlanes(usize),
403    WidthError(core::num::TryFromIntError),
404    HeightError(core::num::TryFromIntError),
405    StrideError,
406    NoChannelIndex(ColorChannel),
407    ValidationError(u32),
408}
409
410impl Texel {
411    pub fn new_u8(parts: SampleParts) -> Self {
412        use SampleBits::*;
413        Self::pixel_from_bits(parts, [UInt8, UInt8x2, UInt8x3, UInt8x4])
414    }
415
416    pub fn new_i8(parts: SampleParts) -> Self {
417        use SampleBits::*;
418        Self::pixel_from_bits(parts, [Int8, Int8x2, Int8x3, Int8x4])
419    }
420
421    pub fn new_u16(parts: SampleParts) -> Self {
422        use SampleBits::*;
423        Self::pixel_from_bits(parts, [UInt16, UInt16x2, UInt16x3, UInt16x4])
424    }
425
426    pub fn new_i16(parts: SampleParts) -> Self {
427        use SampleBits::*;
428        Self::pixel_from_bits(parts, [Int16, Int16x2, Int16x3, Int16x4])
429    }
430
431    pub fn new_f32(parts: SampleParts) -> Self {
432        use SampleBits::*;
433        Self::pixel_from_bits(parts, [Float32, Float32x2, Float32x3, Float32x4])
434    }
435
436    fn pixel_from_bits(parts: SampleParts, bits: [SampleBits; 4]) -> Self {
437        Texel {
438            block: Block::Pixel,
439            bits: bits[(parts.num_components() - 1) as usize],
440            parts,
441        }
442    }
443
444    /// Get the texel describing a single channel.
445    /// Returns None if the channel is not contained, or if it can not be extracted on its own.
446    ///
447    /// FIXME(color): review what this means for block layouts. But since it only works for `UInt?x?`
448    /// this is not a big deal yet.
449    pub fn channel_texel(&self, channel: ColorChannel) -> Option<Texel> {
450        use sample_parts::*;
451        use Block::*;
452        use SampleBits::*;
453
454        #[allow(non_upper_case_globals)]
455        let parts = match self.parts {
456            Rgb => match channel {
457                ColorChannel::R => R,
458                ColorChannel::G => G,
459                ColorChannel::B => B,
460                _ => return None,
461            },
462            RgbA | BgrA | ABgr | ARgb => match channel {
463                ColorChannel::R => R,
464                ColorChannel::G => G,
465                ColorChannel::B => B,
466                ColorChannel::Alpha => A,
467                _ => return None,
468            },
469            _ => return None,
470        };
471
472        let bits = match self.bits {
473            UInt8 | UInt8x3 | UInt8x4 => UInt8,
474            Int8 | Int8x3 | Int8x4 => Int8,
475            UInt16 | UInt16x3 | UInt16x4 => UInt16,
476            Int16 | Int16x3 | Int16x4 => Int16,
477            _ => return None,
478        };
479
480        let block = match self.block {
481            // FIXME: really?
482            Yuv422 | Yuy2 | Yuv411 => return None,
483            _ => self.block,
484        };
485
486        Some(Texel { bits, parts, block })
487    }
488}
489
490impl ColorChannel {
491    /// The color model of the channel.
492    ///
493    /// Returns `None` if it does not belong to any singular color model.
494    pub const fn in_model(self, model: ColorChannelModel) -> bool {
495        self.canonical_index_in(model).is_some()
496    }
497
498    // Figure out how to expose this.. Return type is not entirely clear.
499    const fn canonical_index_in(self, model: ColorChannelModel) -> Option<u8> {
500        use ColorChannel::*;
501        use ColorChannelModel::*;
502
503        Some(match (self, model) {
504            (R | X, Rgb) => 0,
505            (G | Y, Rgb) => 1,
506            (B | Z, Rgb) => 2,
507            (ColorChannel::Luma, Yuv) => 0,
508            (ColorChannel::Luma, ColorChannelModel::Luma) => 0,
509            (Cb, Yuv) => 1,
510            (Cr, Yuv) => 2,
511            (L, Lab) => 0,
512            (LABa | C, Lab) => 1,
513            (LABb | LABh, Lab) => 2,
514            // Alpha represented here as no ink.
515            (Alpha, Cmyk) => return None,
516            // Alpha allowed anywhere, as the last component.
517            (Alpha, _) => 3,
518            // FIXME: Scalar0, Scalar1, Scalar2
519            _ => return None,
520        })
521    }
522
523    /// Infallible version of `canonical_index_in` which should be used only when a previous call
524    /// has validated that the color channel is allowed in the model. That check may be done by a
525    /// call to `canonical_index_in` or by source validation.
526    ///
527    /// The return value is arbitrarily wrong when this validation failed.
528    const fn canonical_index_in_surely(self, model: ColorChannelModel) -> u8 {
529        debug_assert!(
530            Self::canonical_index_in(self, model).is_some(),
531            "you had one job, to validate the channels for the model"
532        );
533
534        match Self::canonical_index_in(self, model) {
535            Some(idx) => idx,
536            None => 0,
537        }
538    }
539}
540
541impl SampleBits {
542    pub(crate) const MAX_COMPONENTS: usize = 8;
543
544    /// Determine the number of bytes for texels containing these samples.
545    pub fn bytes(self) -> u16 {
546        use SampleBits::*;
547
548        #[allow(non_upper_case_globals)]
549        match self {
550            Int8 | UInt8 | UInt1x8 | UInt2x4 | UInt332 | UInt233 | UInt4x2 => 1,
551            Int8x2 | UInt8x2 | Int16 | UInt16 | UInt565 | UInt4x4 | UInt444_ | UInt_444 => 2,
552            Int8x3 | UInt8x3 | UInt4x6 => 3,
553            Int8x4 | UInt8x4 | Int16x2 | UInt16x2 | UInt1010102 | UInt2101010 | UInt101010_
554            | UInt_101010 | Float32 => 4,
555            UInt8x6 | Int16x3 | UInt16x3 => 6,
556            Int16x4 | UInt16x4 | Float16x4 | Float32x2 => 8,
557            UInt16x6 | Float32x3 => 12,
558            Float32x4 => 16,
559            Float32x6 => 24,
560        }
561    }
562
563    fn as_array(self) -> Option<(TexelLayout, u8)> {
564        use image_texel::AsTexel;
565        use SampleBits::*;
566
567        Some(match self {
568            UInt8 | UInt8x2 | UInt8x3 | UInt8x4 | UInt8x6 => {
569                (u8::texel().into(), self.bytes() as u8)
570            }
571            Int8 | Int8x2 | Int8x3 | Int8x4 => (i8::texel().into(), self.bytes() as u8),
572            UInt16 | UInt16x2 | UInt16x3 | UInt16x4 | UInt16x6 => {
573                (u16::texel().into(), self.bytes() as u8 / 2)
574            }
575            Int16 | Int16x2 | Int16x3 | Int16x4 => (i16::texel().into(), self.bytes() as u8 / 2),
576            Float32 | Float32x2 | Float32x3 | Float32x4 | Float32x6 => {
577                (u32::texel().into(), self.bytes() as u8 / 4)
578            }
579            _ => return None,
580        })
581    }
582
583    fn layout(self) -> TexelLayout {
584        use crate::shader::{GenericTexelAction, TexelKind};
585        struct ToLayout;
586
587        impl GenericTexelAction<TexelLayout> for ToLayout {
588            fn run<T>(self, texel: image_texel::Texel<T>) -> TexelLayout {
589                texel.into()
590            }
591        }
592
593        TexelKind::from(self).action(ToLayout)
594    }
595
596    pub(crate) fn bit_encoding(self) -> ([BitEncoding; Self::MAX_COMPONENTS], u8) {
597        const M: usize = SampleBits::MAX_COMPONENTS;
598        use SampleBits::*;
599
600        match self {
601            UInt8 | UInt8x2 | UInt8x3 | UInt8x4 | UInt8x6 => {
602                ([BitEncoding::UInt; M], self.bytes() as u8)
603            }
604            UInt1x8 => ([BitEncoding::UInt; M], 8),
605            UInt2x4 => ([BitEncoding::UInt; M], 4),
606            Int8 | Int8x2 | Int8x3 | Int8x4 => ([BitEncoding::Int; M], self.bytes() as u8),
607            UInt16 | UInt16x2 | UInt16x3 | UInt16x4 | UInt16x6 => {
608                ([BitEncoding::UInt; M], self.bytes() as u8 / 2)
609            }
610            Int16 | Int16x2 | Int16x3 | Int16x4 => ([BitEncoding::Int; M], self.bytes() as u8 / 2),
611            Float32 | Float32x2 | Float32x3 | Float32x4 | Float32x6 => {
612                ([BitEncoding::Float; M], self.bytes() as u8 / 4)
613            }
614            UInt332 | UInt233 | UInt565 => ([BitEncoding::UInt; M], 3),
615            UInt4x2 => ([BitEncoding::UInt; M], 2),
616            UInt4x4 => ([BitEncoding::UInt; M], 4),
617            UInt4x6 => ([BitEncoding::UInt; M], 6),
618            UInt_444 | SampleBits::UInt444_ => ([BitEncoding::UInt; M], 3),
619            UInt101010_ | UInt_101010 => ([BitEncoding::Float; M], 3),
620            UInt1010102 | UInt2101010 => ([BitEncoding::Float; M], 4),
621            Float16x4 => ([BitEncoding::Float; M], 4),
622        }
623    }
624}
625
626impl SampleParts {
627    /// Create from up to four color channels.
628    ///
629    /// This is suitable for describing the channels of a single pixel, and relating it to the bit
630    /// parts in the corresponding texel.
631    ///
632    /// The order of parts will be remembered. All color channels must belong to a common color
633    /// representation.
634    pub fn new(parts: [Option<ColorChannel>; 4], model: ColorChannelModel) -> Option<Self> {
635        let color_index = Self::color_index(&parts, model)?;
636
637        Some(SampleParts { parts, color_index })
638    }
639
640    /// Extract a single channel.
641    ///
642    /// The channel is extract as if part of the ColorChannelModel used in the construction of
643    /// these sample parts.
644    pub fn with_channel(&self, ch: ColorChannel) -> Option<Self> {
645        let pos = self.parts.iter().position(|part| *part == Some(ch))?;
646        let mut parts = [None; 4];
647        parts[0] = self.parts[pos];
648        let color_index = (self.color_index >> (2 * pos)) & 0x3;
649
650        Some(SampleParts { parts, color_index })
651    }
652
653    /// Test if these parts contain the provided channel.
654    pub fn contains(&self, ch: ColorChannel) -> bool {
655        self.with_channel(ch).is_some()
656    }
657
658    /// Get an array of up to four color channel present.
659    pub fn color_channels(&self) -> [Option<ColorChannel>; 4] {
660        self.parts
661    }
662
663    fn color_index(parts: &[Option<ColorChannel>; 4], model: ColorChannelModel) -> Option<u8> {
664        let mut unused = [true; 4];
665        let mut color_index = [0; 4];
666        for (part, pos) in parts.into_iter().zip(&mut color_index) {
667            if let Some(p) = part {
668                let idx = p.canonical_index_in(model)?;
669                if !core::mem::take(&mut unused[idx as usize]) {
670                    return None;
671                }
672                *pos = idx;
673            }
674        }
675
676        let color_index = color_index
677            .into_iter()
678            .enumerate()
679            .fold(0u8, |acc, (idx, pos)| acc | pos << (2 * idx));
680
681        Some(color_index)
682    }
683
684    /// Create parts that describe 4:2:2 subsampled color channels.
685    ///
686    /// These parts represent a 1x2 block, with 4 channels total.
687    pub fn with_yuv_422(
688        parts: [Option<ColorChannel>; 3],
689        model: ColorChannelModel,
690    ) -> Option<Self> {
691        let parts = [parts[0], parts[1], parts[2], None];
692        // FIXME(color): should we allow arbitrary channels to be subsampled like this?
693        // Or do we need to perform stronger checks on the `ColorChannel` input?
694        let color_index = Self::color_index(&parts, model)?;
695
696        Some(SampleParts { parts, color_index })
697    }
698
699    /// Create parts that describe 4:1:1 subsampled color channels.
700    ///
701    /// These parts represent a 1x4 block, with 6 channels total.
702    pub fn with_yuv_411(
703        parts: [Option<ColorChannel>; 3],
704        model: ColorChannelModel,
705    ) -> Option<Self> {
706        // FIXME(color): implement this properly, `color_index` check included, see `with_yuv_422`.
707        let parts = [parts[0], parts[1], parts[2], None];
708        let color_index = Self::color_index(&parts, model)?;
709
710        Some(SampleParts { parts, color_index })
711    }
712
713    pub fn num_components(self) -> u8 {
714        self.parts.iter().map(|ch| u8::from(ch.is_some())).sum()
715    }
716
717    pub fn has_alpha(self) -> bool {
718        self.parts
719            .iter()
720            .any(|c| matches!(c, Some(ColorChannel::Alpha)))
721    }
722
723    pub(crate) fn channels(&self) -> impl '_ + Iterator<Item = (Option<ColorChannel>, u8)> {
724        (0..4).map(|i| (self.parts[i], (self.color_index >> (2 * i)) & 0x3))
725    }
726}
727
728impl Block {
729    pub fn width(&self) -> u32 {
730        use Block::*;
731        match self {
732            Pixel => 1,
733            Pack1x2 | Sub1x2 | Sub2x2 | Yuv422 | Yuy2 => 2,
734            Pack1x4 | Sub1x4 | Sub2x4 | Sub4x4 | Yuv411 => 4,
735            Pack1x8 => 8,
736        }
737    }
738
739    pub fn height(&self) -> u32 {
740        use Block::*;
741        match self {
742            Pixel | Sub1x2 | Sub1x4 | Yuv422 | Yuy2 | Yuv411 => 1,
743            Pack1x2 | Pack1x4 | Pack1x8 => 1,
744            Sub2x2 | Sub2x4 => 2,
745            Sub4x4 => 3,
746        }
747    }
748
749    pub(crate) fn block_width(&self, pixels: u32) -> u32 {
750        let div = self.width();
751        pixels / div + if pixels % div == 0 { 0 } else { 1 }
752    }
753
754    pub(crate) fn block_height(&self, pixels: u32) -> u32 {
755        let div = self.height();
756        pixels / div + if pixels % div == 0 { 0 } else { 1 }
757    }
758}
759
760impl CanvasLayout {
761    /// Construct a full frame from a single plane.
762    pub fn with_plane(bytes: PlaneBytes) -> Self {
763        CanvasLayout::from(&bytes)
764    }
765
766    /// Create from a list of planes, and the texel they describe when merged.
767    pub fn with_planes(layers: &[PlaneBytes], texel: Texel) -> Result<Self, LayoutError> {
768        if layers.len() == 0 {
769            return Err(LayoutError::NO_PLANES);
770        }
771
772        if layers.len() > 1 {
773            // FIXME(planar): should support validation of this.
774            return Err(LayoutError::bad_planes(layers.len()));
775        }
776
777        let spec = layers[0].matrix.spec();
778        let width: u32 = spec.width.try_into().map_err(LayoutError::width_error)?;
779        let min_height_stride = spec.width_stride as u32 * width;
780        let height_stride = spec
781            .height_stride
782            .try_into()
783            .map_err(LayoutError::height_error)?;
784
785        if min_height_stride > height_stride {
786            // FIXME(planar): should support validation of this.
787            return Err(LayoutError::bad_planes(0));
788        }
789
790        Self::validate(CanvasLayout {
791            bytes: ByteLayout {
792                width: layers[0].width,
793                height: layers[0].height,
794                bytes_per_row: height_stride,
795            },
796            planes: Box::default(),
797            offset: 0,
798            texel,
799            color: None,
800        })
801    }
802
803    /// Create a buffer layout given the layout of a simple, strided matrix.
804    pub fn with_row_layout(rows: &RowLayoutDescription) -> Result<Self, LayoutError> {
805        let bytes_per_texel = rows.texel.bits.bytes();
806        let bytes_per_row = usize::try_from(rows.row_stride).map_err(LayoutError::width_error)?;
807
808        let stride = StrideSpec {
809            offset: 0,
810            width: rows.texel.block.block_width(rows.width) as usize,
811            height: rows.texel.block.block_height(rows.height) as usize,
812            element: rows.texel.bits.layout(),
813            height_stride: bytes_per_row,
814            width_stride: bytes_per_texel.into(),
815        };
816
817        let bytes = PlaneBytes {
818            texel: rows.texel.clone(),
819            width: rows.width,
820            height: rows.height,
821            matrix: StridedBytes::new(stride).map_err(LayoutError::stride_error)?,
822        };
823
824        Self::with_planes(&[bytes], rows.texel.clone())
825    }
826
827    /// Create a buffer layout from a texel and dimensions.
828    ///
829    /// This is a simplification of `with_row_layout` which itself is a simplified `new`.
830    pub fn with_texel(texel: &Texel, width: u32, height: u32) -> Result<Self, LayoutError> {
831        let texel_stride = u64::from(texel.bits.bytes());
832        let width_sub = texel.block.block_width(width);
833
834        Self::with_row_layout(&RowLayoutDescription {
835            width,
836            height,
837            // Note: with_row_layout will do an overflow check anyways.
838            row_stride: u64::from(width_sub) * texel_stride,
839            texel: texel.clone(),
840        })
841    }
842
843    /// Get the texel represented by the canvas *as a whole*.
844    ///
845    /// For non-planar images this is exactly the same as the texel of the first place. Otherwise,
846    /// it is a merged representation.
847    pub fn texel(&self) -> &Texel {
848        &self.texel
849    }
850
851    /// Get the color space used by the image.
852    pub fn color(&self) -> Option<&Color> {
853        self.color.as_ref()
854    }
855
856    /// Returns the index of a texel in a slice of planar image data.
857    pub fn texel_index(&self, x: u32, y: u32) -> u64 {
858        let bytes_per_texel = self.texel.bits.bytes();
859        let byte_index = u64::from(x) * u64::from(self.bytes.bytes_per_row)
860            + u64::from(y) * u64::from(bytes_per_texel);
861        byte_index / u64::from(bytes_per_texel)
862    }
863
864    /// Returns a matrix descriptor that can store all bytes.
865    ///
866    /// Note: for the moment, all layouts are row-wise matrices. This will be relaxed in the future
867    /// to also permit the construction from planar image layouts. In this case, the method will
868    /// return a descriptor that does _not_ equal this layout. Instead, an image buffer shaped like
869    /// the returned descriptor can be used to re-arrange all bytes into a simple matrix form.
870    pub fn as_row_layout(&self) -> RowLayoutDescription {
871        RowLayoutDescription {
872            width: self.bytes.width,
873            height: self.bytes.height,
874            texel: self.texel.clone(),
875            row_stride: u64::from(self.bytes.bytes_per_row),
876        }
877    }
878
879    /// Returns the width of the underlying image in pixels.
880    pub fn width(&self) -> u32 {
881        self.bytes.width
882    }
883
884    /// Returns the height of the underlying image in pixels.
885    pub fn height(&self) -> u32 {
886        self.bytes.height
887    }
888
889    /// Returns the memory usage as a `u64`.
890    pub fn u64_len(&self) -> u64 {
891        // No overflow due to inner invariant.
892        u64::from(self.bytes.bytes_per_row) * u64::from(self.bytes.height)
893    }
894
895    /// Returns the memory usage as a `usize`.
896    pub fn byte_len(&self) -> usize {
897        // No overflow due to inner invariant.
898        (self.bytes.bytes_per_row as usize) * (self.bytes.height as usize)
899    }
900
901    /// Set the color of the layout, if compatible with the texel.
902    pub fn set_color(&mut self, color: Color) -> Result<(), LayoutError> {
903        let model = color.model().ok_or(LayoutError::NO_MODEL)?;
904
905        for (channel, idx) in self.texel.parts.channels() {
906            if let Some(channel) = channel {
907                let other_idx = match channel.canonical_index_in(model) {
908                    Some(idx) => idx,
909                    None => return Err(LayoutError::no_index(channel)),
910                };
911
912                if other_idx != idx {
913                    return Err(LayoutError::NO_INFO);
914                }
915            }
916        }
917
918        self.color = Some(color);
919        Ok(())
920    }
921
922    pub(crate) fn plane(&self, idx: u8) -> Option<PlaneBytes> {
923        if !self.planes.is_empty() {
924            // FIXME(planar): should support returning layout of this.
925            return None;
926        }
927
928        if idx != 0 {
929            return None;
930        }
931
932        let matrix = StridedBytes::with_row_major(
933            MatrixBytes::from_width_height(
934                self.texel.bits.layout(),
935                self.texel.block.block_width(self.bytes.width) as usize,
936                self.texel.block.block_height(self.bytes.height) as usize,
937            )
938            .unwrap(),
939        );
940
941        Some(PlaneBytes {
942            texel: self.texel.clone(),
943            width: self.bytes.width,
944            height: self.bytes.height,
945            matrix,
946        })
947    }
948
949    pub(crate) fn num_planes(&self) -> u8 {
950        if self.planes.is_empty() {
951            1
952        } else {
953            self.planes.len() as u8
954        }
955    }
956
957    pub fn as_plane(&self) -> Option<PlaneBytes> {
958        // Not only a single plane.
959        if !self.planes.is_empty() {
960            return None;
961        }
962
963        self.plane(0)
964    }
965
966    /// Verify that the byte-length is below `isize::MAX`.
967    fn validate(this: Self) -> Result<Self, LayoutError> {
968        let mut start = this.offset;
969        // For now, validation requires that planes are successive.
970        // This can probably stay true for quite a while..
971        for plane in 0..this.num_planes() {
972            // Require that the number of planes actually works..
973            let plane = this.plane(plane).ok_or(LayoutError::validation(line!()))?;
974            let spec = plane.matrix.spec();
975
976            let offset = plane.matrix.spec().offset;
977            let texel_offset = plane
978                .offset_in_texels()
979                .checked_mul(spec.element.size())
980                .ok_or(LayoutError::validation(line!()))?;
981
982            // FIXME(planar): decide on this issue.
983            if texel_offset != offset {
984                return Err(LayoutError::validation(line!()));
985            }
986
987            // TODO: should we require that planes are aligned to MAX_ALIGN?
988            // Probably useful for some methods but that's something for planar layouts.
989            if texel_offset % 256 != 0 {
990                return Err(LayoutError::validation(line!()));
991            }
992
993            let plane_end = offset
994                .checked_add(plane.matrix.byte_len())
995                .ok_or(LayoutError::validation(line!()))?;
996
997            let texel_layout = plane.texel.bits.layout();
998            if !spec.element.superset_of(texel_layout) {
999                return Err(LayoutError::validation(line!()));
1000            }
1001
1002            if start > offset {
1003                return Err(LayoutError::validation(line!()));
1004            }
1005
1006            start = plane_end;
1007        }
1008
1009        let lines = usize::try_from(this.bytes.width).map_err(LayoutError::width_error)?;
1010        let height = usize::try_from(this.bytes.height).map_err(LayoutError::height_error)?;
1011        let ok = height
1012            .checked_mul(lines)
1013            .map_or(false, |len| len < isize::MAX as usize);
1014
1015        if ok {
1016            Ok(this)
1017        } else {
1018            Err(LayoutError::validation(line!()))
1019        }
1020    }
1021}
1022
1023impl PlaneBytes {
1024    /// Get the texel represented by this plane.
1025    pub fn texel(&self) -> &Texel {
1026        &self.texel
1027    }
1028
1029    pub(crate) fn sub_offset(&mut self, offset: usize) {
1030        let mut spec = self.matrix.spec();
1031        assert!(offset % spec.element.size() == 0);
1032        assert!(offset % 256 == 0);
1033
1034        spec.offset = spec.offset.saturating_sub(offset);
1035        self.matrix = StridedBytes::new(spec).unwrap();
1036    }
1037
1038    pub(crate) fn as_channel_bytes(&self) -> Option<ChannelBytes> {
1039        let (channel_layout, channels) = self.texel.bits.as_array()?;
1040        Some(ChannelBytes {
1041            channel_stride: channel_layout.size(),
1042            channels,
1043            inner: self.matrix.clone(),
1044            texel: self.texel.clone(),
1045        })
1046    }
1047
1048    pub(crate) fn is_compatible<T>(&self, texel: image_texel::Texel<T>) -> Option<PlanarLayout<T>> {
1049        use image_texel::layout::TryMend;
1050        Some(PlanarLayout {
1051            texel: self.texel.clone(),
1052            matrix: texel.try_mend(&self.matrix).ok()?,
1053        })
1054    }
1055
1056    pub(crate) fn offset_in_texels(&self) -> usize {
1057        self.matrix.spec().offset / self.matrix.spec().element.size()
1058    }
1059
1060    pub(crate) fn fill_texel_indices_impl(
1061        &self,
1062        idx: &mut [usize],
1063        iter: &[[u32; 2]],
1064        chunk: ChunkSpec,
1065    ) {
1066        debug_assert_eq!(idx.len(), iter.len());
1067
1068        if self.texel.bits.bytes() == 0 {
1069            unreachable!("No texel with zero bytes");
1070        }
1071
1072        if self.matrix.spec().height_stride % usize::from(self.texel.bits.bytes()) == 0 {
1073            let pitch = self.matrix.spec().height_stride / usize::from(self.texel.bits.bytes());
1074            return Self::fill_indices_constant_size(idx, iter, pitch, chunk);
1075        }
1076
1077        // FIXME(perf): do we need common divisors? perf shows that a significant time is spent
1078        // on division by `bytes_per_texel` but the common cases (1, 2, 3, 4, 8, etc) should
1079        // all optimize a lot better.
1080
1081        // Fallback, actually generate everything by hard.
1082        for (&[x, y], idx) in iter.iter().zip(idx) {
1083            *idx = self.texel_index(x, y) as usize;
1084        }
1085    }
1086
1087    /// Returns the index of a texel in a slice of planar image data.
1088    ///
1089    /// This is hidden since it supposes that every plane can be treated like a matrix, but we do
1090    /// not want to advertise our representation of 'opaque' data. Indeed we haven't really chosen
1091    /// one so assume it is some `(1, len)` pseudo-matrix of register- or byte-sized values.
1092    fn texel_index(&self, x: u32, y: u32) -> u64 {
1093        let bytes_per_texel = self.texel.bits.bytes();
1094        let byte_index = u64::from(x) * (self.matrix.spec().height_stride as u64)
1095            + u64::from(y) * u64::from(bytes_per_texel);
1096        byte_index / u64::from(bytes_per_texel)
1097    }
1098
1099    fn fill_indices_constant_size(
1100        idx: &mut [usize],
1101        iter: &[[u32; 2]],
1102        pitch: usize,
1103        spec: ChunkSpec,
1104    ) {
1105        debug_assert_eq!(iter.len(), idx.len());
1106
1107        let mut index_chunks = idx.chunks_mut(spec.chunk_size);
1108        let mut iter = iter.chunks(spec.chunk_size);
1109
1110        for _ in &mut spec.chunks[..] {
1111            let (idx, iter) = match (index_chunks.next(), iter.next()) {
1112                (Some(idx), Some(iter)) => (idx, iter),
1113                _ => break,
1114            };
1115
1116            for (&[x, y], idx) in iter.iter().zip(&mut idx[..]) {
1117                let texindex = (x as usize) * pitch + (y as usize);
1118                *idx = texindex as usize;
1119            }
1120        }
1121
1122        if spec.should_defer_texel_ops {
1123            for (idx, chunk_spec) in idx.chunks_mut(spec.chunk_size).zip(spec.chunks) {
1124                let mut contig = true;
1125                for wnd in idx.windows(2) {
1126                    if wnd[1].saturating_sub(wnd[0]) != 1 {
1127                        contig = false;
1128                    }
1129                }
1130
1131                let contiguous_start = idx[0];
1132                if contig {
1133                    *chunk_spec = [contiguous_start, idx.len()];
1134                }
1135            }
1136        }
1137    }
1138}
1139
1140impl<T> PlanarLayout<T> {
1141    /// Get the texel represented by this plane.
1142    pub fn texel(&self) -> &Texel {
1143        &self.texel
1144    }
1145
1146    pub(crate) fn offset_in_texels(&self) -> usize {
1147        self.matrix.spec().offset / self.matrix.spec().element.size()
1148    }
1149}
1150
1151impl ChannelBytes {
1152    pub fn spec(&self) -> ChannelSpec {
1153        let StrideSpec {
1154            width,
1155            width_stride,
1156            height,
1157            height_stride,
1158            ..
1159        } = self.inner.spec();
1160
1161        ChannelSpec {
1162            channels: self.channels,
1163            channel_stride: self.channel_stride,
1164            height: height as u32,
1165            height_stride,
1166            width: width as u32,
1167            width_stride,
1168        }
1169    }
1170
1171    pub(crate) fn is_compatible<T>(
1172        &self,
1173        texel: image_texel::Texel<T>,
1174    ) -> Option<ChannelLayout<T>> {
1175        if self.channel_stride == texel.size() {
1176            Some(ChannelLayout {
1177                channel: texel,
1178                inner: self.clone(),
1179            })
1180        } else {
1181            None
1182        }
1183    }
1184}
1185
1186impl<T> ChannelLayout<T> {
1187    /// Get the texel represented by this plane.
1188    pub fn texel(&self) -> &Texel {
1189        &self.inner.texel
1190    }
1191
1192    pub fn spec(&self) -> ChannelSpec {
1193        self.inner.spec()
1194    }
1195
1196    fn from_planar_assume_u8<const N: usize>(from: PlanarLayout<[T; N]>) -> Self {
1197        let channel = from.matrix.texel().array_element();
1198        let inner = StridedBytes::decay(from.matrix);
1199        ChannelLayout {
1200            channel,
1201            inner: ChannelBytes {
1202                texel: from.texel,
1203                channels: N as u8,
1204                channel_stride: channel.size(),
1205                inner,
1206            },
1207        }
1208    }
1209}
1210
1211impl PlaneOf<CanvasLayout> for PlaneIdx {
1212    type Plane = PlaneBytes;
1213
1214    fn get_plane(self, layout: &CanvasLayout) -> Option<Self::Plane> {
1215        layout.plane(self.0)
1216    }
1217}
1218
1219impl LayoutError {
1220    const NO_INFO: Self = LayoutError {
1221        inner: LayoutErrorInner::NoInfo,
1222    };
1223
1224    const NO_PLANES: Self = LayoutError {
1225        inner: LayoutErrorInner::NoPlanes,
1226    };
1227
1228    const NO_MODEL: Self = LayoutError {
1229        inner: LayoutErrorInner::NoModel,
1230    };
1231
1232    fn validation(num: u32) -> Self {
1233        LayoutError {
1234            inner: LayoutErrorInner::ValidationError(num),
1235        }
1236    }
1237
1238    fn bad_planes(num: usize) -> Self {
1239        LayoutError {
1240            inner: LayoutErrorInner::TooManyPlanes(num),
1241        }
1242    }
1243
1244    fn width_error(err: core::num::TryFromIntError) -> Self {
1245        LayoutError {
1246            inner: LayoutErrorInner::WidthError(err),
1247        }
1248    }
1249
1250    fn height_error(err: core::num::TryFromIntError) -> Self {
1251        LayoutError {
1252            inner: LayoutErrorInner::HeightError(err),
1253        }
1254    }
1255
1256    fn stride_error(_: image_texel::layout::BadStrideError) -> Self {
1257        LayoutError {
1258            inner: LayoutErrorInner::StrideError,
1259        }
1260    }
1261
1262    fn no_index(ch: ColorChannel) -> Self {
1263        LayoutError {
1264            inner: LayoutErrorInner::NoChannelIndex(ch),
1265        }
1266    }
1267}
1268
1269impl ImageLayout for CanvasLayout {
1270    fn byte_len(&self) -> usize {
1271        CanvasLayout::byte_len(self)
1272    }
1273}
1274
1275impl Decay<PlaneBytes> for CanvasLayout {
1276    fn decay(from: PlaneBytes) -> Self {
1277        CanvasLayout::from(&from)
1278    }
1279}
1280
1281impl ImageLayout for PlaneBytes {
1282    fn byte_len(&self) -> usize {
1283        self.matrix.byte_len()
1284    }
1285}
1286
1287impl<T> ImageLayout for PlanarLayout<T> {
1288    fn byte_len(&self) -> usize {
1289        self.matrix.byte_len()
1290    }
1291}
1292
1293impl<T> SliceLayout for PlanarLayout<T> {
1294    type Sample = T;
1295
1296    fn sample(&self) -> image_texel::Texel<Self::Sample> {
1297        self.matrix.texel()
1298    }
1299}
1300
1301impl<T> Raster<T> for PlanarLayout<T> {
1302    fn dimensions(&self) -> Coord {
1303        let StrideSpec { width, height, .. } = self.matrix.spec();
1304        // The PlanarLayout should only be constructed from u32 width and height, guaranteeing that
1305        // this conversion works. If it doesn't, these should be traced to the constructor.
1306        debug_assert!(u32::try_from(width).is_ok(), "Invalid dimension: {}", width);
1307        debug_assert!(
1308            u32::try_from(height).is_ok(),
1309            "Invalid dimension: {}",
1310            height
1311        );
1312        Coord(width as u32, height as u32)
1313    }
1314
1315    // FIXME: requires testing and validation, etc.
1316    fn get(from: ImageRef<&Self>, at: Coord) -> Option<T> {
1317        let (x, y) = at.xy();
1318        let layout = from.layout();
1319        let matrix = &layout.matrix;
1320        let texel = matrix.texel();
1321        // TODO: should we add a method to `canvas::Matrix`?
1322        let StrideSpec { width_stride, .. } = matrix.spec();
1323
1324        debug_assert!(
1325            width_stride % texel.size() == 0,
1326            "Invalid stride: {} not valid for {:?}",
1327            width_stride,
1328            texel
1329        );
1330
1331        let idx = y as usize * (width_stride / texel.size()) + x as usize;
1332        let slice = from.as_texels(texel);
1333        let value = slice.get(layout.offset_in_texels()..)?.get(idx)?;
1334        Some(texel.copy_val(value))
1335    }
1336}
1337
1338impl<T> Decay<PlanarLayout<T>> for PlaneBytes {
1339    fn decay(from: PlanarLayout<T>) -> Self {
1340        let spec = from.matrix.spec();
1341        // This is a pixel layout.
1342        PlaneBytes {
1343            texel: from.texel,
1344            width: spec.width as u32,
1345            height: spec.height as u32,
1346            matrix: StridedBytes::decay(from.matrix),
1347        }
1348    }
1349}
1350
1351impl ImageLayout for ChannelBytes {
1352    fn byte_len(&self) -> usize {
1353        self.inner.byte_len()
1354    }
1355}
1356
1357impl<T> ImageLayout for ChannelLayout<T> {
1358    fn byte_len(&self) -> usize {
1359        self.inner.byte_len()
1360    }
1361}
1362
1363impl<T> SliceLayout for ChannelLayout<T> {
1364    type Sample = T;
1365
1366    fn sample(&self) -> image_texel::Texel<Self::Sample> {
1367        self.channel
1368    }
1369}
1370
1371impl<T> Decay<PlanarLayout<[T; 1]>> for ChannelLayout<T> {
1372    fn decay(from: PlanarLayout<[T; 1]>) -> Self {
1373        ChannelLayout::from_planar_assume_u8(from)
1374    }
1375}
1376
1377impl<T> Decay<PlanarLayout<[T; 2]>> for ChannelLayout<T> {
1378    fn decay(from: PlanarLayout<[T; 2]>) -> Self {
1379        ChannelLayout::from_planar_assume_u8(from)
1380    }
1381}
1382
1383impl<T> Decay<PlanarLayout<[T; 3]>> for ChannelLayout<T> {
1384    fn decay(from: PlanarLayout<[T; 3]>) -> Self {
1385        ChannelLayout::from_planar_assume_u8(from)
1386    }
1387}
1388
1389impl<T> Decay<PlanarLayout<[T; 4]>> for ChannelLayout<T> {
1390    fn decay(from: PlanarLayout<[T; 4]>) -> Self {
1391        ChannelLayout::from_planar_assume_u8(from)
1392    }
1393}
1394
1395impl<T> Decay<ChannelLayout<T>> for ChannelBytes {
1396    fn decay(from: ChannelLayout<T>) -> Self {
1397        from.inner
1398    }
1399}
1400
1401impl From<&'_ PlaneBytes> for CanvasLayout {
1402    fn from(plane: &PlaneBytes) -> Self {
1403        let StrideSpec {
1404            width: _,
1405            height: _,
1406            width_stride: _,
1407            height_stride,
1408            element: _,
1409            offset,
1410        } = plane.matrix.spec();
1411
1412        CanvasLayout {
1413            bytes: ByteLayout {
1414                width: plane.width,
1415                height: plane.height,
1416                bytes_per_row: height_stride as u32,
1417            },
1418            texel: plane.texel.clone(),
1419            offset,
1420            color: None,
1421            planes: Box::default(),
1422        }
1423    }
1424}
1425
1426impl Relocate for PlaneBytes {
1427    fn byte_offset(&self) -> usize {
1428        self.matrix.spec().offset
1429    }
1430
1431    fn relocate(&mut self, offset: AlignedOffset) {
1432        let mut spec = self.matrix.spec();
1433        spec.offset = offset.get();
1434        self.matrix = match StridedBytes::new(spec) {
1435            Err(_) => panic!("Relocated offset out-of-bounds"),
1436            Ok(m) => m,
1437        };
1438    }
1439}