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.601**: SD content (480i, 576i)
31/// - **BT.2020**: UHD/HDR content (4K, 8K)
32/// - **sRGB**: Computer graphics, web content
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
34#[non_exhaustive]
35pub enum ColorSpace {
36    /// ITU-R BT.709 - HD television standard (most common for HD video)
37    #[default]
38    Bt709,
39    /// ITU-R BT.601 - SD television standard
40    Bt601,
41    /// ITU-R BT.2020 - UHD/HDR television standard
42    Bt2020,
43    /// DCI-P3 - digital cinema wide color gamut
44    DciP3,
45    /// sRGB color space - computer graphics and web
46    Srgb,
47    /// Color space is not specified or unknown
48    Unknown,
49}
50
51impl ColorSpace {
52    /// Returns the name of the color space as a human-readable string.
53    ///
54    /// # Examples
55    ///
56    /// ```
57    /// use ff_format::color::ColorSpace;
58    ///
59    /// assert_eq!(ColorSpace::Bt709.name(), "bt709");
60    /// assert_eq!(ColorSpace::Bt601.name(), "bt601");
61    /// ```
62    #[must_use]
63    pub const fn name(&self) -> &'static str {
64        match self {
65            Self::Bt709 => "bt709",
66            Self::Bt601 => "bt601",
67            Self::Bt2020 => "bt2020",
68            Self::DciP3 => "dcip3",
69            Self::Srgb => "srgb",
70            Self::Unknown => "unknown",
71        }
72    }
73
74    /// Returns `true` if this is an HD color space (BT.709).
75    ///
76    /// # Examples
77    ///
78    /// ```
79    /// use ff_format::color::ColorSpace;
80    ///
81    /// assert!(ColorSpace::Bt709.is_hd());
82    /// assert!(!ColorSpace::Bt601.is_hd());
83    /// ```
84    #[must_use]
85    pub const fn is_hd(&self) -> bool {
86        matches!(self, Self::Bt709)
87    }
88
89    /// Returns `true` if this is an SD color space (BT.601).
90    ///
91    /// # Examples
92    ///
93    /// ```
94    /// use ff_format::color::ColorSpace;
95    ///
96    /// assert!(ColorSpace::Bt601.is_sd());
97    /// assert!(!ColorSpace::Bt709.is_sd());
98    /// ```
99    #[must_use]
100    pub const fn is_sd(&self) -> bool {
101        matches!(self, Self::Bt601)
102    }
103
104    /// Returns `true` if this is a UHD/HDR color space (BT.2020).
105    ///
106    /// # Examples
107    ///
108    /// ```
109    /// use ff_format::color::ColorSpace;
110    ///
111    /// assert!(ColorSpace::Bt2020.is_uhd());
112    /// assert!(!ColorSpace::Bt709.is_uhd());
113    /// ```
114    #[must_use]
115    pub const fn is_uhd(&self) -> bool {
116        matches!(self, Self::Bt2020)
117    }
118
119    /// Returns `true` if this is a cinema/DCI color space (DCI-P3).
120    ///
121    /// # Examples
122    ///
123    /// ```
124    /// use ff_format::color::ColorSpace;
125    ///
126    /// assert!(ColorSpace::DciP3.is_cinema());
127    /// assert!(!ColorSpace::Bt709.is_cinema());
128    /// ```
129    #[must_use]
130    pub const fn is_cinema(&self) -> bool {
131        matches!(self, Self::DciP3)
132    }
133
134    /// Returns `true` if the color space is unknown.
135    ///
136    /// # Examples
137    ///
138    /// ```
139    /// use ff_format::color::ColorSpace;
140    ///
141    /// assert!(ColorSpace::Unknown.is_unknown());
142    /// assert!(!ColorSpace::Bt709.is_unknown());
143    /// ```
144    #[must_use]
145    pub const fn is_unknown(&self) -> bool {
146        matches!(self, Self::Unknown)
147    }
148}
149
150impl fmt::Display for ColorSpace {
151    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152        write!(f, "{}", self.name())
153    }
154}
155
156/// Color range defining the valid range of color values.
157///
158/// Video typically uses "limited" range where black is at level 16 and white
159/// at level 235 (for 8-bit). Computer graphics typically use "full" range
160/// where black is 0 and white is 255.
161///
162/// # Common Usage
163///
164/// - **Limited**: Broadcast video, Blu-ray, streaming services
165/// - **Full**: Computer graphics, screenshots, game capture
166#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
167#[non_exhaustive]
168pub enum ColorRange {
169    /// Limited/TV range (16-235 for Y, 16-240 for UV in 8-bit)
170    #[default]
171    Limited,
172    /// Full/PC range (0-255 for all components in 8-bit)
173    Full,
174    /// Color range is not specified or unknown
175    Unknown,
176}
177
178impl ColorRange {
179    /// Returns the name of the color range as a human-readable string.
180    ///
181    /// # Examples
182    ///
183    /// ```
184    /// use ff_format::color::ColorRange;
185    ///
186    /// assert_eq!(ColorRange::Limited.name(), "limited");
187    /// assert_eq!(ColorRange::Full.name(), "full");
188    /// ```
189    #[must_use]
190    pub const fn name(&self) -> &'static str {
191        match self {
192            Self::Limited => "limited",
193            Self::Full => "full",
194            Self::Unknown => "unknown",
195        }
196    }
197
198    /// Returns `true` if this is full (PC) range.
199    ///
200    /// # Examples
201    ///
202    /// ```
203    /// use ff_format::color::ColorRange;
204    ///
205    /// assert!(ColorRange::Full.is_full());
206    /// assert!(!ColorRange::Limited.is_full());
207    /// ```
208    #[must_use]
209    pub const fn is_full(&self) -> bool {
210        matches!(self, Self::Full)
211    }
212
213    /// Returns `true` if this is limited (TV) range.
214    ///
215    /// # Examples
216    ///
217    /// ```
218    /// use ff_format::color::ColorRange;
219    ///
220    /// assert!(ColorRange::Limited.is_limited());
221    /// assert!(!ColorRange::Full.is_limited());
222    /// ```
223    #[must_use]
224    pub const fn is_limited(&self) -> bool {
225        matches!(self, Self::Limited)
226    }
227
228    /// Returns `true` if the color range is unknown.
229    ///
230    /// # Examples
231    ///
232    /// ```
233    /// use ff_format::color::ColorRange;
234    ///
235    /// assert!(ColorRange::Unknown.is_unknown());
236    /// assert!(!ColorRange::Limited.is_unknown());
237    /// ```
238    #[must_use]
239    pub const fn is_unknown(&self) -> bool {
240        matches!(self, Self::Unknown)
241    }
242
243    /// Returns the minimum value for luma (Y) in 8-bit.
244    ///
245    /// # Examples
246    ///
247    /// ```
248    /// use ff_format::color::ColorRange;
249    ///
250    /// assert_eq!(ColorRange::Limited.luma_min_8bit(), 16);
251    /// assert_eq!(ColorRange::Full.luma_min_8bit(), 0);
252    /// ```
253    #[must_use]
254    pub const fn luma_min_8bit(&self) -> u8 {
255        match self {
256            Self::Limited => 16,
257            Self::Full | Self::Unknown => 0,
258        }
259    }
260
261    /// Returns the maximum value for luma (Y) in 8-bit.
262    ///
263    /// # Examples
264    ///
265    /// ```
266    /// use ff_format::color::ColorRange;
267    ///
268    /// assert_eq!(ColorRange::Limited.luma_max_8bit(), 235);
269    /// assert_eq!(ColorRange::Full.luma_max_8bit(), 255);
270    /// ```
271    #[must_use]
272    pub const fn luma_max_8bit(&self) -> u8 {
273        match self {
274            Self::Limited => 235,
275            Self::Full | Self::Unknown => 255,
276        }
277    }
278}
279
280impl fmt::Display for ColorRange {
281    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
282        write!(f, "{}", self.name())
283    }
284}
285
286/// Color primaries defining the color gamut (the range of colors that can be represented).
287///
288/// Different standards define different primary colors (red, green, blue points)
289/// which determine the overall range of colors that can be displayed.
290///
291/// # Common Usage
292///
293/// - **BT.709**: HD content, same as sRGB primaries
294/// - **BT.601**: SD content (NTSC or PAL)
295/// - **BT.2020**: Wide color gamut for UHD/HDR
296#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
297#[non_exhaustive]
298pub enum ColorPrimaries {
299    /// ITU-R BT.709 primaries (same as sRGB, most common)
300    #[default]
301    Bt709,
302    /// ITU-R BT.601 primaries (SD video)
303    Bt601,
304    /// ITU-R BT.2020 primaries (wide color gamut for UHD/HDR)
305    Bt2020,
306    /// Color primaries are not specified or unknown
307    Unknown,
308}
309
310impl ColorPrimaries {
311    /// Returns the name of the color primaries as a human-readable string.
312    ///
313    /// # Examples
314    ///
315    /// ```
316    /// use ff_format::color::ColorPrimaries;
317    ///
318    /// assert_eq!(ColorPrimaries::Bt709.name(), "bt709");
319    /// assert_eq!(ColorPrimaries::Bt2020.name(), "bt2020");
320    /// ```
321    #[must_use]
322    pub const fn name(&self) -> &'static str {
323        match self {
324            Self::Bt709 => "bt709",
325            Self::Bt601 => "bt601",
326            Self::Bt2020 => "bt2020",
327            Self::Unknown => "unknown",
328        }
329    }
330
331    /// Returns `true` if this uses wide color gamut (BT.2020).
332    ///
333    /// # Examples
334    ///
335    /// ```
336    /// use ff_format::color::ColorPrimaries;
337    ///
338    /// assert!(ColorPrimaries::Bt2020.is_wide_gamut());
339    /// assert!(!ColorPrimaries::Bt709.is_wide_gamut());
340    /// ```
341    #[must_use]
342    pub const fn is_wide_gamut(&self) -> bool {
343        matches!(self, Self::Bt2020)
344    }
345
346    /// Returns `true` if the color primaries are unknown.
347    ///
348    /// # Examples
349    ///
350    /// ```
351    /// use ff_format::color::ColorPrimaries;
352    ///
353    /// assert!(ColorPrimaries::Unknown.is_unknown());
354    /// assert!(!ColorPrimaries::Bt709.is_unknown());
355    /// ```
356    #[must_use]
357    pub const fn is_unknown(&self) -> bool {
358        matches!(self, Self::Unknown)
359    }
360}
361
362impl fmt::Display for ColorPrimaries {
363    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
364        write!(f, "{}", self.name())
365    }
366}
367
368/// Color transfer characteristic (opto-electronic transfer function).
369///
370/// The transfer characteristic defines how scene luminance maps to the signal
371/// level stored in the video bitstream. Different HDR and SDR standards use
372/// different curves.
373///
374/// # Common Usage
375///
376/// - **`Bt709`**: Standard SDR video (HD television)
377/// - **`Pq`**: HDR10 and Dolby Vision (SMPTE ST 2084 / Perceptual Quantizer)
378/// - **`Hlg`**: Hybrid Log-Gamma — broadcast-compatible HDR (ARIB STD-B67)
379/// - **`Bt2020_10`** / **`Bt2020_12`**: BT.2020 SDR at 10/12-bit depth
380/// - **`Linear`**: Linear light, no gamma applied
381#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
382#[non_exhaustive]
383pub enum ColorTransfer {
384    /// ITU-R BT.709 transfer characteristic (standard SDR)
385    #[default]
386    Bt709,
387    /// ITU-R BT.2020 for 10-bit content
388    Bt2020_10,
389    /// ITU-R BT.2020 for 12-bit content
390    Bt2020_12,
391    /// Hybrid Log-Gamma (ARIB STD-B67) — broadcast HDR
392    Hlg,
393    /// Perceptual Quantizer / SMPTE ST 2084 — HDR10
394    Pq,
395    /// Linear light transfer (no gamma)
396    Linear,
397    /// Transfer characteristic is not specified or unknown
398    Unknown,
399}
400
401impl ColorTransfer {
402    /// Returns the name of the color transfer characteristic as a string.
403    ///
404    /// # Examples
405    ///
406    /// ```
407    /// use ff_format::color::ColorTransfer;
408    ///
409    /// assert_eq!(ColorTransfer::Bt709.name(), "bt709");
410    /// assert_eq!(ColorTransfer::Hlg.name(), "hlg");
411    /// assert_eq!(ColorTransfer::Pq.name(), "pq");
412    /// ```
413    #[must_use]
414    pub const fn name(&self) -> &'static str {
415        match self {
416            Self::Bt709 => "bt709",
417            Self::Bt2020_10 => "bt2020-10",
418            Self::Bt2020_12 => "bt2020-12",
419            Self::Hlg => "hlg",
420            Self::Pq => "pq",
421            Self::Linear => "linear",
422            Self::Unknown => "unknown",
423        }
424    }
425
426    /// Returns `true` if this is an HDR transfer characteristic (`Pq` or `Hlg`).
427    ///
428    /// # Examples
429    ///
430    /// ```
431    /// use ff_format::color::ColorTransfer;
432    ///
433    /// assert!(ColorTransfer::Pq.is_hdr());
434    /// assert!(ColorTransfer::Hlg.is_hdr());
435    /// assert!(!ColorTransfer::Bt709.is_hdr());
436    /// ```
437    #[must_use]
438    pub const fn is_hdr(&self) -> bool {
439        matches!(self, Self::Pq | Self::Hlg)
440    }
441
442    /// Returns `true` if this is Hybrid Log-Gamma (HLG).
443    ///
444    /// # Examples
445    ///
446    /// ```
447    /// use ff_format::color::ColorTransfer;
448    ///
449    /// assert!(ColorTransfer::Hlg.is_hlg());
450    /// assert!(!ColorTransfer::Pq.is_hlg());
451    /// ```
452    #[must_use]
453    pub const fn is_hlg(&self) -> bool {
454        matches!(self, Self::Hlg)
455    }
456
457    /// Returns `true` if this is Perceptual Quantizer / SMPTE ST 2084 (PQ).
458    ///
459    /// # Examples
460    ///
461    /// ```
462    /// use ff_format::color::ColorTransfer;
463    ///
464    /// assert!(ColorTransfer::Pq.is_pq());
465    /// assert!(!ColorTransfer::Hlg.is_pq());
466    /// ```
467    #[must_use]
468    pub const fn is_pq(&self) -> bool {
469        matches!(self, Self::Pq)
470    }
471
472    /// Returns `true` if the transfer characteristic is unknown.
473    ///
474    /// # Examples
475    ///
476    /// ```
477    /// use ff_format::color::ColorTransfer;
478    ///
479    /// assert!(ColorTransfer::Unknown.is_unknown());
480    /// assert!(!ColorTransfer::Bt709.is_unknown());
481    /// ```
482    #[must_use]
483    pub const fn is_unknown(&self) -> bool {
484        matches!(self, Self::Unknown)
485    }
486}
487
488impl fmt::Display for ColorTransfer {
489    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
490        write!(f, "{}", self.name())
491    }
492}
493
494#[cfg(test)]
495mod tests {
496    use super::*;
497
498    mod color_space_tests {
499        use super::*;
500
501        #[test]
502        fn test_names() {
503            assert_eq!(ColorSpace::Bt709.name(), "bt709");
504            assert_eq!(ColorSpace::Bt601.name(), "bt601");
505            assert_eq!(ColorSpace::Bt2020.name(), "bt2020");
506            assert_eq!(ColorSpace::DciP3.name(), "dcip3");
507            assert_eq!(ColorSpace::Srgb.name(), "srgb");
508            assert_eq!(ColorSpace::Unknown.name(), "unknown");
509        }
510
511        #[test]
512        fn test_display() {
513            assert_eq!(format!("{}", ColorSpace::Bt709), "bt709");
514            assert_eq!(format!("{}", ColorSpace::Bt2020), "bt2020");
515        }
516
517        #[test]
518        fn test_default() {
519            assert_eq!(ColorSpace::default(), ColorSpace::Bt709);
520        }
521
522        #[test]
523        fn test_is_hd_sd_uhd() {
524            assert!(ColorSpace::Bt709.is_hd());
525            assert!(!ColorSpace::Bt709.is_sd());
526            assert!(!ColorSpace::Bt709.is_uhd());
527
528            assert!(!ColorSpace::Bt601.is_hd());
529            assert!(ColorSpace::Bt601.is_sd());
530            assert!(!ColorSpace::Bt601.is_uhd());
531
532            assert!(!ColorSpace::Bt2020.is_hd());
533            assert!(!ColorSpace::Bt2020.is_sd());
534            assert!(ColorSpace::Bt2020.is_uhd());
535        }
536
537        #[test]
538        fn dcip3_is_cinema_should_return_true() {
539            assert!(ColorSpace::DciP3.is_cinema());
540            assert!(!ColorSpace::Bt709.is_cinema());
541            assert!(!ColorSpace::Bt2020.is_cinema());
542        }
543
544        #[test]
545        fn test_is_unknown() {
546            assert!(ColorSpace::Unknown.is_unknown());
547            assert!(!ColorSpace::Bt709.is_unknown());
548        }
549
550        #[test]
551        fn test_debug() {
552            assert_eq!(format!("{:?}", ColorSpace::Bt709), "Bt709");
553            assert_eq!(format!("{:?}", ColorSpace::Srgb), "Srgb");
554        }
555
556        #[test]
557        fn test_equality_and_hash() {
558            use std::collections::HashSet;
559
560            assert_eq!(ColorSpace::Bt709, ColorSpace::Bt709);
561            assert_ne!(ColorSpace::Bt709, ColorSpace::Bt601);
562
563            let mut set = HashSet::new();
564            set.insert(ColorSpace::Bt709);
565            set.insert(ColorSpace::Bt601);
566            assert!(set.contains(&ColorSpace::Bt709));
567            assert!(!set.contains(&ColorSpace::Bt2020));
568        }
569
570        #[test]
571        fn test_copy() {
572            let space = ColorSpace::Bt709;
573            let copied = space;
574            assert_eq!(space, copied);
575        }
576    }
577
578    mod color_range_tests {
579        use super::*;
580
581        #[test]
582        fn test_names() {
583            assert_eq!(ColorRange::Limited.name(), "limited");
584            assert_eq!(ColorRange::Full.name(), "full");
585            assert_eq!(ColorRange::Unknown.name(), "unknown");
586        }
587
588        #[test]
589        fn test_display() {
590            assert_eq!(format!("{}", ColorRange::Limited), "limited");
591            assert_eq!(format!("{}", ColorRange::Full), "full");
592        }
593
594        #[test]
595        fn test_default() {
596            assert_eq!(ColorRange::default(), ColorRange::Limited);
597        }
598
599        #[test]
600        fn test_is_full_limited() {
601            assert!(ColorRange::Full.is_full());
602            assert!(!ColorRange::Full.is_limited());
603
604            assert!(!ColorRange::Limited.is_full());
605            assert!(ColorRange::Limited.is_limited());
606        }
607
608        #[test]
609        fn test_is_unknown() {
610            assert!(ColorRange::Unknown.is_unknown());
611            assert!(!ColorRange::Limited.is_unknown());
612        }
613
614        #[test]
615        fn test_luma_values() {
616            assert_eq!(ColorRange::Limited.luma_min_8bit(), 16);
617            assert_eq!(ColorRange::Limited.luma_max_8bit(), 235);
618
619            assert_eq!(ColorRange::Full.luma_min_8bit(), 0);
620            assert_eq!(ColorRange::Full.luma_max_8bit(), 255);
621
622            assert_eq!(ColorRange::Unknown.luma_min_8bit(), 0);
623            assert_eq!(ColorRange::Unknown.luma_max_8bit(), 255);
624        }
625
626        #[test]
627        fn test_equality_and_hash() {
628            use std::collections::HashSet;
629
630            assert_eq!(ColorRange::Limited, ColorRange::Limited);
631            assert_ne!(ColorRange::Limited, ColorRange::Full);
632
633            let mut set = HashSet::new();
634            set.insert(ColorRange::Limited);
635            set.insert(ColorRange::Full);
636            assert!(set.contains(&ColorRange::Limited));
637            assert!(!set.contains(&ColorRange::Unknown));
638        }
639    }
640
641    mod color_primaries_tests {
642        use super::*;
643
644        #[test]
645        fn test_names() {
646            assert_eq!(ColorPrimaries::Bt709.name(), "bt709");
647            assert_eq!(ColorPrimaries::Bt601.name(), "bt601");
648            assert_eq!(ColorPrimaries::Bt2020.name(), "bt2020");
649            assert_eq!(ColorPrimaries::Unknown.name(), "unknown");
650        }
651
652        #[test]
653        fn test_display() {
654            assert_eq!(format!("{}", ColorPrimaries::Bt709), "bt709");
655            assert_eq!(format!("{}", ColorPrimaries::Bt2020), "bt2020");
656        }
657
658        #[test]
659        fn test_default() {
660            assert_eq!(ColorPrimaries::default(), ColorPrimaries::Bt709);
661        }
662
663        #[test]
664        fn test_is_wide_gamut() {
665            assert!(ColorPrimaries::Bt2020.is_wide_gamut());
666            assert!(!ColorPrimaries::Bt709.is_wide_gamut());
667            assert!(!ColorPrimaries::Bt601.is_wide_gamut());
668        }
669
670        #[test]
671        fn test_is_unknown() {
672            assert!(ColorPrimaries::Unknown.is_unknown());
673            assert!(!ColorPrimaries::Bt709.is_unknown());
674        }
675
676        #[test]
677        fn test_equality_and_hash() {
678            use std::collections::HashSet;
679
680            assert_eq!(ColorPrimaries::Bt709, ColorPrimaries::Bt709);
681            assert_ne!(ColorPrimaries::Bt709, ColorPrimaries::Bt2020);
682
683            let mut set = HashSet::new();
684            set.insert(ColorPrimaries::Bt709);
685            set.insert(ColorPrimaries::Bt2020);
686            assert!(set.contains(&ColorPrimaries::Bt709));
687            assert!(!set.contains(&ColorPrimaries::Bt601));
688        }
689    }
690
691    mod color_transfer_tests {
692        use super::*;
693
694        #[test]
695        fn test_names() {
696            assert_eq!(ColorTransfer::Bt709.name(), "bt709");
697            assert_eq!(ColorTransfer::Bt2020_10.name(), "bt2020-10");
698            assert_eq!(ColorTransfer::Bt2020_12.name(), "bt2020-12");
699            assert_eq!(ColorTransfer::Hlg.name(), "hlg");
700            assert_eq!(ColorTransfer::Pq.name(), "pq");
701            assert_eq!(ColorTransfer::Linear.name(), "linear");
702            assert_eq!(ColorTransfer::Unknown.name(), "unknown");
703        }
704
705        #[test]
706        fn test_display() {
707            assert_eq!(format!("{}", ColorTransfer::Hlg), "hlg");
708            assert_eq!(format!("{}", ColorTransfer::Pq), "pq");
709            assert_eq!(format!("{}", ColorTransfer::Bt709), "bt709");
710        }
711
712        #[test]
713        fn test_default() {
714            assert_eq!(ColorTransfer::default(), ColorTransfer::Bt709);
715        }
716
717        #[test]
718        fn hlg_is_hdr_should_return_true() {
719            assert!(ColorTransfer::Hlg.is_hdr());
720            assert!(ColorTransfer::Hlg.is_hlg());
721            assert!(!ColorTransfer::Hlg.is_pq());
722        }
723
724        #[test]
725        fn pq_is_hdr_should_return_true() {
726            assert!(ColorTransfer::Pq.is_hdr());
727            assert!(ColorTransfer::Pq.is_pq());
728            assert!(!ColorTransfer::Pq.is_hlg());
729        }
730
731        #[test]
732        fn sdr_transfers_are_not_hdr() {
733            assert!(!ColorTransfer::Bt709.is_hdr());
734            assert!(!ColorTransfer::Bt2020_10.is_hdr());
735            assert!(!ColorTransfer::Bt2020_12.is_hdr());
736            assert!(!ColorTransfer::Linear.is_hdr());
737        }
738
739        #[test]
740        fn is_unknown_should_only_match_unknown() {
741            assert!(ColorTransfer::Unknown.is_unknown());
742            assert!(!ColorTransfer::Bt709.is_unknown());
743            assert!(!ColorTransfer::Hlg.is_unknown());
744        }
745
746        #[test]
747        fn test_equality_and_hash() {
748            use std::collections::HashSet;
749
750            assert_eq!(ColorTransfer::Hlg, ColorTransfer::Hlg);
751            assert_ne!(ColorTransfer::Hlg, ColorTransfer::Pq);
752
753            let mut set = HashSet::new();
754            set.insert(ColorTransfer::Hlg);
755            set.insert(ColorTransfer::Pq);
756            assert!(set.contains(&ColorTransfer::Hlg));
757            assert!(!set.contains(&ColorTransfer::Bt709));
758        }
759    }
760}