Skip to main content

oxigdal_jpeg2000/
codestream.rs

1//! JPEG2000 codestream parsing
2//!
3//! This module handles parsing of JPEG2000 codestream markers and segments.
4
5use crate::error::{Jpeg2000Error, Result};
6use byteorder::{BigEndian, ReadBytesExt};
7use std::io::Read;
8
9/// JPEG2000 codestream markers
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum Marker {
12    /// Start of codestream (SOC)
13    Soc = 0xFF4F,
14    /// Start of tile-part (SOT)
15    Sot = 0xFF90,
16    /// Start of data (SOD)
17    Sod = 0xFF93,
18    /// End of codestream (EOC)
19    Eoc = 0xFFD9,
20    /// Image and tile size (SIZ)
21    Siz = 0xFF51,
22    /// Coding style default (COD)
23    Cod = 0xFF52,
24    /// Coding style component (COC)
25    Coc = 0xFF53,
26    /// Quantization default (QCD)
27    Qcd = 0xFF5C,
28    /// Quantization component (QCC)
29    Qcc = 0xFF5D,
30    /// Region of interest (RGN)
31    Rgn = 0xFF5E,
32    /// Progression order change (POC)
33    Poc = 0xFF5F,
34    /// Packet length, main header (PLM)
35    Plm = 0xFF57,
36    /// Packet length, tile-part header (PLT)
37    Plt = 0xFF58,
38    /// Packed packet headers, main header (PPM)
39    Ppm = 0xFF60,
40    /// Packed packet headers, tile-part header (PPT)
41    Ppt = 0xFF61,
42    /// Component registration (CRG)
43    Crg = 0xFF63,
44    /// Comment (COM)
45    Com = 0xFF64,
46}
47
48impl Marker {
49    /// Create marker from u16 value
50    pub fn from_u16(value: u16) -> Result<Self> {
51        match value {
52            0xFF4F => Ok(Self::Soc),
53            0xFF90 => Ok(Self::Sot),
54            0xFF93 => Ok(Self::Sod),
55            0xFFD9 => Ok(Self::Eoc),
56            0xFF51 => Ok(Self::Siz),
57            0xFF52 => Ok(Self::Cod),
58            0xFF53 => Ok(Self::Coc),
59            0xFF5C => Ok(Self::Qcd),
60            0xFF5D => Ok(Self::Qcc),
61            0xFF5E => Ok(Self::Rgn),
62            0xFF5F => Ok(Self::Poc),
63            0xFF57 => Ok(Self::Plm),
64            0xFF58 => Ok(Self::Plt),
65            0xFF60 => Ok(Self::Ppm),
66            0xFF61 => Ok(Self::Ppt),
67            0xFF63 => Ok(Self::Crg),
68            0xFF64 => Ok(Self::Com),
69            _ => Err(Jpeg2000Error::InvalidMarker(value)),
70        }
71    }
72
73    /// Check if marker has associated segment
74    pub fn has_segment(&self) -> bool {
75        !matches!(self, Self::Soc | Self::Sod | Self::Eoc)
76    }
77}
78
79/// Image and tile size parameters from SIZ marker
80#[derive(Debug, Clone)]
81pub struct ImageSize {
82    /// Reference grid width
83    pub width: u32,
84    /// Reference grid height
85    pub height: u32,
86    /// Horizontal offset
87    pub x_offset: u32,
88    /// Vertical offset
89    pub y_offset: u32,
90    /// Tile width
91    pub tile_width: u32,
92    /// Tile height
93    pub tile_height: u32,
94    /// Tile horizontal offset
95    pub tile_x_offset: u32,
96    /// Tile vertical offset
97    pub tile_y_offset: u32,
98    /// Number of components
99    pub num_components: u16,
100    /// Component parameters
101    pub components: Vec<ComponentSize>,
102}
103
104/// Component size parameters
105#[derive(Debug, Clone)]
106pub struct ComponentSize {
107    /// Precision in bits (1-38)
108    pub precision: u8,
109    /// Is signed
110    pub is_signed: bool,
111    /// Horizontal subsampling
112    pub dx: u8,
113    /// Vertical subsampling
114    pub dy: u8,
115}
116
117impl ImageSize {
118    /// Parse SIZ marker segment
119    pub fn parse<R: Read>(reader: &mut R, length: u16) -> Result<Self> {
120        let _capability = reader.read_u16::<BigEndian>()?;
121        let width = reader.read_u32::<BigEndian>()?;
122        let height = reader.read_u32::<BigEndian>()?;
123        let x_offset = reader.read_u32::<BigEndian>()?;
124        let y_offset = reader.read_u32::<BigEndian>()?;
125        let tile_width = reader.read_u32::<BigEndian>()?;
126        let tile_height = reader.read_u32::<BigEndian>()?;
127        let tile_x_offset = reader.read_u32::<BigEndian>()?;
128        let tile_y_offset = reader.read_u32::<BigEndian>()?;
129        let num_components = reader.read_u16::<BigEndian>()?;
130
131        if num_components == 0 {
132            return Err(Jpeg2000Error::InvalidImageHeader(
133                "Number of components must be > 0".to_string(),
134            ));
135        }
136
137        // Length parameter has already had the 2-byte length field subtracted by read_segment_length()
138        // Expected: 2 (capability) + 32 (8×4 byte fields) + 2 (num_components) + (num_components × 3)
139        let expected_length = 36 + (num_components as usize * 3);
140        if length as usize != expected_length {
141            return Err(Jpeg2000Error::InvalidImageHeader(format!(
142                "Invalid SIZ segment length: expected {}, got {}",
143                expected_length, length
144            )));
145        }
146
147        let mut components = Vec::with_capacity(num_components as usize);
148        for _ in 0..num_components {
149            let ssiz = reader.read_u8()?;
150            let dx = reader.read_u8()?;
151            let dy = reader.read_u8()?;
152
153            let is_signed = (ssiz & 0x80) != 0;
154            let precision = (ssiz & 0x7F) + 1;
155
156            components.push(ComponentSize {
157                precision,
158                is_signed,
159                dx,
160                dy,
161            });
162        }
163
164        Ok(Self {
165            width,
166            height,
167            x_offset,
168            y_offset,
169            tile_width,
170            tile_height,
171            tile_x_offset,
172            tile_y_offset,
173            num_components,
174            components,
175        })
176    }
177
178    /// Calculate number of tiles in horizontal direction
179    pub fn num_tiles_x(&self) -> u32 {
180        (self.width + self.tile_width - 1 - self.tile_x_offset) / self.tile_width
181    }
182
183    /// Calculate number of tiles in vertical direction
184    pub fn num_tiles_y(&self) -> u32 {
185        (self.height + self.tile_height - 1 - self.tile_y_offset) / self.tile_height
186    }
187
188    /// Calculate total number of tiles
189    pub fn num_tiles(&self) -> u32 {
190        self.num_tiles_x() * self.num_tiles_y()
191    }
192}
193
194/// Coding style parameters from COD marker
195#[derive(Debug, Clone)]
196pub struct CodingStyle {
197    /// Progression order
198    pub progression_order: ProgressionOrder,
199    /// Number of quality layers
200    pub num_layers: u16,
201    /// Multiple component transform
202    pub use_mct: bool,
203    /// Number of decomposition levels
204    pub num_levels: u8,
205    /// Code-block width exponent
206    pub code_block_width: u8,
207    /// Code-block height exponent
208    pub code_block_height: u8,
209    /// Code-block style flags
210    pub code_block_style: u8,
211    /// Wavelet transformation
212    pub wavelet: WaveletTransform,
213}
214
215/// Progression order types
216#[derive(Debug, Clone, Copy, PartialEq, Eq)]
217pub enum ProgressionOrder {
218    /// Layer-resolution-component-position
219    Lrcp = 0,
220    /// Resolution-layer-component-position
221    Rlcp = 1,
222    /// Resolution-position-component-layer
223    Rpcl = 2,
224    /// Position-component-resolution-layer
225    Pcrl = 3,
226    /// Component-position-resolution-layer
227    Cprl = 4,
228}
229
230impl ProgressionOrder {
231    /// Create from u8 value
232    pub fn from_u8(value: u8) -> Result<Self> {
233        match value {
234            0 => Ok(Self::Lrcp),
235            1 => Ok(Self::Rlcp),
236            2 => Ok(Self::Rpcl),
237            3 => Ok(Self::Pcrl),
238            4 => Ok(Self::Cprl),
239            _ => Err(Jpeg2000Error::CodestreamError(format!(
240                "Invalid progression order: {}",
241                value
242            ))),
243        }
244    }
245}
246
247/// Wavelet transformation types
248#[derive(Debug, Clone, Copy, PartialEq, Eq)]
249pub enum WaveletTransform {
250    /// 9-7 irreversible (lossy)
251    Irreversible97,
252    /// 5-3 reversible (lossless)
253    Reversible53,
254}
255
256impl WaveletTransform {
257    /// Create from u8 value
258    pub fn from_u8(value: u8) -> Result<Self> {
259        match value {
260            0 => Ok(Self::Irreversible97),
261            1 => Ok(Self::Reversible53),
262            _ => Err(Jpeg2000Error::CodestreamError(format!(
263                "Invalid wavelet transform: {}",
264                value
265            ))),
266        }
267    }
268}
269
270impl CodingStyle {
271    /// Parse COD marker segment
272    pub fn parse<R: Read>(reader: &mut R, _length: u16) -> Result<Self> {
273        let scod = reader.read_u8()?;
274        let progression_order = ProgressionOrder::from_u8(reader.read_u8()?)?;
275        let num_layers = reader.read_u16::<BigEndian>()?;
276        let mct = reader.read_u8()?;
277        let num_levels = reader.read_u8()?;
278        let code_block_width = reader.read_u8()?;
279        let code_block_height = reader.read_u8()?;
280        let code_block_style = reader.read_u8()?;
281        let wavelet = WaveletTransform::from_u8(reader.read_u8()?)?;
282
283        let use_mct = mct != 0;
284
285        // Verify coding style flags
286        if (scod & 0x01) != 0 {
287            // Precinct size is defined
288            return Err(Jpeg2000Error::UnsupportedFeature(
289                "Custom precinct sizes not yet supported".to_string(),
290            ));
291        }
292
293        Ok(Self {
294            progression_order,
295            num_layers,
296            use_mct,
297            num_levels,
298            code_block_width,
299            code_block_height,
300            code_block_style,
301            wavelet,
302        })
303    }
304}
305
306/// Quantization parameters from QCD marker
307#[derive(Debug, Clone)]
308pub struct Quantization {
309    /// Quantization style
310    pub style: QuantizationStyle,
311    /// Step sizes
312    pub step_sizes: Vec<u16>,
313}
314
315/// Quantization style types
316#[derive(Debug, Clone, Copy, PartialEq, Eq)]
317pub enum QuantizationStyle {
318    /// No quantization
319    NoQuantization,
320    /// Scalar derived (used with reversible transform)
321    ScalarDerived,
322    /// Scalar expounded (explicit step sizes)
323    ScalarExpounded,
324}
325
326impl QuantizationStyle {
327    /// Create from u8 value
328    pub fn from_u8(value: u8) -> Result<Self> {
329        match value & 0x1F {
330            0 => Ok(Self::NoQuantization),
331            1 => Ok(Self::ScalarDerived),
332            2 => Ok(Self::ScalarExpounded),
333            _ => Err(Jpeg2000Error::CodestreamError(format!(
334                "Invalid quantization style: {}",
335                value
336            ))),
337        }
338    }
339}
340
341impl Quantization {
342    /// Parse QCD marker segment
343    pub fn parse<R: Read>(reader: &mut R, length: u16) -> Result<Self> {
344        let sqcd = reader.read_u8()?;
345        let style = QuantizationStyle::from_u8(sqcd)?;
346
347        let mut step_sizes = Vec::new();
348        let remaining = (length - 1) as usize;
349
350        match style {
351            QuantizationStyle::NoQuantization | QuantizationStyle::ScalarDerived => {
352                // Each step size is 1 byte
353                for _ in 0..remaining {
354                    step_sizes.push(u16::from(reader.read_u8()?));
355                }
356            }
357            QuantizationStyle::ScalarExpounded => {
358                // Each step size is 2 bytes
359                for _ in 0..(remaining / 2) {
360                    step_sizes.push(reader.read_u16::<BigEndian>()?);
361                }
362            }
363        }
364
365        Ok(Self { style, step_sizes })
366    }
367}
368
369/// Codestream parser
370pub struct CodestreamParser<R> {
371    reader: R,
372}
373
374impl<R: Read> CodestreamParser<R> {
375    /// Create new codestream parser
376    pub fn new(reader: R) -> Self {
377        Self { reader }
378    }
379
380    /// Read next marker
381    pub fn read_marker(&mut self) -> Result<Option<Marker>> {
382        match self.reader.read_u16::<BigEndian>() {
383            Ok(value) => Ok(Some(Marker::from_u16(value)?)),
384            Err(ref e) if e.kind() == std::io::ErrorKind::UnexpectedEof => Ok(None),
385            Err(e) => Err(e.into()),
386        }
387    }
388
389    /// Read marker segment length (excluding marker)
390    pub fn read_segment_length(&mut self) -> Result<u16> {
391        let length = self.reader.read_u16::<BigEndian>()?;
392        // Length includes the 2 bytes of the length field itself
393        if length < 2 {
394            return Err(Jpeg2000Error::CodestreamError(format!(
395                "Invalid segment length: {}",
396                length
397            )));
398        }
399        Ok(length - 2)
400    }
401
402    /// Skip segment data
403    pub fn skip_segment(&mut self, length: u16) -> Result<()> {
404        let mut buffer = vec![0u8; length as usize];
405        self.reader.read_exact(&mut buffer)?;
406        Ok(())
407    }
408
409    /// Parse SIZ marker segment
410    pub fn parse_siz(&mut self) -> Result<ImageSize> {
411        let length = self.read_segment_length()?;
412        ImageSize::parse(&mut self.reader, length)
413    }
414
415    /// Parse COD marker segment
416    pub fn parse_cod(&mut self) -> Result<CodingStyle> {
417        let length = self.read_segment_length()?;
418        CodingStyle::parse(&mut self.reader, length)
419    }
420
421    /// Parse QCD marker segment
422    pub fn parse_qcd(&mut self) -> Result<Quantization> {
423        let length = self.read_segment_length()?;
424        Quantization::parse(&mut self.reader, length)
425    }
426}
427
428#[cfg(test)]
429mod tests {
430    use super::*;
431
432    #[test]
433    fn test_marker_conversion() {
434        assert_eq!(Marker::from_u16(0xFF4F).ok(), Some(Marker::Soc));
435        assert_eq!(Marker::from_u16(0xFFD9).ok(), Some(Marker::Eoc));
436        assert!(Marker::from_u16(0x0000).is_err());
437    }
438
439    #[test]
440    fn test_progression_order() {
441        assert_eq!(
442            ProgressionOrder::from_u8(0).ok(),
443            Some(ProgressionOrder::Lrcp)
444        );
445        assert_eq!(
446            ProgressionOrder::from_u8(4).ok(),
447            Some(ProgressionOrder::Cprl)
448        );
449        assert!(ProgressionOrder::from_u8(5).is_err());
450    }
451
452    #[test]
453    fn test_wavelet_transform() {
454        assert_eq!(
455            WaveletTransform::from_u8(0).ok(),
456            Some(WaveletTransform::Irreversible97)
457        );
458        assert_eq!(
459            WaveletTransform::from_u8(1).ok(),
460            Some(WaveletTransform::Reversible53)
461        );
462        assert!(WaveletTransform::from_u8(2).is_err());
463    }
464}