Skip to main content

zenjxl_decoder/api/inner/
mod.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
6#[cfg(test)]
7use crate::api::FrameCallback;
8use crate::{
9    api::JxlFrameHeader,
10    error::{Error, Result},
11};
12
13use super::{JxlBasicInfo, JxlColorProfile, JxlDecoderOptions, JxlPixelFormat};
14use crate::container::frame_index::FrameIndexBox;
15use crate::container::gain_map::GainMapBundle;
16use box_parser::BoxParser;
17use codestream_parser::CodestreamParser;
18
19mod box_parser;
20mod codestream_parser;
21mod process;
22
23/// Low-level, less-type-safe API.
24pub struct JxlDecoderInner {
25    options: JxlDecoderOptions,
26    box_parser: BoxParser,
27    codestream_parser: CodestreamParser,
28}
29
30impl JxlDecoderInner {
31    /// Creates a new decoder with the given options and, optionally, CMS.
32    pub fn new(options: JxlDecoderOptions) -> Self {
33        JxlDecoderInner {
34            options,
35            box_parser: BoxParser::new(),
36            codestream_parser: CodestreamParser::new(),
37        }
38    }
39
40    #[cfg(test)]
41    pub fn set_frame_callback(&mut self, callback: Box<FrameCallback>) {
42        self.codestream_parser.frame_callback = Some(callback);
43    }
44
45    #[cfg(test)]
46    pub fn decoded_frames(&self) -> usize {
47        self.codestream_parser.decoded_frames
48    }
49
50    /// Obtains the image's basic information, if available.
51    pub fn basic_info(&self) -> Option<&JxlBasicInfo> {
52        self.codestream_parser.basic_info.as_ref()
53    }
54
55    /// Retrieves the file's color profile, if available.
56    pub fn embedded_color_profile(&self) -> Option<&JxlColorProfile> {
57        self.codestream_parser.embedded_color_profile.as_ref()
58    }
59
60    /// Retrieves the current output color profile, if available.
61    pub fn output_color_profile(&self) -> Option<&JxlColorProfile> {
62        self.codestream_parser.output_color_profile.as_ref()
63    }
64
65    /// Specifies the preferred color profile to be used for outputting data.
66    /// Same semantics as JxlDecoderSetOutputColorProfile.
67    pub fn set_output_color_profile(&mut self, profile: JxlColorProfile) -> Result<()> {
68        if let (JxlColorProfile::Icc(_), None) = (&profile, &self.options.cms) {
69            return Err(Error::ICCOutputNoCMS);
70        }
71        self.codestream_parser.output_color_profile = Some(profile);
72        self.codestream_parser.output_color_profile_set_by_user = true;
73        Ok(())
74    }
75
76    pub fn current_pixel_format(&self) -> Option<&JxlPixelFormat> {
77        self.codestream_parser.pixel_format.as_ref()
78    }
79
80    pub fn set_pixel_format(&mut self, pixel_format: JxlPixelFormat) {
81        // TODO(veluca): return an error if we are asking for both planar and
82        // interleaved-in-color alpha.
83        self.codestream_parser.pixel_format = Some(pixel_format);
84        self.codestream_parser.update_default_output_color_profile();
85    }
86
87    pub fn frame_header(&self) -> Option<JxlFrameHeader> {
88        let frame_header = self.codestream_parser.frame.as_ref()?.header();
89        // The render pipeline always adds ExtendToImageDimensionsStage which extends
90        // frames to the full image size. So the output size is always the image size,
91        // not the frame's upsampled size.
92        let size = self.codestream_parser.basic_info.as_ref()?.size;
93        Some(JxlFrameHeader {
94            name: frame_header.name.clone(),
95            duration: self
96                .codestream_parser
97                .animation
98                .as_ref()
99                .map(|anim| frame_header.duration(anim)),
100            size,
101        })
102    }
103
104    /// Number of passes we have full data for.
105    /// Returns the minimum number of passes completed across all groups.
106    pub fn num_completed_passes(&self) -> Option<usize> {
107        Some(self.codestream_parser.num_completed_passes())
108    }
109
110    /// Fully resets the decoder to its initial state.
111    ///
112    /// This clears all state including pixel_format. For animation loop playback,
113    /// consider using [`rewind`](Self::rewind) instead which preserves pixel_format.
114    ///
115    /// After calling this, the caller should provide input from the beginning of the file.
116    pub fn reset(&mut self) {
117        // TODO(veluca): keep track of frame offsets for skipping.
118        self.box_parser = BoxParser::new();
119        self.codestream_parser = CodestreamParser::new();
120    }
121
122    /// Rewinds for animation loop replay, keeping pixel_format setting.
123    ///
124    /// This resets the decoder but preserves the pixel_format configuration,
125    /// so the caller doesn't need to re-set it after rewinding.
126    ///
127    /// After calling this, the caller should provide input from the beginning of the file.
128    /// Headers will be re-parsed, then frames can be decoded again.
129    ///
130    /// Returns `true` if pixel_format was preserved, `false` if none was set.
131    pub fn rewind(&mut self) -> bool {
132        self.box_parser = BoxParser::new();
133        self.codestream_parser.rewind().is_some()
134    }
135
136    pub fn has_more_frames(&self) -> bool {
137        self.codestream_parser.has_more_frames
138    }
139
140    /// Returns the reconstructed JPEG bytes if the file contained a JBRD box.
141    #[cfg(feature = "jpeg")]
142    pub fn take_jpeg_reconstruction(&mut self) -> Option<Vec<u8>> {
143        self.codestream_parser.jpeg_bytes.take()
144    }
145
146    /// Returns the parsed frame index box, if the file contained one.
147    pub fn frame_index(&self) -> Option<&FrameIndexBox> {
148        self.box_parser.frame_index.as_ref()
149    }
150
151    /// Returns a reference to the parsed gain map bundle, if the file contained one.
152    pub fn gain_map(&self) -> Option<&GainMapBundle> {
153        self.box_parser.gain_map.as_ref()
154    }
155
156    /// Takes the parsed gain map bundle, if the file contained one.
157    /// After calling this, `gain_map()` will return `None`.
158    pub fn take_gain_map(&mut self) -> Option<GainMapBundle> {
159        self.box_parser.gain_map.take()
160    }
161
162    /// Returns the raw EXIF data from the `Exif` container box, if present.
163    ///
164    /// The 4-byte TIFF header offset prefix has been stripped; this returns
165    /// the raw EXIF/TIFF bytes starting with the byte-order marker (`II` or `MM`).
166    /// Returns `None` for bare codestreams or files without an `Exif` box.
167    pub fn exif(&self) -> Option<&[u8]> {
168        self.box_parser.exif.as_deref()
169    }
170
171    /// Takes the EXIF data, leaving `None` in its place.
172    pub fn take_exif(&mut self) -> Option<Vec<u8>> {
173        self.box_parser.exif.take()
174    }
175
176    /// Returns the raw XMP data from the `xml ` container box, if present.
177    ///
178    /// Returns `None` for bare codestreams or files without an `xml ` box.
179    pub fn xmp(&self) -> Option<&[u8]> {
180        self.box_parser.xmp.as_deref()
181    }
182
183    /// Takes the XMP data, leaving `None` in its place.
184    pub fn take_xmp(&mut self) -> Option<Vec<u8>> {
185        self.box_parser.xmp.take()
186    }
187
188    #[cfg(test)]
189    pub(crate) fn set_use_simple_pipeline(&mut self, u: bool) {
190        self.codestream_parser.set_use_simple_pipeline(u);
191    }
192}