Skip to main content

screencapturekit/stream/configuration/
colors.rs

1//! Color and pixel format configuration
2//!
3//! Methods for configuring color space, pixel format, and background color.
4
5use crate::utils::{
6    ffi_string::{ffi_string_from_buffer, SMALL_BUFFER_SIZE},
7    four_char_code::FourCharCode,
8};
9
10const DEFAULT_ALPHA: f32 = 1.0;
11type BackgroundColor = (f32, f32, f32, f32);
12
13use super::{internal::SCStreamConfiguration, pixel_format::PixelFormat};
14
15impl SCStreamConfiguration {
16    /// Set the pixel format for captured frames
17    ///
18    /// Streams created via [`Self::new`] / [`Self::default`] are pinned to
19    /// [`PixelFormat::BGRA`] at construction time, so calling this method is
20    /// only required when you want a non-BGRA format (e.g. YUV `420v` for
21    /// video encoding, or `l10r` for HDR). Apple's runtime default for
22    /// `SCStreamConfiguration()` varies by macOS release — see
23    /// [`PixelFormat::BGRA`] for context.
24    ///
25    /// # Examples
26    ///
27    /// ```
28    /// use screencapturekit::stream::configuration::{SCStreamConfiguration, PixelFormat};
29    ///
30    /// let mut config = SCStreamConfiguration::default();
31    /// config.set_pixel_format(PixelFormat::BGRA);
32    /// ```
33    pub fn set_pixel_format(&mut self, pixel_format: PixelFormat) -> &mut Self {
34        let four_char_code: FourCharCode = pixel_format.into();
35        unsafe {
36            crate::ffi::sc_stream_configuration_set_pixel_format(
37                self.as_ptr(),
38                four_char_code.as_u32(),
39            );
40        }
41        self
42    }
43
44    /// Set the pixel format (builder pattern)
45    #[must_use]
46    pub fn with_pixel_format(mut self, pixel_format: PixelFormat) -> Self {
47        self.set_pixel_format(pixel_format);
48        self
49    }
50
51    /// Get the current pixel format
52    pub fn pixel_format(&self) -> PixelFormat {
53        unsafe {
54            let value = crate::ffi::sc_stream_configuration_get_pixel_format(self.as_ptr());
55            PixelFormat::from(value)
56        }
57    }
58
59    /// Set the background color for captured content with an explicit alpha value.
60    ///
61    /// Available on macOS 13.0+
62    pub fn set_background_color_rgba(&mut self, r: f32, g: f32, b: f32, a: f32) -> &mut Self {
63        unsafe {
64            crate::ffi::sc_stream_configuration_set_background_color(self.as_ptr(), r, g, b, a);
65        }
66        self
67    }
68
69    /// Set the background color for captured content.
70    ///
71    /// This convenience overload uses an opaque alpha channel (`1.0`).
72    pub fn set_background_color(&mut self, r: f32, g: f32, b: f32) -> &mut Self {
73        self.set_background_color_rgba(r, g, b, DEFAULT_ALPHA)
74    }
75
76    /// Set the background color with an explicit alpha value (builder pattern).
77    #[must_use]
78    pub fn with_background_color_rgba(mut self, r: f32, g: f32, b: f32, a: f32) -> Self {
79        self.set_background_color_rgba(r, g, b, a);
80        self
81    }
82
83    /// Set the background color (builder pattern).
84    ///
85    /// This convenience overload uses an opaque alpha channel (`1.0`).
86    #[must_use]
87    pub fn with_background_color(mut self, r: f32, g: f32, b: f32) -> Self {
88        self.set_background_color(r, g, b);
89        self
90    }
91
92    /// Get the current background color, if it was set through this wrapper.
93    pub fn background_color(&self) -> Option<BackgroundColor> {
94        let mut r = 0.0f32;
95        let mut g = 0.0f32;
96        let mut b = 0.0f32;
97        let mut a = 0.0f32;
98        // The value is read back from the Swift bridge's per-configuration
99        // state (keyed by object identity and released with the configuration),
100        // so there is no Rust-side cache to leak or to go stale on pointer reuse.
101        let was_set = unsafe {
102            crate::ffi::sc_stream_configuration_get_background_color(
103                self.as_ptr(),
104                &mut r,
105                &mut g,
106                &mut b,
107                &mut a,
108            )
109        };
110        was_set.then_some((r, g, b, a))
111    }
112
113    /// Set the color space name for captured content.
114    ///
115    /// Available on macOS 13.0+
116    ///
117    /// If `name` contains an interior NUL byte it cannot be converted to a C
118    /// string and the call is silently ignored (the configuration is left
119    /// unchanged). Valid color-space names never contain NUL bytes.
120    pub fn set_color_space_name(&mut self, name: &str) -> &mut Self {
121        if let Ok(c_name) = std::ffi::CString::new(name) {
122            unsafe {
123                crate::ffi::sc_stream_configuration_set_color_space_name(
124                    self.as_ptr(),
125                    c_name.as_ptr(),
126                );
127            }
128        }
129        self
130    }
131
132    /// Set the color space name (builder pattern).
133    #[must_use]
134    pub fn with_color_space_name(mut self, name: &str) -> Self {
135        self.set_color_space_name(name);
136        self
137    }
138
139    /// Get the color space name for captured content.
140    pub fn color_space_name(&self) -> Option<String> {
141        unsafe {
142            ffi_string_from_buffer(SMALL_BUFFER_SIZE, |buf, len| {
143                crate::ffi::sc_stream_configuration_get_color_space_name(self.as_ptr(), buf, len)
144            })
145        }
146    }
147
148    /// Set the color matrix for captured content
149    ///
150    /// Available on macOS 13.0+. The matrix should be a 3x3 array in row-major order.
151    ///
152    /// If `matrix` contains an interior NUL byte it cannot be converted to a C
153    /// string and the call is silently ignored (the configuration is left
154    /// unchanged). Valid matrix-name strings never contain NUL bytes.
155    pub fn set_color_matrix(&mut self, matrix: &str) -> &mut Self {
156        if let Ok(c_matrix) = std::ffi::CString::new(matrix) {
157            unsafe {
158                crate::ffi::sc_stream_configuration_set_color_matrix(
159                    self.as_ptr(),
160                    c_matrix.as_ptr(),
161                );
162            }
163        }
164        self
165    }
166
167    /// Get the color matrix for captured content.
168    ///
169    /// Returns the color matrix as a string, or `None` if not set.
170    pub fn color_matrix(&self) -> Option<String> {
171        unsafe {
172            ffi_string_from_buffer(SMALL_BUFFER_SIZE, |buf, len| {
173                crate::ffi::sc_stream_configuration_get_color_matrix(self.as_ptr(), buf, len)
174            })
175        }
176    }
177
178    /// Set the color matrix (builder pattern)
179    #[must_use]
180    pub fn with_color_matrix(mut self, matrix: &str) -> Self {
181        self.set_color_matrix(matrix);
182        self
183    }
184}