Skip to main content

openh264/formats/
rgb.rs

1/// Source of arbitrarily formatted RGB data.
2///
3/// This is the "compatible" trait for RGB sources, but it will
4/// be slow, since it only supports single pixel lookup.
5pub trait RGBSource {
6    /// Returns the underlying image size as an `i32` tuple `(w, h)`.
7    #[must_use]
8    fn dimensions_i32(&self) -> (i32, i32) {
9        let (w, h) = self.dimensions();
10        (w as i32, h as i32)
11    }
12
13    /// Returns the underlying image size as an `usize` tuple `(w, h)`.
14    #[must_use]
15    fn dimensions(&self) -> (usize, usize);
16
17    /// Extract the pixel value at the specified location. Pixel values are
18    /// expected to be floats in the range `[0, 255]` (`u8` represented as `f32`).
19    #[must_use]
20    fn pixel_f32(&self, x: usize, y: usize) -> (f32, f32, f32);
21}
22
23/// Source of RGB8 data for fast pixel access.
24///
25/// This is the "fast" trait for RGB sources. If you can expose continuous pixels
26/// slices with RGB8 data you might (eventually) be rewarded with SIMD conversion.
27pub trait RGB8Source: RGBSource {
28    /// Returns padded dimensions of the underlying slice.
29    ///
30    /// For example, the data might have a display format of 100x100, but the
31    /// underlying RGB8 array is of size 128x100.
32    #[must_use]
33    fn dimensions_padded(&self) -> (usize, usize);
34
35    /// Slice of RGB8 data, with given padding.
36    #[must_use]
37    fn rgb8_data(&self) -> &[u8];
38}
39
40/// Container for a slice of contiguous `[R G B R G B ...]` data.<sup>⭐</sup>
41///
42/// This is the preferred format for reading data, for use with `_rgb8` methods.
43#[derive(Copy, Clone, Debug)]
44#[must_use]
45pub struct RgbSliceU8<'a> {
46    data: &'a [u8],
47    dimensions: (usize, usize),
48}
49
50/// Container for a slice of contiguous `[B G R B G R ...]` data.
51#[derive(Copy, Clone, Debug)]
52#[must_use]
53pub struct BgrSliceU8<'a> {
54    data: &'a [u8],
55    dimensions: (usize, usize),
56}
57
58/// Container for a slice of contiguous `[R G B A R G B A ...]` data.
59#[derive(Copy, Clone, Debug)]
60#[must_use]
61pub struct RgbaSliceU8<'a> {
62    data: &'a [u8],
63    dimensions: (usize, usize),
64}
65
66/// Container for a slice of contiguous `[RGBA RGBA ...]` data.
67///
68/// The platform endianness of the data is irrelevant: R is the highest byte and A is the lowest.
69#[derive(Copy, Clone, Debug)]
70#[must_use]
71pub struct RgbaSliceU32<'a> {
72    data: &'a [u32],
73    dimensions: (usize, usize),
74}
75
76/// Container for a slice of contiguous `[B G R A B G R A ...]` data.
77#[derive(Copy, Clone, Debug)]
78#[must_use]
79pub struct BgraSliceU8<'a> {
80    data: &'a [u8],
81    dimensions: (usize, usize),
82}
83
84/// Container for a slice of contiguous `[BGRA BGRA ...]` data.
85///
86/// The platform endianness of the data is irrelevant: B is the highest byte and A is the lowest.
87#[derive(Copy, Clone, Debug)]
88#[must_use]
89pub struct BgraSliceU32<'a> {
90    data: &'a [u32],
91    dimensions: (usize, usize),
92}
93
94/// Container for a slice of contiguous `[A B G R A B G R ...]` data.
95#[derive(Copy, Clone, Debug)]
96#[must_use]
97pub struct AbgrSliceU8<'a> {
98    data: &'a [u8],
99    dimensions: (usize, usize),
100}
101
102/// Container for a slice of contiguous `[ABGR ABGR ...]` data.
103///
104/// The platform endianness of the data is irrelevant: A is the highest byte and R is the lowest.
105#[derive(Copy, Clone, Debug)]
106#[must_use]
107pub struct AbgrSliceU32<'a> {
108    data: &'a [u32],
109    dimensions: (usize, usize),
110}
111
112/// Container for a slice of contiguous `[A R G B A R G B ...]` data.
113#[derive(Copy, Clone, Debug)]
114#[must_use]
115pub struct ArgbSliceU8<'a> {
116    data: &'a [u8],
117    dimensions: (usize, usize),
118}
119
120/// Container for a slice of contiguous `[ARGB ARGB ...]` data.
121///
122/// The platform endianness of the data is irrelevant: A is the highest byte and B is the lowest.
123#[derive(Copy, Clone, Debug)]
124#[must_use]
125pub struct ArgbSliceU32<'a> {
126    data: &'a [u32],
127    dimensions: (usize, usize),
128}
129
130macro_rules! impl_slice_wrapper_u8 {
131    ($t:ty, $stride:expr, $offsets:expr) => {
132        impl<'a> $t {
133            /// Creates a new instance given the byte slice and dimensions.
134            ///
135            /// # Panics
136            ///
137            /// May panic if the given sizes are not multiples of 2, or if the slice length mismatches the given dimensions.
138            #[allow(unused)]
139            pub fn new(data: &'a [u8], dimensions: (usize, usize)) -> Self {
140                assert_eq!(data.len(), dimensions.0 * dimensions.1 * $stride);
141                assert_eq!(dimensions.0 % 2, 0, "width needs to be multiple of 2");
142                assert_eq!(dimensions.1 % 2, 0, "height needs to be a multiple of 2");
143
144                Self { data, dimensions }
145            }
146        }
147
148        impl<'a> RGBSource for $t {
149            fn dimensions(&self) -> (usize, usize) {
150                self.dimensions
151            }
152
153            fn pixel_f32(&self, x: usize, y: usize) -> (f32, f32, f32) {
154                let base_pos = (x + y * self.dimensions.0) * $stride;
155                (
156                    self.data[base_pos + $offsets[0]].into(),
157                    self.data[base_pos + $offsets[1]].into(),
158                    self.data[base_pos + $offsets[2]].into(),
159                )
160            }
161        }
162    };
163}
164
165macro_rules! impl_slice_wrapper_u32 {
166    ($t:ty, $offsets:expr) => {
167        impl<'a> $t {
168            /// Creates a new instance given the data slice and dimensions.
169            ///
170            /// # Panics
171            ///
172            /// May panic if the given sizes are not multiples of 2, or if the slice length mismatches the given dimensions.
173            #[allow(unused)]
174            pub fn new(data: &'a [u32], dimensions: (usize, usize)) -> Self {
175                assert_eq!(data.len(), dimensions.0 * dimensions.1);
176                assert_eq!(dimensions.0 % 2, 0, "width needs to be multiple of 2");
177                assert_eq!(dimensions.1 % 2, 0, "height needs to be a multiple of 2");
178
179                Self { data, dimensions }
180            }
181        }
182
183        impl<'a> RGBSource for $t {
184            fn dimensions(&self) -> (usize, usize) {
185                self.dimensions
186            }
187
188            fn pixel_f32(&self, x: usize, y: usize) -> (f32, f32, f32) {
189                let px = self.data[x + y * self.dimensions.0];
190                (
191                    ((px >> $offsets[0]) & 0xFF) as f32,
192                    ((px >> $offsets[1]) & 0xFF) as f32,
193                    ((px >> $offsets[2]) & 0xFF) as f32,
194                )
195            }
196        }
197    };
198}
199
200impl_slice_wrapper_u8!(RgbSliceU8<'a>, 3, [0, 1, 2]);
201impl_slice_wrapper_u8!(RgbaSliceU8<'a>, 4, [0, 1, 2]);
202impl_slice_wrapper_u8!(BgrSliceU8<'a>, 3, [2, 1, 0]);
203impl_slice_wrapper_u8!(BgraSliceU8<'a>, 4, [2, 1, 0]);
204impl_slice_wrapper_u8!(ArgbSliceU8<'a>, 4, [1, 2, 3]);
205impl_slice_wrapper_u8!(AbgrSliceU8<'a>, 4, [3, 2, 1]);
206
207impl_slice_wrapper_u32!(RgbaSliceU32<'a>, [24, 16, 8]);
208impl_slice_wrapper_u32!(BgraSliceU32<'a>, [8, 16, 24]);
209impl_slice_wrapper_u32!(AbgrSliceU32<'a>, [0, 8, 16]);
210impl_slice_wrapper_u32!(ArgbSliceU32<'a>, [16, 8, 0]);
211
212impl RGB8Source for RgbSliceU8<'_> {
213    fn dimensions_padded(&self) -> (usize, usize) {
214        self.dimensions()
215    }
216
217    fn rgb8_data(&self) -> &[u8] {
218        self.data
219    }
220}
221
222#[cfg(test)]
223mod tests {
224    use super::{AbgrSliceU32, ArgbSliceU32, BgrSliceU8, BgraSliceU32, RGBSource, RgbSliceU8, RgbaSliceU32};
225
226    #[test]
227    fn rgb_slice_4x4() {
228        let vec: Vec<u8> = (0..4 * 4 * 3).collect();
229        let slice = RgbSliceU8::new(&vec, (4, 4));
230        assert_eq!(slice.pixel_f32(0, 0), (0., 1., 2.));
231        assert_eq!(slice.pixel_f32(1, 0), (3., 4., 5.));
232        assert_eq!(slice.pixel_f32(2, 0), (6., 7., 8.));
233        assert_eq!(slice.pixel_f32(0, 1), (12., 13., 14.));
234        assert_eq!(slice.pixel_f32(1, 1), (15., 16., 17.));
235        assert_eq!(slice.pixel_f32(2, 1), (18., 19., 20.));
236        assert_eq!(slice.pixel_f32(0, 2), (24., 25., 26.));
237        assert_eq!(slice.pixel_f32(1, 2), (27., 28., 29.));
238        assert_eq!(slice.pixel_f32(2, 2), (30., 31., 32.));
239    }
240
241    #[test]
242    fn bgr_slice_4x4() {
243        let vec: Vec<u8> = (0..4 * 4 * 3).collect();
244        let slice = BgrSliceU8::new(&vec, (4, 4));
245        assert_eq!(slice.pixel_f32(0, 0), (2., 1., 0.));
246        assert_eq!(slice.pixel_f32(1, 0), (5., 4., 3.));
247        assert_eq!(slice.pixel_f32(2, 0), (8., 7., 6.));
248        assert_eq!(slice.pixel_f32(0, 1), (14.0, 13.0, 12.0));
249        assert_eq!(slice.pixel_f32(1, 1), (17.0, 16.0, 15.0));
250        assert_eq!(slice.pixel_f32(2, 1), (20.0, 19.0, 18.0));
251        assert_eq!(slice.pixel_f32(0, 2), (26.0, 25.0, 24.0));
252        assert_eq!(slice.pixel_f32(1, 2), (29.0, 28.0, 27.0));
253        assert_eq!(slice.pixel_f32(2, 2), (32.0, 31.0, 30.0));
254    }
255
256    #[test]
257    fn rgba_slice_2x2() {
258        let data: [u32; 5] = [0xFF000102, 0xFF010002, 0xFF000201, 0xFF020001, 0xAABBCCDD];
259        let slice = RgbaSliceU32::new(&data[1..], (2, 2));
260        assert_eq!(slice.pixel_f32(0, 0), (255., 1., 0.));
261        assert_eq!(slice.pixel_f32(1, 0), (255., 0., 2.));
262        assert_eq!(slice.pixel_f32(0, 1), (255., 2., 0.));
263        assert_eq!(slice.pixel_f32(1, 1), (170., 187., 204.));
264    }
265
266    #[test]
267    fn argb_slice_2x2() {
268        let data: [u32; 5] = [0xFF000102, 0xFF010002, 0xFF000201, 0xFF020001, 0xAABBCCDD];
269        let slice = ArgbSliceU32::new(&data[1..], (2, 2));
270        assert_eq!(slice.pixel_f32(0, 0), (1., 0., 2.));
271        assert_eq!(slice.pixel_f32(1, 0), (0., 2., 1.));
272        assert_eq!(slice.pixel_f32(0, 1), (2., 0., 1.));
273        assert_eq!(slice.pixel_f32(1, 1), (187., 204., 221.));
274    }
275
276    #[test]
277    fn bgra_slice_2x2() {
278        let data: [u32; 5] = [0xFF000102, 0xFF010002, 0xFF000201, 0xFF020001, 0xAABBCCDD];
279        let slice = BgraSliceU32::new(&data[1..], (2, 2));
280        assert_eq!(slice.pixel_f32(0, 0), (0., 1., 255.));
281        assert_eq!(slice.pixel_f32(1, 0), (2., 0., 255.));
282        assert_eq!(slice.pixel_f32(0, 1), (0., 2., 255.));
283        assert_eq!(slice.pixel_f32(1, 1), (204., 187., 170.));
284    }
285
286    #[test]
287    fn abgr_slice_2x2() {
288        let data: [u32; 5] = [0xFF000102, 0xFF010002, 0xFF000201, 0xFF020001, 0xAABBCCDD];
289        let slice = AbgrSliceU32::new(&data[1..], (2, 2));
290        assert_eq!(slice.pixel_f32(0, 0), (2., 0., 1.));
291        assert_eq!(slice.pixel_f32(1, 0), (1., 2., 0.));
292        assert_eq!(slice.pixel_f32(0, 1), (1., 0., 2.));
293        assert_eq!(slice.pixel_f32(1, 1), (221., 204., 187.));
294    }
295}