Skip to main content

ff_format/
color.rs

1//! Color space and related type definitions.
2//!
3//! This module provides enums for color-related metadata commonly found
4//! in video streams, including color space, color range, and color primaries.
5//!
6//! # Examples
7//!
8//! ```
9//! use ff_format::color::{ColorSpace, ColorRange, ColorPrimaries};
10//!
11//! // HD video typically uses BT.709
12//! let space = ColorSpace::Bt709;
13//! let range = ColorRange::Limited;
14//! let primaries = ColorPrimaries::Bt709;
15//!
16//! assert!(space.is_hd());
17//! assert!(!range.is_full());
18//! ```
19
20use std::fmt;
21
22/// Color space (matrix coefficients) for YUV to RGB conversion.
23///
24/// The color space defines how YUV values are converted to RGB and vice versa.
25/// Different standards use different matrix coefficients for this conversion.
26///
27/// # Common Usage
28///
29/// - **BT.709**: HD content (720p, 1080p)
30/// - **BT.470BG / SMPTE-170M**: SD content (576i PAL / 480i NTSC)
31/// - **BT.2020 NCL / CL**: UHD/HDR content (4K, 8K)
32/// - **RGB**: Identity matrix for RGB/GBR content
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
34#[non_exhaustive]
35pub enum ColorSpace {
36    /// ITU-R BT.709 — HD television matrix (most common for HD video)
37    #[default]
38    Bt709,
39    /// ITU-R BT.470BG — BT.601 625-line (PAL/SECAM SD) matrix
40    Bt470bg,
41    /// SMPTE 170M — BT.601 525-line (NTSC SD) matrix
42    Smpte170m,
43    /// ITU-R BT.2020 non-constant luminance — UHD/HDR matrix
44    Bt2020Ncl,
45    /// ITU-R BT.2020 constant luminance — UHD/HDR matrix
46    Bt2020Cl,
47    /// Identity / RGB (GBR planar) — no YUV matrix
48    Rgb,
49    /// FCC — legacy NTSC 1953 matrix
50    Fcc,
51    /// SMPTE 240M — legacy HD matrix
52    Smpte240m,
53    /// `YCgCo` — reversible `YCgCo` matrix
54    Ycgco,
55    /// Color space matrix is not specified or unknown
56    Unknown,
57}
58
59impl ColorSpace {
60    /// Returns the name of the color space as a human-readable string.
61    ///
62    /// # Examples
63    ///
64    /// ```
65    /// use ff_format::color::ColorSpace;
66    ///
67    /// assert_eq!(ColorSpace::Bt709.name(), "bt709");
68    /// assert_eq!(ColorSpace::Bt2020Ncl.name(), "bt2020ncl");
69    /// ```
70    #[must_use]
71    pub const fn name(&self) -> &'static str {
72        match self {
73            Self::Bt709 => "bt709",
74            Self::Bt470bg => "bt470bg",
75            Self::Smpte170m => "smpte170m",
76            Self::Bt2020Ncl => "bt2020ncl",
77            Self::Bt2020Cl => "bt2020cl",
78            Self::Rgb => "rgb",
79            Self::Fcc => "fcc",
80            Self::Smpte240m => "smpte240m",
81            Self::Ycgco => "ycgco",
82            Self::Unknown => "unknown",
83        }
84    }
85
86    /// Returns `true` if this is the HD matrix (BT.709).
87    ///
88    /// # Examples
89    ///
90    /// ```
91    /// use ff_format::color::ColorSpace;
92    ///
93    /// assert!(ColorSpace::Bt709.is_hd());
94    /// assert!(!ColorSpace::Smpte170m.is_hd());
95    /// ```
96    #[must_use]
97    pub const fn is_hd(&self) -> bool {
98        matches!(self, Self::Bt709)
99    }
100
101    /// Returns `true` if this is an SD matrix (BT.601: BT.470BG or SMPTE-170M).
102    ///
103    /// # Examples
104    ///
105    /// ```
106    /// use ff_format::color::ColorSpace;
107    ///
108    /// assert!(ColorSpace::Smpte170m.is_sd());
109    /// assert!(!ColorSpace::Bt709.is_sd());
110    /// ```
111    #[must_use]
112    pub const fn is_sd(&self) -> bool {
113        matches!(self, Self::Bt470bg | Self::Smpte170m)
114    }
115
116    /// Returns `true` if this is a UHD/HDR matrix (BT.2020 NCL or CL).
117    ///
118    /// # Examples
119    ///
120    /// ```
121    /// use ff_format::color::ColorSpace;
122    ///
123    /// assert!(ColorSpace::Bt2020Ncl.is_uhd());
124    /// assert!(!ColorSpace::Bt709.is_uhd());
125    /// ```
126    #[must_use]
127    pub const fn is_uhd(&self) -> bool {
128        matches!(self, Self::Bt2020Ncl | Self::Bt2020Cl)
129    }
130
131    /// Returns `true` if the color space is unknown.
132    ///
133    /// # Examples
134    ///
135    /// ```
136    /// use ff_format::color::ColorSpace;
137    ///
138    /// assert!(ColorSpace::Unknown.is_unknown());
139    /// assert!(!ColorSpace::Bt709.is_unknown());
140    /// ```
141    #[must_use]
142    pub const fn is_unknown(&self) -> bool {
143        matches!(self, Self::Unknown)
144    }
145}
146
147impl fmt::Display for ColorSpace {
148    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149        write!(f, "{}", self.name())
150    }
151}
152
153/// Color range defining the valid range of color values.
154///
155/// Video typically uses "limited" range where black is at level 16 and white
156/// at level 235 (for 8-bit). Computer graphics typically use "full" range
157/// where black is 0 and white is 255.
158///
159/// # Common Usage
160///
161/// - **Limited**: Broadcast video, Blu-ray, streaming services
162/// - **Full**: Computer graphics, screenshots, game capture
163#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
164#[non_exhaustive]
165pub enum ColorRange {
166    /// Limited/TV range (16-235 for Y, 16-240 for UV in 8-bit)
167    #[default]
168    Limited,
169    /// Full/PC range (0-255 for all components in 8-bit)
170    Full,
171    /// Color range is not specified or unknown
172    Unknown,
173}
174
175impl ColorRange {
176    /// Returns the name of the color range as a human-readable string.
177    ///
178    /// # Examples
179    ///
180    /// ```
181    /// use ff_format::color::ColorRange;
182    ///
183    /// assert_eq!(ColorRange::Limited.name(), "limited");
184    /// assert_eq!(ColorRange::Full.name(), "full");
185    /// ```
186    #[must_use]
187    pub const fn name(&self) -> &'static str {
188        match self {
189            Self::Limited => "limited",
190            Self::Full => "full",
191            Self::Unknown => "unknown",
192        }
193    }
194
195    /// Returns `true` if this is full (PC) range.
196    ///
197    /// # Examples
198    ///
199    /// ```
200    /// use ff_format::color::ColorRange;
201    ///
202    /// assert!(ColorRange::Full.is_full());
203    /// assert!(!ColorRange::Limited.is_full());
204    /// ```
205    #[must_use]
206    pub const fn is_full(&self) -> bool {
207        matches!(self, Self::Full)
208    }
209
210    /// Returns `true` if this is limited (TV) range.
211    ///
212    /// # Examples
213    ///
214    /// ```
215    /// use ff_format::color::ColorRange;
216    ///
217    /// assert!(ColorRange::Limited.is_limited());
218    /// assert!(!ColorRange::Full.is_limited());
219    /// ```
220    #[must_use]
221    pub const fn is_limited(&self) -> bool {
222        matches!(self, Self::Limited)
223    }
224
225    /// Returns `true` if the color range is unknown.
226    ///
227    /// # Examples
228    ///
229    /// ```
230    /// use ff_format::color::ColorRange;
231    ///
232    /// assert!(ColorRange::Unknown.is_unknown());
233    /// assert!(!ColorRange::Limited.is_unknown());
234    /// ```
235    #[must_use]
236    pub const fn is_unknown(&self) -> bool {
237        matches!(self, Self::Unknown)
238    }
239
240    /// Returns the minimum value for luma (Y) in 8-bit.
241    ///
242    /// # Examples
243    ///
244    /// ```
245    /// use ff_format::color::ColorRange;
246    ///
247    /// assert_eq!(ColorRange::Limited.luma_min_8bit(), 16);
248    /// assert_eq!(ColorRange::Full.luma_min_8bit(), 0);
249    /// ```
250    #[must_use]
251    pub const fn luma_min_8bit(&self) -> u8 {
252        match self {
253            Self::Limited => 16,
254            Self::Full | Self::Unknown => 0,
255        }
256    }
257
258    /// Returns the maximum value for luma (Y) in 8-bit.
259    ///
260    /// # Examples
261    ///
262    /// ```
263    /// use ff_format::color::ColorRange;
264    ///
265    /// assert_eq!(ColorRange::Limited.luma_max_8bit(), 235);
266    /// assert_eq!(ColorRange::Full.luma_max_8bit(), 255);
267    /// ```
268    #[must_use]
269    pub const fn luma_max_8bit(&self) -> u8 {
270        match self {
271            Self::Limited => 235,
272            Self::Full | Self::Unknown => 255,
273        }
274    }
275}
276
277impl fmt::Display for ColorRange {
278    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
279        write!(f, "{}", self.name())
280    }
281}
282
283/// Color primaries defining the color gamut (the range of colors that can be represented).
284///
285/// Different standards define different primary colors (red, green, blue points)
286/// which determine the overall range of colors that can be displayed.
287///
288/// # Common Usage
289///
290/// - **BT.709**: HD content, same as sRGB primaries
291/// - **BT.470BG / SMPTE-170M**: SD content (PAL/SECAM / NTSC)
292/// - **DCI-P3 / Display P3**: digital cinema and wide-gamut displays
293/// - **BT.2020**: Wide color gamut for UHD/HDR
294#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
295#[non_exhaustive]
296pub enum ColorPrimaries {
297    /// ITU-R BT.709 primaries (same as sRGB, most common)
298    #[default]
299    Bt709,
300    /// ITU-R BT.470BG primaries (PAL/SECAM SD video)
301    Bt470bg,
302    /// SMPTE 170M primaries (NTSC SD video)
303    Smpte170m,
304    /// SMPTE 240M primaries (legacy HD)
305    Smpte240m,
306    /// Generic film primaries (Illuminant C)
307    Film,
308    /// DCI-P3 primaries (SMPTE RP 431-2, digital cinema)
309    DciP3,
310    /// Display P3 primaries (SMPTE EG 432-1, wide-gamut displays)
311    DisplayP3,
312    /// ITU-R BT.2020 primaries (wide color gamut for UHD/HDR)
313    Bt2020,
314    /// Color primaries are not specified or unknown
315    Unknown,
316}
317
318impl ColorPrimaries {
319    /// Returns the name of the color primaries as a human-readable string.
320    ///
321    /// # Examples
322    ///
323    /// ```
324    /// use ff_format::color::ColorPrimaries;
325    ///
326    /// assert_eq!(ColorPrimaries::Bt709.name(), "bt709");
327    /// assert_eq!(ColorPrimaries::Bt2020.name(), "bt2020");
328    /// ```
329    #[must_use]
330    pub const fn name(&self) -> &'static str {
331        match self {
332            Self::Bt709 => "bt709",
333            Self::Bt470bg => "bt470bg",
334            Self::Smpte170m => "smpte170m",
335            Self::Smpte240m => "smpte240m",
336            Self::Film => "film",
337            Self::DciP3 => "dci-p3",
338            Self::DisplayP3 => "display-p3",
339            Self::Bt2020 => "bt2020",
340            Self::Unknown => "unknown",
341        }
342    }
343
344    /// Returns `true` if this uses a wide color gamut (BT.2020, DCI-P3, or Display P3).
345    ///
346    /// # Examples
347    ///
348    /// ```
349    /// use ff_format::color::ColorPrimaries;
350    ///
351    /// assert!(ColorPrimaries::Bt2020.is_wide_gamut());
352    /// assert!(ColorPrimaries::DciP3.is_wide_gamut());
353    /// assert!(!ColorPrimaries::Bt709.is_wide_gamut());
354    /// ```
355    #[must_use]
356    pub const fn is_wide_gamut(&self) -> bool {
357        matches!(self, Self::Bt2020 | Self::DciP3 | Self::DisplayP3)
358    }
359
360    /// Returns `true` if the color primaries are unknown.
361    ///
362    /// # Examples
363    ///
364    /// ```
365    /// use ff_format::color::ColorPrimaries;
366    ///
367    /// assert!(ColorPrimaries::Unknown.is_unknown());
368    /// assert!(!ColorPrimaries::Bt709.is_unknown());
369    /// ```
370    #[must_use]
371    pub const fn is_unknown(&self) -> bool {
372        matches!(self, Self::Unknown)
373    }
374}
375
376impl fmt::Display for ColorPrimaries {
377    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
378        write!(f, "{}", self.name())
379    }
380}
381
382/// Color transfer characteristic (opto-electronic transfer function).
383///
384/// The transfer characteristic defines how scene luminance maps to the signal
385/// level stored in the video bitstream. Different HDR and SDR standards use
386/// different curves.
387///
388/// # Common Usage
389///
390/// - **`Bt709`**: Standard SDR video (HD television)
391/// - **`Gamma22`** / **`Gamma28`**: Pure power-law gamma 2.2 / 2.8 (legacy SDR)
392/// - **`Smpte170m`** / **`Smpte240m`**: SD / legacy-HD transfer characteristics
393/// - **`Srgb`**: sRGB / IEC 61966-2-1 (computer graphics, web)
394/// - **`Pq`**: HDR10 and Dolby Vision (SMPTE ST 2084 / Perceptual Quantizer)
395/// - **`Hlg`**: Hybrid Log-Gamma — broadcast-compatible HDR (ARIB STD-B67)
396/// - **`Bt2020_10`** / **`Bt2020_12`**: BT.2020 SDR at 10/12-bit depth
397/// - **`Linear`**: Linear light, no gamma applied
398#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
399#[non_exhaustive]
400pub enum ColorTransfer {
401    /// ITU-R BT.709 transfer characteristic (standard SDR)
402    #[default]
403    Bt709,
404    /// Pure power-law gamma 2.2 (assumed display gamma)
405    Gamma22,
406    /// Pure power-law gamma 2.8 (BT.470 System B/G)
407    Gamma28,
408    /// SMPTE 170M transfer characteristic (SD)
409    Smpte170m,
410    /// SMPTE 240M transfer characteristic (legacy HD)
411    Smpte240m,
412    /// Linear light transfer (no gamma)
413    Linear,
414    /// sRGB / IEC 61966-2-1 transfer characteristic
415    Srgb,
416    /// ITU-R BT.2020 for 10-bit content
417    Bt2020_10,
418    /// ITU-R BT.2020 for 12-bit content
419    Bt2020_12,
420    /// Perceptual Quantizer / SMPTE ST 2084 — HDR10
421    Pq,
422    /// Hybrid Log-Gamma (ARIB STD-B67) — broadcast HDR
423    Hlg,
424    /// Transfer characteristic is not specified or unknown
425    Unknown,
426}
427
428impl ColorTransfer {
429    /// Returns the name of the color transfer characteristic as a string.
430    ///
431    /// # Examples
432    ///
433    /// ```
434    /// use ff_format::color::ColorTransfer;
435    ///
436    /// assert_eq!(ColorTransfer::Bt709.name(), "bt709");
437    /// assert_eq!(ColorTransfer::Hlg.name(), "hlg");
438    /// assert_eq!(ColorTransfer::Pq.name(), "pq");
439    /// ```
440    #[must_use]
441    pub const fn name(&self) -> &'static str {
442        match self {
443            Self::Bt709 => "bt709",
444            Self::Gamma22 => "gamma22",
445            Self::Gamma28 => "gamma28",
446            Self::Smpte170m => "smpte170m",
447            Self::Smpte240m => "smpte240m",
448            Self::Linear => "linear",
449            Self::Srgb => "srgb",
450            Self::Bt2020_10 => "bt2020-10",
451            Self::Bt2020_12 => "bt2020-12",
452            Self::Pq => "pq",
453            Self::Hlg => "hlg",
454            Self::Unknown => "unknown",
455        }
456    }
457
458    /// Returns `true` if this is an HDR transfer characteristic (`Pq` or `Hlg`).
459    ///
460    /// # Examples
461    ///
462    /// ```
463    /// use ff_format::color::ColorTransfer;
464    ///
465    /// assert!(ColorTransfer::Pq.is_hdr());
466    /// assert!(ColorTransfer::Hlg.is_hdr());
467    /// assert!(!ColorTransfer::Bt709.is_hdr());
468    /// ```
469    #[must_use]
470    pub const fn is_hdr(&self) -> bool {
471        matches!(self, Self::Pq | Self::Hlg)
472    }
473
474    /// Returns `true` if this is Hybrid Log-Gamma (HLG).
475    ///
476    /// # Examples
477    ///
478    /// ```
479    /// use ff_format::color::ColorTransfer;
480    ///
481    /// assert!(ColorTransfer::Hlg.is_hlg());
482    /// assert!(!ColorTransfer::Pq.is_hlg());
483    /// ```
484    #[must_use]
485    pub const fn is_hlg(&self) -> bool {
486        matches!(self, Self::Hlg)
487    }
488
489    /// Returns `true` if this is Perceptual Quantizer / SMPTE ST 2084 (PQ).
490    ///
491    /// # Examples
492    ///
493    /// ```
494    /// use ff_format::color::ColorTransfer;
495    ///
496    /// assert!(ColorTransfer::Pq.is_pq());
497    /// assert!(!ColorTransfer::Hlg.is_pq());
498    /// ```
499    #[must_use]
500    pub const fn is_pq(&self) -> bool {
501        matches!(self, Self::Pq)
502    }
503
504    /// Returns `true` if the transfer characteristic is unknown.
505    ///
506    /// # Examples
507    ///
508    /// ```
509    /// use ff_format::color::ColorTransfer;
510    ///
511    /// assert!(ColorTransfer::Unknown.is_unknown());
512    /// assert!(!ColorTransfer::Bt709.is_unknown());
513    /// ```
514    #[must_use]
515    pub const fn is_unknown(&self) -> bool {
516        matches!(self, Self::Unknown)
517    }
518}
519
520impl fmt::Display for ColorTransfer {
521    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
522        write!(f, "{}", self.name())
523    }
524}
525
526/// Alpha modes.
527///
528/// The alpha mode defines how the alpha channel should be handled when
529/// converting video frames.
530#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
531#[non_exhaustive]
532pub enum AlphaMode {
533    /// Unassociated alpha.
534    #[default]
535    Straight,
536    /// Associated alpha.
537    Premultiplied,
538    /// Alpha mode is not specified or unknown
539    Unknown,
540}
541
542impl AlphaMode {
543    /// Returns the name of the alpha mode as a string.
544    ///
545    /// # Examples
546    ///
547    /// ```
548    /// use ff_format::color::AlphaMode;
549    ///
550    /// assert_eq!(AlphaMode::Straight.name(), "straight");
551    /// assert_eq!(AlphaMode::Premultiplied.name(), "premultiplied");
552    /// ```
553    #[must_use]
554    pub const fn name(&self) -> &'static str {
555        match self {
556            Self::Straight => "straight",
557            Self::Premultiplied => "premultiplied",
558            Self::Unknown => "unknown",
559        }
560    }
561
562    /// Returns `true` if this is a straight alpha mode.
563    ///
564    /// # Examples
565    ///
566    /// ```
567    /// use ff_format::color::AlphaMode;
568    ///
569    /// assert!(AlphaMode::Straight.is_straight());
570    /// assert!(!AlphaMode::Premultiplied.is_straight());
571    /// ```
572    #[must_use]
573    pub const fn is_straight(&self) -> bool {
574        matches!(self, Self::Straight)
575    }
576
577    /// Returns `true` if this is a premultiplied alpha mode.
578    ///
579    /// # Examples
580    ///
581    /// ```
582    /// use ff_format::color::AlphaMode;
583    ///
584    /// assert!(AlphaMode::Premultiplied.is_premultiplied());
585    /// assert!(!AlphaMode::Straight.is_premultiplied());
586    /// ```
587    #[must_use]
588    pub const fn is_premultiplied(&self) -> bool {
589        matches!(self, Self::Premultiplied)
590    }
591
592    /// Returns `true` if the alpha mode is unknown.
593    ///
594    /// # Examples
595    ///
596    /// ```
597    /// use ff_format::color::AlphaMode;
598    ///
599    /// assert!(AlphaMode::Unknown.is_unknown());
600    /// assert!(!AlphaMode::Straight.is_unknown());
601    /// ```
602    #[must_use]
603    pub const fn is_unknown(&self) -> bool {
604        matches!(self, Self::Unknown)
605    }
606}
607
608impl fmt::Display for AlphaMode {
609    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
610        write!(f, "{}", self.name())
611    }
612}
613
614#[cfg(test)]
615mod tests {
616    use super::*;
617
618    mod color_space_tests {
619        use super::*;
620
621        #[test]
622        fn test_names() {
623            assert_eq!(ColorSpace::Bt709.name(), "bt709");
624            assert_eq!(ColorSpace::Bt470bg.name(), "bt470bg");
625            assert_eq!(ColorSpace::Smpte170m.name(), "smpte170m");
626            assert_eq!(ColorSpace::Bt2020Ncl.name(), "bt2020ncl");
627            assert_eq!(ColorSpace::Bt2020Cl.name(), "bt2020cl");
628            assert_eq!(ColorSpace::Rgb.name(), "rgb");
629            assert_eq!(ColorSpace::Fcc.name(), "fcc");
630            assert_eq!(ColorSpace::Smpte240m.name(), "smpte240m");
631            assert_eq!(ColorSpace::Ycgco.name(), "ycgco");
632            assert_eq!(ColorSpace::Unknown.name(), "unknown");
633        }
634
635        #[test]
636        fn test_display() {
637            assert_eq!(format!("{}", ColorSpace::Bt709), "bt709");
638            assert_eq!(format!("{}", ColorSpace::Bt2020Ncl), "bt2020ncl");
639        }
640
641        #[test]
642        fn test_default() {
643            assert_eq!(ColorSpace::default(), ColorSpace::Bt709);
644        }
645
646        #[test]
647        fn test_is_hd_sd_uhd() {
648            assert!(ColorSpace::Bt709.is_hd());
649            assert!(!ColorSpace::Bt709.is_sd());
650            assert!(!ColorSpace::Bt709.is_uhd());
651
652            assert!(!ColorSpace::Smpte170m.is_hd());
653            assert!(ColorSpace::Smpte170m.is_sd());
654            assert!(ColorSpace::Bt470bg.is_sd());
655            assert!(!ColorSpace::Smpte170m.is_uhd());
656
657            assert!(!ColorSpace::Bt2020Ncl.is_hd());
658            assert!(!ColorSpace::Bt2020Ncl.is_sd());
659            assert!(ColorSpace::Bt2020Ncl.is_uhd());
660            assert!(ColorSpace::Bt2020Cl.is_uhd());
661        }
662
663        #[test]
664        fn test_is_unknown() {
665            assert!(ColorSpace::Unknown.is_unknown());
666            assert!(!ColorSpace::Bt709.is_unknown());
667        }
668
669        #[test]
670        fn test_debug() {
671            assert_eq!(format!("{:?}", ColorSpace::Bt709), "Bt709");
672            assert_eq!(format!("{:?}", ColorSpace::Rgb), "Rgb");
673        }
674
675        #[test]
676        fn test_equality_and_hash() {
677            use std::collections::HashSet;
678
679            assert_eq!(ColorSpace::Bt709, ColorSpace::Bt709);
680            assert_ne!(ColorSpace::Bt709, ColorSpace::Smpte170m);
681
682            let mut set = HashSet::new();
683            set.insert(ColorSpace::Bt709);
684            set.insert(ColorSpace::Smpte170m);
685            assert!(set.contains(&ColorSpace::Bt709));
686            assert!(!set.contains(&ColorSpace::Bt2020Ncl));
687        }
688
689        #[test]
690        fn test_copy() {
691            let space = ColorSpace::Bt709;
692            let copied = space;
693            assert_eq!(space, copied);
694        }
695    }
696
697    mod color_range_tests {
698        use super::*;
699
700        #[test]
701        fn test_names() {
702            assert_eq!(ColorRange::Limited.name(), "limited");
703            assert_eq!(ColorRange::Full.name(), "full");
704            assert_eq!(ColorRange::Unknown.name(), "unknown");
705        }
706
707        #[test]
708        fn test_display() {
709            assert_eq!(format!("{}", ColorRange::Limited), "limited");
710            assert_eq!(format!("{}", ColorRange::Full), "full");
711        }
712
713        #[test]
714        fn test_default() {
715            assert_eq!(ColorRange::default(), ColorRange::Limited);
716        }
717
718        #[test]
719        fn test_is_full_limited() {
720            assert!(ColorRange::Full.is_full());
721            assert!(!ColorRange::Full.is_limited());
722
723            assert!(!ColorRange::Limited.is_full());
724            assert!(ColorRange::Limited.is_limited());
725        }
726
727        #[test]
728        fn test_is_unknown() {
729            assert!(ColorRange::Unknown.is_unknown());
730            assert!(!ColorRange::Limited.is_unknown());
731        }
732
733        #[test]
734        fn test_luma_values() {
735            assert_eq!(ColorRange::Limited.luma_min_8bit(), 16);
736            assert_eq!(ColorRange::Limited.luma_max_8bit(), 235);
737
738            assert_eq!(ColorRange::Full.luma_min_8bit(), 0);
739            assert_eq!(ColorRange::Full.luma_max_8bit(), 255);
740
741            assert_eq!(ColorRange::Unknown.luma_min_8bit(), 0);
742            assert_eq!(ColorRange::Unknown.luma_max_8bit(), 255);
743        }
744
745        #[test]
746        fn test_equality_and_hash() {
747            use std::collections::HashSet;
748
749            assert_eq!(ColorRange::Limited, ColorRange::Limited);
750            assert_ne!(ColorRange::Limited, ColorRange::Full);
751
752            let mut set = HashSet::new();
753            set.insert(ColorRange::Limited);
754            set.insert(ColorRange::Full);
755            assert!(set.contains(&ColorRange::Limited));
756            assert!(!set.contains(&ColorRange::Unknown));
757        }
758    }
759
760    mod color_primaries_tests {
761        use super::*;
762
763        #[test]
764        fn test_names() {
765            assert_eq!(ColorPrimaries::Bt709.name(), "bt709");
766            assert_eq!(ColorPrimaries::Bt470bg.name(), "bt470bg");
767            assert_eq!(ColorPrimaries::Smpte170m.name(), "smpte170m");
768            assert_eq!(ColorPrimaries::Smpte240m.name(), "smpte240m");
769            assert_eq!(ColorPrimaries::Film.name(), "film");
770            assert_eq!(ColorPrimaries::DciP3.name(), "dci-p3");
771            assert_eq!(ColorPrimaries::DisplayP3.name(), "display-p3");
772            assert_eq!(ColorPrimaries::Bt2020.name(), "bt2020");
773            assert_eq!(ColorPrimaries::Unknown.name(), "unknown");
774        }
775
776        #[test]
777        fn test_display() {
778            assert_eq!(format!("{}", ColorPrimaries::Bt709), "bt709");
779            assert_eq!(format!("{}", ColorPrimaries::Bt2020), "bt2020");
780        }
781
782        #[test]
783        fn test_default() {
784            assert_eq!(ColorPrimaries::default(), ColorPrimaries::Bt709);
785        }
786
787        #[test]
788        fn test_is_wide_gamut() {
789            assert!(ColorPrimaries::Bt2020.is_wide_gamut());
790            assert!(ColorPrimaries::DciP3.is_wide_gamut());
791            assert!(ColorPrimaries::DisplayP3.is_wide_gamut());
792            assert!(!ColorPrimaries::Bt709.is_wide_gamut());
793            assert!(!ColorPrimaries::Smpte170m.is_wide_gamut());
794        }
795
796        #[test]
797        fn test_is_unknown() {
798            assert!(ColorPrimaries::Unknown.is_unknown());
799            assert!(!ColorPrimaries::Bt709.is_unknown());
800        }
801
802        #[test]
803        fn test_equality_and_hash() {
804            use std::collections::HashSet;
805
806            assert_eq!(ColorPrimaries::Bt709, ColorPrimaries::Bt709);
807            assert_ne!(ColorPrimaries::Bt709, ColorPrimaries::Bt2020);
808
809            let mut set = HashSet::new();
810            set.insert(ColorPrimaries::Bt709);
811            set.insert(ColorPrimaries::Bt2020);
812            assert!(set.contains(&ColorPrimaries::Bt709));
813            assert!(!set.contains(&ColorPrimaries::Smpte170m));
814        }
815    }
816
817    mod color_transfer_tests {
818        use super::*;
819
820        #[test]
821        fn test_names() {
822            assert_eq!(ColorTransfer::Bt709.name(), "bt709");
823            assert_eq!(ColorTransfer::Gamma22.name(), "gamma22");
824            assert_eq!(ColorTransfer::Gamma28.name(), "gamma28");
825            assert_eq!(ColorTransfer::Smpte170m.name(), "smpte170m");
826            assert_eq!(ColorTransfer::Smpte240m.name(), "smpte240m");
827            assert_eq!(ColorTransfer::Linear.name(), "linear");
828            assert_eq!(ColorTransfer::Srgb.name(), "srgb");
829            assert_eq!(ColorTransfer::Bt2020_10.name(), "bt2020-10");
830            assert_eq!(ColorTransfer::Bt2020_12.name(), "bt2020-12");
831            assert_eq!(ColorTransfer::Pq.name(), "pq");
832            assert_eq!(ColorTransfer::Hlg.name(), "hlg");
833            assert_eq!(ColorTransfer::Unknown.name(), "unknown");
834        }
835
836        #[test]
837        fn test_display() {
838            assert_eq!(format!("{}", ColorTransfer::Hlg), "hlg");
839            assert_eq!(format!("{}", ColorTransfer::Pq), "pq");
840            assert_eq!(format!("{}", ColorTransfer::Bt709), "bt709");
841        }
842
843        #[test]
844        fn test_default() {
845            assert_eq!(ColorTransfer::default(), ColorTransfer::Bt709);
846        }
847
848        #[test]
849        fn hlg_is_hdr_should_return_true() {
850            assert!(ColorTransfer::Hlg.is_hdr());
851            assert!(ColorTransfer::Hlg.is_hlg());
852            assert!(!ColorTransfer::Hlg.is_pq());
853        }
854
855        #[test]
856        fn pq_is_hdr_should_return_true() {
857            assert!(ColorTransfer::Pq.is_hdr());
858            assert!(ColorTransfer::Pq.is_pq());
859            assert!(!ColorTransfer::Pq.is_hlg());
860        }
861
862        #[test]
863        fn sdr_transfers_are_not_hdr() {
864            assert!(!ColorTransfer::Bt709.is_hdr());
865            assert!(!ColorTransfer::Gamma22.is_hdr());
866            assert!(!ColorTransfer::Gamma28.is_hdr());
867            assert!(!ColorTransfer::Smpte170m.is_hdr());
868            assert!(!ColorTransfer::Smpte240m.is_hdr());
869            assert!(!ColorTransfer::Srgb.is_hdr());
870            assert!(!ColorTransfer::Bt2020_10.is_hdr());
871            assert!(!ColorTransfer::Bt2020_12.is_hdr());
872            assert!(!ColorTransfer::Linear.is_hdr());
873        }
874
875        #[test]
876        fn is_unknown_should_only_match_unknown() {
877            assert!(ColorTransfer::Unknown.is_unknown());
878            assert!(!ColorTransfer::Bt709.is_unknown());
879            assert!(!ColorTransfer::Hlg.is_unknown());
880        }
881
882        #[test]
883        fn test_equality_and_hash() {
884            use std::collections::HashSet;
885
886            assert_eq!(ColorTransfer::Hlg, ColorTransfer::Hlg);
887            assert_ne!(ColorTransfer::Hlg, ColorTransfer::Pq);
888
889            let mut set = HashSet::new();
890            set.insert(ColorTransfer::Hlg);
891            set.insert(ColorTransfer::Pq);
892            assert!(set.contains(&ColorTransfer::Hlg));
893            assert!(!set.contains(&ColorTransfer::Bt709));
894        }
895    }
896
897    mod alpha_mode_tests {
898        use super::*;
899
900        #[test]
901        fn test_names() {
902            assert_eq!(AlphaMode::Straight.name(), "straight");
903            assert_eq!(AlphaMode::Premultiplied.name(), "premultiplied");
904            assert_eq!(AlphaMode::Unknown.name(), "unknown");
905        }
906
907        #[test]
908        fn test_display() {
909            assert_eq!(format!("{}", AlphaMode::Straight), "straight");
910            assert_eq!(format!("{}", AlphaMode::Premultiplied), "premultiplied");
911            assert_eq!(format!("{}", AlphaMode::Unknown), "unknown");
912        }
913
914        #[test]
915        fn test_default() {
916            assert_eq!(AlphaMode::default(), AlphaMode::Straight);
917        }
918
919        #[test]
920        fn is_straight_should_only_match_straight() {
921            assert!(AlphaMode::Straight.is_straight());
922            assert!(!AlphaMode::Premultiplied.is_straight());
923            assert!(!AlphaMode::Unknown.is_straight());
924        }
925
926        #[test]
927        fn is_premultiplied_should_only_match_premultiplied() {
928            assert!(AlphaMode::Premultiplied.is_premultiplied());
929            assert!(!AlphaMode::Straight.is_premultiplied());
930            assert!(!AlphaMode::Unknown.is_premultiplied());
931        }
932
933        #[test]
934        fn is_unknown_should_only_match_unknown() {
935            assert!(AlphaMode::Unknown.is_unknown());
936            assert!(!AlphaMode::Straight.is_unknown());
937            assert!(!AlphaMode::Premultiplied.is_unknown());
938        }
939
940        #[test]
941        fn test_equality_and_hash() {
942            use std::collections::HashSet;
943
944            assert_eq!(AlphaMode::Straight, AlphaMode::Straight);
945            assert_ne!(AlphaMode::Premultiplied, AlphaMode::Straight);
946
947            let mut set = HashSet::new();
948            set.insert(AlphaMode::Straight);
949            assert!(set.contains(&AlphaMode::Straight));
950            assert!(!set.contains(&AlphaMode::Premultiplied));
951        }
952    }
953}