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#[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 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 pub fn has_modifier(&self) -> bool {
118 self.modifier() != 0
119 }
120
121 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 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 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 pub fn clear_modifier(&mut self) {
144 self.0.modifier = 0;
145 }
146
147 pub const fn to_raw(self) -> (u32, u64) {
149 (self.0.fourcc, self.0.modifier)
150 }
151
152 pub const fn from_raw_parts(fourcc: u32, modifier: u64) -> Self {
154 PixelFormat::new(fourcc, modifier)
155 }
156
157 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 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 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
280pub 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 pub fn len(&self) -> usize {
292 unsafe { libcamera_pixel_formats_size(self.ptr.as_ptr()) as _ }
293 }
294
295 pub fn is_empty(&self) -> bool {
297 self.len() == 0
298 }
299
300 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 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}