Skip to main content

rskit_media/encoding/
color.rs

1//! Color space, color range, and pixel format types.
2
3use std::sync::Arc;
4
5use serde::{Deserialize, Serialize};
6
7/// Color space (color primaries + matrix coefficients).
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
9pub enum ColorSpace {
10    /// ITU-R BT.601 (SD video).
11    Bt601,
12    /// ITU-R BT.709 (HD video, sRGB).
13    Bt709,
14    /// ITU-R BT.2020 (UHD/HDR video).
15    Bt2020,
16    /// SMPTE 240M (legacy HDTV).
17    Smpte240m,
18    /// sRGB (images, web).
19    Srgb,
20    /// DCI-P3 (cinema, wide gamut displays).
21    DciP3,
22    /// Display P3 (Apple displays).
23    DisplayP3,
24    /// Unknown or unrecognized color space.
25    Unknown,
26}
27
28impl ColorSpace {
29    /// Convert to FFmpeg color space string.
30    pub fn as_ffmpeg_arg(&self) -> Option<&str> {
31        match self {
32            Self::Bt601 => Some("bt470bg"),
33            Self::Bt709 => Some("bt709"),
34            Self::Bt2020 => Some("bt2020nc"),
35            Self::Smpte240m => Some("smpte240m"),
36            Self::Srgb => Some("bt709"),
37            Self::DciP3 => Some("bt2020nc"),
38            Self::DisplayP3 => Some("bt2020nc"),
39            Self::Unknown => None,
40        }
41    }
42
43    /// Parse from FFmpeg colorspace string.
44    pub fn from_ffmpeg(s: &str) -> Self {
45        match s {
46            "bt709" => Self::Bt709,
47            "bt470bg" | "smpte170m" => Self::Bt601,
48            "bt2020nc" | "bt2020c" => Self::Bt2020,
49            "smpte240m" => Self::Smpte240m,
50            _ => Self::Unknown,
51        }
52    }
53}
54
55/// Color range (signal levels).
56#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
57pub enum ColorRange {
58    /// Limited range (16–235 for 8-bit luma).
59    Limited,
60    /// Full range (0–255 for 8-bit luma).
61    Full,
62}
63
64impl ColorRange {
65    /// Convert to FFmpeg color range string.
66    pub fn as_ffmpeg_arg(&self) -> &str {
67        match self {
68            Self::Limited => "tv",
69            Self::Full => "pc",
70        }
71    }
72
73    /// Parse from FFmpeg color range string.
74    pub fn from_ffmpeg(s: &str) -> Option<Self> {
75        match s {
76            "tv" | "limited" | "mpeg" => Some(Self::Limited),
77            "pc" | "full" | "jpeg" => Some(Self::Full),
78            _ => None,
79        }
80    }
81}
82
83/// Open pixel format identifier (e.g., "yuv420p", "rgb24", "nv12").
84///
85/// Like [`Codec`](crate::codec::Codec) and [`Format`](crate::format::Format),
86/// this is an open identifier with well-known constants.
87#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
88pub struct PixelFormat(Arc<str>);
89
90impl PixelFormat {
91    /// Create a new pixel format identifier.
92    pub fn new(id: impl Into<Arc<str>>) -> Self {
93        Self(id.into())
94    }
95
96    /// The pixel format identifier string.
97    pub fn id(&self) -> &str {
98        &self.0
99    }
100
101    /// Bit depth per component (approximate, based on well-known formats).
102    pub fn bit_depth(&self) -> Option<u8> {
103        match self.0.as_ref() {
104            "yuv420p" | "yuv422p" | "yuv444p" | "rgb24" | "bgr24" | "nv12" | "nv21" | "yuyv422"
105            | "uyvy422" => Some(8),
106            "yuv420p10le" | "yuv422p10le" | "yuv444p10le" | "p010le" => Some(10),
107            "yuv420p12le" | "yuv422p12le" | "yuv444p12le" => Some(12),
108            "rgb48le" | "yuv444p16le" => Some(16),
109            _ => None,
110        }
111    }
112
113    /// Whether this format has an alpha channel.
114    pub fn has_alpha(&self) -> bool {
115        matches!(
116            self.0.as_ref(),
117            "rgba" | "bgra" | "argb" | "abgr" | "yuva420p" | "yuva444p"
118        )
119    }
120}
121
122impl std::fmt::Display for PixelFormat {
123    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124        f.write_str(&self.0)
125    }
126}
127
128/// Well-known pixel formats.
129pub mod pixel_formats {
130    use super::PixelFormat;
131
132    /// YUV 4:2:0 planar, 8-bit (most common video format).
133    pub fn yuv420p() -> PixelFormat {
134        PixelFormat::new("yuv420p")
135    }
136    /// YUV 4:2:2 planar, 8-bit.
137    pub fn yuv422p() -> PixelFormat {
138        PixelFormat::new("yuv422p")
139    }
140    /// YUV 4:4:4 planar, 8-bit.
141    pub fn yuv444p() -> PixelFormat {
142        PixelFormat::new("yuv444p")
143    }
144    /// YUV 4:2:0 planar, 10-bit little-endian (HDR).
145    pub fn yuv420p10le() -> PixelFormat {
146        PixelFormat::new("yuv420p10le")
147    }
148    /// YUV 4:2:0 planar, 12-bit little-endian.
149    pub fn yuv420p12le() -> PixelFormat {
150        PixelFormat::new("yuv420p12le")
151    }
152    /// RGB packed, 8-bit per channel.
153    pub fn rgb24() -> PixelFormat {
154        PixelFormat::new("rgb24")
155    }
156    /// RGBA packed, 8-bit per channel.
157    pub fn rgba() -> PixelFormat {
158        PixelFormat::new("rgba")
159    }
160    /// NV12 (YUV 4:2:0 semi-planar, common for hw decoders).
161    pub fn nv12() -> PixelFormat {
162        PixelFormat::new("nv12")
163    }
164    /// P010LE (10-bit NV12, common for HDR hw decoders).
165    pub fn p010le() -> PixelFormat {
166        PixelFormat::new("p010le")
167    }
168}