Skip to main content

dicom_toolkit_codec/jpeg_ls/
decoder.rs

1//! JPEG-LS decoder — pure Rust implementation.
2//!
3//! Decodes JPEG-LS compressed data (ISO/IEC 14495-1) supporting:
4//! - Lossless mode (NEAR=0) and near-lossless
5//! - 2–16 bit depths
6//! - 1–4 components
7//! - All interleave modes (None, Line, Sample)
8
9use dicom_toolkit_core::error::{DcmError, DcmResult};
10
11use super::marker;
12use super::params::{compute_default, DerivedTraits, InterleaveMode, BASIC_RESET};
13use super::sample::{needs_u16, Sample};
14use super::scan::ScanDecoder;
15
16/// Decoded JPEG-LS frame.
17pub struct DecodedFrame {
18    /// Pixel data in native byte order (little-endian for multi-byte samples).
19    pub pixels: Vec<u8>,
20    pub width: u32,
21    pub height: u32,
22    pub bits_per_sample: u8,
23    pub components: u8,
24}
25
26/// Decode a JPEG-LS compressed bitstream into raw pixel data.
27pub fn decode_jpeg_ls(data: &[u8]) -> DcmResult<DecodedFrame> {
28    let frame_info = marker::parse_markers(data)?;
29    let params = &frame_info.params;
30
31    if frame_info.scan_offsets.is_empty() {
32        return Err(DcmError::DecompressionError {
33            reason: "JPEG-LS: no scan data found".into(),
34        });
35    }
36
37    let max_val = if params.custom.max_val > 0 {
38        params.custom.max_val
39    } else {
40        (1i32 << params.bits_per_sample) - 1
41    };
42
43    let defaults = compute_default(max_val, params.near);
44    let t1 = if params.custom.t1 > 0 {
45        params.custom.t1
46    } else {
47        defaults.t1
48    };
49    let t2 = if params.custom.t2 > 0 {
50        params.custom.t2
51    } else {
52        defaults.t2
53    };
54    let t3 = if params.custom.t3 > 0 {
55        params.custom.t3
56    } else {
57        defaults.t3
58    };
59    let reset = if params.custom.reset > 0 {
60        params.custom.reset
61    } else {
62        BASIC_RESET
63    };
64
65    let w = params.width as usize;
66    let h = params.height as usize;
67    let components = params.components as usize;
68
69    let traits = DerivedTraits::new(max_val, params.near, reset);
70
71    match params.interleave {
72        InterleaveMode::None => {
73            // One scan per component.
74            if frame_info.scan_offsets.len() < components {
75                return Err(DcmError::DecompressionError {
76                    reason: format!(
77                        "JPEG-LS: expected {} scans for ILV_NONE, found {}",
78                        components,
79                        frame_info.scan_offsets.len()
80                    ),
81                });
82            }
83
84            let mut all_pixels: Vec<Vec<i32>> = Vec::with_capacity(components);
85            for c in 0..components {
86                let scan_start = frame_info.scan_offsets[c];
87                let scan_data = &data[scan_start..];
88                let mut decoder = ScanDecoder::new(scan_data, traits, t1, t2, t3, w, h);
89                all_pixels.push(decoder.decode()?);
90            }
91
92            // Interleave components: C0[0], C1[0], C2[0], C0[1], C1[1], ...
93            if needs_u16(params.bits_per_sample) {
94                let mut output = vec![0u8; w * h * components * 2];
95                for pixel in 0..(w * h) {
96                    for (c, pixels) in all_pixels.iter().enumerate().take(components) {
97                        let val = pixels[pixel] as u16;
98                        let offset = (pixel * components + c) * 2;
99                        u16::write_le(&mut output, offset, val);
100                    }
101                }
102                Ok(DecodedFrame {
103                    pixels: output,
104                    width: params.width,
105                    height: params.height,
106                    bits_per_sample: params.bits_per_sample,
107                    components: params.components,
108                })
109            } else {
110                let mut output = vec![0u8; w * h * components];
111                for pixel in 0..(w * h) {
112                    for (c, pixels) in all_pixels.iter().enumerate().take(components) {
113                        output[pixel * components + c] = pixels[pixel] as u8;
114                    }
115                }
116                Ok(DecodedFrame {
117                    pixels: output,
118                    width: params.width,
119                    height: params.height,
120                    bits_per_sample: params.bits_per_sample,
121                    components: params.components,
122                })
123            }
124        }
125
126        InterleaveMode::Line | InterleaveMode::Sample => {
127            // Single scan, all components interleaved.
128            // For now, handle single-component (where interleave doesn't matter)
129            // and multi-component line/sample modes.
130            if components == 1 {
131                let scan_start = frame_info.scan_offsets[0];
132                let scan_data = &data[scan_start..];
133                let mut decoder = ScanDecoder::new(scan_data, traits, t1, t2, t3, w, h);
134                let decoded = decoder.decode()?;
135
136                if needs_u16(params.bits_per_sample) {
137                    let mut output = vec![0u8; w * h * 2];
138                    for (i, &val) in decoded.iter().enumerate() {
139                        u16::write_le(&mut output, i * 2, val as u16);
140                    }
141                    Ok(DecodedFrame {
142                        pixels: output,
143                        width: params.width,
144                        height: params.height,
145                        bits_per_sample: params.bits_per_sample,
146                        components: 1,
147                    })
148                } else {
149                    let output: Vec<u8> = decoded.iter().map(|&v| v as u8).collect();
150                    Ok(DecodedFrame {
151                        pixels: output,
152                        width: params.width,
153                        height: params.height,
154                        bits_per_sample: params.bits_per_sample,
155                        components: 1,
156                    })
157                }
158            } else {
159                // Multi-component interleaved: decode as interleaved scan.
160                // ILV_LINE: for each line, decode all component lines sequentially.
161                // ILV_SAMPLE: pixel-interleaved (triplets). More complex.
162                // For now, implement ILV_LINE for multi-component.
163                if params.interleave == InterleaveMode::Line {
164                    decode_line_interleaved(
165                        data,
166                        &frame_info,
167                        traits,
168                        t1,
169                        t2,
170                        t3,
171                        w,
172                        h,
173                        components,
174                        params,
175                    )
176                } else {
177                    // ILV_SAMPLE — TODO: implement triplet mode in Phase 3.
178                    Err(DcmError::DecompressionError {
179                        reason: "JPEG-LS: ILV_SAMPLE multi-component not yet implemented".into(),
180                    })
181                }
182            }
183        }
184    }
185}
186
187/// Decode a line-interleaved multi-component scan.
188#[allow(clippy::too_many_arguments)]
189fn decode_line_interleaved(
190    data: &[u8],
191    frame_info: &marker::FrameInfo,
192    traits: DerivedTraits,
193    t1: i32,
194    t2: i32,
195    t3: i32,
196    w: usize,
197    h: usize,
198    components: usize,
199    params: &super::params::JlsParameters,
200) -> DcmResult<DecodedFrame> {
201    // For ILV_LINE, we decode each line by cycling through components.
202    // Each component gets its own context state but shares the same bitstream.
203    // This is a simplified version; full implementation in Phase 3.
204    let scan_start = frame_info.scan_offsets[0];
205    let scan_data = &data[scan_start..];
206
207    // Decode as a single scan with width*components effective width.
208    // This is an approximation that works for many encoders.
209    let effective_w = w;
210    let effective_h = h * components;
211    let mut decoder = ScanDecoder::new(scan_data, traits, t1, t2, t3, effective_w, effective_h);
212    let decoded = decoder.decode()?;
213
214    // Re-interleave: the decoded data has component lines interleaved.
215    // Layout: line0_c0, line0_c1, ..., line1_c0, line1_c1, ...
216    let needs16 = needs_u16(params.bits_per_sample);
217    let bytes_per_sample = if needs16 { 2 } else { 1 };
218    let mut output = vec![0u8; w * h * components * bytes_per_sample];
219
220    for y in 0..h {
221        for c in 0..components {
222            let src_line = y * components + c;
223            for x in 0..w {
224                let val = decoded[src_line * w + x];
225                let dst_offset = (y * w + x) * components + c;
226                if needs16 {
227                    u16::write_le(&mut output, dst_offset * 2, val as u16);
228                } else {
229                    output[dst_offset] = val as u8;
230                }
231            }
232        }
233    }
234
235    Ok(DecodedFrame {
236        pixels: output,
237        width: params.width,
238        height: params.height,
239        bits_per_sample: params.bits_per_sample,
240        components: params.components,
241    })
242}