framing/
chunky.rs

1use rayon::prelude::*;
2use std::marker::PhantomData;
3use std::ptr;
4use std::sync::atomic::{AtomicPtr, Ordering};
5use super::{Image, AsBytes};
6
7/// A [chunky frame](https://en.wikipedia.org/wiki/Packed_pixel).
8///
9/// In this format, each pixel is stored contiguously, and the entire image is
10/// stored in row-major order. For example, this means that an RGBA image would
11/// store the RGBA values of the top-left pixel, then each of the RGBA values of
12/// the pixel immediately to the right, and so on, moving down through each row.
13#[derive(Clone, Debug)]
14pub struct Chunky<T, V = Vec<u8>> {
15    bytes: V,
16    width: usize,
17    height: usize,
18    pixel: PhantomData<T>
19}
20
21impl<T, V> Image for Chunky<T, V>
22where
23    T: AsBytes,
24    V: AsRef<[u8]>
25{
26    type Pixel = T;
27
28    fn width(&self)  -> usize { self.width }
29    fn height(&self) -> usize { self.height }
30
31    unsafe fn pixel(&self, x: usize, y: usize) -> Self::Pixel {
32        let off = T::width() * (y * self.width + x);
33        let mut bytes = T::Bytes::default();
34
35        ptr::copy_nonoverlapping(
36            self.bytes.as_ref().as_ptr().offset(off as isize),
37            bytes.as_mut().as_mut_ptr(),
38            T::width()
39        );
40
41        bytes.into()
42    }
43}
44
45impl<T, V> Chunky<T, V> {
46    /// Creates a new frame backed by the provided byte source.
47    ///
48    /// # Panics
49    ///
50    /// Panics if the length of the buffer is not
51    /// `width * height * bytes_per_pixel`.
52    pub fn from_bytes(width: usize, height: usize, bytes: V) -> Self
53    where
54        T: AsBytes,
55        V: AsRef<[u8]>
56    {
57        assert_eq!(bytes.as_ref().len(), width * height * T::width());
58        Self { bytes, width, height, pixel: PhantomData }
59    }
60
61    /// Returns a read-only view into the frame's byte source.
62    pub fn bytes(&self) -> &V {
63        &self.bytes
64    }
65
66    /// Recovers the byte source.
67    pub fn into_bytes(self) -> V {
68        self.bytes
69    }
70
71    /// Returns a mutable view into the frame's byte source.
72    pub fn bytes_mut(&mut self) -> &mut [u8]
73    where
74        V: AsMut<[u8]>
75    {
76        self.bytes.as_mut()
77    }
78
79    /// Set the frame's contents to that of the given frame.
80    ///
81    /// # Panics
82    ///
83    /// Panics if the width and height of the given frame are not exactly the
84    /// same as the width and height of the chunky frame.
85    pub fn copy_from<U>(&mut self, frame: U)
86    where
87        T: AsBytes,
88        U: Image<Pixel = T> + Sync,
89        V: AsMut<[u8]>
90    {
91        assert_eq!(frame.width(), self.width);
92        assert_eq!(frame.height(), self.height);
93
94        let length = self.width * self.height;
95        let ptr = AtomicPtr::new(self.bytes.as_mut().as_mut_ptr());
96        let width = self.width;
97
98        (0..length).into_par_iter().for_each(|i| unsafe {
99            let ptr = ptr.load(Ordering::Relaxed);
100            let (x, y) = (i % width, i / width);
101            let bytes = T::Bytes::from(frame.pixel(x, y).into());
102
103            ptr::copy_nonoverlapping(
104                bytes.as_ref().as_ptr(),
105                ptr.offset((T::width() * i) as _),
106                T::width()
107            );
108        });
109    }
110}
111
112impl<T> Chunky<T> where T: AsBytes {
113    /// Creates a new frame using the given frame to fill the buffer.
114    /// It is guaranteed that the mapping will be called **exactly once** for
115    /// each of the integers in the range `[0, width) * [0, height)`.
116    pub fn new<U>(frame: U) -> Self
117    where
118        U: Image<Pixel = T> + Sync
119    {
120        let (width, height) = (frame.width(), frame.height());
121        let length = width * height;
122        let size = T::width() * length;
123
124        let mut bytes = Vec::with_capacity(size);
125        unsafe { bytes.set_len(size); }
126
127        let mut chunky = Self::from_bytes(width, height, bytes);
128        chunky.copy_from(frame);
129        chunky
130    }
131}
132
133#[test]
134fn black() {
135    use super::{Function, Rgba, iter};
136
137    let (w, h) = (1920, 1080);
138    let frame = Chunky::new(
139        Function::new(w, h, |_, _| Rgba(0, 0, 0, 0))
140    );
141
142    assert_eq!(frame.width(), w);
143    assert_eq!(frame.height(), h);
144    assert_eq!(frame.bytes, vec![0; w * h * 4]);
145    assert_eq!(iter(&frame).count(), w * h);
146    assert!(iter(&frame).all(|(_, _, x)| x == Rgba(0, 0, 0, 0)));
147}