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