Skip to main content

zenjxl_decoder/headers/
image_metadata.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::{
7    bit_reader::BitReader,
8    error::Error,
9    headers::{bit_depth::*, color_encoding::*, encodings::*, extra_channels::*, size::*},
10    image::Rect,
11};
12use jxl_macros::UnconditionalCoder;
13use num_derive::FromPrimitive;
14
15#[derive(Debug, Default, Clone)]
16pub struct Signature;
17
18impl Signature {
19    #[allow(dead_code)]
20    pub fn new() -> Signature {
21        Signature {}
22    }
23}
24
25impl crate::headers::encodings::UnconditionalCoder<()> for Signature {
26    type Nonserialized = Empty;
27    fn read_unconditional(_: &(), br: &mut BitReader, _: &Empty) -> Result<Signature, Error> {
28        let sig1 = br.read(8)? as u8;
29        let sig2 = br.read(8)? as u8;
30        if (sig1, sig2) != (0xff, 0x0a) {
31            Err(Error::InvalidSignature)
32        } else {
33            Ok(Signature {})
34        }
35    }
36}
37
38#[derive(UnconditionalCoder, Copy, Clone, PartialEq, Debug, FromPrimitive)]
39pub enum Orientation {
40    Identity = 1,
41    FlipHorizontal = 2,
42    Rotate180 = 3,
43    FlipVertical = 4,
44    Transpose = 5,
45    Rotate90Cw = 6,
46    AntiTranspose = 7,
47    Rotate90Ccw = 8,
48}
49
50impl Orientation {
51    pub fn is_transposing(&self) -> bool {
52        matches!(
53            self,
54            Orientation::Transpose
55                | Orientation::AntiTranspose
56                | Orientation::Rotate90Cw
57                | Orientation::Rotate90Ccw
58        )
59    }
60
61    pub fn map_size(&self, size: (usize, usize)) -> (usize, usize) {
62        if self.is_transposing() {
63            (size.1, size.0)
64        } else {
65            size
66        }
67    }
68
69    pub fn display_pixel(&self, (x, y): (usize, usize), size: (usize, usize)) -> (usize, usize) {
70        match self {
71            Orientation::Identity => (x, y),
72            Orientation::FlipHorizontal => (size.0 - 1 - x, y),
73            Orientation::Rotate180 => (size.0 - 1 - x, size.1 - 1 - y),
74            Orientation::FlipVertical => (x, size.1 - 1 - y),
75            Orientation::Transpose => (y, x),
76            Orientation::Rotate90Cw => (size.1 - 1 - y, x),
77            Orientation::AntiTranspose => (size.1 - 1 - y, size.0 - 1 - x),
78            Orientation::Rotate90Ccw => (y, size.0 - 1 - x),
79        }
80    }
81
82    /// Returns the per-pixel step in display coordinates when iterating along
83    /// the frame's x axis. Equivalent to
84    /// `display_pixel((1, y), size) - display_pixel((0, y), size)` but works
85    /// for any non-empty `size` (the latter form would underflow when
86    /// `size.0 == 1` and the orientation flips x).
87    pub fn display_row_step(&self) -> (isize, isize) {
88        match self {
89            Orientation::Identity | Orientation::FlipVertical => (1, 0),
90            Orientation::FlipHorizontal | Orientation::Rotate180 => (-1, 0),
91            Orientation::Transpose | Orientation::Rotate90Cw => (0, 1),
92            Orientation::AntiTranspose | Orientation::Rotate90Ccw => (0, -1),
93        }
94    }
95
96    pub fn display_rect(
97        &self,
98        Rect {
99            size: (sx, sy),
100            origin: (ox, oy),
101        }: Rect,
102        size: (usize, usize),
103    ) -> Rect {
104        match self {
105            Orientation::Identity => Rect {
106                origin: (ox, oy),
107                size: (sx, sy),
108            },
109            Orientation::FlipHorizontal => Rect {
110                origin: (size.0 - sx - ox, oy),
111                size: (sx, sy),
112            },
113            Orientation::Rotate180 => Rect {
114                origin: (size.0 - sx - ox, size.1 - sy - oy),
115                size: (sx, sy),
116            },
117            Orientation::FlipVertical => Rect {
118                origin: (ox, size.1 - sy - oy),
119                size: (sx, sy),
120            },
121            Orientation::Transpose => Rect {
122                origin: (oy, ox),
123                size: (sy, sx),
124            },
125            Orientation::Rotate90Cw => Rect {
126                origin: (size.1 - sy - oy, ox),
127                size: (sy, sx),
128            },
129            Orientation::AntiTranspose => Rect {
130                origin: (size.1 - sy - oy, size.0 - sx - ox),
131                size: (sy, sx),
132            },
133            Orientation::Rotate90Ccw => Rect {
134                origin: (oy, size.0 - sx - ox),
135                size: (sy, sx),
136            },
137        }
138    }
139}
140
141#[derive(UnconditionalCoder, Debug, Clone)]
142pub struct Animation {
143    #[coder(u2S(100, 1000, Bits(10) + 1, Bits(30) + 1))]
144    pub tps_numerator: u32,
145    #[coder(u2S(1, 1001, Bits(8) + 1, Bits(10) + 1))]
146    pub tps_denominator: u32,
147    #[coder(u2S(0, Bits(3), Bits(16), Bits(32)))]
148    pub num_loops: u32,
149    pub have_timecodes: bool,
150}
151
152#[derive(UnconditionalCoder, Debug, Clone)]
153#[validate]
154pub struct ToneMapping {
155    #[all_default]
156    #[allow(dead_code)] // Used by UnconditionalCoder derive macro for default detection
157    pub all_default: bool,
158    #[default(255.0)]
159    pub intensity_target: f32,
160    #[default(0.0)]
161    pub min_nits: f32,
162    #[default(false)]
163    pub relative_to_max_display: bool,
164    #[default(0.0)]
165    pub linear_below: f32,
166}
167
168impl ToneMapping {
169    #[cfg(test)]
170    pub fn empty() -> ToneMapping {
171        ToneMapping {
172            all_default: false,
173            intensity_target: 0f32,
174            min_nits: 0f32,
175            relative_to_max_display: false,
176            linear_below: 0f32,
177        }
178    }
179    pub fn check(&self, _: &Empty) -> Result<(), Error> {
180        if self.intensity_target <= 0.0 {
181            Err(Error::InvalidIntensityTarget(self.intensity_target))
182        } else if self.min_nits < 0.0 || self.min_nits > self.intensity_target {
183            Err(Error::InvalidMinNits(self.min_nits))
184        } else if self.linear_below < 0.0
185            || (self.relative_to_max_display && self.linear_below > 1.0)
186        {
187            Err(Error::InvalidLinearBelow(
188                self.relative_to_max_display,
189                self.linear_below,
190            ))
191        } else {
192            Ok(())
193        }
194    }
195}
196
197#[allow(dead_code)]
198#[derive(UnconditionalCoder, Debug, Clone)]
199#[validate]
200pub struct ImageMetadata {
201    #[all_default]
202    all_default: bool,
203    #[default(false)]
204    extra_fields: bool,
205    #[condition(extra_fields)]
206    #[default(Orientation::Identity)]
207    #[coder(Bits(3) + 1)]
208    pub orientation: Orientation,
209    #[condition(extra_fields)]
210    #[default(false)]
211    have_intrinsic_size: bool, // TODO(veluca93): fold have_ fields in Option.
212    #[condition(have_intrinsic_size)]
213    pub intrinsic_size: Option<Size>,
214    #[condition(extra_fields)]
215    #[default(false)]
216    have_preview: bool,
217    #[condition(have_preview)]
218    pub preview: Option<Preview>,
219    #[condition(extra_fields)]
220    #[default(false)]
221    have_animation: bool,
222    #[condition(have_animation)]
223    pub animation: Option<Animation>,
224    #[default(BitDepth::default(&field_nonserialized))]
225    pub bit_depth: BitDepth,
226    #[default(true)]
227    pub modular_16bit_sufficient: bool,
228    #[size_coder(implicit(u2S(0, 1, Bits(4) + 2, Bits(12) + 1)))]
229    pub extra_channel_info: Vec<ExtraChannelInfo>,
230    #[default(true)]
231    pub xyb_encoded: bool,
232    #[default(ColorEncoding::default(&field_nonserialized))]
233    pub color_encoding: ColorEncoding,
234    #[condition(extra_fields)]
235    #[default(ToneMapping::default(&field_nonserialized))]
236    pub tone_mapping: ToneMapping,
237    extensions: Option<Extensions>,
238}
239
240impl ImageMetadata {
241    fn check(&self, _: &Empty) -> Result<(), Error> {
242        if self.extra_channel_info.len() > 256 {
243            return Err(Error::TooManyExtraChannels(self.extra_channel_info.len()));
244        }
245        Ok(())
246    }
247}