Skip to main content

screencapturekit/stream/configuration/
pixel_format.rs

1//! Pixel format enumeration
2//!
3//! Defines the available pixel formats for captured frames.
4
5use core::fmt;
6use std::fmt::{Display, Formatter};
7
8use crate::utils::four_char_code::FourCharCode;
9
10/// Pixel format for captured video frames
11///
12/// Specifies the layout and encoding of pixel data in captured frames.
13///
14/// This enum is `#[non_exhaustive]`. Apple may add new pixel formats in
15/// future macOS releases; downstream code that exhaustively matches on
16/// `PixelFormat` must include a wildcard arm. Pixel formats this crate
17/// does not yet recognise are surfaced via the [`PixelFormat::Unknown`]
18/// variant rather than being silently coerced to [`PixelFormat::BGRA`]
19/// (which would mislead callers that branch on the format).
20///
21/// # Equality and hashing
22///
23/// `PixelFormat` compares and hashes by its underlying [`FourCharCode`]
24/// rather than by enum variant. This means
25/// `PixelFormat::BGRA == PixelFormat::Unknown(FourCharCode::from_bytes(*b"BGRA"))`
26/// — both round-trip to the same wire-level format — and they hash the
27/// same. Without this normalisation, the user-facing `Unknown(known_code)`
28/// footgun (constructing the variant with a code that already maps to a
29/// named variant) would silently produce two `PixelFormat` values that
30/// represent the same underlying format but compared unequal and indexed
31/// `HashMap`s twice.
32///
33/// # Examples
34///
35/// ```
36/// use screencapturekit::stream::configuration::PixelFormat;
37/// use screencapturekit::FourCharCode;
38///
39/// let format = PixelFormat::BGRA;
40/// println!("Format: {}", format); // Prints "BGRA"
41///
42/// // Equality normalises through FourCharCode:
43/// let synonym = PixelFormat::Unknown(FourCharCode::from_bytes(*b"BGRA"));
44/// assert_eq!(PixelFormat::BGRA, synonym);
45/// ```
46#[allow(non_camel_case_types)]
47#[derive(Debug, Clone, Copy, Default)]
48#[non_exhaustive]
49pub enum PixelFormat {
50    /// Packed little endian ARGB8888 (most common)
51    #[default]
52    BGRA,
53    /// Packed little endian ARGB2101010 (10-bit color)
54    l10r,
55    /// Two-plane "video" range YCbCr 4:2:0
56    YCbCr_420v,
57    /// Two-plane "full" range YCbCr 4:2:0
58    YCbCr_420f,
59    /// Two-plane "full" range `YCbCr10` 4:4:4 (10-bit)
60    xf44,
61    /// 64-bit RGBA IEEE half-precision float, 16-bit little-endian (HDR)
62    RGhA,
63    /// A pixel format reported by `ScreenCaptureKit` that this crate does not
64    /// model as a named variant. The wrapped [`FourCharCode`] preserves the
65    /// raw four-character code so callers can branch on it explicitly or
66    /// log it for diagnostics.
67    Unknown(FourCharCode),
68}
69
70// `PixelFormat` is `Eq`/`Hash` via its underlying FourCharCode so that
71// `Unknown(known_code)` and the corresponding named variant compare and
72// hash identically. See the type-level docs for the rationale.
73impl PartialEq for PixelFormat {
74    fn eq(&self, other: &Self) -> bool {
75        FourCharCode::from(*self) == FourCharCode::from(*other)
76    }
77}
78
79impl Eq for PixelFormat {}
80
81impl std::hash::Hash for PixelFormat {
82    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
83        FourCharCode::from(*self).hash(state);
84    }
85}
86impl Display for PixelFormat {
87    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
88        let c: FourCharCode = (*self).into();
89        write!(f, "{}", c.display())
90    }
91}
92
93impl From<PixelFormat> for FourCharCode {
94    fn from(val: PixelFormat) -> Self {
95        // Use infallible byte array constructor for compile-time constants
96        match val {
97            PixelFormat::BGRA => Self::from_bytes(*b"BGRA"),
98            PixelFormat::l10r => Self::from_bytes(*b"l10r"),
99            PixelFormat::YCbCr_420v => Self::from_bytes(*b"420v"),
100            PixelFormat::YCbCr_420f => Self::from_bytes(*b"420f"),
101            PixelFormat::xf44 => Self::from_bytes(*b"xf44"),
102            PixelFormat::RGhA => Self::from_bytes(*b"RGhA"),
103            PixelFormat::Unknown(code) => code,
104        }
105    }
106}
107impl From<u32> for PixelFormat {
108    fn from(value: u32) -> Self {
109        // FourCharCode stores u32 directly, no byte conversion needed
110        let c = FourCharCode::from_u32(value);
111        c.into()
112    }
113}
114impl From<FourCharCode> for PixelFormat {
115    fn from(val: FourCharCode) -> Self {
116        match val.display().as_str() {
117            "BGRA" => Self::BGRA,
118            "l10r" => Self::l10r,
119            "420v" => Self::YCbCr_420v,
120            "420f" => Self::YCbCr_420f,
121            "xf44" => Self::xf44,
122            "RGhA" => Self::RGhA,
123            // Preserve the raw code rather than silently coercing to BGRA.
124            // Callers that branched on the format would otherwise misread
125            // YUV/HDR/etc. samples as BGRA.
126            _ => Self::Unknown(val),
127        }
128    }
129}