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    pub fn display_rect(
83        &self,
84        Rect {
85            size: (sx, sy),
86            origin: (ox, oy),
87        }: Rect,
88        size: (usize, usize),
89    ) -> Rect {
90        match self {
91            Orientation::Identity => Rect {
92                origin: (ox, oy),
93                size: (sx, sy),
94            },
95            Orientation::FlipHorizontal => Rect {
96                origin: (size.0 - sx - ox, oy),
97                size: (sx, sy),
98            },
99            Orientation::Rotate180 => Rect {
100                origin: (size.0 - sx - ox, size.1 - sy - oy),
101                size: (sx, sy),
102            },
103            Orientation::FlipVertical => Rect {
104                origin: (ox, size.1 - sy - oy),
105                size: (sx, sy),
106            },
107            Orientation::Transpose => Rect {
108                origin: (oy, ox),
109                size: (sy, sx),
110            },
111            Orientation::Rotate90Cw => Rect {
112                origin: (size.1 - sy - oy, ox),
113                size: (sy, sx),
114            },
115            Orientation::AntiTranspose => Rect {
116                origin: (size.1 - sy - oy, size.0 - sx - ox),
117                size: (sy, sx),
118            },
119            Orientation::Rotate90Ccw => Rect {
120                origin: (oy, size.0 - sx - ox),
121                size: (sy, sx),
122            },
123        }
124    }
125}
126
127#[derive(UnconditionalCoder, Debug, Clone)]
128pub struct Animation {
129    #[coder(u2S(100, 1000, Bits(10) + 1, Bits(30) + 1))]
130    pub tps_numerator: u32,
131    #[coder(u2S(1, 1001, Bits(8) + 1, Bits(10) + 1))]
132    pub tps_denominator: u32,
133    #[coder(u2S(0, Bits(3), Bits(16), Bits(32)))]
134    pub num_loops: u32,
135    pub have_timecodes: bool,
136}
137
138#[derive(UnconditionalCoder, Debug, Clone)]
139#[validate]
140pub struct ToneMapping {
141    #[all_default]
142    #[allow(dead_code)] // Used by UnconditionalCoder derive macro for default detection
143    pub all_default: bool,
144    #[default(255.0)]
145    pub intensity_target: f32,
146    #[default(0.0)]
147    pub min_nits: f32,
148    #[default(false)]
149    pub relative_to_max_display: bool,
150    #[default(0.0)]
151    pub linear_below: f32,
152}
153
154impl ToneMapping {
155    #[cfg(test)]
156    pub fn empty() -> ToneMapping {
157        ToneMapping {
158            all_default: false,
159            intensity_target: 0f32,
160            min_nits: 0f32,
161            relative_to_max_display: false,
162            linear_below: 0f32,
163        }
164    }
165    pub fn check(&self, _: &Empty) -> Result<(), Error> {
166        if self.intensity_target <= 0.0 {
167            Err(Error::InvalidIntensityTarget(self.intensity_target))
168        } else if self.min_nits < 0.0 || self.min_nits > self.intensity_target {
169            Err(Error::InvalidMinNits(self.min_nits))
170        } else if self.linear_below < 0.0
171            || (self.relative_to_max_display && self.linear_below > 1.0)
172        {
173            Err(Error::InvalidLinearBelow(
174                self.relative_to_max_display,
175                self.linear_below,
176            ))
177        } else {
178            Ok(())
179        }
180    }
181}
182
183#[allow(dead_code)]
184#[derive(UnconditionalCoder, Debug, Clone)]
185#[validate]
186pub struct ImageMetadata {
187    #[all_default]
188    all_default: bool,
189    #[default(false)]
190    extra_fields: bool,
191    #[condition(extra_fields)]
192    #[default(Orientation::Identity)]
193    #[coder(Bits(3) + 1)]
194    pub orientation: Orientation,
195    #[condition(extra_fields)]
196    #[default(false)]
197    have_intrinsic_size: bool, // TODO(veluca93): fold have_ fields in Option.
198    #[condition(have_intrinsic_size)]
199    pub intrinsic_size: Option<Size>,
200    #[condition(extra_fields)]
201    #[default(false)]
202    have_preview: bool,
203    #[condition(have_preview)]
204    pub preview: Option<Preview>,
205    #[condition(extra_fields)]
206    #[default(false)]
207    have_animation: bool,
208    #[condition(have_animation)]
209    pub animation: Option<Animation>,
210    #[default(BitDepth::default(&field_nonserialized))]
211    pub bit_depth: BitDepth,
212    #[default(true)]
213    pub modular_16bit_sufficient: bool,
214    #[size_coder(implicit(u2S(0, 1, Bits(4) + 2, Bits(12) + 1)))]
215    pub extra_channel_info: Vec<ExtraChannelInfo>,
216    #[default(true)]
217    pub xyb_encoded: bool,
218    #[default(ColorEncoding::default(&field_nonserialized))]
219    pub color_encoding: ColorEncoding,
220    #[condition(extra_fields)]
221    #[default(ToneMapping::default(&field_nonserialized))]
222    pub tone_mapping: ToneMapping,
223    extensions: Option<Extensions>,
224}
225
226impl ImageMetadata {
227    fn check(&self, _: &Empty) -> Result<(), Error> {
228        if self.extra_channel_info.len() > 256 {
229            return Err(Error::TooManyExtraChannels(self.extra_channel_info.len()));
230        }
231        Ok(())
232    }
233}