gamut_color/format.rs
1//! Coded-plane bit depth and chroma subsampling.
2//!
3//! These describe a codec's *coded* planes, distinct from an interleaved buffer's layout — that is
4//! the [`Pixel`](gamut_core::Pixel) vocabulary (`Rgb8`, `Rgba8`, …) in `gamut-core`. [`BitDepth`] is
5//! wired into the AV1 reconstruction; [`ChromaSubsampling`] models only `Cs444` (4:4:4) at M0, with
6//! the subsampled variants reserved for M2 (see `gamut-avif/STATUS.md`).
7
8/// Bits per sample of a coded plane.
9#[repr(u8)]
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11pub enum BitDepth {
12 /// 8 bits per sample.
13 Eight = 8,
14 /// 10 bits per sample (M2).
15 Ten = 10,
16 /// 12 bits per sample (M2).
17 Twelve = 12,
18}
19
20impl BitDepth {
21 /// Number of bits per sample.
22 #[must_use]
23 pub fn bits(self) -> u8 {
24 self as u8
25 }
26
27 /// The [`BitDepth`] for `bits` (8, 10, or 12), or `None` for any other value. The inverse of
28 /// [`BitDepth::bits`], for turning a codec's raw integer bit depth back into the typed form.
29 #[must_use]
30 pub fn from_bits(bits: u32) -> Option<Self> {
31 match bits {
32 8 => Some(BitDepth::Eight),
33 10 => Some(BitDepth::Ten),
34 12 => Some(BitDepth::Twelve),
35 _ => None,
36 }
37 }
38}
39
40/// Chroma subsampling of the coded planes (AV1 `subsampling_x` / `subsampling_y`, §5.5.2).
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
42pub enum ChromaSubsampling {
43 /// 4:4:4 — full-resolution chroma (`subsampling_x = subsampling_y = 0`). Required for identity.
44 Cs444,
45 /// 4:2:2 — horizontally halved chroma (M2).
46 Cs422,
47 /// 4:2:0 — halved in both directions (M2).
48 Cs420,
49 /// 4:0:0 — monochrome, no chroma planes (M2).
50 Cs400,
51}
52
53impl ChromaSubsampling {
54 /// Returns `(subsampling_x, subsampling_y)` as the AV1 sequence-header flags.
55 #[must_use]
56 pub fn subsampling(self) -> (u8, u8) {
57 match self {
58 ChromaSubsampling::Cs444 | ChromaSubsampling::Cs400 => (0, 0),
59 ChromaSubsampling::Cs422 => (1, 0),
60 ChromaSubsampling::Cs420 => (1, 1),
61 }
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68
69 #[test]
70 fn bit_depth_bits() {
71 assert_eq!(BitDepth::Eight.bits(), 8);
72 assert_eq!(BitDepth::Ten.bits(), 10);
73 assert_eq!(BitDepth::Twelve.bits(), 12);
74 // from_bits is the inverse of bits() for the three valid depths; other values are None.
75 for d in [BitDepth::Eight, BitDepth::Ten, BitDepth::Twelve] {
76 assert_eq!(BitDepth::from_bits(u32::from(d.bits())), Some(d));
77 }
78 assert_eq!(BitDepth::from_bits(16), None);
79 assert_eq!(BitDepth::from_bits(0), None);
80 }
81
82 #[test]
83 fn subsampling_flags() {
84 assert_eq!(ChromaSubsampling::Cs444.subsampling(), (0, 0));
85 assert_eq!(ChromaSubsampling::Cs420.subsampling(), (1, 1));
86 assert_eq!(ChromaSubsampling::Cs422.subsampling(), (1, 0));
87 }
88}