oxideav-pixfmt 0.1.11

Pure-Rust pixel-format conversions, palette quantisation, and dithering — the oxideav framework's shared pixfmt layer
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
//! Per-format metadata: subsampling, planes, and bit depth.
//!
//! Callers that need to allocate or walk plane strides for a given
//! [`PixelFormat`] can look the format up here instead of open-coding
//! the decision tree.

use oxideav_core::PixelFormat;

/// Compact description of a pixel format's layout.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct FormatInfo {
    /// Component bit depth (before packing). 8 for Rgb24, 16 for Gray16Le,
    /// 10 for Yuv420P10Le, …
    pub bit_depth: u8,
    /// Number of distinct planes — matches [`PixelFormat::plane_count`].
    pub planes: u8,
    /// Chroma horizontal subsampling factor (1 = no subsample). 2 for
    /// 4:2:x, 1 for 4:4:4 / non-YUV.
    pub chroma_w_sub: u8,
    /// Chroma vertical subsampling factor (1 = no subsample). 2 for
    /// 4:2:0 / 4:4:0, 1 for 4:2:2 / 4:4:4.
    pub chroma_h_sub: u8,
    /// True for any planar YUV-style layout.
    pub is_planar: bool,
    /// True when alpha is carried as part of the format (explicit or
    /// through a separate plane).
    pub has_alpha: bool,
    /// True for `Pal8`.
    pub is_palette: bool,
}

impl FormatInfo {
    /// Look up static metadata for `fmt`.
    pub const fn of(fmt: PixelFormat) -> Self {
        use PixelFormat as P;
        match fmt {
            // 8-bit YUV planar
            P::Yuv420P | P::YuvJ420P => Self::yuv(8, 2, 2),
            P::Yuv422P | P::YuvJ422P => Self::yuv(8, 2, 1),
            P::Yuv444P | P::YuvJ444P => Self::yuv(8, 1, 1),
            // 4:1:1 — luma at full res, chroma horizontally subsampled by 4.
            P::Yuv411P => Self::yuv(8, 4, 1),
            P::Yuv420P10Le => Self::yuv(10, 2, 2),
            P::Yuv422P10Le => Self::yuv(10, 2, 1),
            P::Yuv444P10Le => Self::yuv(10, 1, 1),
            P::Yuv420P12Le => Self::yuv(12, 2, 2),
            P::Yuv422P12Le => Self::yuv(12, 2, 1),
            P::Yuv444P12Le => Self::yuv(12, 1, 1),
            // 16-bit planar YUV — same three-plane LE-word layout as the
            // 10/12-bit variants, but ALL 16 bits of every word are
            // significant (full-scale is 65535, per the oxideav-core
            // variant docs). `bit_depth` therefore reports 16.
            P::Yuv420P16Le => Self::yuv(16, 2, 2),
            P::Yuv422P16Le => Self::yuv(16, 2, 1),
            P::Yuv444P16Le => Self::yuv(16, 1, 1),
            // 4:4:0 (core 0.1.35) — full-width, half-height chroma: the
            // transpose of 4:2:2. Same depth ladder as the other planar
            // samplings (8-bit bytes; 10/12 significant low bits of a
            // LE word; full-scale 16).
            P::Yuv440P => Self::yuv(8, 1, 2),
            P::Yuv440P10Le => Self::yuv(10, 1, 2),
            P::Yuv440P12Le => Self::yuv(12, 1, 2),
            P::Yuv440P16Le => Self::yuv(16, 1, 2),
            // Planar GBR / GBRA — 3-or-4 planes at 4:4:4 sampling, alpha
            // only on the `Gbrap*` variants. Each sample is stored as a
            // 16-bit LE word with the high bits zero; the bit-depth field
            // carries the *significant* bit count, matching how the
            // 10/12-bit YUV variants report theirs.
            P::Gbrp10Le => Self::gbr(10, false),
            P::Gbrap10Le => Self::gbr(10, true),
            P::Gbrp12Le => Self::gbr(12, false),
            P::Gbrap12Le => Self::gbr(12, true),
            P::Gbrp14Le => Self::gbr(14, false),
            P::Gbrap14Le => Self::gbr(14, true),
            // GBR(A) depth-ladder ends (core 0.1.33). `Gbrp8` stores one
            // byte per sample (all 8 bits significant); the 16-bit pair
            // stores LE words with ALL 16 bits significant (full-scale
            // 65535, matching the `Yuv*P16Le` convention).
            P::Gbrp8 => Self::gbr(8, false),
            P::Gbrp16Le => Self::gbr(16, false),
            P::Gbrap16Le => Self::gbr(16, true),
            // 8-bit planar GBR + alpha (core 0.1.34): byte samples, all
            // 8 bits significant, alpha as full-resolution plane 3 —
            // the alpha-carrying companion completing the GBR(A) ladder
            // at every depth.
            P::Gbrap8 => Self::gbr(8, true),
            // 8-bit planar YUV + a full-resolution alpha plane (plane 3
            // is always `w × h` regardless of the chroma subsampling).
            P::Yuva420P => Self::yuva(2, 2),
            P::Yuva422P => Self::yuva(2, 1),
            P::Yuva444P => Self::yuva(1, 1),
            // Deep planar YUV + full-resolution alpha: same 4-plane
            // layout as the 8-bit `Yuva*` trio, but every sample —
            // alpha included — is a 16-bit LE word carrying `bit_depth`
            // significant low bits (all 16 for the `P16Le` variants,
            // full-scale 65535), per the oxideav-core variant docs.
            P::Yuva422P10Le => Self::yuva_deep(10, 2, 1),
            P::Yuva422P12Le => Self::yuva_deep(12, 2, 1),
            P::Yuva422P16Le => Self::yuva_deep(16, 2, 1),
            P::Yuva444P10Le => Self::yuva_deep(10, 1, 1),
            P::Yuva444P12Le => Self::yuva_deep(12, 1, 1),
            P::Yuva444P16Le => Self::yuva_deep(16, 1, 1),
            // Deep Yuva at the remaining 4:2:0 siting (core 0.1.33) —
            // same 4-plane layout, chroma on the 2×2 grid, alpha always
            // full resolution.
            P::Yuva420P10Le => Self::yuva_deep(10, 2, 2),
            P::Yuva420P12Le => Self::yuva_deep(12, 2, 2),
            P::Yuva420P16Le => Self::yuva_deep(16, 2, 2),
            P::Nv12 | P::Nv21 => Self {
                bit_depth: 8,
                planes: 2,
                chroma_w_sub: 2,
                chroma_h_sub: 2,
                is_planar: true,
                has_alpha: false,
                is_palette: false,
            },
            // Packed 4:2:2 — single byte-stream plane, but chroma is
            // horizontally subsampled by 2 (one U/V pair shared
            // between two luma samples), matching the field doc that
            // says "2 for 4:2:x".
            P::Yuyv422 | P::Uyvy422 => Self {
                bit_depth: 8,
                planes: 1,
                chroma_w_sub: 2,
                chroma_h_sub: 1,
                is_planar: false,
                has_alpha: false,
                is_palette: false,
            },
            // RGB family
            P::Rgb24 | P::Bgr24 => Self::packed(8, false),
            P::Rgba | P::Bgra | P::Argb | P::Abgr => Self::packed(8, true),
            // CMYK — 4 components packed 8-bit, no alpha. The inverted
            // variant (core 0.1.34) shares the byte layout exactly and
            // differs only in the ink-value convention (0 = full ink),
            // which is a sample-semantics matter, not a layout one.
            P::Cmyk | P::CmykInverted => Self::packed(8, false),
            P::Rgb48Le => Self::packed(16, false),
            P::Rgba64Le => Self::packed(16, true),
            // Gray
            P::Gray8 => Self::packed(8, false),
            P::Gray16Le => Self::packed(16, false),
            P::Gray10Le => Self::packed(10, false),
            P::Gray12Le => Self::packed(12, false),
            P::Ya8 => Self::packed(8, true),
            // Packed 16-bit gray + alpha (core 0.1.34): interleaved Y
            // then A, each a LE 16-bit word with ALL 16 bits
            // significant (full-scale 65535, the `Gray16Le`
            // convention).
            P::Ya16Le => Self::packed(16, true),
            // Scene-referred 32-bit float (core 0.1.35): binary32 LE
            // words, one per sample, linear light with no integer
            // full-scale. `bit_depth` reports the 32-bit word width so
            // the "deeper than 8" pivot policies treat the family as
            // deep carriage.
            P::GrayF32Le => Self::packed(32, false),
            P::RgbF32Le => Self::packed(32, false),
            P::RgbaF32Le => Self::packed(32, true),
            P::GbrpF32Le => Self::gbr(32, false),
            P::GbrapF32Le => Self::gbr(32, true),
            P::MonoBlack | P::MonoWhite => Self {
                bit_depth: 1,
                planes: 1,
                chroma_w_sub: 1,
                chroma_h_sub: 1,
                is_planar: false,
                has_alpha: false,
                is_palette: false,
            },
            // Palette
            P::Pal8 => Self {
                bit_depth: 8,
                planes: 1,
                chroma_w_sub: 1,
                chroma_h_sub: 1,
                is_planar: false,
                has_alpha: false,
                is_palette: true,
            },
            // The enum is `#[non_exhaustive]`; a future variant that
            // lands in oxideav-core without a matching arm here falls
            // back to a conservative "single packed 8-bit plane"
            // descriptor. Flagged in dev builds so we catch the gap.
            _ => {
                debug_assert!(false, "FormatInfo::of: unhandled PixelFormat variant");
                Self::packed(8, false)
            }
        }
    }

    const fn yuv(bits: u8, wsub: u8, hsub: u8) -> Self {
        Self {
            bit_depth: bits,
            planes: 3,
            chroma_w_sub: wsub,
            chroma_h_sub: hsub,
            is_planar: true,
            has_alpha: false,
            is_palette: false,
        }
    }

    /// 8-bit planar YUV + alpha descriptor: 4 planes (Y, U, V, A) where
    /// the alpha plane is always full resolution and only U/V follow the
    /// chroma subsampling factors.
    const fn yuva(wsub: u8, hsub: u8) -> Self {
        Self {
            bit_depth: 8,
            planes: 4,
            chroma_w_sub: wsub,
            chroma_h_sub: hsub,
            is_planar: true,
            has_alpha: true,
            is_palette: false,
        }
    }

    /// Deep planar YUV + alpha descriptor: 4 planes (Y, U, V, A), each
    /// sample a 16-bit LE word with `bits` significant low bits. The
    /// alpha plane is always full resolution; only U/V follow the
    /// chroma subsampling factors.
    const fn yuva_deep(bits: u8, wsub: u8, hsub: u8) -> Self {
        Self {
            bit_depth: bits,
            planes: 4,
            chroma_w_sub: wsub,
            chroma_h_sub: hsub,
            is_planar: true,
            has_alpha: true,
            is_palette: false,
        }
    }

    const fn packed(bits: u8, alpha: bool) -> Self {
        Self {
            bit_depth: bits,
            planes: 1,
            chroma_w_sub: 1,
            chroma_h_sub: 1,
            is_planar: false,
            has_alpha: alpha,
            is_palette: false,
        }
    }

    /// Planar GBR / GBRA descriptor: 3 planes (G, B, R) — or 4 with an
    /// extra alpha plane — all at 4:4:4 sampling, no chroma subsample.
    const fn gbr(bits: u8, alpha: bool) -> Self {
        Self {
            bit_depth: bits,
            planes: if alpha { 4 } else { 3 },
            chroma_w_sub: 1,
            chroma_h_sub: 1,
            is_planar: true,
            has_alpha: alpha,
            is_palette: false,
        }
    }

    /// Typed view of the chroma subsampling for a YUV-style format.
    ///
    /// Translates the raw `chroma_w_sub` / `chroma_h_sub` factor pair
    /// into the named 4:n:m convention the rest of the framework
    /// addresses by name — useful for callers that branch on the
    /// scheme (e.g. picking a packed-422 or NV12 dispatch path) without
    /// open-coding the `(w, h) == (2, 2)` etc. checks at every site.
    ///
    /// Returns:
    ///
    /// - [`ChromaSubsampling::None`] for formats with no chroma plane
    ///   (RGB family, grayscale, mono, palette, packed CMYK, packed
    ///   GBR(A) — anything where `chroma_w_sub == chroma_h_sub == 1`
    ///   and `is_planar == false`, or the planar GBR(A) variants where
    ///   the three colour planes are co-sited at 4:4:4 but the format
    ///   is RGB rather than YUV).
    /// - [`ChromaSubsampling::C444`] for 1×1 chroma siting on a YUV
    ///   carrier (`Yuv444P*`, `YuvJ444P`, `Yuva444P`).
    /// - [`ChromaSubsampling::C422`] for 2×1 chroma siting
    ///   (`Yuv422P*`, `YuvJ422P`, `Yuva422P`, `Yuyv422`, `Uyvy422`).
    /// - [`ChromaSubsampling::C420`] for 2×2 chroma siting
    ///   (`Yuv420P*`, `YuvJ420P`, `Nv12`, `Nv21`, `Yuva420P`).
    /// - [`ChromaSubsampling::C411`] for 4×1 chroma siting
    ///   (`Yuv411P` — native NTSC DV-25 layout).
    /// - [`ChromaSubsampling::C440`] for 1×2 chroma siting
    ///   (`Yuv440P*` — full-width, half-height chroma).
    /// - [`ChromaSubsampling::Other`] for any future YUV variant whose
    ///   `(w, h)` pair this method doesn't yet name. Lets callers
    ///   detect "subsampled but not one of the four common schemes"
    ///   without losing the raw factors carried on the `FormatInfo`.
    pub const fn chroma_subsampling(&self) -> ChromaSubsampling {
        // Palette indices and any RGB/grayscale carrier with no chroma
        // subsampling map to None.
        if self.is_palette {
            return ChromaSubsampling::None;
        }
        // A chroma-less carrier shows up as (1, 1) on a non-planar
        // format: RGB / grayscale / mono / packed CMYK. Planar layouts
        // with (1, 1) are either YUV 4:4:4 or planar GBR(A) — both
        // collapse to C444 below (callers that need to distinguish
        // YUV-vs-GBR look at PixelFormat directly).
        if self.chroma_w_sub == 1 && self.chroma_h_sub == 1 && !self.is_planar {
            return ChromaSubsampling::None;
        }
        if self.chroma_w_sub == 1 && self.chroma_h_sub == 1 {
            // YUV 4:4:4 has 3 planes and no alpha (or 4 with alpha on
            // a future variant); planar GBR also has 3-or-4. The
            // difference is whether this is a YUV carrier. We can't
            // tell that from `FormatInfo` alone, so we map both 4:4:4
            // planar layouts to `C444` here — callers that need to
            // distinguish "luma+chroma 4:4:4" from "GBR 4:4:4" should
            // branch on the `PixelFormat` directly. This matches how
            // the rest of the conversion surface treats them: the
            // chroma-subsample factor pair is the conservative answer.
            return ChromaSubsampling::C444;
        }
        match (self.chroma_w_sub, self.chroma_h_sub) {
            (2, 2) => ChromaSubsampling::C420,
            (2, 1) => ChromaSubsampling::C422,
            (4, 1) => ChromaSubsampling::C411,
            (1, 2) => ChromaSubsampling::C440,
            _ => ChromaSubsampling::Other,
        }
    }

    /// True when the format carries chroma planes at less than full
    /// luma resolution (`C422`, `C420`, `C411`, `C440`, or `Other`). Sugar
    /// over [`Self::chroma_subsampling`] for the common
    /// "is this losing chroma detail relative to 4:4:4?" predicate.
    pub const fn is_chroma_subsampled(&self) -> bool {
        matches!(
            self.chroma_subsampling(),
            ChromaSubsampling::C422
                | ChromaSubsampling::C420
                | ChromaSubsampling::C411
                | ChromaSubsampling::C440
                | ChromaSubsampling::Other,
        )
    }
}

/// Named view of the 4:n:m chroma siting scheme on a YUV-style format.
///
/// Returned by [`FormatInfo::chroma_subsampling`]. The variants line up
/// with how the rest of the framework addresses chroma layouts by name
/// — see [`oxideav_core::PixelFormat`] for the per-variant docs.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum ChromaSubsampling {
    /// No chroma plane at all — RGB / grayscale / mono / palette /
    /// packed-CMYK carriers.
    None,
    /// 4:4:4 chroma siting (`chroma_w_sub == chroma_h_sub == 1`) on a
    /// planar carrier.
    C444,
    /// 4:2:2 chroma siting (horizontal subsample by 2, vertical full).
    C422,
    /// 4:2:0 chroma siting (horizontal AND vertical subsample by 2).
    C420,
    /// 4:1:1 chroma siting (horizontal subsample by 4, vertical full).
    /// Used by NTSC DV-25 and as a legal JPEG sampling layout.
    C411,
    /// 4:4:0 chroma siting (horizontal full, vertical subsample by 2) —
    /// the transpose of 4:2:2. A legal JPEG sampling layout (luma
    /// H=1, V=2) and a coded sampling mode of bitstreams with
    /// independent horizontal / vertical chroma decimation flags.
    C440,
    /// Any other `(chroma_w_sub, chroma_h_sub)` pair on a YUV carrier
    /// that this enum doesn't yet name. Callers that need the exact
    /// factors should read [`FormatInfo::chroma_w_sub`] and
    /// [`FormatInfo::chroma_h_sub`] directly.
    Other,
}

#[cfg(test)]
mod tests {
    use super::*;
    use oxideav_core::PixelFormat as P;

    #[test]
    fn gbr_planar_variants() {
        // 10-bit planar GBR — 3 planes, no alpha, no chroma subsample.
        let info = FormatInfo::of(P::Gbrp10Le);
        assert_eq!(info.bit_depth, 10);
        assert_eq!(info.planes, 3);
        assert_eq!(info.chroma_w_sub, 1);
        assert_eq!(info.chroma_h_sub, 1);
        assert!(info.is_planar);
        assert!(!info.has_alpha);
        assert!(!info.is_palette);

        // 10-bit planar GBR + alpha — 4 planes, alpha set.
        let info = FormatInfo::of(P::Gbrap10Le);
        assert_eq!(info.bit_depth, 10);
        assert_eq!(info.planes, 4);
        assert!(info.is_planar);
        assert!(info.has_alpha);

        // 12-bit no-alpha and alpha pair.
        let info = FormatInfo::of(P::Gbrp12Le);
        assert_eq!(info.bit_depth, 12);
        assert_eq!(info.planes, 3);
        assert!(!info.has_alpha);
        let info = FormatInfo::of(P::Gbrap12Le);
        assert_eq!(info.bit_depth, 12);
        assert_eq!(info.planes, 4);
        assert!(info.has_alpha);

        // 14-bit no-alpha and alpha pair.
        let info = FormatInfo::of(P::Gbrp14Le);
        assert_eq!(info.bit_depth, 14);
        assert_eq!(info.planes, 3);
        assert!(!info.has_alpha);
        let info = FormatInfo::of(P::Gbrap14Le);
        assert_eq!(info.bit_depth, 14);
        assert_eq!(info.planes, 4);
        assert!(info.has_alpha);

        // Ladder ends (core 0.1.33): native 8-bit and full-width 16-bit.
        let info = FormatInfo::of(P::Gbrp8);
        assert_eq!(info.bit_depth, 8);
        assert_eq!(info.planes, 3);
        assert!(info.is_planar);
        assert!(!info.has_alpha);
        let info = FormatInfo::of(P::Gbrp16Le);
        assert_eq!(info.bit_depth, 16);
        assert_eq!(info.planes, 3);
        assert!(!info.has_alpha);
        let info = FormatInfo::of(P::Gbrap16Le);
        assert_eq!(info.bit_depth, 16);
        assert_eq!(info.planes, 4);
        assert!(info.has_alpha);

        // 8-bit + alpha (core 0.1.34): the last hole in the ladder —
        // byte samples, 4 planes, alpha set.
        let info = FormatInfo::of(P::Gbrap8);
        assert_eq!(info.bit_depth, 8);
        assert_eq!(info.planes, 4);
        assert!(info.is_planar);
        assert!(info.has_alpha);
        // Layout-metadata identical to Gbrp8 except the alpha plane.
        let plain = FormatInfo::of(P::Gbrp8);
        assert_eq!(info.planes, plain.planes + 1);
        assert_eq!(info.bit_depth, plain.bit_depth);
        assert_eq!(info.chroma_w_sub, plain.chroma_w_sub);
        assert_eq!(info.chroma_h_sub, plain.chroma_h_sub);
    }

    #[test]
    fn ya16le_packed_deep_gray_alpha() {
        // Packed 16-bit gray + alpha (core 0.1.34): single interleaved
        // plane of LE16 words, all 16 bits significant, alpha carried.
        let info = FormatInfo::of(P::Ya16Le);
        assert_eq!(info.bit_depth, 16);
        assert_eq!(info.planes, 1);
        assert!(!info.is_planar);
        assert!(info.has_alpha);
        assert!(!info.is_palette);
        assert_eq!(info.chroma_subsampling(), ChromaSubsampling::None);
        // Mirrors Ya8 in every dimension except depth.
        let ya8 = FormatInfo::of(P::Ya8);
        assert_eq!(info.planes, ya8.planes);
        assert_eq!(info.has_alpha, ya8.has_alpha);
        assert_eq!(ya8.bit_depth, 8);
        // Core agreement.
        assert_eq!(info.planes as usize, P::Ya16Le.plane_count());
        assert_eq!(info.has_alpha, P::Ya16Le.has_alpha());
        assert_eq!(info.is_planar, P::Ya16Le.is_planar());
    }

    #[test]
    fn cmyk_inverted_mirrors_cmyk_layout() {
        // CmykInverted (core 0.1.34) is metadata-identical to Cmyk: the
        // inversion is a sample-value convention, not a layout change.
        let reg = FormatInfo::of(P::Cmyk);
        let inv = FormatInfo::of(P::CmykInverted);
        assert_eq!(reg, inv);
        assert_eq!(inv.bit_depth, 8);
        assert_eq!(inv.planes, 1);
        assert!(!inv.is_planar);
        assert!(!inv.has_alpha);
        assert_eq!(inv.chroma_subsampling(), ChromaSubsampling::None);
        // Core agreement.
        assert_eq!(inv.planes as usize, P::CmykInverted.plane_count());
        assert_eq!(inv.has_alpha, P::CmykInverted.has_alpha());
        assert_eq!(inv.is_planar, P::CmykInverted.is_planar());
    }

    #[test]
    fn gbr_plane_count_matches_pixel_format() {
        // FormatInfo::planes must agree with the canonical
        // PixelFormat::plane_count for every GBR variant.
        for fmt in [
            P::Gbrp8,
            P::Gbrap8,
            P::Gbrp10Le,
            P::Gbrap10Le,
            P::Gbrp12Le,
            P::Gbrap12Le,
            P::Gbrp14Le,
            P::Gbrap14Le,
            P::Gbrp16Le,
            P::Gbrap16Le,
        ] {
            let info = FormatInfo::of(fmt);
            assert_eq!(
                info.planes as usize,
                fmt.plane_count(),
                "plane count mismatch for {fmt:?}",
            );
            assert_eq!(
                info.has_alpha,
                fmt.has_alpha(),
                "alpha flag mismatch for {fmt:?}",
            );
            assert_eq!(
                info.is_planar,
                fmt.is_planar(),
                "planar flag mismatch for {fmt:?}",
            );
        }
    }

    #[test]
    fn yuv_12bit_422_and_444_variants() {
        // 12-bit 4:2:2 — 3 planes, horizontal chroma subsample by 2.
        let info = FormatInfo::of(P::Yuv422P12Le);
        assert_eq!(info.bit_depth, 12);
        assert_eq!(info.planes, 3);
        assert_eq!(info.chroma_w_sub, 2);
        assert_eq!(info.chroma_h_sub, 1);
        assert!(info.is_planar);
        assert!(!info.has_alpha);
        assert!(!info.is_palette);

        // 12-bit 4:4:4 — 3 planes, no chroma subsample.
        let info = FormatInfo::of(P::Yuv444P12Le);
        assert_eq!(info.bit_depth, 12);
        assert_eq!(info.planes, 3);
        assert_eq!(info.chroma_w_sub, 1);
        assert_eq!(info.chroma_h_sub, 1);
        assert!(info.is_planar);
        assert!(!info.has_alpha);
    }

    #[test]
    fn chroma_subsampling_named_view() {
        use ChromaSubsampling as C;

        // 4:2:0 — both 8-bit and full-range, planar and semi-planar
        // carriers map to C420.
        for fmt in [P::Yuv420P, P::YuvJ420P, P::Nv12, P::Nv21, P::Yuva420P] {
            assert_eq!(
                FormatInfo::of(fmt).chroma_subsampling(),
                C::C420,
                "expected C420 for {fmt:?}",
            );
        }
        // 10/12/16-bit 4:2:0 mirror the 8-bit answer — bit depth is
        // orthogonal to chroma siting. The deep 4:2:0 Yuva trio (core
        // 0.1.33) shares the grid.
        for fmt in [
            P::Yuv420P10Le,
            P::Yuv420P12Le,
            P::Yuv420P16Le,
            P::Yuva420P10Le,
            P::Yuva420P12Le,
            P::Yuva420P16Le,
        ] {
            assert_eq!(FormatInfo::of(fmt).chroma_subsampling(), C::C420);
        }

        // 4:2:2 — planar + packed carriers, all bit depths, plus the
        // alpha-bearing planar layouts (8-bit and deep).
        for fmt in [
            P::Yuv422P,
            P::YuvJ422P,
            P::Yuyv422,
            P::Uyvy422,
            P::Yuv422P10Le,
            P::Yuv422P12Le,
            P::Yuv422P16Le,
            P::Yuva422P,
            P::Yuva422P10Le,
            P::Yuva422P12Le,
            P::Yuva422P16Le,
        ] {
            assert_eq!(
                FormatInfo::of(fmt).chroma_subsampling(),
                C::C422,
                "expected C422 for {fmt:?}",
            );
        }

        // 4:4:4 — all bit depths. Planar GBR(A) lands on C444 too
        // because its sample siting is 4:4:4 — callers that need to
        // distinguish YUV-vs-GBR look at PixelFormat directly.
        for fmt in [
            P::Yuv444P,
            P::YuvJ444P,
            P::Yuv444P10Le,
            P::Yuv444P12Le,
            P::Yuv444P16Le,
            P::Yuva444P,
            P::Yuva444P10Le,
            P::Yuva444P12Le,
            P::Yuva444P16Le,
            P::Gbrp8,
            P::Gbrap8,
            P::Gbrp10Le,
            P::Gbrap10Le,
            P::Gbrp12Le,
            P::Gbrap12Le,
            P::Gbrp14Le,
            P::Gbrap14Le,
            P::Gbrp16Le,
            P::Gbrap16Le,
        ] {
            assert_eq!(
                FormatInfo::of(fmt).chroma_subsampling(),
                C::C444,
                "expected C444 for {fmt:?}",
            );
        }

        // 4:1:1 — NTSC DV-25 layout.
        assert_eq!(FormatInfo::of(P::Yuv411P).chroma_subsampling(), C::C411,);

        // Chroma-less carriers — RGB family, grayscale, mono, palette,
        // packed CMYK. All map to None and `is_chroma_subsampled`
        // returns false.
        for fmt in [
            P::Rgb24,
            P::Bgr24,
            P::Rgba,
            P::Bgra,
            P::Argb,
            P::Abgr,
            P::Rgb48Le,
            P::Rgba64Le,
            P::Gray8,
            P::Gray16Le,
            P::Gray10Le,
            P::Gray12Le,
            P::Ya8,
            P::Ya16Le,
            P::MonoBlack,
            P::MonoWhite,
            P::Pal8,
            P::Cmyk,
            P::CmykInverted,
        ] {
            let info = FormatInfo::of(fmt);
            assert_eq!(
                info.chroma_subsampling(),
                C::None,
                "expected None for {fmt:?}",
            );
            assert!(
                !info.is_chroma_subsampled(),
                "{fmt:?} unexpectedly flagged as chroma-subsampled",
            );
        }

        // `is_chroma_subsampled` discriminates C422 / C420 / C411
        // (true) from C444 / None (false).
        assert!(FormatInfo::of(P::Yuv420P).is_chroma_subsampled());
        assert!(FormatInfo::of(P::Yuv422P).is_chroma_subsampled());
        assert!(FormatInfo::of(P::Yuv411P).is_chroma_subsampled());
        assert!(FormatInfo::of(P::Yuv440P).is_chroma_subsampled());
        assert!(!FormatInfo::of(P::Yuv444P).is_chroma_subsampled());
        assert!(!FormatInfo::of(P::Gbrp10Le).is_chroma_subsampled());
    }

    #[test]
    fn chroma_subsampling_matches_raw_factors() {
        // Every variant the static `FormatInfo::of` table covers must
        // line up: chroma_subsampling()'s decision agrees with the
        // raw (chroma_w_sub, chroma_h_sub) pair.
        use ChromaSubsampling as C;
        let cases: &[(P, C)] = &[
            (P::Yuv420P, C::C420),
            (P::Yuv422P, C::C422),
            (P::Yuv444P, C::C444),
            (P::Yuv411P, C::C411),
            (P::Yuv440P, C::C440),
            (P::Yuv440P10Le, C::C440),
            (P::Yuv440P12Le, C::C440),
            (P::Yuv440P16Le, C::C440),
            (P::Nv12, C::C420),
            (P::Yuva420P, C::C420),
            (P::Rgb24, C::None),
            (P::Gray8, C::None),
            (P::Gbrp10Le, C::C444),
        ];
        for &(fmt, expected) in cases {
            let info = FormatInfo::of(fmt);
            assert_eq!(info.chroma_subsampling(), expected);
            // Sanity: factors and the named view agree.
            match expected {
                C::C420 => {
                    assert_eq!(info.chroma_w_sub, 2);
                    assert_eq!(info.chroma_h_sub, 2);
                }
                C::C422 => {
                    assert_eq!(info.chroma_w_sub, 2);
                    assert_eq!(info.chroma_h_sub, 1);
                }
                C::C411 => {
                    assert_eq!(info.chroma_w_sub, 4);
                    assert_eq!(info.chroma_h_sub, 1);
                }
                C::C440 => {
                    assert_eq!(info.chroma_w_sub, 1);
                    assert_eq!(info.chroma_h_sub, 2);
                    assert!(info.is_chroma_subsampled());
                }
                C::C444 => {
                    assert_eq!(info.chroma_w_sub, 1);
                    assert_eq!(info.chroma_h_sub, 1);
                }
                C::None => {
                    // For chroma-less carriers the factors are also
                    // trivially 1×1.
                    assert_eq!(info.chroma_w_sub, 1);
                    assert_eq!(info.chroma_h_sub, 1);
                }
                C::Other => {}
            }
        }
    }

    #[test]
    fn yuv_16bit_mirrors_10bit_layout_at_full_width() {
        // The 16-bit trio must mirror the 10-bit layouts in every
        // dimension except bit_depth, which reports the full 16 (all 16
        // bits of every LE word are significant — full-scale 65535).
        let pairs = [
            (P::Yuv420P10Le, P::Yuv420P16Le),
            (P::Yuv422P10Le, P::Yuv422P16Le),
            (P::Yuv444P10Le, P::Yuv444P16Le),
        ];
        for (a, b) in pairs {
            let ia = FormatInfo::of(a);
            let ib = FormatInfo::of(b);
            assert_eq!(ia.planes, ib.planes);
            assert_eq!(ia.chroma_w_sub, ib.chroma_w_sub);
            assert_eq!(ia.chroma_h_sub, ib.chroma_h_sub);
            assert_eq!(ia.is_planar, ib.is_planar);
            assert_eq!(ia.has_alpha, ib.has_alpha);
            assert_eq!(ib.bit_depth, 16);
        }
        // Core agreement: plane count / alpha / planar flags.
        for fmt in [P::Yuv420P16Le, P::Yuv422P16Le, P::Yuv444P16Le] {
            let info = FormatInfo::of(fmt);
            assert_eq!(info.planes as usize, fmt.plane_count());
            assert_eq!(info.has_alpha, fmt.has_alpha());
            assert_eq!(info.is_planar, fmt.is_planar());
        }
    }

    #[test]
    fn yuva_family_shares_layout_with_alpha_less_siblings() {
        // Each Yuva* variant mirrors its Yuv* sibling's chroma grid and
        // adds exactly one (full-resolution, 8-bit) alpha plane.
        let pairs = [
            (P::Yuv420P, P::Yuva420P),
            (P::Yuv422P, P::Yuva422P),
            (P::Yuv444P, P::Yuva444P),
        ];
        for (yuv, yuva) in pairs {
            let iy = FormatInfo::of(yuv);
            let ia = FormatInfo::of(yuva);
            assert_eq!(ia.bit_depth, 8);
            assert_eq!(ia.planes, iy.planes + 1, "{yuva:?}");
            assert_eq!(ia.chroma_w_sub, iy.chroma_w_sub);
            assert_eq!(ia.chroma_h_sub, iy.chroma_h_sub);
            assert!(ia.is_planar);
            assert!(ia.has_alpha);
            assert!(!ia.is_palette);
            // Core agreement.
            assert_eq!(ia.planes as usize, yuva.plane_count());
            assert_eq!(ia.has_alpha, yuva.has_alpha());
            assert_eq!(ia.is_planar, yuva.is_planar());
        }
    }

    #[test]
    fn deep_yuva_mirrors_alpha_less_sibling_plus_alpha_plane() {
        // Each deep Yuva* variant mirrors its alpha-less deep sibling's
        // chroma grid and bit depth, adding exactly one full-resolution
        // alpha plane stored at the same depth.
        let pairs = [
            (P::Yuv420P10Le, P::Yuva420P10Le),
            (P::Yuv420P12Le, P::Yuva420P12Le),
            (P::Yuv420P16Le, P::Yuva420P16Le),
            (P::Yuv422P10Le, P::Yuva422P10Le),
            (P::Yuv422P12Le, P::Yuva422P12Le),
            (P::Yuv422P16Le, P::Yuva422P16Le),
            (P::Yuv444P10Le, P::Yuva444P10Le),
            (P::Yuv444P12Le, P::Yuva444P12Le),
            (P::Yuv444P16Le, P::Yuva444P16Le),
        ];
        for (yuv, yuva) in pairs {
            let iy = FormatInfo::of(yuv);
            let ia = FormatInfo::of(yuva);
            assert_eq!(ia.bit_depth, iy.bit_depth, "{yuva:?}");
            assert_eq!(ia.planes, iy.planes + 1, "{yuva:?}");
            assert_eq!(ia.chroma_w_sub, iy.chroma_w_sub);
            assert_eq!(ia.chroma_h_sub, iy.chroma_h_sub);
            assert!(ia.is_planar);
            assert!(ia.has_alpha);
            assert!(!ia.is_palette);
            // Core agreement: plane count / alpha / planar flags.
            assert_eq!(ia.planes as usize, yuva.plane_count());
            assert_eq!(ia.has_alpha, yuva.has_alpha());
            assert_eq!(ia.is_planar, yuva.is_planar());
        }
    }

    #[test]
    fn deep_yuva_mirrors_8bit_yuva_grid_at_depth() {
        // The deep Yuva variants (4:2:0 / 4:2:2 / 4:4:4) share their
        // chroma grid with the 8-bit Yuva sibling — depth is orthogonal
        // to siting.
        for (eight, deep) in [
            (P::Yuva420P, P::Yuva420P10Le),
            (P::Yuva420P, P::Yuva420P12Le),
            (P::Yuva420P, P::Yuva420P16Le),
            (P::Yuva422P, P::Yuva422P10Le),
            (P::Yuva422P, P::Yuva422P12Le),
            (P::Yuva422P, P::Yuva422P16Le),
            (P::Yuva444P, P::Yuva444P10Le),
            (P::Yuva444P, P::Yuva444P12Le),
            (P::Yuva444P, P::Yuva444P16Le),
        ] {
            let i8 = FormatInfo::of(eight);
            let id = FormatInfo::of(deep);
            assert_eq!(i8.chroma_w_sub, id.chroma_w_sub, "{deep:?}");
            assert_eq!(i8.chroma_h_sub, id.chroma_h_sub, "{deep:?}");
            assert_eq!(i8.planes, id.planes, "{deep:?}");
            assert!(id.bit_depth > 8, "{deep:?}");
        }
    }

    #[test]
    fn yuv_12bit_mirrors_10bit_layout() {
        // 12-bit YUV variants must mirror the 10-bit equivalents in
        // every dimension except bit_depth — they only differ in
        // sample width.
        let pairs = [
            (P::Yuv420P10Le, P::Yuv420P12Le),
            (P::Yuv422P10Le, P::Yuv422P12Le),
            (P::Yuv444P10Le, P::Yuv444P12Le),
        ];
        for (a, b) in pairs {
            let ia = FormatInfo::of(a);
            let ib = FormatInfo::of(b);
            assert_eq!(ia.planes, ib.planes);
            assert_eq!(ia.chroma_w_sub, ib.chroma_w_sub);
            assert_eq!(ia.chroma_h_sub, ib.chroma_h_sub);
            assert_eq!(ia.is_planar, ib.is_planar);
            assert_eq!(ia.has_alpha, ib.has_alpha);
            assert_eq!(ia.is_palette, ib.is_palette);
            assert_eq!(ia.bit_depth, 10);
            assert_eq!(ib.bit_depth, 12);
        }
    }
}