Skip to main content

openjph_core/codestream/
mod.rs

1//! JPEG 2000 codestream parser and generator.
2//!
3//! Port of `ojph_codestream.h/cpp`. The [`Codestream`] struct is the main
4//! entry point for encoding and decoding HTJ2K images.
5//!
6//! # Encoding workflow
7//!
8//! 1. Create a [`Codestream`] and configure parameters via
9//!    [`access_siz_mut()`](Codestream::access_siz_mut),
10//!    [`access_cod_mut()`](Codestream::access_cod_mut), and
11//!    [`access_qcd_mut()`](Codestream::access_qcd_mut).
12//! 2. Call [`write_headers()`](Codestream::write_headers) to emit the
13//!    main header to an [`OutfileBase`] implementor.
14//! 3. Push image lines with [`exchange()`](Codestream::exchange).
15//! 4. Call [`flush()`](Codestream::flush) to write tile data and the EOC marker.
16//!
17//! # Decoding workflow
18//!
19//! 1. Create a [`Codestream`] and call
20//!    [`read_headers()`](Codestream::read_headers) on an [`InfileBase`] implementor.
21//! 2. Optionally inspect SIZ/COD/QCD parameters.
22//! 3. Call [`create()`](Codestream::create) to build internal structures.
23//! 4. Pull decoded lines with [`pull()`](Codestream::pull).
24
25pub mod bitbuffer_read;
26pub mod bitbuffer_write;
27pub(crate) mod codeblock;
28pub(crate) mod codeblock_fun;
29pub(crate) mod local;
30pub(crate) mod precinct;
31pub(crate) mod resolution;
32pub(crate) mod subband;
33pub(crate) mod tile;
34pub(crate) mod tile_comp;
35
36use crate::error::Result;
37use crate::file::{InfileBase, OutfileBase};
38use crate::params::{CommentExchange, ParamCod, ParamNlt, ParamQcd, ParamSiz};
39
40/// The main codestream interface for encoding and decoding HTJ2K images.
41///
42/// `Codestream` is the public wrapper over the internal codec engine,
43/// mirroring the C++ `ojph::codestream` class (which uses a pImpl pattern).
44///
45/// # Examples
46///
47/// **Lossless encode + decode round-trip (single-component 8-bit)**
48///
49/// ```rust
50/// use openjph_core::codestream::Codestream;
51/// use openjph_core::file::{MemOutfile, MemInfile};
52/// use openjph_core::types::{Point, Size};
53///
54/// // Configure a minimal 8×8 grayscale image
55/// let (w, h) = (8u32, 8u32);
56/// let mut cs = Codestream::new();
57/// cs.access_siz_mut().set_image_extent(Point::new(w, h));
58/// cs.access_siz_mut().set_num_components(1);
59/// cs.access_siz_mut().set_comp_info(0, Point::new(1, 1), 8, false);
60/// cs.access_siz_mut().set_tile_size(Size::new(w, h));
61/// cs.access_cod_mut().set_num_decomposition(0);
62/// cs.access_cod_mut().set_reversible(true);
63/// cs.access_cod_mut().set_color_transform(false);
64/// cs.set_planar(0);
65///
66/// // Encode
67/// let mut out = MemOutfile::new();
68/// cs.write_headers(&mut out, &[]).unwrap();
69/// let row = vec![42i32; w as usize];
70/// for _ in 0..h { cs.exchange(&row, 0).unwrap(); }
71/// cs.flush(&mut out).unwrap();
72///
73/// // Decode
74/// let data = out.get_data().to_vec();
75/// let mut inp = MemInfile::new(&data);
76/// let mut dec = Codestream::new();
77/// dec.read_headers(&mut inp).unwrap();
78/// dec.create(&mut inp).unwrap();
79/// for _ in 0..h {
80///     let line = dec.pull(0).unwrap();
81///     assert_eq!(line, row);
82/// }
83/// ```
84#[derive(Debug, Default)]
85pub struct Codestream {
86    inner: local::CodestreamLocal,
87}
88
89impl Codestream {
90    /// Creates a new codestream with default parameters.
91    ///
92    /// All marker segments (SIZ, COD, QCD, NLT) are initialised to sensible
93    /// defaults. You must at least set the image extent, number of components,
94    /// and component info before encoding.
95    pub fn new() -> Self {
96        Self {
97            inner: local::CodestreamLocal::new(),
98        }
99    }
100
101    /// Resets the codestream so it can be reused for another encode/decode
102    /// operation without reallocating.
103    pub fn restart(&mut self) {
104        self.inner.restart();
105    }
106
107    // ----- Parameter access -----
108
109    /// Returns a shared reference to the SIZ (image/tile size) parameters.
110    pub fn access_siz(&self) -> &ParamSiz {
111        self.inner.access_siz()
112    }
113
114    /// Returns a mutable reference to the SIZ parameters for configuration.
115    ///
116    /// Use this before calling [`write_headers()`](Self::write_headers) to set
117    /// image extent, tile size, number of components, and per-component info.
118    pub fn access_siz_mut(&mut self) -> &mut ParamSiz {
119        self.inner.access_siz_mut()
120    }
121
122    /// Returns a shared reference to the COD (coding style) parameters.
123    pub fn access_cod(&self) -> &ParamCod {
124        self.inner.access_cod()
125    }
126
127    /// Returns a mutable reference to the COD parameters for configuration.
128    ///
129    /// Use this before calling [`write_headers()`](Self::write_headers) to set
130    /// decomposition levels, reversibility, block sizes, and color transform.
131    pub fn access_cod_mut(&mut self) -> &mut ParamCod {
132        self.inner.access_cod_mut()
133    }
134
135    /// Returns a shared reference to the QCD (quantization) parameters.
136    pub fn access_qcd(&self) -> &ParamQcd {
137        self.inner.access_qcd()
138    }
139
140    /// Returns a mutable reference to the QCD parameters for configuration.
141    ///
142    /// For lossy (irreversible) compression, use
143    /// [`set_delta()`](crate::params::ParamQcd::set_delta) to set the
144    /// base quantization step size.
145    pub fn access_qcd_mut(&mut self) -> &mut ParamQcd {
146        self.inner.access_qcd_mut()
147    }
148
149    /// Returns a shared reference to the NLT (nonlinearity) parameters.
150    pub fn access_nlt(&self) -> &ParamNlt {
151        self.inner.access_nlt()
152    }
153
154    /// Returns a mutable reference to the NLT parameters for configuration.
155    pub fn access_nlt_mut(&mut self) -> &mut ParamNlt {
156        self.inner.access_nlt_mut()
157    }
158
159    // ----- Configuration -----
160
161    /// Enables resilient (error-tolerant) mode for decoding.
162    ///
163    /// When enabled, the decoder attempts to recover from malformed tile-part
164    /// headers rather than returning an error.
165    pub fn enable_resilience(&mut self) {
166        self.inner.enable_resilience();
167    }
168
169    /// Sets the line exchange mode.
170    ///
171    /// - `0` — interleaved (all components for each line exchanged together)
172    /// - non-zero — planar (one component at a time)
173    pub fn set_planar(&mut self, planar: i32) {
174        self.inner.set_planar(planar);
175    }
176
177    /// Sets the codestream profile.
178    ///
179    /// Accepted values: `"IMF"`, `"BROADCAST"`, `"CINEMA2K"`, `"CINEMA4K"`, etc.
180    ///
181    /// # Errors
182    ///
183    /// Returns [`OjphError::InvalidParam`](crate::OjphError::InvalidParam) if
184    /// `name` is not a recognised profile string.
185    pub fn set_profile(&mut self, name: &str) -> Result<()> {
186        self.inner.set_profile(name)
187    }
188
189    /// Sets tilepart division flags (bit-field).
190    ///
191    /// Use `0x1` for resolution-based divisions and `0x2` for
192    /// component-based divisions.
193    pub fn set_tilepart_divisions(&mut self, value: u32) {
194        self.inner.set_tilepart_divisions(value);
195    }
196
197    /// Requests that TLM (tile-part length) markers be written in the
198    /// codestream header.
199    pub fn request_tlm_marker(&mut self, needed: bool) {
200        self.inner.request_tlm_marker(needed);
201    }
202
203    #[cfg(test)]
204    pub(crate) fn debug_inner(&self) -> &local::CodestreamLocal {
205        &self.inner
206    }
207
208    /// Restricts the number of resolution levels used during decoding.
209    ///
210    /// `skipped_res_for_data` controls how many resolution levels are
211    /// skipped when reading tile data. `skipped_res_for_recon` controls
212    /// how many are skipped for reconstruction.
213    pub fn restrict_input_resolution(
214        &mut self,
215        skipped_res_for_data: u32,
216        skipped_res_for_recon: u32,
217    ) {
218        self.inner
219            .restrict_input_resolution(skipped_res_for_data, skipped_res_for_recon);
220    }
221
222    // ----- Write path -----
223
224    /// Writes the codestream main header (SOC through to the end of the
225    /// main header) into `file`.
226    ///
227    /// Optional COM (comment) markers can be included via `comments`.
228    ///
229    /// # Errors
230    ///
231    /// Returns an error if parameter validation fails or writing to `file`
232    /// encounters an I/O error.
233    pub fn write_headers(
234        &mut self,
235        file: &mut dyn OutfileBase,
236        comments: &[CommentExchange],
237    ) -> Result<()> {
238        self.inner.write_headers(file, comments)
239    }
240
241    /// Pushes one line of image data for the specified component.
242    ///
243    /// Call this repeatedly (height × num_components times in interleaved
244    /// mode, or height times per component in planar mode) to supply the
245    /// full image.
246    ///
247    /// Returns `Some(next_line_index)` while more lines are needed, or
248    /// `None` when all lines for the current tile have been pushed.
249    ///
250    /// # Errors
251    ///
252    /// Returns an error if the internal encoder encounters a problem.
253    pub fn exchange(&mut self, line: &[i32], comp_num: u32) -> Result<Option<usize>> {
254        self.inner.exchange(line, comp_num)
255    }
256
257    /// Flushes the encoder: encodes all pending tiles and writes tile data
258    /// plus the EOC (end of codestream) marker to `file`.
259    ///
260    /// # Errors
261    ///
262    /// Returns an error on I/O failure or if the encoder state is invalid.
263    pub fn flush(&mut self, file: &mut dyn OutfileBase) -> Result<()> {
264        self.inner.flush(file)
265    }
266
267    // ----- Read path -----
268
269    /// Reads codestream main headers from `file`.
270    ///
271    /// After this call, you can inspect image parameters through
272    /// [`access_siz()`](Self::access_siz) and friends.
273    ///
274    /// # Errors
275    ///
276    /// Returns an error if the stream does not contain valid JPEG 2000
277    /// codestream headers.
278    pub fn read_headers(&mut self, file: &mut dyn InfileBase) -> Result<()> {
279        self.inner.read_headers(file)
280    }
281
282    /// Builds internal decoding structures and decodes tile data from `file`.
283    ///
284    /// Must be called after [`read_headers()`](Self::read_headers). After
285    /// this call, decoded lines can be retrieved with [`pull()`](Self::pull).
286    ///
287    /// # Errors
288    ///
289    /// Returns an error if decoding fails (corrupt data, unsupported
290    /// features, etc.).
291    pub fn create(&mut self, file: &mut dyn InfileBase) -> Result<()> {
292        self.inner.create(file)
293    }
294
295    /// Pulls the next decoded line for the given `comp_num`.
296    ///
297    /// Returns `None` when all lines for this component have been returned.
298    pub fn pull(&mut self, comp_num: u32) -> Option<Vec<i32>> {
299        self.inner.pull(comp_num)
300    }
301
302    // ----- Query -----
303
304    /// Returns `true` if planar mode is active.
305    pub fn is_planar(&self) -> bool {
306        self.inner.is_planar()
307    }
308
309    /// Returns the number of tiles in the x and y directions.
310    pub fn get_num_tiles(&self) -> crate::types::Size {
311        self.inner.num_tiles
312    }
313}