libcamera/
pixel_format.rs

1use std::{ffi::CStr, fmt, ptr::NonNull, str::FromStr};
2
3use drm_fourcc::{DrmFormat, DrmFourcc, DrmModifier};
4use libcamera_sys::*;
5
6use crate::geometry::Size;
7
8mod pixel_format_info_generated {
9    include!(concat!(env!("OUT_DIR"), "/pixel_format_info.rs"));
10}
11use pixel_format_info_generated::{PixelFormatInfoData, PIXEL_FORMAT_INFO};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum ColourEncoding {
15    Rgb,
16    Yuv,
17    Raw,
18    Unknown(u32),
19}
20
21impl From<u32> for ColourEncoding {
22    fn from(v: u32) -> Self {
23        match v {
24            0 => ColourEncoding::Rgb,
25            1 => ColourEncoding::Yuv,
26            2 => ColourEncoding::Raw,
27            other => ColourEncoding::Unknown(other),
28        }
29    }
30}
31
32#[derive(Debug, Clone)]
33pub struct PixelFormatPlaneInfo {
34    pub bytes_per_group: u32,
35    pub vertical_sub_sampling: u32,
36}
37
38#[derive(Debug, Clone)]
39pub struct PixelFormatInfo {
40    pub name: String,
41    pub format: PixelFormat,
42    pub bits_per_pixel: u32,
43    pub colour_encoding: ColourEncoding,
44    pub packed: bool,
45    pub pixels_per_group: u32,
46    pub planes: Vec<PixelFormatPlaneInfo>,
47    pub v4l2_formats: Vec<u32>,
48}
49
50impl PixelFormatInfo {
51    fn from_data(fmt: PixelFormat, data: &PixelFormatInfoData) -> Self {
52        let planes = data
53            .planes
54            .iter()
55            .filter(|p| p.bytes_per_group > 0 && p.vertical_sub_sampling > 0)
56            .map(|p| PixelFormatPlaneInfo {
57                bytes_per_group: p.bytes_per_group,
58                vertical_sub_sampling: p.vertical_sub_sampling,
59            })
60            .collect();
61        Self {
62            name: data.name.to_string(),
63            format: fmt,
64            bits_per_pixel: data.bits_per_pixel,
65            colour_encoding: data.colour_encoding.into(),
66            packed: data.packed,
67            pixels_per_group: data.pixels_per_group,
68            planes,
69            v4l2_formats: data.v4l2_formats.to_vec(),
70        }
71    }
72}
73
74/// Represents `libcamera::PixelFormat`, which itself is a pair of fourcc code and u64 modifier as defined in `libdrm`.
75#[derive(Clone, Copy)]
76pub struct PixelFormat(pub(crate) libcamera_pixel_format_t);
77
78impl PixelFormat {
79    fn info_entry(&self) -> Option<&'static PixelFormatInfoData> {
80        let (fourcc, modifier) = self.to_raw();
81        PIXEL_FORMAT_INFO
82            .iter()
83            .find(|info| info.fourcc == fourcc && info.modifier == modifier)
84    }
85
86    /// Constructs new [PixelFormat] from given fourcc code and modifier.
87    ///
88    /// # Examples
89    ///
90    /// ```rust
91    /// use libcamera::pixel_format::PixelFormat;
92    /// // Constructs MJPEG pixel format
93    /// const PIXEL_FORMAT_MJPEG: PixelFormat =
94    ///     PixelFormat::new(u32::from_le_bytes([b'M', b'J', b'P', b'G']), 0);
95    /// ```
96    pub const fn new(fourcc: u32, modifier: u64) -> Self {
97        Self(libcamera_pixel_format_t { fourcc, modifier })
98    }
99
100    pub fn fourcc(&self) -> u32 {
101        self.0.fourcc
102    }
103
104    pub fn set_fourcc(&mut self, fourcc: u32) {
105        self.0.fourcc = fourcc;
106    }
107
108    pub fn modifier(&self) -> u64 {
109        self.0.modifier
110    }
111
112    pub fn set_modifier(&mut self, modifier: u64) {
113        self.0.modifier = modifier;
114    }
115
116    /// Returns true if this format has a non-zero modifier set.
117    pub fn has_modifier(&self) -> bool {
118        self.modifier() != 0
119    }
120
121    /// Compute the stride for a plane given width and optional alignment.
122    pub fn stride(&self, width: u32, plane: u32, align: u32) -> u32 {
123        self.info_entry()
124            .map(|info| compute_stride(info, width, plane, align))
125            .unwrap_or(0)
126    }
127
128    /// Compute plane size for the given frame size and plane index.
129    pub fn plane_size(&self, size: Size, plane: u32, align: u32) -> u32 {
130        self.info_entry()
131            .map(|info| compute_plane_size(info, size, plane, align))
132            .unwrap_or(0)
133    }
134
135    /// Compute total frame size for the given dimensions.
136    pub fn frame_size(&self, size: Size, align: u32) -> u32 {
137        self.info_entry()
138            .map(|info| compute_frame_size(info, size, align))
139            .unwrap_or(0)
140    }
141
142    /// Clears the modifier to zero.
143    pub fn clear_modifier(&mut self) {
144        self.0.modifier = 0;
145    }
146
147    /// Returns the raw `(fourcc, modifier)` tuple.
148    pub const fn to_raw(self) -> (u32, u64) {
149        (self.0.fourcc, self.0.modifier)
150    }
151
152    /// Constructs a PixelFormat from raw `(fourcc, modifier)` parts.
153    pub const fn from_raw_parts(fourcc: u32, modifier: u64) -> Self {
154        PixelFormat::new(fourcc, modifier)
155    }
156
157    /// Parse a PixelFormat from its string representation (e.g. "YUYV").
158    pub fn parse(name: &str) -> Option<Self> {
159        let cstr = std::ffi::CString::new(name).ok()?;
160        let fmt = unsafe { libcamera_pixel_format_from_str(cstr.as_ptr()) };
161        let pf = PixelFormat(fmt);
162        if pf.is_valid() {
163            Some(pf)
164        } else {
165            None
166        }
167    }
168
169    /// Returns true if the PixelFormat represents a valid libcamera format.
170    pub fn is_valid(&self) -> bool {
171        unsafe { libcamera_pixel_format_is_valid(&self.0) }
172    }
173
174    pub fn info(&self) -> Option<PixelFormatInfo> {
175        self.info_entry().map(|data| PixelFormatInfo::from_data(*self, data))
176    }
177}
178
179impl FromStr for PixelFormat {
180    type Err = String;
181
182    fn from_str(s: &str) -> Result<Self, Self::Err> {
183        PixelFormat::parse(s).ok_or_else(|| format!("unrecognized pixel format: {s}"))
184    }
185}
186
187impl PartialEq for PixelFormat {
188    fn eq(&self, other: &Self) -> bool {
189        self.0.fourcc.eq(&other.0.fourcc) && self.0.modifier.eq(&other.0.modifier)
190    }
191}
192
193impl Eq for PixelFormat {}
194
195impl core::fmt::Debug for PixelFormat {
196    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
197        let ptr = unsafe { libcamera_pixel_format_str(&self.0) };
198        let out = unsafe { CStr::from_ptr(ptr) }.to_str().unwrap();
199        f.write_str(out)?;
200        unsafe { libc::free(ptr.cast()) };
201        Ok(())
202    }
203}
204
205impl fmt::Display for PixelFormat {
206    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207        write!(f, "{:?}", self)
208    }
209}
210
211fn compute_stride(info: &PixelFormatInfoData, width: u32, plane: u32, align: u32) -> u32 {
212    if plane as usize >= info.planes.len() {
213        return 0;
214    }
215    let plane_info = &info.planes[plane as usize];
216    if plane_info.bytes_per_group == 0 || plane_info.vertical_sub_sampling == 0 {
217        return 0;
218    }
219    let groups = (width as u64).div_ceil(info.pixels_per_group as u64);
220    let mut stride = groups * plane_info.bytes_per_group as u64;
221    if align > 0 {
222        stride = stride.div_ceil(align as u64) * align as u64;
223    }
224    stride as u32
225}
226
227fn compute_plane_size(info: &PixelFormatInfoData, size: Size, plane: u32, align: u32) -> u32 {
228    if plane as usize >= info.planes.len() {
229        return 0;
230    }
231    let plane_info = &info.planes[plane as usize];
232    if plane_info.vertical_sub_sampling == 0 {
233        return 0;
234    }
235    let stride = compute_stride(info, size.width, plane, align) as u64;
236    // ceil(height / vertical_sub_sampling)
237    let height = (size.height as u64).div_ceil(plane_info.vertical_sub_sampling as u64);
238    (stride * height) as u32
239}
240
241fn compute_frame_size(info: &PixelFormatInfoData, size: Size, align: u32) -> u32 {
242    let mut total: u64 = 0;
243    for p in 0..info.planes.len() {
244        let plane = &info.planes[p];
245        if plane.bytes_per_group == 0 || plane.vertical_sub_sampling == 0 {
246            continue;
247        }
248        total += compute_plane_size(info, size, p as u32, align) as u64;
249    }
250    total as u32
251}
252
253impl From<u8> for ColourEncoding {
254    fn from(v: u8) -> Self {
255        match v {
256            0 => ColourEncoding::Rgb,
257            1 => ColourEncoding::Yuv,
258            2 => ColourEncoding::Raw,
259            other => ColourEncoding::Unknown(other as u32),
260        }
261    }
262}
263
264impl TryFrom<PixelFormat> for DrmFormat {
265    type Error = drm_fourcc::UnrecognizedFourcc;
266
267    fn try_from(value: PixelFormat) -> Result<Self, Self::Error> {
268        let code = DrmFourcc::try_from(value.0.fourcc)?;
269        let modifier = DrmModifier::from(value.0.modifier);
270        Ok(DrmFormat { code, modifier })
271    }
272}
273
274impl From<DrmFormat> for PixelFormat {
275    fn from(f: DrmFormat) -> Self {
276        PixelFormat::new(f.code as u32, f.modifier.into())
277    }
278}
279
280/// Vector of [PixelFormat]
281pub struct PixelFormats {
282    ptr: NonNull<libcamera_pixel_formats_t>,
283}
284
285impl PixelFormats {
286    pub(crate) unsafe fn from_ptr(ptr: NonNull<libcamera_pixel_formats_t>) -> Self {
287        Self { ptr }
288    }
289
290    /// Number of [PixelFormat]
291    pub fn len(&self) -> usize {
292        unsafe { libcamera_pixel_formats_size(self.ptr.as_ptr()) as _ }
293    }
294
295    /// Returns `true` if there there are no pixel formats
296    pub fn is_empty(&self) -> bool {
297        self.len() == 0
298    }
299
300    /// Returns [PixelFormat] at a given index.
301    ///
302    /// Return None if index is out of range.
303    pub fn get(&self, index: usize) -> Option<PixelFormat> {
304        if index >= self.len() {
305            None
306        } else {
307            Some(unsafe { self.get_unchecked(index) })
308        }
309    }
310
311    /// Returns [PixelFormat] at a given index without checking bounds.
312    ///
313    /// # Safety
314    ///
315    /// `index` must be less than [PixelFormats::len()].
316    pub unsafe fn get_unchecked(&self, index: usize) -> PixelFormat {
317        PixelFormat(unsafe { libcamera_pixel_formats_get(self.ptr.as_ptr(), index as _) })
318    }
319}
320
321impl<'d> IntoIterator for &'d PixelFormats {
322    type Item = PixelFormat;
323
324    type IntoIter = PixelFormatsIterator<'d>;
325
326    fn into_iter(self) -> Self::IntoIter {
327        PixelFormatsIterator {
328            formats: self,
329            index: 0,
330        }
331    }
332}
333
334impl Drop for PixelFormats {
335    fn drop(&mut self) {
336        unsafe { libcamera_pixel_formats_destroy(self.ptr.as_ptr()) }
337    }
338}
339
340pub struct PixelFormatsIterator<'d> {
341    formats: &'d PixelFormats,
342    index: usize,
343}
344
345impl Iterator for PixelFormatsIterator<'_> {
346    type Item = PixelFormat;
347
348    fn next(&mut self) -> Option<Self::Item> {
349        if let Some(next) = self.formats.get(self.index) {
350            self.index += 1;
351            Some(next)
352        } else {
353            None
354        }
355    }
356}