use libc::{c_char, c_int, c_uchar, c_uint, size_t};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum GDCMError {
#[error("Unknown error.")]
Unknown,
#[error("Input buffer pointer is NULL.")]
PointerNULL,
#[error("Empty input buffer.")]
EmptyBuffer,
#[error("[GDCM PRE] {0}")]
Pre(Error),
#[error("[GDCM PHOTO] {0}")]
Photo(Error),
#[error("[GDCM POST] {0}")]
Post(Error),
}
#[derive(Error, Debug)]
pub enum Error {
#[error("Could not read stream.")]
ReadStream,
#[error("Invalid photometric interpretation.")]
InvalidPhotometricInterpretation,
#[error("Could not execute change.")]
ExecuteChange,
#[error("Could not execute LUT change.")]
ExecuteLUTChange,
#[error("Could not write stream.")]
WriteStream,
#[error("Could not execute FileExplicitFilter.")]
FileExplicitFilter,
#[error("Invalid transfer syntax.")]
InvalidTransferSyntax,
#[error("Could not derive file.")]
DeriveFile,
}
#[derive(Copy, Clone, Debug)]
pub enum TransferSyntax {
None,
ImplicitVRLittleEndian,
ExplicitVRLittleEndian,
ExplicitVRBigEndian,
RLELossless,
JPEGBaselineProcess1(u32),
JPEGExtendedProcess2_4(u32),
JPEGLosslessProcess14,
JPEGLosslessProcess14_1,
JPEGLSLossless,
JPEGLSNearLossless(u32),
JPEG2000Lossless,
JPEG2000(u32, u32, u32, bool),
JPEG2000Part2Lossless,
JPEG2000Part2(u32, u32, u32, bool),
MPEG2MainProfile,
}
impl TransferSyntax {
pub fn to_id(self) -> i32 {
match self {
TransferSyntax::None => 0,
TransferSyntax::ImplicitVRLittleEndian => 1,
TransferSyntax::ExplicitVRLittleEndian => 2,
TransferSyntax::ExplicitVRBigEndian => 3,
TransferSyntax::JPEGBaselineProcess1(_) => 4,
TransferSyntax::JPEGExtendedProcess2_4(_) => 5,
TransferSyntax::JPEGLosslessProcess14 => 6,
TransferSyntax::JPEGLosslessProcess14_1 => 7,
TransferSyntax::JPEGLSLossless => 8,
TransferSyntax::JPEGLSNearLossless(_) => 9,
TransferSyntax::JPEG2000Lossless => 10,
TransferSyntax::JPEG2000(_, _, _, _) => 11,
TransferSyntax::JPEG2000Part2Lossless => 12,
TransferSyntax::JPEG2000Part2(_, _, _, _) => 13,
TransferSyntax::RLELossless => 14,
TransferSyntax::MPEG2MainProfile => 15,
}
}
}
#[derive(Copy, Clone)]
pub enum PhotometricInterpretation {
None,
Monochrome1,
Monochrome2,
PaletteColor,
RGB,
HSV,
ARGB,
CMYK,
YbrFull,
YbrFull422,
YbrPartial422,
YbrPartial420,
YbrIct,
YbrRct,
}
impl PhotometricInterpretation {
pub fn to_id(self) -> i32 {
match self {
PhotometricInterpretation::None => 0,
PhotometricInterpretation::Monochrome1 => 1,
PhotometricInterpretation::Monochrome2 => 2,
PhotometricInterpretation::PaletteColor => 3,
PhotometricInterpretation::RGB => 4,
PhotometricInterpretation::HSV => 5,
PhotometricInterpretation::ARGB => 6,
PhotometricInterpretation::CMYK => 7,
PhotometricInterpretation::YbrFull => 8,
PhotometricInterpretation::YbrFull422 => 9,
PhotometricInterpretation::YbrPartial422 => 10,
PhotometricInterpretation::YbrPartial420 => 11,
PhotometricInterpretation::YbrIct => 12,
PhotometricInterpretation::YbrRct => 13,
}
}
}
#[repr(C)]
struct output_t {
status: c_uint,
size: size_t,
}
unsafe extern "C" {
fn c_convert(
source_ptr: *const c_uchar,
source_len: size_t,
max_size: size_t,
transfer_syntax_pre: c_int,
transfer_syntax_post: c_int,
photometric_interpretation: c_int,
is_lossy: c_char, quality1: c_int, quality2: c_int, quality3: c_int, irreversible: c_char, allow_error: c_int, ) -> output_t;
}
pub fn pipeline(
mut source: Vec<u8>,
estimated_length: Option<usize>,
transfer_syntax_pre: TransferSyntax,
photometric_interpretation: PhotometricInterpretation,
transfer_syntax_post: TransferSyntax,
) -> Result<Vec<u8>, GDCMError> {
let mut ret;
let (is_lossy, quality1, quality2, quality3, irreversible, allow_error) =
match transfer_syntax_post {
TransferSyntax::JPEGBaselineProcess1(t) => {
if t > 0 {
(true, t, 0, 0, false, 0)
} else {
(false, 0, 0, 0, false, 0)
}
}
TransferSyntax::JPEGExtendedProcess2_4(t) => {
if t > 0 {
(true, t, 0, 0, false, 0)
} else {
(false, 0, 0, 0, false, 0)
}
}
TransferSyntax::JPEGLSNearLossless(t) => {
if t > 0 {
(true, 0, 0, 0, false, t)
} else {
(false, 0, 0, 0, false, t)
}
}
TransferSyntax::JPEG2000(t1, t2, t3, t4) => {
if t1 != 0 || t2 != 0 || t3 != 0 || t4 {
(true, t1, t2, t3, t4, 0)
} else {
(false, 0, 0, 0, false, 0)
}
}
TransferSyntax::JPEG2000Part2(t1, t2, t3, t4) => {
if t1 != 0 || t2 != 0 || t3 != 0 || t4 {
(true, t1, t2, t3, t4, 0)
} else {
(false, 0, 0, 0, false, 0)
}
}
_ => (false, 0, 0, 0, false, 0),
};
if let Some(t) = estimated_length {
source.reserve(t);
} else {
source.reserve(source.len() * 3);
}
ret = unsafe {
c_convert(
source.as_ptr(),
source.len() as size_t,
source.capacity() as size_t,
transfer_syntax_pre.to_id(),
transfer_syntax_post.to_id(),
photometric_interpretation.to_id(),
is_lossy as c_char,
quality1 as i32,
quality2 as i32,
quality3 as i32,
irreversible as c_char,
allow_error as i32,
)
};
if ret.status == 0xFF {
println!(
"OVERSIZED [{:?}] input: {} estimated: {:?} needed: {}",
transfer_syntax_pre,
source.len(),
estimated_length,
ret.size,
);
source.reserve(ret.size);
ret = unsafe {
c_convert(
source.as_ptr(),
source.len() as size_t,
ret.size as size_t,
transfer_syntax_pre.to_id(),
transfer_syntax_post.to_id(),
photometric_interpretation.to_id(),
is_lossy as c_char,
quality1 as i32,
quality2 as i32,
quality3 as i32,
irreversible as c_char,
allow_error as i32,
)
};
}
match ret.status {
0x00 => {
unsafe {
source.set_len(ret.size);
}
Ok(source)
}
0x11 => Err(GDCMError::Pre(Error::ReadStream)),
0x12 => Err(GDCMError::Pre(Error::FileExplicitFilter)),
0x13 => Err(GDCMError::Pre(Error::InvalidTransferSyntax)),
0x14 => Err(GDCMError::Pre(Error::ExecuteChange)),
0x15 => Err(GDCMError::Pre(Error::DeriveFile)),
0x16 => Err(GDCMError::Pre(Error::WriteStream)),
0x21 => Err(GDCMError::Photo(Error::ReadStream)),
0x22 => Err(GDCMError::Photo(Error::InvalidPhotometricInterpretation)),
0x23 => Err(GDCMError::Photo(Error::ExecuteChange)),
0x24 => Err(GDCMError::Photo(Error::ExecuteLUTChange)),
0x25 => Err(GDCMError::Photo(Error::WriteStream)),
0x31 => Err(GDCMError::Post(Error::ReadStream)),
0x32 => Err(GDCMError::Post(Error::FileExplicitFilter)),
0x33 => Err(GDCMError::Post(Error::InvalidTransferSyntax)),
0x34 => Err(GDCMError::Post(Error::ExecuteChange)),
0x35 => Err(GDCMError::Post(Error::DeriveFile)),
0x36 => Err(GDCMError::Post(Error::WriteStream)),
0x0F => Err(GDCMError::PointerNULL),
0x1F => Err(GDCMError::EmptyBuffer),
_ => Err(GDCMError::Unknown),
}
}