libcamera/
color_space.rs

1use std::ffi::CString;
2
3use libcamera_sys::*;
4
5/// Color primaries
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum Primaries {
8    Raw,
9    Smpte170m,
10    Rec709,
11    Rec2020,
12}
13
14/// Transfer function
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum TransferFunction {
17    Linear,
18    Srgb,
19    Rec709,
20}
21
22/// YCbCr encoding
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub enum YcbcrEncoding {
25    None,
26    Rec601,
27    Rec709,
28    Rec2020,
29}
30
31/// Color range
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub enum Range {
34    Full,
35    Limited,
36}
37
38/// Represents `libcamera::ColorSpace`.
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub struct ColorSpace {
41    pub primaries: Primaries,
42    pub transfer_function: TransferFunction,
43    pub ycbcr_encoding: YcbcrEncoding,
44    pub range: Range,
45}
46
47impl ColorSpace {
48    pub const fn new(
49        primaries: Primaries,
50        transfer_function: TransferFunction,
51        ycbcr_encoding: YcbcrEncoding,
52        range: Range,
53    ) -> Self {
54        Self {
55            primaries,
56            transfer_function,
57            ycbcr_encoding,
58            range,
59        }
60    }
61
62    // Predefined color spaces from libcamera
63    pub fn raw() -> Self {
64        unsafe { libcamera_color_space_raw() }.into()
65    }
66    pub fn srgb() -> Self {
67        unsafe { libcamera_color_space_srgb() }.into()
68    }
69    pub fn sycc() -> Self {
70        unsafe { libcamera_color_space_sycc() }.into()
71    }
72    pub fn smpte170m() -> Self {
73        unsafe { libcamera_color_space_smpte170m() }.into()
74    }
75    pub fn rec709() -> Self {
76        unsafe { libcamera_color_space_rec709() }.into()
77    }
78    pub fn rec2020() -> Self {
79        unsafe { libcamera_color_space_rec2020() }.into()
80    }
81
82    /// Returns libcamera string representation (e.g. "Smpte170m/Rec709/Full").
83    pub fn to_repr(&self) -> String {
84        unsafe {
85            let ptr = libcamera_color_space_to_string(&(*self).into());
86            if ptr.is_null() {
87                return String::new();
88            }
89            let s = std::ffi::CStr::from_ptr(ptr).to_string_lossy().into_owned();
90            libc::free(ptr.cast());
91            s
92        }
93    }
94
95    /// Parse color space from libcamera string representation. Returns None on failure.
96    pub fn from_string(s: &str) -> Option<Self> {
97        let cstr = CString::new(s).ok()?;
98        let mut cs = libcamera_color_space_t {
99            primaries: libcamera_color_space_primaries::LIBCAMERA_COLOR_SPACE_PRIMARIES_RAW,
100            transfer_function: libcamera_color_space_transfer_function::LIBCAMERA_COLOR_SPACE_TRANSFER_FUNCTION_LINEAR,
101            ycbcr_encoding: libcamera_color_space_ycbcr_encoding::LIBCAMERA_COLOR_SPACE_YCBCR_ENCODING_NONE,
102            range: libcamera_color_space_range::LIBCAMERA_COLOR_SPACE_RANGE_FULL,
103        };
104        let ok = unsafe { libcamera_color_space_from_string(cstr.as_ptr(), &mut cs) };
105        if ok {
106            Some(ColorSpace::from(cs))
107        } else {
108            None
109        }
110    }
111
112    /// Adjust this color space for a given pixel format.
113    ///
114    /// Returns `true` if the color space was modified to make it compatible with the pixel format,
115    /// or `false` if it was already compatible.
116    pub fn adjust_for_format(&mut self, pixel_format: crate::pixel_format::PixelFormat) -> bool {
117        let mut cs = (*self).into();
118        let adjusted = unsafe { libcamera_color_space_adjust(&mut cs, &pixel_format.0) };
119        *self = cs.into();
120        adjusted
121    }
122
123    /// Returns a clone of this color space adjusted for the given pixel format.
124    ///
125    /// libcamera always treats the color space as compatible (possibly changing it), so this
126    /// will always return `Some`.
127    pub fn with_adjusted_for_format(&self, pixel_format: crate::pixel_format::PixelFormat) -> Option<Self> {
128        let mut clone = *self;
129        let _ = clone.adjust_for_format(pixel_format);
130        Some(clone)
131    }
132}
133
134impl core::fmt::Display for ColorSpace {
135    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
136        f.write_str(&self.to_repr())
137    }
138}
139
140impl From<ColorSpace> for libcamera_color_space_t {
141    fn from(cs: ColorSpace) -> Self {
142        unsafe {
143            libcamera_color_space_make(
144                match cs.primaries {
145                    Primaries::Raw => libcamera_color_space_primaries::LIBCAMERA_COLOR_SPACE_PRIMARIES_RAW,
146                    Primaries::Smpte170m => libcamera_color_space_primaries::LIBCAMERA_COLOR_SPACE_PRIMARIES_SMPTE170M,
147                    Primaries::Rec709 => libcamera_color_space_primaries::LIBCAMERA_COLOR_SPACE_PRIMARIES_REC709,
148                    Primaries::Rec2020 => libcamera_color_space_primaries::LIBCAMERA_COLOR_SPACE_PRIMARIES_REC2020,
149                },
150                match cs.transfer_function {
151                    TransferFunction::Linear => {
152                        libcamera_color_space_transfer_function::LIBCAMERA_COLOR_SPACE_TRANSFER_FUNCTION_LINEAR
153                    }
154                    TransferFunction::Srgb => {
155                        libcamera_color_space_transfer_function::LIBCAMERA_COLOR_SPACE_TRANSFER_FUNCTION_SRGB
156                    }
157                    TransferFunction::Rec709 => {
158                        libcamera_color_space_transfer_function::LIBCAMERA_COLOR_SPACE_TRANSFER_FUNCTION_REC709
159                    }
160                },
161                match cs.ycbcr_encoding {
162                    YcbcrEncoding::None => {
163                        libcamera_color_space_ycbcr_encoding::LIBCAMERA_COLOR_SPACE_YCBCR_ENCODING_NONE
164                    }
165                    YcbcrEncoding::Rec601 => {
166                        libcamera_color_space_ycbcr_encoding::LIBCAMERA_COLOR_SPACE_YCBCR_ENCODING_REC601
167                    }
168                    YcbcrEncoding::Rec709 => {
169                        libcamera_color_space_ycbcr_encoding::LIBCAMERA_COLOR_SPACE_YCBCR_ENCODING_REC709
170                    }
171                    YcbcrEncoding::Rec2020 => {
172                        libcamera_color_space_ycbcr_encoding::LIBCAMERA_COLOR_SPACE_YCBCR_ENCODING_REC2020
173                    }
174                },
175                match cs.range {
176                    Range::Full => libcamera_color_space_range::LIBCAMERA_COLOR_SPACE_RANGE_FULL,
177                    Range::Limited => libcamera_color_space_range::LIBCAMERA_COLOR_SPACE_RANGE_LIMITED,
178                },
179            )
180        }
181    }
182}
183
184impl From<libcamera_color_space_t> for ColorSpace {
185    fn from(cs: libcamera_color_space_t) -> Self {
186        let primaries = match cs.primaries {
187            libcamera_color_space_primaries::LIBCAMERA_COLOR_SPACE_PRIMARIES_RAW => Primaries::Raw,
188            libcamera_color_space_primaries::LIBCAMERA_COLOR_SPACE_PRIMARIES_SMPTE170M => Primaries::Smpte170m,
189            libcamera_color_space_primaries::LIBCAMERA_COLOR_SPACE_PRIMARIES_REC709 => Primaries::Rec709,
190            libcamera_color_space_primaries::LIBCAMERA_COLOR_SPACE_PRIMARIES_REC2020 => Primaries::Rec2020,
191            _ => Primaries::Raw,
192        };
193        let transfer_function = match cs.transfer_function {
194            libcamera_color_space_transfer_function::LIBCAMERA_COLOR_SPACE_TRANSFER_FUNCTION_LINEAR => {
195                TransferFunction::Linear
196            }
197            libcamera_color_space_transfer_function::LIBCAMERA_COLOR_SPACE_TRANSFER_FUNCTION_SRGB => {
198                TransferFunction::Srgb
199            }
200            libcamera_color_space_transfer_function::LIBCAMERA_COLOR_SPACE_TRANSFER_FUNCTION_REC709 => {
201                TransferFunction::Rec709
202            }
203            _ => TransferFunction::Linear,
204        };
205        let ycbcr_encoding = match cs.ycbcr_encoding {
206            libcamera_color_space_ycbcr_encoding::LIBCAMERA_COLOR_SPACE_YCBCR_ENCODING_NONE => YcbcrEncoding::None,
207            libcamera_color_space_ycbcr_encoding::LIBCAMERA_COLOR_SPACE_YCBCR_ENCODING_REC601 => YcbcrEncoding::Rec601,
208            libcamera_color_space_ycbcr_encoding::LIBCAMERA_COLOR_SPACE_YCBCR_ENCODING_REC709 => YcbcrEncoding::Rec709,
209            libcamera_color_space_ycbcr_encoding::LIBCAMERA_COLOR_SPACE_YCBCR_ENCODING_REC2020 => {
210                YcbcrEncoding::Rec2020
211            }
212            _ => YcbcrEncoding::None,
213        };
214        let range = match cs.range {
215            libcamera_color_space_range::LIBCAMERA_COLOR_SPACE_RANGE_FULL => Range::Full,
216            libcamera_color_space_range::LIBCAMERA_COLOR_SPACE_RANGE_LIMITED => Range::Limited,
217            _ => Range::Full,
218        };
219
220        ColorSpace {
221            primaries,
222            transfer_function,
223            ycbcr_encoding,
224            range,
225        }
226    }
227}