1use std::collections::HashMap;
7use std::sync::{Arc, LazyLock, RwLock};
8
9use dicom_toolkit_core::error::{DcmError, DcmResult};
10use dicom_toolkit_data::value::PixelData;
11use dicom_toolkit_dict::ts::transfer_syntaxes;
12
13pub trait ImageCodec: Send + Sync {
20 fn transfer_syntax_uids(&self) -> &[&str];
22
23 fn decode(
27 &self,
28 encapsulated: &PixelData,
29 rows: u16,
30 columns: u16,
31 samples_per_pixel: u8,
32 bits_allocated: u8,
33 ) -> DcmResult<Vec<u8>>;
34
35 fn encode(
37 &self,
38 pixels: &[u8],
39 rows: u16,
40 columns: u16,
41 samples_per_pixel: u8,
42 bits_allocated: u8,
43 ) -> DcmResult<PixelData>;
44}
45
46struct RleCodec;
49
50impl ImageCodec for RleCodec {
51 fn transfer_syntax_uids(&self) -> &[&str] {
52 &[transfer_syntaxes::RLE_LOSSLESS.uid]
53 }
54
55 fn decode(
56 &self,
57 pixel_data: &PixelData,
58 rows: u16,
59 columns: u16,
60 samples_per_pixel: u8,
61 bits_allocated: u8,
62 ) -> DcmResult<Vec<u8>> {
63 let fragments = match pixel_data {
64 PixelData::Encapsulated { fragments, .. } => fragments,
65 PixelData::Native { bytes } => return Ok(bytes.clone()),
66 };
67
68 let mut all_frames = Vec::new();
69 for fragment in fragments {
70 let frame = crate::rle::rle_decode_frame(
71 fragment,
72 rows,
73 columns,
74 samples_per_pixel,
75 bits_allocated,
76 )?;
77 all_frames.extend_from_slice(&frame);
78 }
79 Ok(all_frames)
80 }
81
82 fn encode(
83 &self,
84 pixels: &[u8],
85 rows: u16,
86 columns: u16,
87 samples_per_pixel: u8,
88 bits_allocated: u8,
89 ) -> DcmResult<PixelData> {
90 let encoded =
91 crate::rle::rle_encode_frame(pixels, rows, columns, samples_per_pixel, bits_allocated)?;
92 Ok(PixelData::Encapsulated {
93 offset_table: vec![0],
94 fragments: vec![encoded],
95 })
96 }
97}
98
99struct JpegCodec {
102 uids: Vec<&'static str>,
103}
104
105impl JpegCodec {
106 fn baseline() -> Self {
107 Self {
108 uids: vec![
109 transfer_syntaxes::JPEG_BASELINE.uid,
110 transfer_syntaxes::JPEG_EXTENDED.uid,
111 ],
112 }
113 }
114}
115
116impl ImageCodec for JpegCodec {
117 fn transfer_syntax_uids(&self) -> &[&str] {
118 &self.uids
119 }
120
121 fn decode(
122 &self,
123 pixel_data: &PixelData,
124 _rows: u16,
125 _columns: u16,
126 _samples_per_pixel: u8,
127 _bits_allocated: u8,
128 ) -> DcmResult<Vec<u8>> {
129 let fragments = match pixel_data {
130 PixelData::Encapsulated { fragments, .. } => fragments,
131 PixelData::Native { bytes } => return Ok(bytes.clone()),
132 };
133
134 let mut all_frames = Vec::new();
135 for fragment in fragments {
136 let frame = crate::jpeg::decoder::decode_jpeg(fragment)?;
137 all_frames.extend_from_slice(&frame.data);
138 }
139 Ok(all_frames)
140 }
141
142 fn encode(
143 &self,
144 pixels: &[u8],
145 rows: u16,
146 columns: u16,
147 samples_per_pixel: u8,
148 _bits_allocated: u8,
149 ) -> DcmResult<PixelData> {
150 use crate::jpeg::params::JpegParams;
151 let encoded = crate::jpeg::encoder::encode_jpeg(
152 pixels,
153 columns,
154 rows,
155 samples_per_pixel,
156 &JpegParams::default(),
157 )?;
158 Ok(PixelData::Encapsulated {
159 offset_table: vec![0],
160 fragments: vec![encoded],
161 })
162 }
163}
164
165struct JpegLsCodec;
168
169impl ImageCodec for JpegLsCodec {
170 fn transfer_syntax_uids(&self) -> &[&str] {
171 &[
172 transfer_syntaxes::JPEG_LS_LOSSLESS.uid,
173 transfer_syntaxes::JPEG_LS_LOSSY.uid,
174 ]
175 }
176
177 fn decode(
178 &self,
179 pixel_data: &PixelData,
180 _rows: u16,
181 _columns: u16,
182 _samples_per_pixel: u8,
183 _bits_allocated: u8,
184 ) -> DcmResult<Vec<u8>> {
185 let fragments = match pixel_data {
186 PixelData::Encapsulated { fragments, .. } => fragments,
187 PixelData::Native { bytes } => return Ok(bytes.clone()),
188 };
189 let empty = vec![];
190 let data = fragments.first().unwrap_or(&empty);
191 let decoded = crate::jpeg_ls::decoder::decode_jpeg_ls(data)?;
192 Ok(decoded.pixels)
193 }
194
195 fn encode(
196 &self,
197 pixels: &[u8],
198 rows: u16,
199 columns: u16,
200 samples_per_pixel: u8,
201 bits_allocated: u8,
202 ) -> DcmResult<PixelData> {
203 let near = 0; let encoded = crate::jpeg_ls::encoder::encode_jpeg_ls(
205 pixels,
206 columns as u32,
207 rows as u32,
208 bits_allocated,
209 samples_per_pixel,
210 near,
211 )?;
212 Ok(PixelData::Encapsulated {
213 offset_table: vec![],
214 fragments: vec![encoded],
215 })
216 }
217}
218
219pub struct CodecRegistry {
223 codecs: RwLock<HashMap<String, Arc<dyn ImageCodec>>>,
224}
225
226impl CodecRegistry {
227 pub fn new() -> Self {
229 Self {
230 codecs: RwLock::new(HashMap::new()),
231 }
232 }
233
234 pub fn register(&self, codec: Arc<dyn ImageCodec>) {
236 let mut map = self.codecs.write().unwrap();
237 for uid in codec.transfer_syntax_uids() {
238 map.insert(uid.to_string(), Arc::clone(&codec));
239 }
240 }
241
242 pub fn find(&self, transfer_syntax_uid: &str) -> Option<Arc<dyn ImageCodec>> {
244 self.codecs
245 .read()
246 .unwrap()
247 .get(transfer_syntax_uid)
248 .cloned()
249 }
250
251 pub fn find_required(&self, transfer_syntax_uid: &str) -> DcmResult<Arc<dyn ImageCodec>> {
253 self.find(transfer_syntax_uid)
254 .ok_or_else(|| DcmError::NoCodec {
255 uid: transfer_syntax_uid.to_string(),
256 })
257 }
258}
259
260impl Default for CodecRegistry {
261 fn default() -> Self {
262 Self::new()
263 }
264}
265
266pub static GLOBAL_REGISTRY: LazyLock<CodecRegistry> = LazyLock::new(|| {
270 let reg = CodecRegistry::new();
271 reg.register(Arc::new(RleCodec));
272 reg.register(Arc::new(JpegCodec::baseline()));
273 reg.register(Arc::new(JpegLsCodec));
274 reg
275});
276
277const SUPPORTED_TS: &[&str] = &[
281 transfer_syntaxes::RLE_LOSSLESS.uid,
282 transfer_syntaxes::JPEG_BASELINE.uid,
283 transfer_syntaxes::JPEG_EXTENDED.uid,
284 transfer_syntaxes::JPEG_LOSSLESS.uid,
285 transfer_syntaxes::JPEG_LOSSLESS_SV1.uid,
286 transfer_syntaxes::JPEG_LS_LOSSLESS.uid,
287 transfer_syntaxes::JPEG_LS_LOSSY.uid,
288];
289
290#[derive(Debug, Clone, Copy)]
292pub struct CodecInfo {
293 pub transfer_syntax_uid: &'static str,
295 pub name: &'static str,
297}
298
299pub fn can_decode(ts_uid: &str) -> bool {
301 SUPPORTED_TS.contains(&ts_uid)
302}
303
304pub fn supported_transfer_syntaxes() -> &'static [&'static str] {
306 SUPPORTED_TS
307}
308
309pub fn decode_pixel_data(
314 ts_uid: &str,
315 data: &[u8],
316 rows: u16,
317 cols: u16,
318 bits_allocated: u16,
319 samples: u16,
320) -> DcmResult<Vec<u8>> {
321 match ts_uid {
322 uid if uid == transfer_syntaxes::RLE_LOSSLESS.uid => {
323 crate::rle::RleCodec::decode(data, rows, cols, bits_allocated, samples)
324 }
325 uid if uid == transfer_syntaxes::JPEG_BASELINE.uid
326 || uid == transfer_syntaxes::JPEG_EXTENDED.uid
327 || uid == transfer_syntaxes::JPEG_LOSSLESS.uid
328 || uid == transfer_syntaxes::JPEG_LOSSLESS_SV1.uid =>
329 {
330 crate::jpeg::JpegDecoder::decode_frame(data).map(|f| f.pixels)
331 }
332 uid if uid == transfer_syntaxes::JPEG_LS_LOSSLESS.uid
333 || uid == transfer_syntaxes::JPEG_LS_LOSSY.uid =>
334 {
335 crate::jpeg_ls::JpegLsCodec::decode_frame(data).map(|f| f.pixels)
336 }
337 uid => Err(DcmError::NoCodec {
338 uid: uid.to_string(),
339 }),
340 }
341}
342
343#[cfg(test)]
346mod tests {
347 use super::*;
348 use dicom_toolkit_dict::ts::transfer_syntaxes;
349
350 #[test]
351 fn global_registry_has_rle() {
352 let codec = GLOBAL_REGISTRY.find(transfer_syntaxes::RLE_LOSSLESS.uid);
353 assert!(codec.is_some());
354 }
355
356 #[test]
357 fn global_registry_has_jpeg_baseline() {
358 let codec = GLOBAL_REGISTRY.find(transfer_syntaxes::JPEG_BASELINE.uid);
359 assert!(codec.is_some());
360 }
361
362 #[test]
363 fn global_registry_has_jpeg_extended() {
364 let codec = GLOBAL_REGISTRY.find(transfer_syntaxes::JPEG_EXTENDED.uid);
365 assert!(codec.is_some());
366 }
367
368 #[test]
369 fn global_registry_has_jpeg_ls() {
370 let codec = GLOBAL_REGISTRY.find(transfer_syntaxes::JPEG_LS_LOSSLESS.uid);
371 assert!(codec.is_some());
372 }
373
374 #[test]
375 fn unknown_uid_returns_none() {
376 let codec = GLOBAL_REGISTRY.find("1.2.3.4.5.999");
377 assert!(codec.is_none());
378 }
379
380 #[test]
381 fn find_required_returns_error_for_unknown() {
382 let result = GLOBAL_REGISTRY.find_required("1.9.9.9.9");
383 assert!(matches!(result, Err(DcmError::NoCodec { .. })));
384 }
385
386 #[test]
389 fn codec_registry_can_decode_rle() {
390 assert!(can_decode(transfer_syntaxes::RLE_LOSSLESS.uid));
391 }
392
393 #[test]
394 fn codec_registry_can_decode_jpeg_baseline() {
395 assert!(can_decode(transfer_syntaxes::JPEG_BASELINE.uid));
396 }
397
398 #[test]
399 fn codec_registry_cannot_decode_unknown() {
400 assert!(!can_decode("1.2.3.4.5.999"));
401 }
402
403 #[test]
404 fn supported_transfer_syntaxes_is_non_empty() {
405 let list = supported_transfer_syntaxes();
406 assert!(!list.is_empty());
407 assert!(list.contains(&transfer_syntaxes::RLE_LOSSLESS.uid));
408 assert!(list.contains(&transfer_syntaxes::JPEG_BASELINE.uid));
409 }
410
411 #[test]
412 fn rle_codec_roundtrip_via_registry() {
413 use crate::rle::rle_encode_frame;
414
415 let rows = 4u16;
416 let cols = 4u16;
417 let samples = 1u8;
418 let bits = 8u8;
419 let pixels: Vec<u8> = (0u8..16).collect();
420
421 let encoded_frame = rle_encode_frame(&pixels, rows, cols, samples, bits).unwrap();
422 let pixel_data = PixelData::Encapsulated {
423 offset_table: vec![0],
424 fragments: vec![encoded_frame],
425 };
426
427 let codec = GLOBAL_REGISTRY
428 .find(transfer_syntaxes::RLE_LOSSLESS.uid)
429 .unwrap();
430 let decoded = codec
431 .decode(&pixel_data, rows, cols, samples, bits)
432 .unwrap();
433 assert_eq!(&decoded[..16], &pixels[..]);
434 }
435}