Skip to main content

dicom_encoding/transfer_syntax/
mod.rs

1//! Module containing the DICOM Transfer Syntax data structure and related methods.
2//! Similar to the DcmCodec in DCMTK, the `TransferSyntax` contains all of the necessary
3//! algorithms for decoding and encoding DICOM data in a certain transfer syntax.
4//!
5//! This crate does not host specific transfer syntaxes. Instead, they are created in
6//! other crates and registered in the global transfer syntax registry,
7//! which implements [`TransferSyntaxIndex`].
8//! For more information, please see the [`dicom-transfer-syntax-registry`] crate,
9//! which provides built-in implementations.
10//!
11//! This module allows you to register your own transfer syntaxes.
12//! With the `inventory-registry` Cargo feature,
13//! you can use the macro [`submit_transfer_syntax`](crate::submit_transfer_syntax)
14//! or [`submit_ele_transfer_syntax`](crate::submit_ele_transfer_syntax)
15//! to instruct the compiler to include your implementation in the registry.
16//! Without the `inventory`-based registry
17//! (in case your environment does not support it),
18//! you can still roll your own [transfer syntax index][1].
19//!
20//! [1]: TransferSyntaxIndex
21//! [`dicom-transfer-syntax-registry`]: https://docs.rs/dicom-transfer-syntax-registry
22
23use crate::adapters::{
24    DynPixelDataReader, DynPixelDataWriter, NeverPixelAdapter, PixelDataReader, PixelDataWriter,
25};
26use crate::decode::{
27    DecodeFrom, basic::BasicDecoder, explicit_be::ExplicitVRBigEndianDecoder,
28    explicit_le::ExplicitVRLittleEndianDecoder, implicit_le::ImplicitVRLittleEndianDecoder,
29};
30use crate::encode::{
31    EncodeTo, EncoderFor, explicit_be::ExplicitVRBigEndianEncoder,
32    explicit_le::ExplicitVRLittleEndianEncoder, implicit_le::ImplicitVRLittleEndianEncoder,
33};
34use std::io::{Read, Write};
35
36pub use byteordered::Endianness;
37
38/// A decoder with its type erased.
39pub type DynDecoder<S> = Box<dyn DecodeFrom<S>>;
40
41/// An encoder with its type erased.
42pub type DynEncoder<'w, W> = Box<dyn EncodeTo<W> + 'w>;
43
44/// A DICOM transfer syntax specifier.
45///
46/// Custom encoding and decoding capabilities
47/// are defined via the parameter types `D` and `P`,
48/// The type parameter `D` specifies
49/// an adapter for reading and writing data sets,
50/// whereas `P` specifies the encoder and decoder of encapsulated pixel data.
51///
52/// This type is usually consumed in its "type erased" form,
53/// with its default parameter types.
54/// On the other hand, implementers of `TransferSyntax` will typically specify
55/// concrete types for `D` and `P`,
56/// which are type-erased before registration.
57/// If the transfer syntax requires no data set codec,
58/// `D` can be assigned to the utility type [`NeverAdapter`].
59/// If pixel data encoding/decoding is not needed or not supported,
60/// you can assign `P` to [`NeverPixelAdapter`].
61#[derive(Debug)]
62pub struct TransferSyntax<D = DynDataRWAdapter, R = DynPixelDataReader, W = DynPixelDataWriter> {
63    /// The unique identifier of the transfer syntax.
64    uid: &'static str,
65    /// The name of the transfer syntax.
66    name: &'static str,
67    /// The byte order of data.
68    byte_order: Endianness,
69    /// Whether the transfer syntax mandates an explicit value representation,
70    /// or the VR is implicit.
71    explicit_vr: bool,
72    /// The transfer syntax' requirements and implemented capabilities.
73    codec: Codec<D, R, W>,
74}
75
76/// Wrapper type for a provider of transfer syntax descriptors.
77///
78/// This is a piece of the plugin interface for
79/// registering and collecting transfer syntaxes.
80/// Implementers and consumers of transfer syntaxes
81/// will usually not interact with it directly.
82/// In order to register a new transfer syntax,
83/// see the macro [`submit_transfer_syntax`](crate::submit_transfer_syntax).
84#[derive(Debug, Copy, Clone)]
85pub struct TransferSyntaxFactory(pub fn() -> TransferSyntax);
86
87#[cfg(feature = "inventory-registry")]
88// Collect transfer syntax specifiers from other crates.
89inventory::collect!(TransferSyntaxFactory);
90
91/// Trait for a container/repository of transfer syntax specifiers.
92///
93/// Types implementing this trait are held responsible for populating
94/// themselves with a set of transfer syntaxes, which can be fully supported,
95/// partially supported, or not supported. Usually, only one implementation
96/// of this trait is used for the entire program,
97/// the most common one being the `TransferSyntaxRegistry` type
98/// from [`transfer-syntax-registry`].
99///
100/// [`transfer-syntax-registry`]: https://docs.rs/dicom-transfer-syntax-registry
101pub trait TransferSyntaxIndex {
102    /// Obtain a DICOM transfer syntax by its respective UID.
103    ///
104    /// Implementations of this method should be robust to the possible
105    /// presence of trailing null characters (`\0`) in `uid`.
106    fn get(&self, uid: &str) -> Option<&TransferSyntax>;
107}
108
109impl<T: ?Sized> TransferSyntaxIndex for &T
110where
111    T: TransferSyntaxIndex,
112{
113    fn get(&self, uid: &str) -> Option<&TransferSyntax> {
114        (**self).get(uid)
115    }
116}
117
118#[cfg(feature = "inventory-registry")]
119#[macro_export]
120/// Submit a transfer syntax specifier to be supported by the
121/// program's runtime. This is to be used by crates wishing to provide
122/// additional support for a certain transfer syntax using the
123/// main transfer syntax registry.
124///
125/// This macro does not actually "run" anything, so place it outside of a
126/// function body at the root of the crate.
127/// The expression is evaluated when the transfer syntax registry is populated
128/// upon the first request,
129/// and must resolve to a value of type [`TransferSyntax<D, P>`],
130/// for valid definitions of the parameter types `D` and `P`.
131/// The macro will type-erase these parameters automatically.
132///
133/// # Example
134///
135/// One common use case is wanting to read data sets
136/// of DICOM objects in a private transfer syntax,
137/// even when a decoder for that pixel data is not available.
138/// By writing a simple stub at your project's root,
139/// the rest of the ecosystem will know
140/// how to read and write data sets in that transfer syntax.
141///
142/// ```
143/// use dicom_encoding::{
144///     submit_transfer_syntax, AdapterFreeTransferSyntax, Codec, Endianness,
145/// };
146///
147/// submit_transfer_syntax!(AdapterFreeTransferSyntax::new(
148///     // Transfer Syntax UID
149///     "1.3.46.670589.33.1.4.1",
150///     // Name/alias
151///     "CT Private ELE",
152///     // Data set byte order
153///     Endianness::Little,
154///     // Explicit VR (true) or Implicit VR (false)
155///     true,
156///     Codec::EncapsulatedPixelData(None, None),  // pixel data codec
157/// ));
158/// ```
159///
160/// With [`Codec::EncapsulatedPixelData(None, None)`][1],
161/// we are indicating that the transfer syntax uses encapsulated pixel data.
162/// albeit without the means to decode or encode it.
163/// See the [`adapters`](crate::adapters) module
164/// to know how to write pixel data encoders and decoders.
165///
166/// [1]: Codec::EncapsulatedPixelData
167macro_rules! submit_transfer_syntax {
168    ($ts: expr) => {
169        $crate::inventory::submit! {
170            $crate::transfer_syntax::TransferSyntaxFactory(|| ($ts).erased())
171        }
172    };
173}
174
175#[cfg(not(feature = "inventory-registry"))]
176#[macro_export]
177/// Submit a transfer syntax specifier to be supported by the
178/// program's runtime. This is to be used by crates wishing to provide
179/// additional support for a certain transfer syntax using the
180/// main transfer syntax registry.
181///
182/// This macro does actually "run" anything, so place it outside of a
183/// function body at the root of the crate.
184///
185/// Without the `inventory-registry` feature, this request is ignored.
186macro_rules! submit_transfer_syntax {
187    ($ts: expr) => {
188        // ignore request
189    };
190}
191
192#[cfg(feature = "inventory-registry")]
193#[macro_export]
194/// Submit an explicit VR little endian transfer syntax specifier
195/// to be supported by the program's runtime.
196///
197/// This macro is equivalent in behavior as [`submit_transfer_syntax`](crate::submit_transfer_syntax),
198/// but it is easier to use when
199/// writing support for compressed pixel data formats,
200/// which are usually in explicit VR little endian.
201///
202/// This macro does not actually "run" anything, so place it outside of a
203/// function body at the root of the crate.
204/// The expression is evaluated when the transfer syntax registry is populated
205/// upon the first request,
206/// and must resolve to a value of type [`Codec<D, R, W>`],
207/// for valid definitions of the parameter types `D`, `R`, and `W`.
208/// The macro will type-erase these parameters automatically.
209///
210/// # Example
211///
212/// One common use case is wanting to read data sets
213/// of DICOM objects in a private transfer syntax,
214/// even when a decoder for that pixel data is not available.
215/// By writing a simple stub at your project's root,
216/// the rest of the ecosystem will know
217/// how to read and write data sets in that transfer syntax.
218///
219/// ```
220/// use dicom_encoding::{submit_ele_transfer_syntax, Codec};
221///
222/// submit_ele_transfer_syntax!(
223///     // Transfer Syntax UID
224///     "1.3.46.670589.33.1.4.1",
225///     // Name/alias
226///     "CT Private ELE",
227///     // pixel data codec
228///     Codec::encapsulated_pixel_data_stub()
229/// );
230/// ```
231///
232/// With [`Codec::EncapsulatedPixelData`],
233/// we are indicating that the transfer syntax uses encapsulated pixel data.
234/// albeit without the means to decode or encode it.
235/// See the [`adapters`](crate::adapters) module
236/// to know how to write pixel data encoders and decoders.
237macro_rules! submit_ele_transfer_syntax {
238    ($uid: expr, $name: expr, $codec: expr) => {
239        $crate::submit_transfer_syntax! {
240            $crate::TransferSyntax::new_ele(
241                $uid,
242                $name,
243                $codec
244            )
245        }
246    };
247}
248
249#[cfg(not(feature = "inventory-registry"))]
250#[macro_export]
251/// Submit an explicit VR little endian transfer syntax specifier
252/// to be supported by the program's runtime.
253///
254/// This macro is equivalent in behavior as [`submit_transfer_syntax`],
255/// but it is easier to use when
256/// writing support for compressed pixel data formats,
257/// which are usually in explicit VR little endian.
258///
259/// This macro does actually "run" anything, so place it outside of a
260/// function body at the root of the crate.
261///
262/// Without the `inventory-registry` feature, this request is ignored.
263macro_rules! submit_ele_transfer_syntax {
264    ($uid: literal, $name: literal, $codec: expr) => {
265        // ignore request
266    };
267}
268
269/// A description and possible implementation regarding
270/// the encoding and decoding requirements of a transfer syntax.
271/// This is also used as a means to describe whether pixel data is encapsulated
272/// and whether this implementation supports decoding and/or encoding it.
273///
274/// ### Type parameters
275///
276/// - `D` should implement [`DataRWAdapter`]
277///   and defines how one should read and write DICOM data sets,
278///   such as in the case for deflated data.
279///   When no special considerations for data set reading and writing
280///   are necessary, this can be set to [`NeverAdapter`].
281/// - `R` should implement [`PixelDataReader`],
282///   and enables programs to convert encapsulated pixel data fragments
283///   into native pixel data.
284/// - `W` should implement [`PixelDataWriter`],
285///   and enables programs to convert native pixel data
286///   into encapsulated pixel data.
287///
288#[derive(Debug, Clone, PartialEq)]
289pub enum Codec<D, R, W> {
290    /// No codec is required for this transfer syntax.
291    ///
292    /// Pixel data, if any, should be in its _native_, unencapsulated format.
293    None,
294    /// Pixel data for this transfer syntax is encapsulated
295    /// and likely subjected to a specific encoding process.
296    /// The first part of the tuple struct contains the pixel data decoder,
297    /// whereas the second item is for the pixel data encoder.
298    ///
299    /// Decoding of the pixel data is not supported
300    /// if the decoder is `None`.
301    /// In this case, the program should still be able to
302    /// parse DICOM data sets
303    /// and fetch the pixel data in its encapsulated form.
304    EncapsulatedPixelData(Option<R>, Option<W>),
305    /// A custom data set codec is required for reading and writing data sets.
306    ///
307    /// If the item in the tuple struct is `None`,
308    /// then no reading and writing whatsoever is supported.
309    /// This could be used by a stub of
310    /// _Deflated Explicit VR Little Endian_, for example.
311    Dataset(Option<D>),
312}
313
314impl Codec<NeverAdapter, NeverPixelAdapter, NeverPixelAdapter> {
315    /// Create a stub codec for encapsulated pixel data
316    pub fn encapsulated_pixel_data_stub() -> Self {
317        Codec::EncapsulatedPixelData(None, None)
318    }
319}
320
321impl<R> Codec<NeverAdapter, R, NeverPixelAdapter> {
322    /// Create a codec for encapsulated pixel data
323    /// with a pixel data decoder but not an encoder
324    pub fn encapsulated_pixel_data_reader(reader: R) -> Self {
325        Codec::EncapsulatedPixelData(Some(reader), None)
326    }
327}
328
329impl<R, W> Codec<NeverAdapter, R, W> {
330    /// Create a codec for encapsulated pixel data
331    /// with a pixel data decoder and encoder
332    pub fn encapsulated_pixel_data(reader: R, writer: W) -> Self {
333        Codec::EncapsulatedPixelData(Some(reader), Some(writer))
334    }
335}
336
337/// An alias for a transfer syntax specifier with no pixel data encapsulation
338/// nor data set deflating.
339pub type AdapterFreeTransferSyntax =
340    TransferSyntax<NeverAdapter, NeverPixelAdapter, NeverPixelAdapter>;
341
342/// A fully dynamic adapter of byte read and write streams.
343pub trait DataRWAdapter {
344    /// Adapt a byte reader.
345    fn adapt_reader<'r>(&self, reader: Box<dyn Read + 'r>) -> Box<dyn Read + 'r>;
346
347    /// Adapt a byte writer.
348    fn adapt_writer<'w>(&self, writer: Box<dyn Write + 'w>) -> Box<dyn Write + 'w>;
349}
350
351/// Alias type for a dynamically dispatched data adapter.
352pub type DynDataRWAdapter = Box<dyn DataRWAdapter + Send + Sync>;
353
354impl<T> DataRWAdapter for &'_ T
355where
356    T: DataRWAdapter,
357{
358    /// Adapt a byte reader.
359    fn adapt_reader<'r>(&self, reader: Box<dyn Read + 'r>) -> Box<dyn Read + 'r> {
360        (**self).adapt_reader(reader)
361    }
362
363    /// Adapt a byte writer.
364    fn adapt_writer<'w>(&self, writer: Box<dyn Write + 'w>) -> Box<dyn Write + 'w> {
365        (**self).adapt_writer(writer)
366    }
367}
368
369/// An immaterial type representing a data set adapter which is never required,
370/// and as such is never instantiated.
371/// Most transfer syntaxes use this,
372/// as they do not have to adapt readers and writers
373/// for encoding and decoding data sets.
374/// The main exception is in the family of
375/// _Deflated Explicit VR Little Endian_ transfer syntaxes.
376#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
377pub enum NeverAdapter {}
378
379impl DataRWAdapter for NeverAdapter {
380    fn adapt_reader<'r>(&self, _reader: Box<dyn Read + 'r>) -> Box<dyn Read + 'r> {
381        unreachable!()
382    }
383
384    fn adapt_writer<'w>(&self, _writer: Box<dyn Write + 'w>) -> Box<dyn Write + 'w> {
385        unreachable!()
386    }
387}
388
389impl<D, R, W> TransferSyntax<D, R, W> {
390    /// Create a new transfer syntax descriptor.
391    ///
392    /// Note that only transfer syntax implementers are expected to
393    /// construct TS descriptors from scratch.
394    /// For a practical usage of transfer syntaxes,
395    /// one should look up an existing transfer syntax registry by UID.
396    ///
397    /// # Example
398    ///
399    /// To register a private transfer syntax in your program,
400    /// use [`submit_transfer_syntax`](crate::submit_transfer_syntax)
401    /// outside of a function body:
402    ///  
403    /// ```no_run
404    /// # use dicom_encoding::{
405    /// #     submit_transfer_syntax, Codec, Endianness,
406    /// #     NeverAdapter, NeverPixelAdapter, TransferSyntax,
407    /// # };
408    /// submit_transfer_syntax! {
409    ///     TransferSyntax::<NeverAdapter, NeverPixelAdapter, NeverPixelAdapter>::new(
410    ///         "1.3.46.670589.33.1.4.1",
411    ///         "CT-Private-ELE",
412    ///         Endianness::Little,
413    ///         true,
414    ///         Codec::EncapsulatedPixelData(None, None),
415    ///     )
416    /// }
417    /// ```
418    pub const fn new(
419        uid: &'static str,
420        name: &'static str,
421        byte_order: Endianness,
422        explicit_vr: bool,
423        codec: Codec<D, R, W>,
424    ) -> Self {
425        TransferSyntax {
426            uid,
427            name,
428            byte_order,
429            explicit_vr,
430            codec,
431        }
432    }
433
434    /// Create a new descriptor
435    /// for a transfer syntax in explicit VR little endian.
436    ///
437    /// Note that only transfer syntax implementers are expected to
438    /// construct TS descriptors from scratch.
439    /// For a practical usage of transfer syntaxes,
440    /// one should look up an existing transfer syntax registry by UID.
441    ///
442    /// # Example
443    ///
444    /// To register a private transfer syntax in your program,
445    /// use [`submit_transfer_syntax`](crate::submit_transfer_syntax)
446    /// outside of a function body:
447    ///  
448    /// ```no_run
449    /// # use dicom_encoding::{
450    /// #     submit_transfer_syntax, Codec,
451    /// #     NeverAdapter, NeverPixelAdapter, TransferSyntax,
452    /// # };
453    /// submit_transfer_syntax! {
454    ///     TransferSyntax::<NeverAdapter, NeverPixelAdapter, NeverPixelAdapter>::new_ele(
455    ///         "1.3.46.670589.33.1.4.1",
456    ///         "CT-Private-ELE",
457    ///         Codec::EncapsulatedPixelData(None, None),
458    ///     )
459    /// }
460    /// ```
461    ///
462    /// See [`submit_ele_transfer_syntax`](crate::submit_ele_transfer_syntax)
463    /// for an alternative.
464    pub const fn new_ele(uid: &'static str, name: &'static str, codec: Codec<D, R, W>) -> Self {
465        TransferSyntax {
466            uid,
467            name,
468            byte_order: Endianness::Little,
469            explicit_vr: true,
470            codec,
471        }
472    }
473
474    /// Obtain this transfer syntax' unique identifier.
475    pub const fn uid(&self) -> &'static str {
476        self.uid
477    }
478
479    /// Obtain the name of this transfer syntax.
480    pub const fn name(&self) -> &'static str {
481        self.name
482    }
483
484    /// Obtain this transfer syntax' expected endianness.
485    pub const fn endianness(&self) -> Endianness {
486        self.byte_order
487    }
488
489    /// Obtain this transfer syntax' codec specification.
490    pub fn codec(&self) -> &Codec<D, R, W> {
491        &self.codec
492    }
493
494    /// Check whether this transfer syntax specifier provides a complete
495    /// implementation,
496    /// meaning that it can both decode and encode in this transfer syntax.
497    pub fn is_fully_supported(&self) -> bool {
498        matches!(
499            self.codec,
500            Codec::None | Codec::Dataset(Some(_)) | Codec::EncapsulatedPixelData(Some(_), Some(_)),
501        )
502    }
503
504    /// Check whether no codecs are required for this transfer syntax,
505    /// meaning that a complete implementation is available
506    /// and no pixel data conversion is required.
507    pub fn is_codec_free(&self) -> bool {
508        matches!(self.codec, Codec::None)
509    }
510
511    /// Check whether neither reading nor writing of data sets is supported.
512    /// If this is `true`, encoding and decoding will not be available.
513    pub fn is_unsupported(&self) -> bool {
514        matches!(self.codec, Codec::Dataset(None))
515    }
516
517    /// Check whether this transfer syntax expects pixel data to be encapsulated.
518    ///
519    /// This does not imply that the pixel data can be decoded.
520    pub fn is_encapsulated_pixel_data(&self) -> bool {
521        matches!(self.codec, Codec::EncapsulatedPixelData(..))
522    }
523
524    /// Check whether reading and writing the pixel data is unsupported.
525    /// If this is `true`, encoding and decoding of the data set may still
526    /// be possible, but the pixel data will only be available in its
527    /// encapsulated form.
528    pub fn is_unsupported_pixel_encapsulation(&self) -> bool {
529        matches!(
530            self.codec,
531            Codec::Dataset(None) | Codec::EncapsulatedPixelData(None, None)
532        )
533    }
534
535    /// Check whether this codec can fully decode
536    /// both data sets and pixel data.
537    pub fn can_decode_all(&self) -> bool {
538        matches!(
539            self.codec,
540            Codec::None | Codec::Dataset(Some(_)) | Codec::EncapsulatedPixelData(Some(_), _)
541        )
542    }
543
544    /// Check whether this codec can decode the data set.
545    pub fn can_decode_dataset(&self) -> bool {
546        matches!(
547            self.codec,
548            Codec::None | Codec::Dataset(Some(_)) | Codec::EncapsulatedPixelData(..)
549        )
550    }
551
552    /// Retrieve the appropriate data element decoder for this transfer syntax.
553    /// Can yield none if decoding is not supported.
554    ///
555    /// The resulting decoder does not consider pixel data encapsulation or
556    /// data set compression rules. This means that the consumer of this method
557    /// needs to adapt the reader before using the decoder.
558    pub fn decoder<'s>(&self) -> Option<DynDecoder<dyn Read + 's>> {
559        self.decoder_for()
560    }
561
562    /// Retrieve the appropriate data element decoder for this transfer syntax
563    /// and given reader type (this method is not object safe).
564    /// Can yield none if decoding is not supported.
565    ///
566    /// The resulting decoder does not consider pixel data encapsulation or
567    /// data set compression rules. This means that the consumer of this method
568    /// needs to adapt the reader before using the decoder.
569    pub fn decoder_for<S>(&self) -> Option<DynDecoder<S>>
570    where
571        Self: Sized,
572        S: ?Sized + Read,
573    {
574        match (self.byte_order, self.explicit_vr) {
575            (Endianness::Little, false) => Some(Box::<ImplicitVRLittleEndianDecoder<_>>::default()),
576            (Endianness::Little, true) => Some(Box::<ExplicitVRLittleEndianDecoder>::default()),
577            (Endianness::Big, true) => Some(Box::<ExplicitVRBigEndianDecoder>::default()),
578            _ => None,
579        }
580    }
581
582    /// Retrieve the appropriate data element encoder for this transfer syntax.
583    /// Can yield none if encoding is not supported. The resulting encoder does not
584    /// consider pixel data encapsulation or data set compression rules.
585    pub fn encoder<'w>(&self) -> Option<DynEncoder<'w, dyn Write + 'w>> {
586        self.encoder_for()
587    }
588
589    /// Retrieve the appropriate data element encoder for this transfer syntax
590    /// and the given writer type (this method is not object safe).
591    /// Can yield none if encoding is not supported. The resulting encoder does not
592    /// consider pixel data encapsulation or data set compression rules.
593    pub fn encoder_for<'w, T>(&self) -> Option<DynEncoder<'w, T>>
594    where
595        Self: Sized,
596        T: ?Sized + Write + 'w,
597    {
598        match (self.byte_order, self.explicit_vr) {
599            (Endianness::Little, false) => Some(Box::new(EncoderFor::new(
600                ImplicitVRLittleEndianEncoder::default(),
601            ))),
602            (Endianness::Little, true) => Some(Box::new(EncoderFor::new(
603                ExplicitVRLittleEndianEncoder::default(),
604            ))),
605            (Endianness::Big, true) => Some(Box::new(EncoderFor::new(
606                ExplicitVRBigEndianEncoder::default(),
607            ))),
608            _ => None,
609        }
610    }
611
612    /// Obtain a dynamic basic decoder, based on this transfer syntax' expected endianness.
613    pub fn basic_decoder(&self) -> BasicDecoder {
614        BasicDecoder::from(self.endianness())
615    }
616
617    /// Obtain a reference to the underlying pixel data reader.
618    ///
619    /// Returns `None` if pixel data is not encapsulated
620    /// or a pixel data decoder implementation is not available.
621    pub fn pixel_data_reader(&self) -> Option<&R> {
622        match &self.codec {
623            Codec::EncapsulatedPixelData(r, _) => r.as_ref(),
624            _ => None,
625        }
626    }
627
628    /// Obtain a reference to the underlying pixel data writer.
629    ///
630    /// Returns `None` if pixel data is not encapsulated
631    /// or a pixel data encoder implementation is not available.
632    pub fn pixel_data_writer(&self) -> Option<&W> {
633        match &self.codec {
634            Codec::EncapsulatedPixelData(_, w) => w.as_ref(),
635            _ => None,
636        }
637    }
638
639    /// Type-erase the pixel data or data set codec.
640    pub fn erased(self) -> TransferSyntax
641    where
642        D: Send + Sync + 'static,
643        D: DataRWAdapter,
644        R: Send + Sync + 'static,
645        R: PixelDataReader,
646        W: Send + Sync + 'static,
647        W: PixelDataWriter,
648    {
649        let codec = match self.codec {
650            Codec::Dataset(d) => Codec::Dataset(d.map(|d| Box::new(d) as _)),
651            Codec::EncapsulatedPixelData(r, w) => Codec::EncapsulatedPixelData(
652                r.map(|r| Box::new(r) as _),
653                w.map(|w| Box::new(w) as _),
654            ),
655            Codec::None => Codec::None,
656        };
657
658        TransferSyntax {
659            uid: self.uid,
660            name: self.name,
661            byte_order: self.byte_order,
662            explicit_vr: self.explicit_vr,
663            codec,
664        }
665    }
666}