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