Skip to main content

ff_format/
pixel.rs

1//! Pixel format definitions for video processing.
2//!
3//! This module provides the [`PixelFormat`] enum which represents various
4//! pixel formats used in video processing. It supports both packed (RGB/BGRA)
5//! and planar (YUV) formats commonly used in video editing.
6//!
7//! # Examples
8//!
9//! ```
10//! use ff_format::PixelFormat;
11//!
12//! let format = PixelFormat::Yuv420p;
13//! assert!(format.is_planar());
14//! assert!(!format.is_packed());
15//! assert_eq!(format.num_planes(), 3);
16//!
17//! let rgba = PixelFormat::Rgba;
18//! assert!(rgba.has_alpha());
19//! assert_eq!(rgba.bits_per_pixel(), Some(32));
20//! ```
21
22use std::fmt;
23
24/// Pixel format for video frames.
25///
26/// This enum represents various pixel formats used in video processing.
27/// It is designed to cover the most common formats used in video editing
28/// while remaining extensible via the `Other` variant.
29///
30/// # Format Categories
31///
32/// - **Packed RGB**: Data stored contiguously (Rgb24, Rgba, Bgr24, Bgra)
33/// - **Planar YUV**: Separate planes for Y, U, V components (Yuv420p, Yuv422p, Yuv444p)
34/// - **Semi-planar**: Y plane + interleaved UV (Nv12, Nv21)
35/// - **High bit depth**: 10-bit formats for HDR content (Yuv420p10le, Yuv422p10le, Yuv444p10le, Yuva444p10le, P010le)
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
37#[non_exhaustive]
38pub enum PixelFormat {
39    // Packed RGB
40    /// 24-bit RGB (8:8:8) - 3 bytes per pixel
41    Rgb24,
42    /// 32-bit RGBA (8:8:8:8) - 4 bytes per pixel with alpha
43    Rgba,
44    /// 24-bit BGR (8:8:8) - 3 bytes per pixel, reversed channel order
45    Bgr24,
46    /// 32-bit BGRA (8:8:8:8) - 4 bytes per pixel with alpha, reversed channel order
47    Bgra,
48
49    // Planar YUV
50    /// YUV 4:2:0 planar - most common video format (H.264, etc.)
51    Yuv420p,
52    /// YUV 4:2:2 planar - higher chroma resolution
53    Yuv422p,
54    /// YUV 4:4:4 planar - full chroma resolution
55    Yuv444p,
56
57    // Semi-planar (NV12/NV21)
58    /// Y plane + interleaved UV - common in hardware decoders
59    Nv12,
60    /// Y plane + interleaved VU - Android camera format
61    Nv21,
62
63    // High bit depth
64    /// 10-bit YUV 4:2:0 planar - HDR content
65    Yuv420p10le,
66    /// 10-bit YUV 4:2:2 planar - `ProRes` 422 profiles
67    Yuv422p10le,
68    /// 10-bit YUV 4:4:4 planar - `ProRes` 4444 (no alpha)
69    Yuv444p10le,
70    /// 10-bit YUVA 4:4:4 planar with alpha - `ProRes` 4444 with alpha
71    Yuva444p10le,
72    /// 10-bit semi-planar NV12 - HDR hardware decoding
73    P010le,
74
75    // Grayscale
76    /// 8-bit grayscale
77    Gray8,
78
79    // Float formats
80    /// 32-bit float planar GBR — native output of the `OpenEXR` decoder
81    Gbrpf32le,
82
83    // Extensibility
84    /// Unknown or unsupported format with `FFmpeg`'s `AVPixelFormat` value
85    Other(u32),
86}
87
88impl PixelFormat {
89    /// Returns the format name as a human-readable string.
90    ///
91    /// # Examples
92    ///
93    /// ```
94    /// use ff_format::PixelFormat;
95    ///
96    /// assert_eq!(PixelFormat::Yuv420p.name(), "yuv420p");
97    /// assert_eq!(PixelFormat::Rgba.name(), "rgba");
98    /// ```
99    #[must_use]
100    pub const fn name(&self) -> &'static str {
101        match self {
102            Self::Rgb24 => "rgb24",
103            Self::Rgba => "rgba",
104            Self::Bgr24 => "bgr24",
105            Self::Bgra => "bgra",
106            Self::Yuv420p => "yuv420p",
107            Self::Yuv422p => "yuv422p",
108            Self::Yuv444p => "yuv444p",
109            Self::Nv12 => "nv12",
110            Self::Nv21 => "nv21",
111            Self::Yuv420p10le => "yuv420p10le",
112            Self::Yuv422p10le => "yuv422p10le",
113            Self::Yuv444p10le => "yuv444p10le",
114            Self::Yuva444p10le => "yuva444p10le",
115            Self::P010le => "p010le",
116            Self::Gray8 => "gray8",
117            Self::Gbrpf32le => "gbrpf32le",
118            Self::Other(_) => "unknown",
119        }
120    }
121
122    /// Returns the number of planes for this format.
123    ///
124    /// - Packed formats (RGB, RGBA, etc.) have 1 plane
125    /// - Planar YUV formats have 3 planes (Y, U, V)
126    /// - Semi-planar formats (NV12, NV21) have 2 planes (Y, UV)
127    /// - Grayscale has 1 plane
128    ///
129    /// # Examples
130    ///
131    /// ```
132    /// use ff_format::PixelFormat;
133    ///
134    /// assert_eq!(PixelFormat::Rgba.num_planes(), 1);
135    /// assert_eq!(PixelFormat::Yuv420p.num_planes(), 3);
136    /// assert_eq!(PixelFormat::Nv12.num_planes(), 2);
137    /// ```
138    #[must_use]
139    pub const fn num_planes(&self) -> usize {
140        match self {
141            // Planar YUV - Y, U, V planes (and YUVA with alpha as 4th plane)
142            Self::Yuv420p
143            | Self::Yuv422p
144            | Self::Yuv444p
145            | Self::Yuv420p10le
146            | Self::Yuv422p10le
147            | Self::Yuv444p10le
148            | Self::Gbrpf32le => 3,
149            Self::Yuva444p10le => 4,
150            // Semi-planar - Y plane + interleaved UV plane
151            Self::Nv12 | Self::Nv21 | Self::P010le => 2,
152            // Packed formats and unknown - single plane
153            Self::Rgb24 | Self::Rgba | Self::Bgr24 | Self::Bgra | Self::Gray8 | Self::Other(_) => 1,
154        }
155    }
156
157    /// Alias for [`num_planes`](Self::num_planes) for API compatibility.
158    ///
159    /// # Examples
160    ///
161    /// ```
162    /// use ff_format::PixelFormat;
163    ///
164    /// assert_eq!(PixelFormat::Yuv420p.plane_count(), 3);
165    /// ```
166    #[must_use]
167    #[inline]
168    pub const fn plane_count(&self) -> usize {
169        self.num_planes()
170    }
171
172    /// Returns `true` if this is a packed format (single plane with interleaved components).
173    ///
174    /// Packed formats store all color components contiguously in memory,
175    /// making them suitable for direct rendering but less efficient for
176    /// video compression.
177    ///
178    /// # Examples
179    ///
180    /// ```
181    /// use ff_format::PixelFormat;
182    ///
183    /// assert!(PixelFormat::Rgba.is_packed());
184    /// assert!(!PixelFormat::Yuv420p.is_packed());
185    /// ```
186    #[must_use]
187    pub const fn is_packed(&self) -> bool {
188        matches!(
189            self,
190            Self::Rgb24 | Self::Rgba | Self::Bgr24 | Self::Bgra | Self::Gray8
191        )
192    }
193
194    /// Returns `true` if this is a planar format (separate planes for each component).
195    ///
196    /// Planar formats store each color component in a separate memory region,
197    /// which is more efficient for video codecs and some GPU operations.
198    ///
199    /// Note: Semi-planar formats (NV12, NV21, P010le) are considered planar
200    /// as they have multiple planes, even though UV is interleaved.
201    ///
202    /// # Examples
203    ///
204    /// ```
205    /// use ff_format::PixelFormat;
206    ///
207    /// assert!(PixelFormat::Yuv420p.is_planar());
208    /// assert!(PixelFormat::Nv12.is_planar());  // Semi-planar is also planar
209    /// assert!(!PixelFormat::Rgba.is_planar());
210    /// ```
211    #[must_use]
212    pub const fn is_planar(&self) -> bool {
213        !self.is_packed()
214    }
215
216    /// Returns `true` if this format has an alpha (transparency) channel.
217    ///
218    /// # Examples
219    ///
220    /// ```
221    /// use ff_format::PixelFormat;
222    ///
223    /// assert!(PixelFormat::Rgba.has_alpha());
224    /// assert!(PixelFormat::Bgra.has_alpha());
225    /// assert!(!PixelFormat::Rgb24.has_alpha());
226    /// assert!(!PixelFormat::Yuv420p.has_alpha());
227    /// ```
228    #[must_use]
229    pub const fn has_alpha(&self) -> bool {
230        matches!(self, Self::Rgba | Self::Bgra | Self::Yuva444p10le)
231    }
232
233    /// Returns `true` if this is an RGB-based format.
234    ///
235    /// # Examples
236    ///
237    /// ```
238    /// use ff_format::PixelFormat;
239    ///
240    /// assert!(PixelFormat::Rgb24.is_rgb());
241    /// assert!(PixelFormat::Rgba.is_rgb());
242    /// assert!(PixelFormat::Bgra.is_rgb());  // BGR is still RGB family
243    /// assert!(!PixelFormat::Yuv420p.is_rgb());
244    /// ```
245    #[must_use]
246    pub const fn is_rgb(&self) -> bool {
247        matches!(
248            self,
249            Self::Rgb24 | Self::Rgba | Self::Bgr24 | Self::Bgra | Self::Gbrpf32le
250        )
251    }
252
253    /// Returns `true` if this is a YUV-based format.
254    ///
255    /// # Examples
256    ///
257    /// ```
258    /// use ff_format::PixelFormat;
259    ///
260    /// assert!(PixelFormat::Yuv420p.is_yuv());
261    /// assert!(PixelFormat::Nv12.is_yuv());
262    /// assert!(!PixelFormat::Rgba.is_yuv());
263    /// ```
264    #[must_use]
265    pub const fn is_yuv(&self) -> bool {
266        matches!(
267            self,
268            Self::Yuv420p
269                | Self::Yuv422p
270                | Self::Yuv444p
271                | Self::Nv12
272                | Self::Nv21
273                | Self::Yuv420p10le
274                | Self::Yuv422p10le
275                | Self::Yuv444p10le
276                | Self::Yuva444p10le
277                | Self::P010le
278        )
279    }
280
281    /// Returns the bits per pixel for packed formats.
282    ///
283    /// For planar formats, this returns `None` because the concept of
284    /// "bits per pixel" doesn't apply directly - use [`bytes_per_pixel`](Self::bytes_per_pixel)
285    /// to get the average bytes per pixel instead.
286    ///
287    /// # Examples
288    ///
289    /// ```
290    /// use ff_format::PixelFormat;
291    ///
292    /// assert_eq!(PixelFormat::Rgb24.bits_per_pixel(), Some(24));
293    /// assert_eq!(PixelFormat::Rgba.bits_per_pixel(), Some(32));
294    /// assert_eq!(PixelFormat::Yuv420p.bits_per_pixel(), None);
295    /// ```
296    #[must_use]
297    pub const fn bits_per_pixel(&self) -> Option<usize> {
298        match self {
299            Self::Rgb24 | Self::Bgr24 => Some(24),
300            Self::Rgba | Self::Bgra => Some(32),
301            Self::Gray8 => Some(8),
302            // Planar formats don't have a simple bits-per-pixel value
303            _ => None,
304        }
305    }
306
307    /// Returns the average bytes per pixel.
308    ///
309    /// For packed formats, this is exact. For planar YUV formats, this
310    /// returns the average considering subsampling:
311    /// - YUV 4:2:0: 1.5 bytes/pixel (12 bits)
312    /// - YUV 4:2:2: 2 bytes/pixel (16 bits)
313    /// - YUV 4:4:4: 3 bytes/pixel (24 bits)
314    ///
315    /// Note: For formats with non-integer bytes per pixel (like `Yuv420p`),
316    /// this rounds up to the nearest byte.
317    ///
318    /// # Examples
319    ///
320    /// ```
321    /// use ff_format::PixelFormat;
322    ///
323    /// assert_eq!(PixelFormat::Rgba.bytes_per_pixel(), 4);
324    /// assert_eq!(PixelFormat::Rgb24.bytes_per_pixel(), 3);
325    /// assert_eq!(PixelFormat::Yuv420p.bytes_per_pixel(), 2);  // Actually 1.5, rounded up
326    /// assert_eq!(PixelFormat::Yuv444p.bytes_per_pixel(), 3);
327    /// ```
328    #[must_use]
329    pub const fn bytes_per_pixel(&self) -> usize {
330        match self {
331            // Grayscale - 1 byte per pixel
332            Self::Gray8 => 1,
333
334            // YUV 4:2:0 (8-bit and 10-bit) and YUV 4:2:2 - average ~2 bytes per pixel
335            Self::Yuv420p
336            | Self::Nv12
337            | Self::Nv21
338            | Self::Yuv420p10le
339            | Self::P010le
340            | Self::Yuv422p
341            | Self::Yuv422p10le => 2,
342
343            // RGB24/BGR24 and YUV 4:4:4 (8-bit and 10-bit) - 3 bytes per pixel
344            Self::Rgb24 | Self::Bgr24 | Self::Yuv444p | Self::Yuv444p10le => 3,
345
346            // 32-bit float planar GBR - 12 bytes per pixel (3 channels × 4 bytes)
347            Self::Gbrpf32le => 12,
348
349            // RGBA/BGRA, YUVA 4:4:4 with alpha, and unknown formats - 4 bytes per pixel
350            Self::Yuva444p10le | Self::Rgba | Self::Bgra | Self::Other(_) => 4,
351        }
352    }
353
354    /// Returns `true` if this is a high bit depth format (> 8 bits per component).
355    ///
356    /// # Examples
357    ///
358    /// ```
359    /// use ff_format::PixelFormat;
360    ///
361    /// assert!(PixelFormat::Yuv420p10le.is_high_bit_depth());
362    /// assert!(PixelFormat::P010le.is_high_bit_depth());
363    /// assert!(!PixelFormat::Yuv420p.is_high_bit_depth());
364    /// ```
365    #[must_use]
366    pub const fn is_high_bit_depth(&self) -> bool {
367        matches!(
368            self,
369            Self::Yuv420p10le
370                | Self::Yuv422p10le
371                | Self::Yuv444p10le
372                | Self::Yuva444p10le
373                | Self::P010le
374                | Self::Gbrpf32le
375        )
376    }
377
378    /// Returns the bit depth per component.
379    ///
380    /// Most formats use 8 bits per component, while high bit depth
381    /// formats use 10 bits.
382    ///
383    /// # Examples
384    ///
385    /// ```
386    /// use ff_format::PixelFormat;
387    ///
388    /// assert_eq!(PixelFormat::Rgba.bit_depth(), 8);
389    /// assert_eq!(PixelFormat::Yuv420p10le.bit_depth(), 10);
390    /// ```
391    #[must_use]
392    pub const fn bit_depth(&self) -> usize {
393        match self {
394            Self::Yuv420p10le
395            | Self::Yuv422p10le
396            | Self::Yuv444p10le
397            | Self::Yuva444p10le
398            | Self::P010le => 10,
399            Self::Gbrpf32le => 32,
400            // All other formats including unknown are 8-bit
401            _ => 8,
402        }
403    }
404}
405
406impl fmt::Display for PixelFormat {
407    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
408        write!(f, "{}", self.name())
409    }
410}
411
412impl Default for PixelFormat {
413    /// Returns the default pixel format.
414    ///
415    /// The default is [`PixelFormat::Yuv420p`] as it's the most common
416    /// format used in video encoding.
417    fn default() -> Self {
418        Self::Yuv420p
419    }
420}
421
422#[cfg(test)]
423mod tests {
424    use super::*;
425
426    #[test]
427    fn test_format_names() {
428        assert_eq!(PixelFormat::Rgb24.name(), "rgb24");
429        assert_eq!(PixelFormat::Rgba.name(), "rgba");
430        assert_eq!(PixelFormat::Bgr24.name(), "bgr24");
431        assert_eq!(PixelFormat::Bgra.name(), "bgra");
432        assert_eq!(PixelFormat::Yuv420p.name(), "yuv420p");
433        assert_eq!(PixelFormat::Yuv422p.name(), "yuv422p");
434        assert_eq!(PixelFormat::Yuv444p.name(), "yuv444p");
435        assert_eq!(PixelFormat::Nv12.name(), "nv12");
436        assert_eq!(PixelFormat::Nv21.name(), "nv21");
437        assert_eq!(PixelFormat::Yuv420p10le.name(), "yuv420p10le");
438        assert_eq!(PixelFormat::P010le.name(), "p010le");
439        assert_eq!(PixelFormat::Gray8.name(), "gray8");
440        assert_eq!(PixelFormat::Other(999).name(), "unknown");
441    }
442
443    #[test]
444    fn test_plane_count() {
445        // Packed formats - 1 plane
446        assert_eq!(PixelFormat::Rgb24.num_planes(), 1);
447        assert_eq!(PixelFormat::Rgba.num_planes(), 1);
448        assert_eq!(PixelFormat::Bgr24.num_planes(), 1);
449        assert_eq!(PixelFormat::Bgra.num_planes(), 1);
450        assert_eq!(PixelFormat::Gray8.num_planes(), 1);
451
452        // Planar YUV - 3 planes
453        assert_eq!(PixelFormat::Yuv420p.num_planes(), 3);
454        assert_eq!(PixelFormat::Yuv422p.num_planes(), 3);
455        assert_eq!(PixelFormat::Yuv444p.num_planes(), 3);
456        assert_eq!(PixelFormat::Yuv420p10le.num_planes(), 3);
457
458        // Semi-planar - 2 planes
459        assert_eq!(PixelFormat::Nv12.num_planes(), 2);
460        assert_eq!(PixelFormat::Nv21.num_planes(), 2);
461        assert_eq!(PixelFormat::P010le.num_planes(), 2);
462
463        // plane_count is alias for num_planes
464        assert_eq!(PixelFormat::Yuv420p.plane_count(), 3);
465    }
466
467    #[test]
468    fn test_packed_vs_planar() {
469        // Packed formats
470        assert!(PixelFormat::Rgb24.is_packed());
471        assert!(PixelFormat::Rgba.is_packed());
472        assert!(PixelFormat::Bgr24.is_packed());
473        assert!(PixelFormat::Bgra.is_packed());
474        assert!(PixelFormat::Gray8.is_packed());
475        assert!(!PixelFormat::Rgb24.is_planar());
476
477        // Planar formats
478        assert!(PixelFormat::Yuv420p.is_planar());
479        assert!(PixelFormat::Yuv422p.is_planar());
480        assert!(PixelFormat::Yuv444p.is_planar());
481        assert!(PixelFormat::Nv12.is_planar());
482        assert!(PixelFormat::Nv21.is_planar());
483        assert!(!PixelFormat::Yuv420p.is_packed());
484    }
485
486    #[test]
487    fn test_has_alpha() {
488        assert!(PixelFormat::Rgba.has_alpha());
489        assert!(PixelFormat::Bgra.has_alpha());
490        assert!(!PixelFormat::Rgb24.has_alpha());
491        assert!(!PixelFormat::Bgr24.has_alpha());
492        assert!(!PixelFormat::Yuv420p.has_alpha());
493        assert!(!PixelFormat::Gray8.has_alpha());
494    }
495
496    #[test]
497    fn test_is_rgb() {
498        assert!(PixelFormat::Rgb24.is_rgb());
499        assert!(PixelFormat::Rgba.is_rgb());
500        assert!(PixelFormat::Bgr24.is_rgb());
501        assert!(PixelFormat::Bgra.is_rgb());
502        assert!(!PixelFormat::Yuv420p.is_rgb());
503        assert!(!PixelFormat::Nv12.is_rgb());
504        assert!(!PixelFormat::Gray8.is_rgb());
505    }
506
507    #[test]
508    fn test_is_yuv() {
509        assert!(PixelFormat::Yuv420p.is_yuv());
510        assert!(PixelFormat::Yuv422p.is_yuv());
511        assert!(PixelFormat::Yuv444p.is_yuv());
512        assert!(PixelFormat::Nv12.is_yuv());
513        assert!(PixelFormat::Nv21.is_yuv());
514        assert!(PixelFormat::Yuv420p10le.is_yuv());
515        assert!(PixelFormat::P010le.is_yuv());
516        assert!(!PixelFormat::Rgb24.is_yuv());
517        assert!(!PixelFormat::Rgba.is_yuv());
518        assert!(!PixelFormat::Gray8.is_yuv());
519    }
520
521    #[test]
522    fn test_bits_per_pixel() {
523        // Packed formats have defined bits per pixel
524        assert_eq!(PixelFormat::Rgb24.bits_per_pixel(), Some(24));
525        assert_eq!(PixelFormat::Bgr24.bits_per_pixel(), Some(24));
526        assert_eq!(PixelFormat::Rgba.bits_per_pixel(), Some(32));
527        assert_eq!(PixelFormat::Bgra.bits_per_pixel(), Some(32));
528        assert_eq!(PixelFormat::Gray8.bits_per_pixel(), Some(8));
529
530        // Planar formats don't have simple bits per pixel
531        assert_eq!(PixelFormat::Yuv420p.bits_per_pixel(), None);
532        assert_eq!(PixelFormat::Nv12.bits_per_pixel(), None);
533    }
534
535    #[test]
536    fn test_bytes_per_pixel() {
537        // Packed formats
538        assert_eq!(PixelFormat::Rgb24.bytes_per_pixel(), 3);
539        assert_eq!(PixelFormat::Bgr24.bytes_per_pixel(), 3);
540        assert_eq!(PixelFormat::Rgba.bytes_per_pixel(), 4);
541        assert_eq!(PixelFormat::Bgra.bytes_per_pixel(), 4);
542        assert_eq!(PixelFormat::Gray8.bytes_per_pixel(), 1);
543
544        // YUV 4:2:0 - 1.5 bytes average, rounded to 2
545        assert_eq!(PixelFormat::Yuv420p.bytes_per_pixel(), 2);
546        assert_eq!(PixelFormat::Nv12.bytes_per_pixel(), 2);
547        assert_eq!(PixelFormat::Nv21.bytes_per_pixel(), 2);
548
549        // YUV 4:2:2 - 2 bytes
550        assert_eq!(PixelFormat::Yuv422p.bytes_per_pixel(), 2);
551
552        // YUV 4:4:4 - 3 bytes
553        assert_eq!(PixelFormat::Yuv444p.bytes_per_pixel(), 3);
554
555        // High bit depth
556        assert_eq!(PixelFormat::Yuv420p10le.bytes_per_pixel(), 2);
557        assert_eq!(PixelFormat::P010le.bytes_per_pixel(), 2);
558    }
559
560    #[test]
561    fn test_high_bit_depth() {
562        assert!(PixelFormat::Yuv420p10le.is_high_bit_depth());
563        assert!(PixelFormat::P010le.is_high_bit_depth());
564        assert!(!PixelFormat::Yuv420p.is_high_bit_depth());
565        assert!(!PixelFormat::Rgba.is_high_bit_depth());
566    }
567
568    #[test]
569    fn test_bit_depth() {
570        assert_eq!(PixelFormat::Rgba.bit_depth(), 8);
571        assert_eq!(PixelFormat::Yuv420p.bit_depth(), 8);
572        assert_eq!(PixelFormat::Yuv420p10le.bit_depth(), 10);
573        assert_eq!(PixelFormat::P010le.bit_depth(), 10);
574    }
575
576    #[test]
577    fn test_display() {
578        assert_eq!(format!("{}", PixelFormat::Yuv420p), "yuv420p");
579        assert_eq!(format!("{}", PixelFormat::Rgba), "rgba");
580        assert_eq!(format!("{}", PixelFormat::Other(123)), "unknown");
581    }
582
583    #[test]
584    fn test_default() {
585        assert_eq!(PixelFormat::default(), PixelFormat::Yuv420p);
586    }
587
588    #[test]
589    fn test_debug() {
590        assert_eq!(format!("{:?}", PixelFormat::Rgba), "Rgba");
591        assert_eq!(format!("{:?}", PixelFormat::Yuv420p), "Yuv420p");
592        assert_eq!(format!("{:?}", PixelFormat::Other(42)), "Other(42)");
593    }
594
595    #[test]
596    fn test_equality_and_hash() {
597        use std::collections::HashSet;
598
599        assert_eq!(PixelFormat::Rgba, PixelFormat::Rgba);
600        assert_ne!(PixelFormat::Rgba, PixelFormat::Bgra);
601        assert_eq!(PixelFormat::Other(1), PixelFormat::Other(1));
602        assert_ne!(PixelFormat::Other(1), PixelFormat::Other(2));
603
604        // Test Hash implementation
605        let mut set = HashSet::new();
606        set.insert(PixelFormat::Rgba);
607        set.insert(PixelFormat::Yuv420p);
608        assert!(set.contains(&PixelFormat::Rgba));
609        assert!(!set.contains(&PixelFormat::Bgra));
610    }
611
612    #[test]
613    fn test_copy() {
614        let format = PixelFormat::Yuv420p;
615        let copied = format;
616        // Both original and copy are still usable (Copy semantics)
617        assert_eq!(format, copied);
618        assert_eq!(format.name(), copied.name());
619    }
620
621    #[test]
622    fn gbrpf32le_name_should_return_gbrpf32le() {
623        assert_eq!(PixelFormat::Gbrpf32le.name(), "gbrpf32le");
624    }
625
626    #[test]
627    fn gbrpf32le_should_have_three_planes() {
628        assert_eq!(PixelFormat::Gbrpf32le.num_planes(), 3);
629        assert_eq!(PixelFormat::Gbrpf32le.plane_count(), 3);
630    }
631
632    #[test]
633    fn gbrpf32le_should_be_planar_not_packed() {
634        assert!(!PixelFormat::Gbrpf32le.is_packed());
635        assert!(PixelFormat::Gbrpf32le.is_planar());
636    }
637
638    #[test]
639    fn gbrpf32le_should_be_rgb_family() {
640        assert!(PixelFormat::Gbrpf32le.is_rgb());
641        assert!(!PixelFormat::Gbrpf32le.is_yuv());
642    }
643
644    #[test]
645    fn gbrpf32le_should_not_have_alpha() {
646        assert!(!PixelFormat::Gbrpf32le.has_alpha());
647    }
648
649    #[test]
650    fn gbrpf32le_should_have_twelve_bytes_per_pixel() {
651        assert_eq!(PixelFormat::Gbrpf32le.bytes_per_pixel(), 12);
652    }
653
654    #[test]
655    fn gbrpf32le_should_be_high_bit_depth() {
656        assert!(PixelFormat::Gbrpf32le.is_high_bit_depth());
657    }
658
659    #[test]
660    fn gbrpf32le_should_have_bit_depth_32() {
661        assert_eq!(PixelFormat::Gbrpf32le.bit_depth(), 32);
662    }
663
664    #[test]
665    fn gbrpf32le_bits_per_pixel_should_be_none() {
666        assert_eq!(PixelFormat::Gbrpf32le.bits_per_pixel(), None);
667    }
668}