image_canvas/
frame.rs

1//! A byte-buffer based image descriptor.
2
3use alloc::borrow::ToOwned;
4use alloc::vec::Vec;
5
6use image_texel::image::{
7    AtomicImage, AtomicImageRef, CellImage, CellImageRef, Image, ImageMut, ImageRef,
8};
9use image_texel::texels::U8;
10
11use crate::color::Color;
12use crate::layout::{CanvasLayout, ChannelLayout, LayoutError, PlanarLayout, PlaneBytes, PlaneIdx};
13use crate::shader::Converter;
14
15/// A byte buffer with dynamic color contents.
16#[derive(Clone)]
17pub struct Canvas {
18    inner: Image<CanvasLayout>,
19}
20
21/// An image buffer that can be shared within a thread.
22#[derive(Clone)]
23pub struct RcCanvas {
24    inner: CellImage<CanvasLayout>,
25}
26
27/// An image buffer that can be shared across threads..
28#[derive(Clone)]
29pub struct ArcCanvas {
30    inner: AtomicImage<CanvasLayout>,
31}
32
33/// A byte buffer containing a single plane.
34#[derive(Clone)]
35pub struct Plane {
36    inner: Image<PlaneBytes>,
37}
38
39/// Represents a single matrix like layer of an image.
40///
41/// Created from [`Canvas::plane`].
42pub struct BytePlaneRef<'data> {
43    pub(crate) inner: ImageRef<'data, PlaneBytes>,
44}
45
46/// Represents a single matrix like layer of an image.
47///
48/// Created from [`Canvas::plane_mut`].
49pub struct BytePlaneMut<'data> {
50    pub(crate) inner: ImageMut<'data, PlaneBytes>,
51}
52
53/// Represents a single matrix like layer of a shared image.
54///
55/// Created from [`Canvas::plane_mut`].
56pub struct BytePlaneCells<'data> {
57    pub(crate) inner: CellImageRef<'data, PlaneBytes>,
58}
59
60/// Represents a single matrix like layer of a shared image.
61///
62/// Created from [`Canvas::plane_mut`].
63pub struct BytePlaneAtomics<'data> {
64    pub(crate) inner: AtomicImageRef<'data, PlaneBytes>,
65}
66
67/// Represents a single matrix like layer of an image.
68///
69/// Created from [`BytePlaneRef::as_texels`].
70pub struct PlaneRef<'data, T> {
71    pub(crate) inner: ImageRef<'data, PlanarLayout<T>>,
72}
73
74/// Represents a single mutable matrix like layer of an image.
75///
76/// Created from [`BytePlaneMut::as_texels`].
77pub struct PlaneMut<'data, T> {
78    pub(crate) inner: ImageMut<'data, PlanarLayout<T>>,
79}
80
81/// Represents an intra-thread shared portion of an image.
82pub struct PlaneCells<'data, T> {
83    pub(crate) inner: CellImageRef<'data, PlanarLayout<T>>,
84}
85
86/// Represents a cross-thread shared portion of an image.
87pub struct PlaneAtomics<'data, T> {
88    pub(crate) inner: AtomicImageRef<'data, PlanarLayout<T>>,
89}
90
91/// Represent a single matrix with uniform channel type.
92///
93/// Created from [`Canvas::channels_u8`] and related methods.
94pub struct ChannelsRef<'data, C> {
95    pub(crate) inner: ImageRef<'data, ChannelLayout<C>>,
96}
97
98/// Represent a single matrix with uniform channel type.
99///
100/// Created from [`Canvas::channels_u8_mut`] and related methods.
101pub struct ChannelsMut<'data, C> {
102    pub(crate) inner: ImageMut<'data, ChannelLayout<C>>,
103}
104
105impl Canvas {
106    /// Create a frame by its layout.
107    ///
108    /// # Usage
109    ///
110    /// ```
111    /// use image_canvas::Canvas;
112    /// use image_canvas::layout::{CanvasLayout, SampleParts, Texel};
113    ///
114    /// // Define what type of color we want to store...
115    /// let texel = Texel::new_u8(SampleParts::RgbA);
116    /// // and which dimensions to use, chooses a stride for us.
117    /// let layout = CanvasLayout::with_texel(&texel, 32, 32)?;
118    ///
119    /// let frame = Canvas::new(layout);
120    /// # use image_canvas::layout::LayoutError;
121    /// # Ok::<(), LayoutError>(())
122    /// ```
123    pub fn new(layout: CanvasLayout) -> Self {
124        Canvas {
125            inner: Image::new(layout),
126        }
127    }
128
129    /// Get a reference to the layout of this frame.
130    pub fn layout(&self) -> &CanvasLayout {
131        self.inner.layout()
132    }
133
134    /// Overwrite the layout, allocate if necessary, and clear the image.
135    pub fn set_layout(&mut self, layout: CanvasLayout) {
136        self.set_layout_conservative(layout);
137        self.inner.as_bytes_mut().fill(0);
138    }
139
140    /// Overwrite the layout, allocate if necessary, _do not_ clear the image.
141    pub fn set_layout_conservative(&mut self, layout: CanvasLayout) {
142        *self.inner.layout_mut_unguarded() = layout;
143        self.inner.ensure_layout();
144    }
145
146    /// Return this image's pixels as a native endian byte slice.
147    ///
148    /// See also [`Self::as_bytes_mut`].
149    pub fn as_bytes(&self) -> &[u8] {
150        self.inner.as_bytes()
151    }
152
153    /// Return this image's pixels as a mutable native endian byte slice.
154    ///
155    /// The exact interpretation of each byte depends on the layout, which also contains a
156    /// descriptor of the color types used. This method is for purposes of foreign-function
157    /// interfaces and as a fallback to process bytes regardless of any restrictions placed by the
158    /// limits of what other methods are offered.
159    ///
160    /// Mind that the library __guarantees__ that the byte slice is actually aligned with an
161    /// alignment larger than `1`. The exact details depend on the underlying platform but are at
162    /// least `8`.
163    ///
164    /// Still, do __not__ write uninitialized bytes to the buffer. This is UB by Rust's semantics.
165    /// (Writing into the buffer by syscalls or assembly most likely never counts as writing
166    /// uninitialized bytes but take this with a grain of salt).
167    pub fn as_bytes_mut(&mut self) -> &mut [u8] {
168        self.inner.as_bytes_mut()
169    }
170
171    /// Return the bytes making up this image as a slice of arbitrary elements.
172    pub fn as_texels<T>(&self, texel: image_texel::Texel<T>) -> &[T] {
173        self.inner.as_texels(texel)
174    }
175
176    /// Return the bytes making up this image as a slice of arbitrary elements.
177    pub fn as_texels_mut<T>(&mut self, texel: image_texel::Texel<T>) -> &mut [T] {
178        self.inner.as_mut_texels(texel)
179    }
180
181    /// Get the matrix-like channel descriptor if the channels are `u8`.
182    ///
183    /// Returns `Some` only when texel is some multiple of `u8`.
184    pub fn channels_u8(&self) -> Option<ChannelsRef<u8>> {
185        let plane = self.inner.layout().as_plane()?;
186        let layout = plane
187            .as_channel_bytes()?
188            .is_compatible(<u8 as image_texel::AsTexel>::texel())?;
189        Some(ChannelsRef {
190            inner: self.inner.as_ref().with_layout(layout)?,
191        })
192    }
193
194    /// Get the matrix-like channel descriptor if the channels are `u16`.
195    ///
196    /// Returns `Some` only when texel is some multiple of `u16`.
197    pub fn channels_u16(&self) -> Option<ChannelsRef<u16>> {
198        let plane = self.inner.layout().as_plane()?;
199        let layout = plane
200            .as_channel_bytes()?
201            .is_compatible(<u16 as image_texel::AsTexel>::texel())?;
202        Some(ChannelsRef {
203            inner: self.inner.as_ref().with_layout(layout)?,
204        })
205    }
206
207    /// Get the matrix-like channel descriptor if the channels are `f32`.
208    ///
209    /// Returns `Some` only when texel is some multiple of `f32`.
210    pub fn channels_f32(&self) -> Option<ChannelsRef<f32>> {
211        let plane = self.inner.layout().as_plane()?;
212        let layout = plane
213            .as_channel_bytes()?
214            .is_compatible(<f32 as image_texel::AsTexel>::texel())?;
215        Some(ChannelsRef {
216            inner: self.inner.as_ref().with_layout(layout)?,
217        })
218    }
219
220    /// Get the matrix-like channel descriptor if the channels are `u8`.
221    ///
222    /// Returns `Some` only when texel is some multiple of `u8`.
223    pub fn channels_u8_mut(&mut self) -> Option<ChannelsMut<u8>> {
224        let plane = self.inner.layout().as_plane()?;
225        let layout = plane
226            .as_channel_bytes()?
227            .is_compatible(<u8 as image_texel::AsTexel>::texel())?;
228        Some(ChannelsMut {
229            inner: self.inner.as_mut().with_layout(layout)?,
230        })
231    }
232
233    /// Get the matrix-like channel descriptor if the channels are `u16`.
234    ///
235    /// Returns `Some` only when texel is some multiple of `u16`.
236    pub fn channels_u16_mut(&mut self) -> Option<ChannelsMut<u16>> {
237        let plane = self.inner.layout().as_plane()?;
238        let layout = plane
239            .as_channel_bytes()?
240            .is_compatible(<u16 as image_texel::AsTexel>::texel())?;
241        Some(ChannelsMut {
242            inner: self.inner.as_mut().with_layout(layout)?,
243        })
244    }
245
246    /// Get the matrix-like channel descriptor if the channels are `f32`.
247    ///
248    /// Returns `Some` only when texel is some multiple of `f32`.
249    pub fn channels_f32_mut(&mut self) -> Option<ChannelsMut<f32>> {
250        let plane = self.inner.layout().as_plane()?;
251        let layout = plane
252            .as_channel_bytes()?
253            .is_compatible(<f32 as image_texel::AsTexel>::texel())?;
254        Some(ChannelsMut {
255            inner: self.inner.as_mut().with_layout(layout)?,
256        })
257    }
258
259    /// Return this image's pixels as a byte vector.
260    pub fn into_bytes(self) -> Vec<u8> {
261        // We can not reuse the allocation of `canvas`.
262        self.as_bytes().to_owned()
263    }
264
265    /// Get the untyped descriptor of the texel matrix.
266    ///
267    /// Returns `None` if the image contains data that can not be described as a single texel
268    /// plane, e.g. multiple planes or if the plane is not a matrix.
269    pub fn plane(&self, idx: u8) -> Option<BytePlaneRef<'_>> {
270        let layout = self.layout().plane(idx)?;
271        Some(BytePlaneRef {
272            inner: self.inner.as_ref().with_layout(layout)?,
273        })
274    }
275
276    /// Get references to multiple planes at the same time.
277    pub fn planes<const N: usize>(&self) -> Option<[BytePlaneRef<'_>; N]> {
278        todo!()
279    }
280
281    /// Get the untyped, mutable reference to the texel matrix.
282    ///
283    /// Returns `None` if the image contains data that can not be described as a single texel
284    /// plane, e.g. multiple planes or if the plane is not a matrix.
285    pub fn plane_mut(&mut self, idx: u8) -> Option<BytePlaneMut<'_>> {
286        let layout = self.layout().plane(idx)?;
287        Some(BytePlaneMut {
288            inner: self.inner.as_mut().with_layout(layout)?,
289        })
290    }
291
292    /// Get mutable references to multiple planes at the same time.
293    ///
294    /// This works because planes never overlap. Note that all planes are aligned to the same byte
295    /// boundary as the complete canvas bytes.
296    pub fn planes_mut<const N: usize>(&mut self) -> Option<[BytePlaneMut<'_>; N]> {
297        use image_texel::layout::Bytes;
298
299        let mut layouts = [(); N].map(|()| None);
300
301        for i in 0..N {
302            if i > u8::MAX as usize {
303                return None;
304            }
305
306            layouts[i] = Some(self.layout().plane(i as u8)?);
307        }
308
309        let layouts = layouts.map(|layout| layout.unwrap());
310
311        let mut offset = 0;
312        // This frame's layout takes 0 bytes, so we can take all contents with split_layout
313        let frame: ImageMut<'_, Bytes> = self.as_mut().decay();
314
315        let &Bytes(total_len) = frame.layout();
316        let mut frame = frame.with_layout(Bytes(0)).expect("zero-byte layout valid");
317
318        let planes = layouts.map(move |mut layout| {
319            layout.sub_offset(offset);
320            let mut plane = frame
321                .split_layout()
322                .with_layout(layout)
323                .expect("plane layout within frame");
324            let tail = plane.split_layout();
325            let &Bytes(tail_len) = tail.layout();
326            // Put back all remaining bytes.
327            frame = tail.with_layout(Bytes(0)).expect("zero-byte layout valid");
328            offset = total_len - tail_len;
329
330            Some(plane)
331        });
332
333        if planes.iter().any(|p| p.is_none()) {
334            return None;
335        }
336
337        Some(planes.map(|p| BytePlaneMut { inner: p.unwrap() }))
338    }
339
340    /// Set the color model.
341    ///
342    /// This never changes the physical layout of data and preserves all its bits. It returns an
343    /// error if the new color is not compatible with the texel's color channels.
344    pub fn set_color(&mut self, color: Color) -> Result<(), LayoutError> {
345        self.inner.layout_mut_unguarded().set_color(color)
346    }
347
348    pub(crate) fn as_mut(&mut self) -> ImageMut<'_, &'_ mut CanvasLayout> {
349        self.inner.as_mut()
350    }
351}
352
353/// Conversion related methods.
354impl Canvas {
355    /// Write into another frame, converting color representation between.
356    ///
357    /// # Design notes
358    ///
359    /// The intention is that we can convert between any canvases through some common connection
360    /// color space. In debug builds we assert that the conversion ran to completion. However,
361    /// consider this a bit finicky. We want to represent canvases that have not yet had their
362    /// conversion implemented, for instance certain planar combinations, or certain ICC profile
363    /// combinations if we get around to it.
364    pub fn convert(&self, into: &mut Self) -> Result<(), crate::shader::ConversionError> {
365        Converter::new().run_to_completion(self, into)
366    }
367}
368
369impl RcCanvas {
370    pub fn new(layout: CanvasLayout) -> Self {
371        RcCanvas {
372            inner: CellImage::new(layout),
373        }
374    }
375
376    /// Get the untyped descriptor of a texel matrix.
377    ///
378    /// Returns `None` if the image contains data that can not be described as a single texel
379    /// plane, e.g. multiple planes or if the plane is not a matrix.
380    pub fn plane(&self, idx: u8) -> Option<BytePlaneCells<'_>> {
381        let [plane] = self.inner.as_ref().into_planes([PlaneIdx(idx)]).ok()?;
382        Some(BytePlaneCells { inner: plane })
383    }
384
385    pub fn to_canvas(&self) -> Canvas {
386        Canvas {
387            inner: self.inner.clone().into_owned(),
388        }
389    }
390}
391
392impl ArcCanvas {
393    pub fn new(layout: CanvasLayout) -> Self {
394        ArcCanvas {
395            inner: AtomicImage::new(layout),
396        }
397    }
398
399    /// Get the untyped descriptor of a texel matrix.
400    ///
401    /// Returns `None` if the image contains data that can not be described as a single texel
402    /// plane, e.g. multiple planes or if the plane is not a matrix.
403    pub fn plane(&self, idx: u8) -> Option<BytePlaneAtomics<'_>> {
404        let [plane] = self.inner.as_ref().into_planes([PlaneIdx(idx)]).ok()?;
405        Some(BytePlaneAtomics { inner: plane })
406    }
407
408    pub fn to_canvas(&self) -> Canvas {
409        Canvas {
410            inner: self.inner.clone().into_owned(),
411        }
412    }
413}
414
415impl<'data> BytePlaneRef<'data> {
416    pub fn layout(&self) -> &PlaneBytes {
417        self.inner.layout()
418    }
419
420    pub fn to_owned(&self) -> Plane {
421        let plane = self.inner.layout();
422        let bytes = self.inner.as_bytes();
423
424        Plane {
425            inner: Image::with_bytes(plane.clone(), bytes),
426        }
427    }
428
429    pub fn to_canvas(&self) -> Canvas {
430        let plane = self.inner.layout();
431        let bytes = self.inner.as_bytes();
432
433        Canvas {
434            inner: Image::with_bytes(plane.into(), bytes),
435        }
436    }
437
438    /// Upgrade to a view with strongly typed texel type.
439    pub fn as_texels<T>(self, texel: image_texel::Texel<T>) -> Option<PlaneRef<'data, T>> {
440        if let Some(layout) = self.inner.layout().is_compatible(texel) {
441            Some(PlaneRef {
442                inner: self.inner.with_layout(layout).unwrap(),
443            })
444        } else {
445            None
446        }
447    }
448}
449
450impl<'data> BytePlaneMut<'data> {
451    pub fn layout(&self) -> &PlaneBytes {
452        self.inner.layout()
453    }
454
455    pub fn to_owned(&self) -> Plane {
456        let plane = self.inner.layout();
457        let bytes = self.inner.as_bytes();
458
459        Plane {
460            inner: Image::with_bytes(plane.clone(), bytes),
461        }
462    }
463
464    pub fn to_canvas(&self) -> Canvas {
465        let plane = self.inner.layout();
466        let bytes = self.inner.as_bytes();
467
468        Canvas {
469            inner: Image::with_bytes(plane.into(), bytes),
470        }
471    }
472
473    /// Upgrade to a view with strongly typed texel type.
474    pub fn as_texels<T>(self, texel: image_texel::Texel<T>) -> Option<PlaneRef<'data, T>> {
475        if let Some(layout) = self.inner.layout().is_compatible(texel) {
476            Some(PlaneRef {
477                inner: self.inner.into_ref().with_layout(layout).unwrap(),
478            })
479        } else {
480            None
481        }
482    }
483
484    /// Upgrade to a mutable view with strongly typed texel type.
485    pub fn as_mut_texels<T>(self, texel: image_texel::Texel<T>) -> Option<PlaneMut<'data, T>> {
486        if let Some(layout) = self.inner.layout().is_compatible(texel) {
487            Some(PlaneMut {
488                inner: self.inner.with_layout(layout).unwrap(),
489            })
490        } else {
491            None
492        }
493    }
494}
495
496impl<'data> BytePlaneCells<'data> {
497    pub fn layout(&self) -> &PlaneBytes {
498        self.inner.layout()
499    }
500
501    pub fn to_owned(&self) -> Plane {
502        Plane {
503            inner: self.inner.clone().into_owned(),
504        }
505    }
506
507    pub fn to_canvas(&self) -> Canvas {
508        self.to_owned().into()
509    }
510}
511
512impl<'data> BytePlaneAtomics<'data> {
513    pub fn layout(&self) -> &PlaneBytes {
514        self.inner.layout()
515    }
516
517    pub fn to_owned(&self) -> Plane {
518        Plane {
519            inner: self.inner.clone().into_owned(),
520        }
521    }
522
523    pub fn to_canvas(&self) -> Canvas {
524        self.to_owned().into()
525    }
526}
527
528impl<'data, C> ChannelsRef<'data, C> {
529    pub fn layout(&self) -> &ChannelLayout<C> {
530        self.inner.layout()
531    }
532
533    pub fn as_slice(&self) -> &[C] {
534        self.inner.as_slice()
535    }
536
537    pub fn into_slice(self) -> &'data [C] {
538        self.inner.into_slice()
539    }
540}
541
542impl<'data, C> ChannelsMut<'data, C> {
543    pub fn layout(&self) -> &ChannelLayout<C> {
544        self.inner.layout()
545    }
546
547    pub fn as_slice(&self) -> &[C] {
548        self.inner.as_slice()
549    }
550
551    pub fn as_mut_slice(&mut self) -> &mut [C] {
552        self.inner.as_mut_slice()
553    }
554
555    pub fn into_slice(self) -> &'data [C] {
556        self.inner.into_slice()
557    }
558
559    pub fn into_mut_slice(self) -> &'data mut [C] {
560        self.inner.into_mut_slice()
561    }
562}
563
564impl<'data, T> From<PlaneRef<'data, T>> for BytePlaneRef<'data> {
565    fn from(plane: PlaneRef<'data, T>) -> Self {
566        BytePlaneRef {
567            inner: plane.inner.decay(),
568        }
569    }
570}
571
572impl<'data, T> From<PlaneMut<'data, T>> for BytePlaneMut<'data> {
573    fn from(plane: PlaneMut<'data, T>) -> Self {
574        BytePlaneMut {
575            inner: plane.inner.decay(),
576        }
577    }
578}
579
580impl From<Plane> for Canvas {
581    fn from(plane: Plane) -> Self {
582        Canvas {
583            inner: plane.inner.decay(),
584        }
585    }
586}
587
588impl From<Canvas> for RcCanvas {
589    fn from(canvas: Canvas) -> Self {
590        RcCanvas {
591            inner: CellImage::with_bytes(canvas.layout().clone(), canvas.as_bytes()),
592        }
593    }
594}
595
596impl From<Canvas> for ArcCanvas {
597    fn from(canvas: Canvas) -> Self {
598        ArcCanvas {
599            inner: AtomicImage::with_bytes(canvas.layout().clone(), canvas.as_bytes()),
600        }
601    }
602}
603
604impl From<RcCanvas> for Canvas {
605    fn from(canvas: RcCanvas) -> Self {
606        Canvas {
607            inner: canvas.inner.into_owned(),
608        }
609    }
610}
611
612impl From<ArcCanvas> for Canvas {
613    fn from(canvas: ArcCanvas) -> Self {
614        Canvas {
615            inner: canvas.inner.into_owned(),
616        }
617    }
618}
619
620impl From<RcCanvas> for ArcCanvas {
621    fn from(canvas: RcCanvas) -> Self {
622        // Here we would want to allocate an Arc buffer straight from the Rc buffer. This is unsafe
623        // code has yet to be written and verified. Instead, we allocate a zeroed buffer under the
624        // assumption that this is conveniently fast, then copy data over.
625        let inner = canvas.inner;
626
627        let mut out = AtomicImage::new(inner.layout().clone());
628        let target = out.get_mut().expect("Still the sole owner");
629        U8.load_cell_slice(inner.as_texels(U8).as_slice_of_cells(), target.as_buf_mut());
630
631        ArcCanvas { inner: out }
632    }
633}
634
635impl From<ArcCanvas> for RcCanvas {
636    fn from(canvas: ArcCanvas) -> Self {
637        // Same choice as the inverse.
638        let inner = canvas.inner;
639
640        let out = CellImage::new(inner.layout().clone());
641        // That copy should be the same as filling mutable byte memory. Feel free to contradict
642        // this with benchmarks especially considering inlining.
643        U8.load_atomic_to_cells(inner.as_texels(U8), out.as_texels(U8).as_slice_of_cells());
644
645        RcCanvas { inner: out }
646    }
647}