Skip to main content

devela/media/visual/image/raster/
borrow.rs

1// devela/src/media/visual/image/raster/borrow.rs
2//
3//! Defines [`RasterSlice`] and [`RasterByteSlice`].
4//
5// TOC
6// - definitions
7// - impl methods
8// - impl traits
9// - inner helpers
10
11use crate::{Boundary1d, Extent2, PhantomData, is};
12use crate::{RasterBuf, RasterBufBytes, RasterFormat, RasterLayout, RasterSamplePacked};
13use crate::{RasterView, RasterViewBytes, RasterViewPacked};
14
15/* RasterSlice */
16
17#[doc = crate::_tags!(image lifetime)]
18/// Borrowed dense raster view over typed samples.
19#[doc = crate::_doc_meta!{
20    location("media/visual/image/raster", struct RasterSlice),
21    #[cfg(target_pointer_width = "32")]
22    test_size_of(RasterSlice<u32, &[u32]> = 28|224),
23    #[cfg(target_pointer_width = "64")]
24    test_size_of(RasterSlice<u32, &[u32]> = 40|320),
25}]
26/// This is the concrete borrowed form of [`RasterView`].
27///
28/// It carries a [`RasterFormat`], a [`RasterLayout`], and a sample slice.
29/// The typed view is accepted only when the layout is dense
30/// and the stored pixel width matches the sample type.
31///
32/// It gives access to dense row-major sample storage
33/// without implying ownership or resizing.
34///
35/// For raw backend-facing bytes, use [`RasterByteSlice`].
36#[derive(Clone, Copy, Debug)]
37pub struct RasterSlice<T, B> {
38    format: RasterFormat,
39    layout: RasterLayout,
40    samples: B,
41    _sample: PhantomData<fn() -> T>,
42}
43
44#[rustfmt::skip]
45impl<T, B> RasterSlice<T, B> {
46    /// Returns the raster format.
47    pub const fn format(&self) -> RasterFormat { self.format }
48    /// Returns the raster layout.
49    pub const fn layout(&self) -> RasterLayout { self.layout }
50    /// Returns the logical extent.
51    pub const fn extent(&self) -> Extent2<u32> { self.layout.extent }
52}
53#[rustfmt::skip]
54impl<'a, T> RasterSlice<T, &'a [T]> {
55    /// Creates a borrowed typed raster from an explicit canonical layout.
56    ///
57    /// Returns `None` if:
58    /// - `T` has zero size or is too large for `RasterLayout`,
59    /// - the layout is not dense and upper-first,
60    /// - the format, layout, and sample type disagree on stored pixel width,
61    /// - the format has no supported logical depth,
62    /// - the extent overflows `usize`,
63    /// - or the sample slice is too short.
64    pub const fn new(format: RasterFormat, layout: RasterLayout, samples: &'a [T]) -> Option<Self> {
65        let Some(needed) = raster_typed_len::<T>(format, layout) else { return None; };
66        if samples.len() < needed { return None; }
67        let (samples, _) = samples.split_at(needed);
68        Some(Self { format, layout, samples, _sample: PhantomData })
69    }
70    /// Creates a borrowed typed raster view from an explicit dense layout
71    /// without checking invariants.
72    ///
73    /// This is semantically unchecked but memory-safe.
74    pub const fn new_unchecked(format: RasterFormat, layout: RasterLayout, samples: &'a [T])
75        -> Self { Self { format, layout, samples, _sample: PhantomData }
76    }
77    /// Creates a borrowed dense typed raster.
78    pub const fn dense(format: RasterFormat, extent: Extent2<u32>, samples: &'a [T])
79        -> Option<Self> {
80        let Some(bytes_per_pixel) = raster_sample_bytes_u8::<T>() else { return None; };
81        let Some(layout) = RasterLayout::dense_interleaved(extent, bytes_per_pixel)
82            else { return None; };
83        Self::new(format, layout, samples)
84    }
85
86    /// Returns the borrowed sample slice.
87    pub const fn samples(&self) -> &[T] { self.samples }
88}
89#[rustfmt::skip]
90impl<'a, T> RasterSlice<T, &'a mut [T]> {
91    /// Creates a borrowed mutable typed raster from an explicit canonical layout.
92    ///
93    /// Returns `None` if:
94    /// - `T` has zero size or is too large for `RasterLayout`,
95    /// - the layout is not dense and upper-first,
96    /// - the format, layout, and sample type disagree on stored pixel width,
97    /// - the format has no supported logical depth,
98    /// - the extent overflows `usize`,
99    /// - or the sample slice is too short.
100    pub const fn new_mut(format: RasterFormat, layout: RasterLayout, samples: &'a mut [T])
101        -> Option<Self> {
102        let Some(needed) = raster_typed_len::<T>(format, layout) else { return None; };
103        if samples.len() < needed { return None; }
104        let (samples, _) = samples.split_at_mut(needed);
105        Some(Self { format, layout, samples, _sample: PhantomData })
106    }
107    /// Creates a borrowed mutable typed raster view from an explicit dense layout
108    /// without checking invariants.
109    ///
110    /// This is semantically unchecked but memory-safe.
111    pub const fn new_mut_unchecked(format: RasterFormat, layout: RasterLayout, samples: &'a mut [T])
112        -> Self { Self { format, layout, samples, _sample: PhantomData }
113    }
114    /// Creates a borrowed mutable dense typed raster.
115    pub const fn dense_mut(format: RasterFormat, extent: Extent2<u32>, samples: &'a mut [T])
116        -> Option<Self> {
117        let Some(bytes_per_pixel) = raster_sample_bytes_u8::<T>() else { return None; };
118        let Some(layout) = RasterLayout::dense_interleaved(extent, bytes_per_pixel)
119            else { return None; };
120        Self::new_mut(format, layout, samples)
121    }
122
123    /// Returns the borrowed sample slice.
124    pub const fn samples(&self) -> &[T] { self.samples }
125    /// Returns the exclusively borrowed sample slice.
126    pub const fn samples_mut(&mut self) -> &mut [T] { self.samples }
127    /// Returns itself as non-mutable.
128    pub const fn as_ref(&self) -> RasterSlice<T, &[T]> {
129        RasterSlice {
130            format: self.format, layout: self.layout, samples: self.samples, _sample: PhantomData,
131        }
132    }
133}
134
135impl<T, B: AsRef<[T]>> RasterView for RasterSlice<T, B> {
136    type Sample = T;
137    fn raster_extent(&self) -> Extent2<u32> {
138        self.layout.extent
139    }
140    fn raster_samples(&self) -> &[T] {
141        self.samples.as_ref()
142    }
143}
144impl<T, B: AsRef<[T]> + AsMut<[T]>> RasterBuf for RasterSlice<T, B> {
145    fn raster_samples_mut(&mut self) -> &mut [T] {
146        self.samples.as_mut()
147    }
148}
149impl<T: RasterSamplePacked, B: AsRef<[T]>> RasterViewPacked for RasterSlice<T, B> {
150    fn raster_depth(&self) -> u8 {
151        raster_depth_u8(self.format).expect("Raster format must have a valid u8 depth")
152    }
153    fn raster_bytes_per_line(&self) -> usize {
154        self.layout.bytes_per_line as usize
155    }
156}
157
158/* RasterByteSlice */
159
160#[doc = crate::_tags!(image lifetime)]
161/// Borrowed byte raster view with explicit row layout.
162#[doc = crate::_doc_meta!{
163    location("media/visual/image/raster", struct RasterByteSlice),
164    #[cfg(target_pointer_width = "32")]
165    test_size_of(RasterByteSlice<&[u8]> = 28|224),
166    #[cfg(target_pointer_width = "64")]
167    test_size_of(RasterByteSlice<&[u8]> = 40|320),
168}]
169/// This is the concrete borrowed form of [`RasterViewBytes`].
170///
171/// It is the safe byte-first bridge for codecs, presentation backends,
172/// and foreign image surfaces. The layout may include row padding.
173///
174/// It gives access to backend-native byte storage
175/// without implying ownership or resizing.
176#[derive(Clone, Copy, Debug)]
177pub struct RasterByteSlice<B> {
178    format: RasterFormat,
179    layout: RasterLayout,
180    bytes: B,
181}
182#[rustfmt::skip]
183impl<B> RasterByteSlice<B> {
184    /// Returns the raster format.
185    pub const fn format(&self) -> RasterFormat { self.format }
186    /// Returns the raster layout.
187    pub const fn layout(&self) -> RasterLayout { self.layout }
188    /// Returns the logical extent.
189    pub const fn extent(&self) -> Extent2<u32> { self.layout.extent }
190}
191#[rustfmt::skip]
192impl<'a> RasterByteSlice<&'a [u8]> {
193    /// Creates a borrowed byte raster from an explicit layout.
194    ///
195    /// Returns `None` if:
196    /// - the format has no supported depth or stored byte width,
197    /// - the format and layout disagree on stored pixel width,
198    /// - the layout has an invalid row stride,
199    /// - the required byte length overflows `usize`,
200    /// - or the byte slice is too short.
201    pub const fn new(format: RasterFormat, layout: RasterLayout, bytes: &'a [u8]) -> Option<Self> {
202        let Some(min_len) = raster_byte_len(format, layout) else { return None; };
203        if bytes.len() < min_len { return None; }
204        Some(Self { format, layout, bytes })
205    }
206    /// Creates a borrowed byte raster view without checking length.
207    ///
208    /// This is semantically unchecked but memory-safe.
209    pub const fn new_unchecked(format: RasterFormat, layout: RasterLayout, bytes: &'a [u8])
210        -> Self { Self { format, layout, bytes }
211    }
212    /// Creates a borrowed dense byte raster view.
213    pub const fn dense(format: RasterFormat, extent: Extent2<u32>, bytes: &'a [u8])
214        -> Option<Self> {
215        let Some(bytes_per_pixel) = raster_bytes_per_pixel_u8(format) else { return None; };
216        let Some(layout) = RasterLayout::dense_interleaved(extent, bytes_per_pixel) else {
217            return None;
218        };
219        Self::new(format, layout, bytes)
220    }
221
222    /// Returns the borrowed byte slice.
223    pub const fn bytes(&self) -> &'a [u8] { self.bytes }
224}
225#[rustfmt::skip]
226impl<'a> RasterByteSlice<&'a mut [u8]> {
227    /// Creates a borrowed mutable byte raster from an explicit layout.
228    ///
229    /// Returns `None` if:
230    /// - the format has no supported depth or stored byte width,
231    /// - the format and layout disagree on stored pixel width,
232    /// - the layout has an invalid row stride,
233    /// - the required byte length overflows `usize`,
234    /// - or the byte slice is too short.
235    pub const fn new_mut(format: RasterFormat, layout: RasterLayout, bytes: &'a mut [u8])
236        -> Option<Self> {
237        let Some(min_len) = raster_byte_len(format, layout) else { return None; };
238        if bytes.len() < min_len { return None; }
239        Some(Self { format, layout, bytes })
240    }
241
242    /// Creates a borrowed mutable byte raster view without checking length.
243    ///
244    /// This is semantically unchecked but memory-safe.
245    pub const fn new_mut_unchecked(format: RasterFormat, layout: RasterLayout, bytes: &'a mut [u8])
246        -> Self { Self { format, layout, bytes }
247    }
248    /// Creates a borrowed mutable dense byte raster view.
249    pub const fn dense_mut(format: RasterFormat, extent: Extent2<u32>, bytes: &'a mut [u8])
250        -> Option<Self> {
251        let Some(bytes_per_pixel) = raster_bytes_per_pixel_u8(format) else { return None; };
252        let Some(layout) = RasterLayout::dense_interleaved(extent, bytes_per_pixel) else {
253            return None;
254        };
255        Self::new_mut(format, layout, bytes)
256    }
257
258    /// Returns the borrowed byte slice.
259    pub const fn bytes(&self) -> &[u8] { self.bytes }
260    /// Returns the exclusively borrowed byte slice.
261    pub const fn bytes_mut(&mut self) -> &mut [u8] { self.bytes }
262    /// Returns itself as non-mutable.
263    pub const fn as_ref(&self) -> RasterByteSlice<&[u8]> {
264        RasterByteSlice { format: self.format, layout: self.layout, bytes: self.bytes }
265    }
266}
267
268impl<B: AsRef<[u8]>> RasterViewBytes for RasterByteSlice<B> {
269    fn raster_extent_bytes(&self) -> Extent2<u32> {
270        self.layout.extent
271    }
272    fn raster_depth(&self) -> u8 {
273        raster_depth_u8(self.format).expect("Raster format must have a valid u8 depth")
274    }
275    fn raster_bytes(&self) -> &[u8] {
276        self.bytes.as_ref()
277    }
278    fn raster_bytes_per_pixel_bytes(&self) -> usize {
279        self.layout.bytes_per_pixel as usize
280    }
281    fn raster_bytes_per_line(&self) -> usize {
282        self.layout.bytes_per_line as usize
283    }
284    fn raster_row_start_bytes(&self) -> Boundary1d {
285        self.layout.row_start
286    }
287}
288impl<B: AsRef<[u8]> + AsMut<[u8]>> RasterBufBytes for RasterByteSlice<B> {
289    fn raster_bytes_mut(&mut self) -> &mut [u8] {
290        self.bytes.as_mut()
291    }
292}
293
294/* inner helpers */
295
296const fn raster_depth_u8(format: RasterFormat) -> Option<u8> {
297    match format.depth_bits() {
298        Some(bits) if bits <= u8::MAX as u16 => Some(bits as u8),
299        _ => None,
300    }
301}
302const fn raster_bytes_per_pixel_u8(format: RasterFormat) -> Option<u8> {
303    match format.stored_bytes_per_pixel() {
304        Some(bytes) if bytes <= u8::MAX as u16 => Some(bytes as u8),
305        _ => None,
306    }
307}
308/// Returns the non-zero stored size of `T` when representable by `RasterLayout`.
309const fn raster_sample_bytes_u8<T>() -> Option<u8> {
310    let bytes = size_of::<T>();
311    is! { bytes == 0 || bytes > u8::MAX as usize, None, Some(bytes as u8) }
312}
313/// Validates a canonical typed raster and returns its exact sample length.
314const fn raster_typed_len<T>(format: RasterFormat, layout: RasterLayout) -> Option<usize> {
315    let Some(sample_bytes) = raster_sample_bytes_u8::<T>() else {
316        return None;
317    };
318    is! { !layout.is_dense(), return None } // RasterView exposes dense typed samples
319    // RasterView has no orientation query,
320    // so typed samples use one canonical logical order: upper row first.
321    is! { !matches!(layout.row_start, Boundary1d::Upper), return None }
322    is! { layout.bytes_per_pixel != sample_bytes, return None }
323    is! { !raster_format_matches_layout(format, layout), return None }
324    raster_sample_len(layout.extent)
325}
326/// Validates a byte raster and returns its minimum required byte length.
327const fn raster_byte_len(format: RasterFormat, layout: RasterLayout) -> Option<usize> {
328    is! { !raster_format_matches_layout(format, layout), return None }
329    layout.min_len_bytes() // also validates row stride through RasterLayout::is_valid()
330}
331/// Returns whether the format and layout describe the same stored pixel width.
332///
333/// This also ensures that the logical depth can be represented by the
334/// `u8` returned from the raster byte/packed traits.
335const fn raster_format_matches_layout(format: RasterFormat, layout: RasterLayout) -> bool {
336    let Some(_depth) = raster_depth_u8(format) else {
337        return false;
338    };
339    let Some(bytes_per_pixel) = raster_bytes_per_pixel_u8(format) else {
340        return false;
341    };
342    bytes_per_pixel == layout.bytes_per_pixel
343}
344/// Returns the number of logical samples in an extent.
345const fn raster_sample_len(extent: Extent2<u32>) -> Option<usize> {
346    let [width, height] = extent.dim;
347    (width as usize).checked_mul(height as usize)
348}