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