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}