Skip to main content

dicom_toolkit_data/io/
codec.rs

1//! Pixel codec trait and built-in codecs (RLE Lossless).
2//!
3//! DICOM RLE Lossless per PS3.5 Annex G (PackBits variant).
4
5use dicom_toolkit_core::error::{DcmError, DcmResult};
6
7// ── PixelCodec trait ──────────────────────────────────────────────────────────
8
9pub trait PixelCodec: Send + Sync {
10    fn uid(&self) -> &'static str;
11    fn decode_frame(&self, data: &[u8], rows: u16, cols: u16, bits: u16) -> DcmResult<Vec<u8>>;
12    fn encode_frame(&self, data: &[u8], rows: u16, cols: u16, bits: u16) -> DcmResult<Vec<u8>>;
13}
14
15// ── RLE Lossless ──────────────────────────────────────────────────────────────
16
17pub struct RleCodec;
18
19impl PixelCodec for RleCodec {
20    fn uid(&self) -> &'static str {
21        "1.2.840.10008.1.2.5"
22    }
23
24    fn decode_frame(&self, data: &[u8], rows: u16, cols: u16, bits: u16) -> DcmResult<Vec<u8>> {
25        rle_decode(data, rows, cols, bits)
26    }
27
28    fn encode_frame(&self, data: &[u8], rows: u16, cols: u16, bits: u16) -> DcmResult<Vec<u8>> {
29        rle_encode(data, rows, cols, bits)
30    }
31}
32
33// ── RLE decode ────────────────────────────────────────────────────────────────
34
35fn rle_decode(data: &[u8], rows: u16, cols: u16, bits: u16) -> DcmResult<Vec<u8>> {
36    if data.len() < 64 {
37        return Err(DcmError::DecompressionError {
38            reason: "RLE header too small".into(),
39        });
40    }
41
42    let pixel_count = rows as usize * cols as usize;
43    let bytes_per_sample = if bits <= 8 { 1usize } else { 2usize };
44
45    let num_segments = u32::from_le_bytes([data[0], data[1], data[2], data[3]]) as usize;
46    if num_segments == 0 || num_segments > 15 {
47        return Err(DcmError::DecompressionError {
48            reason: format!("invalid RLE segment count: {}", num_segments),
49        });
50    }
51
52    // Read segment offsets (15 × u32 at bytes 4..64)
53    let mut offsets = [0u32; 15];
54    for (i, offset) in offsets.iter_mut().enumerate() {
55        let base = 4 + i * 4;
56        *offset = u32::from_le_bytes([data[base], data[base + 1], data[base + 2], data[base + 3]]);
57    }
58
59    // Decode each segment
60    let mut segments: Vec<Vec<u8>> = Vec::with_capacity(num_segments);
61    for i in 0..num_segments {
62        let start = offsets[i] as usize;
63        let end = if i + 1 < num_segments {
64            offsets[i + 1] as usize
65        } else {
66            data.len()
67        };
68        if start > data.len() || end > data.len() || start > end {
69            return Err(DcmError::DecompressionError {
70                reason: format!("invalid RLE segment offset: {} > {}", start, end),
71            });
72        }
73        let seg = rle_decode_segment(&data[start..end], pixel_count)?;
74        segments.push(seg);
75    }
76
77    // Reassemble: for 8-bit mono each segment is one byte plane;
78    // for 16-bit each pair of segments is high/low byte of each pixel.
79    let samples = num_segments / bytes_per_sample;
80    let total_bytes = pixel_count * samples * bytes_per_sample;
81    let mut out = vec![0u8; total_bytes];
82
83    for sample in 0..samples {
84        for pixel in 0..pixel_count {
85            if bytes_per_sample == 1 {
86                out[pixel * samples + sample] = segments[sample][pixel];
87            } else {
88                // 16-bit: high byte is segment[sample*2], low byte is segment[sample*2+1]
89                let hi = segments[sample * 2][pixel];
90                let lo = segments[sample * 2 + 1][pixel];
91                let out_idx = (pixel * samples + sample) * 2;
92                out[out_idx] = lo; // little-endian word
93                out[out_idx + 1] = hi;
94            }
95        }
96    }
97
98    Ok(out)
99}
100
101fn rle_decode_segment(data: &[u8], pixel_count: usize) -> DcmResult<Vec<u8>> {
102    let mut out = Vec::with_capacity(pixel_count);
103    let mut pos = 0;
104
105    while pos < data.len() && out.len() < pixel_count {
106        let n = data[pos] as i8;
107        pos += 1;
108        if n >= 0 {
109            // Literal run: copy next n+1 bytes
110            let count = n as usize + 1;
111            let end = pos + count;
112            if end > data.len() {
113                return Err(DcmError::DecompressionError {
114                    reason: "RLE literal run overflows data".into(),
115                });
116            }
117            out.extend_from_slice(&data[pos..end]);
118            pos = end;
119        } else if n != -128 {
120            // Replicated run: repeat next byte (1 - n) times
121            if pos >= data.len() {
122                return Err(DcmError::DecompressionError {
123                    reason: "RLE replicated run missing byte".into(),
124                });
125            }
126            let byte = data[pos];
127            pos += 1;
128            let count = (1i32 - n as i32) as usize;
129            for _ in 0..count {
130                out.push(byte);
131            }
132        }
133        // n == -128 (0x80) is a no-op
134    }
135
136    Ok(out)
137}
138
139// ── RLE encode ────────────────────────────────────────────────────────────────
140
141fn rle_encode(data: &[u8], rows: u16, cols: u16, bits: u16) -> DcmResult<Vec<u8>> {
142    let pixel_count = rows as usize * cols as usize;
143    let bytes_per_sample = if bits <= 8 { 1usize } else { 2usize };
144
145    let total_pixels = pixel_count * bytes_per_sample;
146    if data.len() < total_pixels {
147        return Err(DcmError::CompressionError {
148            reason: "insufficient pixel data for RLE encoding".into(),
149        });
150    }
151    let samples = data.len() / total_pixels;
152    let num_segments = samples * bytes_per_sample;
153
154    // Split data into byte planes (segments)
155    let mut byte_planes: Vec<Vec<u8>> = vec![Vec::with_capacity(pixel_count); num_segments];
156    for pixel in 0..pixel_count {
157        for sample in 0..samples {
158            if bytes_per_sample == 1 {
159                byte_planes[sample].push(data[pixel * samples + sample]);
160            } else {
161                let idx = (pixel * samples + sample) * 2;
162                let lo = data[idx];
163                let hi = data[idx + 1];
164                // High byte plane first, then low byte plane
165                byte_planes[sample * 2].push(hi);
166                byte_planes[sample * 2 + 1].push(lo);
167            }
168        }
169    }
170
171    // Encode each plane
172    let mut encoded_segments: Vec<Vec<u8>> = Vec::with_capacity(num_segments);
173    for plane in &byte_planes {
174        encoded_segments.push(rle_encode_segment(plane));
175    }
176
177    // Build output: 64-byte header + segment data
178    let mut header = vec![0u8; 64];
179    let n = num_segments as u32;
180    header[0..4].copy_from_slice(&n.to_le_bytes());
181
182    // Segment offsets (relative to start of byte stream, i.e., after header offset 0)
183    let mut offset = 64u32;
184    for (i, seg) in encoded_segments.iter().enumerate() {
185        let base = 4 + i * 4;
186        header[base..base + 4].copy_from_slice(&offset.to_le_bytes());
187        offset += seg.len() as u32;
188    }
189
190    let mut out = header;
191    for seg in encoded_segments {
192        out.extend_from_slice(&seg);
193    }
194
195    // Pad to even length
196    if out.len() % 2 != 0 {
197        out.push(0);
198    }
199
200    Ok(out)
201}
202
203fn rle_encode_segment(data: &[u8]) -> Vec<u8> {
204    let mut out = Vec::new();
205    let mut i = 0;
206
207    while i < data.len() {
208        // Check for run of identical bytes
209        let mut run_len = 1;
210        while i + run_len < data.len() && data[i + run_len] == data[i] && run_len < 128 {
211            run_len += 1;
212        }
213
214        if run_len > 1 {
215            // Encode as replicated run
216            out.push((257 - run_len as i32) as u8);
217            out.push(data[i]);
218            i += run_len;
219        } else {
220            // Find length of literal run (until a run of 2+ identical bytes or end)
221            let lit_start = i;
222            while i < data.len() {
223                let mut next_run = 1;
224                while i + next_run < data.len() && data[i + next_run] == data[i] && next_run < 128 {
225                    next_run += 1;
226                }
227                if next_run >= 2 {
228                    break;
229                }
230                i += 1;
231                if i - lit_start >= 128 {
232                    break;
233                }
234            }
235            let lit_len = i - lit_start;
236            out.push((lit_len - 1) as u8);
237            out.extend_from_slice(&data[lit_start..i]);
238        }
239    }
240
241    out
242}
243
244/// Return a codec for the given transfer syntax UID, if one is built-in.
245pub fn codec_for_uid(uid: &str) -> Option<Box<dyn PixelCodec>> {
246    match uid {
247        "1.2.840.10008.1.2.5" => Some(Box::new(RleCodec)),
248        _ => None,
249    }
250}
251
252// ── Tests ─────────────────────────────────────────────────────────────────────
253
254#[cfg(test)]
255mod tests {
256    use super::*;
257
258    #[test]
259    fn rle_encode_decode_roundtrip_8bit() {
260        let rows = 4u16;
261        let cols = 4u16;
262        let bits = 8u16;
263        let data: Vec<u8> = (0..16u8).collect();
264
265        let codec = RleCodec;
266        let encoded = codec.encode_frame(&data, rows, cols, bits).unwrap();
267        let decoded = codec.decode_frame(&encoded, rows, cols, bits).unwrap();
268        assert_eq!(decoded, data);
269    }
270
271    #[test]
272    fn rle_encode_decode_roundtrip_16bit() {
273        let rows = 2u16;
274        let cols = 2u16;
275        let bits = 16u16;
276        let data: Vec<u8> = vec![0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04];
277
278        let codec = RleCodec;
279        let encoded = codec.encode_frame(&data, rows, cols, bits).unwrap();
280        let decoded = codec.decode_frame(&encoded, rows, cols, bits).unwrap();
281        assert_eq!(decoded, data);
282    }
283
284    #[test]
285    fn rle_decode_segment_runs() {
286        // 0x01 (2 literals: 0xAA, 0xBB), then 0xFE (replicate 0xCC 3 times)
287        let data = [0x01u8, 0xAA, 0xBB, 0xFE, 0xCC];
288        let result = rle_decode_segment(&data, 5).unwrap();
289        assert_eq!(result, vec![0xAA, 0xBB, 0xCC, 0xCC, 0xCC]);
290    }
291}