Skip to main content

zenjxl_decoder/api/
data_types.rs

1// Copyright (c) the JPEG XL Project Authors. All rights reserved.
2//
3// Use of this source code is governed by a BSD-style
4// license that can be found in the LICENSE file.
5
6use crate::{headers::extra_channels::ExtraChannel, image::DataTypeTag};
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq)]
9pub enum JxlColorType {
10    Grayscale,
11    GrayscaleAlpha,
12    Rgb,
13    Rgba,
14    Bgr,
15    Bgra,
16}
17
18impl JxlColorType {
19    pub fn has_alpha(&self) -> bool {
20        match self {
21            Self::Grayscale => false,
22            Self::GrayscaleAlpha => true,
23            Self::Rgb | Self::Bgr => false,
24            Self::Rgba | Self::Bgra => true,
25        }
26    }
27    pub fn samples_per_pixel(&self) -> usize {
28        match self {
29            Self::Grayscale => 1,
30            Self::GrayscaleAlpha => 2,
31            Self::Rgb | Self::Bgr => 3,
32            Self::Rgba | Self::Bgra => 4,
33        }
34    }
35    pub fn is_grayscale(&self) -> bool {
36        match self {
37            Self::Grayscale => true,
38            Self::GrayscaleAlpha => true,
39            Self::Rgb | Self::Bgr => false,
40            Self::Rgba | Self::Bgra => false,
41        }
42    }
43    pub fn add_alpha(&self) -> Self {
44        match self {
45            Self::Grayscale | Self::GrayscaleAlpha => Self::GrayscaleAlpha,
46            Self::Rgb | Self::Rgba => Self::Rgba,
47            Self::Bgr | Self::Bgra => Self::Bgra,
48        }
49    }
50}
51
52/// VarDCT quantizer fields recovered from a JPEG XL frame's `LfGlobal`
53/// section (the lossy quantization state).
54///
55/// These are the raw bitstream values. Mapping them to an encoder "quality"
56/// or Butteraugli distance is encoder-specific and approximate, so it lives in
57/// higher-level crates rather than the decoder.
58#[derive(Clone, Copy, Debug, PartialEq, Eq)]
59pub struct VardctQuantizer {
60    /// The frame's `global_scale` field. Larger = coarser quantization
61    /// (lower quality). Always >= 1.
62    pub global_scale: u32,
63    /// The frame's `quant_lf` field — the low-frequency quantization
64    /// multiplier. Always >= 1.
65    pub quant_lf: u32,
66}
67
68impl VardctQuantizer {
69    /// `2^16 / global_scale`: the inverse global scale. Larger = finer
70    /// quantization (higher quality). This is the objective dequantization
71    /// factor, independent of any encoder's quality mapping.
72    pub fn inv_global_scale(&self) -> f32 {
73        crate::frame::quantizer::GLOBAL_SCALE_DENOM as f32 / self.global_scale as f32
74    }
75}
76
77#[derive(Clone, Copy, Debug, PartialEq, Eq)]
78pub enum Endianness {
79    LittleEndian,
80    BigEndian,
81}
82
83impl Endianness {
84    pub fn native() -> Self {
85        #[cfg(target_endian = "little")]
86        {
87            Endianness::LittleEndian
88        }
89        #[cfg(target_endian = "big")]
90        {
91            Endianness::BigEndian
92        }
93    }
94}
95
96#[derive(Clone, Copy, Debug, PartialEq, Eq)]
97pub enum JxlDataFormat {
98    U8 {
99        bit_depth: u8,
100    },
101    U16 {
102        endianness: Endianness,
103        bit_depth: u8,
104    },
105    F16 {
106        endianness: Endianness,
107    },
108    F32 {
109        endianness: Endianness,
110    },
111}
112
113impl JxlDataFormat {
114    pub fn bytes_per_sample(&self) -> usize {
115        match self {
116            Self::U8 { .. } => 1,
117            Self::U16 { .. } | Self::F16 { .. } => 2,
118            Self::F32 { .. } => 4,
119        }
120    }
121
122    pub fn f32() -> Self {
123        Self::F32 {
124            endianness: Endianness::native(),
125        }
126    }
127
128    pub(crate) fn data_type(&self) -> DataTypeTag {
129        match self {
130            JxlDataFormat::U8 { .. } => DataTypeTag::U8,
131            JxlDataFormat::U16 { .. } => DataTypeTag::U16,
132            JxlDataFormat::F16 { .. } => DataTypeTag::F16,
133            JxlDataFormat::F32 { .. } => DataTypeTag::F32,
134        }
135    }
136
137    /// Returns the byte representation of opaque alpha (1.0) for this format.
138    pub(crate) fn opaque_alpha_bytes(&self) -> Vec<u8> {
139        match self {
140            JxlDataFormat::U8 { bit_depth } => {
141                let val = (1u16 << bit_depth) - 1;
142                vec![val as u8]
143            }
144            JxlDataFormat::U16 {
145                endianness,
146                bit_depth,
147            } => {
148                let val = (1u32 << bit_depth) - 1;
149                let val = val as u16;
150                if *endianness == Endianness::LittleEndian {
151                    val.to_le_bytes().to_vec()
152                } else {
153                    val.to_be_bytes().to_vec()
154                }
155            }
156            JxlDataFormat::F16 { endianness } => {
157                // 1.0 in f16 is 0x3C00
158                let val: u16 = 0x3C00;
159                if *endianness == Endianness::LittleEndian {
160                    val.to_le_bytes().to_vec()
161                } else {
162                    val.to_be_bytes().to_vec()
163                }
164            }
165            JxlDataFormat::F32 { endianness } => {
166                let val: f32 = 1.0;
167                if *endianness == Endianness::LittleEndian {
168                    val.to_le_bytes().to_vec()
169                } else {
170                    val.to_be_bytes().to_vec()
171                }
172            }
173        }
174    }
175}
176
177#[derive(Clone, Debug, PartialEq, Eq)]
178pub struct JxlPixelFormat {
179    pub color_type: JxlColorType,
180    // None -> ignore
181    pub color_data_format: Option<JxlDataFormat>,
182    pub extra_channel_format: Vec<Option<JxlDataFormat>>,
183}
184
185impl JxlPixelFormat {
186    /// Creates an RGBA 8-bit pixel format.
187    pub fn rgba8(num_extra_channels: usize) -> Self {
188        Self {
189            color_type: JxlColorType::Rgba,
190            color_data_format: Some(JxlDataFormat::U8 { bit_depth: 8 }),
191            extra_channel_format: vec![
192                Some(JxlDataFormat::U8 { bit_depth: 8 });
193                num_extra_channels
194            ],
195        }
196    }
197
198    /// Creates an RGBA 16-bit pixel format.
199    pub fn rgba16(num_extra_channels: usize) -> Self {
200        Self {
201            color_type: JxlColorType::Rgba,
202            color_data_format: Some(JxlDataFormat::U16 {
203                endianness: Endianness::native(),
204                bit_depth: 16,
205            }),
206            extra_channel_format: vec![
207                Some(JxlDataFormat::U16 {
208                    endianness: Endianness::native(),
209                    bit_depth: 16,
210                });
211                num_extra_channels
212            ],
213        }
214    }
215
216    /// Creates an RGBA f16 pixel format.
217    pub fn rgba_f16(num_extra_channels: usize) -> Self {
218        Self {
219            color_type: JxlColorType::Rgba,
220            color_data_format: Some(JxlDataFormat::F16 {
221                endianness: Endianness::native(),
222            }),
223            extra_channel_format: vec![
224                Some(JxlDataFormat::F16 {
225                    endianness: Endianness::native(),
226                });
227                num_extra_channels
228            ],
229        }
230    }
231
232    /// Creates an RGBA f32 pixel format.
233    pub fn rgba_f32(num_extra_channels: usize) -> Self {
234        Self {
235            color_type: JxlColorType::Rgba,
236            color_data_format: Some(JxlDataFormat::F32 {
237                endianness: Endianness::native(),
238            }),
239            extra_channel_format: vec![
240                Some(JxlDataFormat::F32 {
241                    endianness: Endianness::native(),
242                });
243                num_extra_channels
244            ],
245        }
246    }
247}
248
249#[derive(Clone, Debug, PartialEq, Eq)]
250pub enum JxlBitDepth {
251    Int {
252        bits_per_sample: u32,
253    },
254    Float {
255        bits_per_sample: u32,
256        exponent_bits_per_sample: u32,
257    },
258}
259
260impl JxlBitDepth {
261    pub fn bits_per_sample(&self) -> u32 {
262        match self {
263            JxlBitDepth::Int { bits_per_sample: b } => *b,
264            JxlBitDepth::Float {
265                bits_per_sample: b, ..
266            } => *b,
267        }
268    }
269}
270
271#[derive(Clone, Debug, PartialEq, Eq)]
272pub struct JxlExtraChannel {
273    pub ec_type: ExtraChannel,
274    pub alpha_associated: bool,
275    /// Bits per sample for this extra channel.
276    pub bits_per_sample: u32,
277    /// Channel name (empty string if unnamed).
278    pub name: String,
279    /// Dimensional shift (0 = full resolution, 1 = half, etc.).
280    pub dim_shift: u32,
281}
282
283#[derive(Clone, Debug, PartialEq, Eq)]
284pub struct JxlAnimation {
285    pub tps_numerator: u32,
286    pub tps_denominator: u32,
287    pub num_loops: u32,
288    pub have_timecodes: bool,
289}
290
291#[derive(Clone, Debug)]
292pub struct JxlFrameHeader {
293    pub name: String,
294    pub duration: Option<f64>,
295    /// Frame size (width, height)
296    pub size: (usize, usize),
297}