Skip to main content

cu_sensor_payloads/
image.rs

1use alloc::vec::Vec;
2use bincode::de::Decoder;
3use bincode::error::DecodeError;
4use bincode::{Decode, Encode};
5use core::fmt::Debug;
6use cu29::prelude::*;
7
8#[cfg(feature = "image")]
9use image::{ImageBuffer, Pixel};
10#[cfg(feature = "kornia")]
11use kornia_image::Image;
12#[cfg(feature = "kornia")]
13use kornia_image::allocator::ImageAllocator;
14use serde::{Deserialize, Serialize, Serializer};
15
16#[derive(Default, Debug, Encode, Decode, Clone, Copy, Serialize, Deserialize, Reflect)]
17pub struct CuImageBufferFormat {
18    pub width: u32,
19    pub height: u32,
20    pub stride: u32,
21    pub pixel_format: [u8; 4],
22}
23
24impl CuImageBufferFormat {
25    pub fn byte_size(&self) -> usize {
26        self.stride as usize * self.height as usize
27    }
28}
29
30#[derive(Debug, Default, Clone, Encode, Reflect)]
31#[reflect(from_reflect = false, no_field_bounds, type_path = false)]
32pub struct CuImage<A>
33where
34    A: ArrayLike<Element = u8> + Send + Sync + 'static,
35{
36    pub seq: u64,
37    pub format: CuImageBufferFormat,
38    #[reflect(ignore)]
39    pub buffer_handle: CuHandle<A>,
40}
41
42impl<A> TypePath for CuImage<A>
43where
44    A: ArrayLike<Element = u8> + Send + Sync + 'static,
45{
46    fn type_path() -> &'static str {
47        "cu_sensor_payloads::CuImage"
48    }
49
50    fn short_type_path() -> &'static str {
51        "CuImage"
52    }
53
54    fn type_ident() -> Option<&'static str> {
55        Some("CuImage")
56    }
57
58    fn crate_name() -> Option<&'static str> {
59        Some("cu_sensor_payloads")
60    }
61
62    fn module_path() -> Option<&'static str> {
63        Some("cu_sensor_payloads")
64    }
65}
66
67impl Decode<()> for CuImage<Vec<u8>> {
68    fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {
69        let seq: u64 = Decode::decode(decoder)?;
70        let format: CuImageBufferFormat = Decode::decode(decoder)?;
71        let buffer: Vec<u8> = Decode::decode(decoder)?;
72        let buffer_handle = CuHandle::new_detached(buffer);
73
74        Ok(Self {
75            seq,
76            format,
77            buffer_handle,
78        })
79    }
80}
81
82impl<'de> Deserialize<'de> for CuImage<Vec<u8>> {
83    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
84    where
85        D: serde::Deserializer<'de>,
86    {
87        #[derive(Deserialize)]
88        struct CuImageWire {
89            seq: u64,
90            format: CuImageBufferFormat,
91            handle: Vec<u8>,
92        }
93
94        let wire = CuImageWire::deserialize(deserializer)?;
95        Ok(Self {
96            seq: wire.seq,
97            format: wire.format,
98            buffer_handle: CuHandle::new_detached(wire.handle),
99        })
100    }
101}
102
103impl<A> Serialize for CuImage<A>
104where
105    A: ArrayLike<Element = u8> + Send + Sync + 'static,
106{
107    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
108    where
109        S: Serializer,
110    {
111        use serde::ser::SerializeStruct;
112        let mut struct_ = serializer.serialize_struct("CuImage", 3)?;
113        struct_.serialize_field("seq", &self.seq)?;
114        struct_.serialize_field("format", &self.format)?;
115        // Use empty Vec as placeholder for buffer_handle to keep it opaque
116        let placeholder_buffer: Vec<u8> = Vec::new();
117        struct_.serialize_field("handle", &placeholder_buffer)?;
118        struct_.end()
119    }
120}
121
122impl<A> CuImage<A>
123where
124    A: ArrayLike<Element = u8> + Send + Sync + 'static,
125{
126    pub fn new(format: CuImageBufferFormat, buffer_handle: CuHandle<A>) -> Self {
127        assert!(
128            format.byte_size() <= buffer_handle.with_inner(|i| i.len()),
129            "Buffer size must at least match the format."
130        );
131        CuImage {
132            seq: 0,
133            format,
134            buffer_handle,
135        }
136    }
137}
138
139impl<A> CuImage<A>
140where
141    A: ArrayLike<Element = u8> + Send + Sync + 'static,
142{
143    /// Builds an ImageBuffer from the image crate backed by the CuImage's pixel data.
144    #[cfg(feature = "image")]
145    pub fn as_image_buffer<P: Pixel>(&self) -> CuResult<ImageBuffer<P, &[P::Subpixel]>> {
146        let width = self.format.width;
147        let height = self.format.height;
148        assert_eq!(
149            width, self.format.stride,
150            "STRIDE must equal WIDTH for ImageBuffer compatibility."
151        );
152
153        let raw_pixels: &[P::Subpixel] = self.buffer_handle.with_inner(|inner| {
154            // SAFETY: The buffer is contiguous, aligned for P::Subpixel (typically u8), and large enough.
155            unsafe {
156                let data: &[u8] = inner;
157                core::slice::from_raw_parts(data.as_ptr() as *const P::Subpixel, data.len())
158            }
159        });
160        ImageBuffer::from_raw(width, height, raw_pixels)
161            .ok_or("Could not create the image:: buffer".into())
162    }
163
164    #[cfg(feature = "kornia")]
165    pub fn as_kornia_image<T: Clone, const C: usize, K: ImageAllocator>(
166        &self,
167        k: K,
168    ) -> CuResult<Image<T, C, K>> {
169        let width = self.format.width as usize;
170        let height = self.format.height as usize;
171
172        assert_eq!(
173            width, self.format.stride as usize,
174            "stride must equal width for Kornia compatibility."
175        );
176
177        let size = width * height * C;
178        let raw_pixels: &[T] = self.buffer_handle.with_inner(|inner| {
179            // SAFETY: The buffer is aligned for T, its length is a multiple of T, and it lives long enough.
180            unsafe {
181                let data: &[u8] = inner;
182                core::slice::from_raw_parts(
183                    data.as_ptr() as *const T,
184                    data.len() / core::mem::size_of::<T>(),
185                )
186            }
187        });
188
189        // SAFETY: raw_pixels points to size elements laid out for the requested shape.
190        unsafe { Image::from_raw_parts([height, width].into(), raw_pixels.as_ptr(), size, k) }
191            .map_err(|e| CuError::new_with_cause("Could not create a Kornia Image", e))
192    }
193}