rustiff 0.1.2

TIFF decoding/encoding library in Rust.
Documentation

use error::{
    DecodeError,
    DecodeErrorKind,
};
use tag::AnyTag;

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum PhotometricInterpretation {
    WhiteIsZero,
    BlackIsZero,
    RGB,
    Palette,
    TransparencyMask,
    CMYK,
    YCbCr,
    CIELab,
}

impl PhotometricInterpretation {
    pub fn from_u16(n: u16) -> Result<PhotometricInterpretation, DecodeError> {
        use self::PhotometricInterpretation::*;

        match n {
            0 => Ok(WhiteIsZero),
            1 => Ok(BlackIsZero),
            2 => Ok(RGB),
            3 => Ok(Palette),
            4 => Ok(TransparencyMask),
            5 => Ok(CMYK),
            6 => Ok(YCbCr),
            7 => Ok(CIELab),
            n => Err(DecodeError::from(DecodeErrorKind::UnsupportedData{ tag: AnyTag::PhotometricInterpretation, data: n as u32 })),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Compression {
    No,
    LZW,
}

impl Compression {
    pub fn from_u16(n: u16) -> Result<Compression, DecodeError> {
        match n {
            1 => Ok(Compression::No),
            5 => Ok(Compression::LZW),
            n => Err(DecodeError::from(DecodeErrorKind::UnsupportedData{ tag: AnyTag::Compression, data: n as u32 })),
        }
    }
}

#[derive(Debug, Fail)]
pub enum BitsPerSampleError {
    #[fail(display = "Invalid values: {:?}", values)]
    InvalidValues { values: Vec<u16> }
}

impl BitsPerSampleError {
    pub fn values(&self) -> &Vec<u16> {
        match self {
            BitsPerSampleError::InvalidValues { values } => values
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BitsPerSample {
    U8_1,
    U8_3,
    U8_4,
    U16_1,
    U16_3,
    U16_4,
}

impl BitsPerSample {
    pub fn new<T: AsRef<[u16]>>(values: T) -> Result<BitsPerSample, BitsPerSampleError> {
        match values.as_ref() {
            [8] => Ok(BitsPerSample::U8_1),
            [8, 8, 8] => Ok(BitsPerSample::U8_3),
            [8, 8, 8, 8] => Ok(BitsPerSample::U8_4),
            [16] => Ok(BitsPerSample::U16_1),
            [16, 16, 16] => Ok(BitsPerSample::U16_3),
            [16, 16, 16, 16] => Ok(BitsPerSample::U16_4),
            _ => Err(BitsPerSampleError::InvalidValues { values: values.as_ref().to_vec() }),
        }
    }

    pub fn len(&self) -> usize {
        match *self {
            BitsPerSample::U8_1 | BitsPerSample::U16_1 => 1,
            BitsPerSample::U8_3 | BitsPerSample::U16_3 => 3,
            BitsPerSample::U8_4 | BitsPerSample::U16_4 => 4,
        }
    }

    pub fn max_value(&self) -> u16 {
        let x = u8::max_value() as u16;
        let y = u16::max_value();

        match *self {
            BitsPerSample::U8_1 | BitsPerSample::U8_3 | BitsPerSample::U8_4 => x,
            BitsPerSample::U16_1 | BitsPerSample::U16_3 | BitsPerSample::U16_4 => y,
        }
    }

    pub fn bits(&self) -> usize {
        match self {
            BitsPerSample::U8_1 | BitsPerSample::U8_3 | BitsPerSample::U8_4 => 8,
            BitsPerSample::U16_1 | BitsPerSample::U16_3 | BitsPerSample::U16_4 => 16
        }
    }
}

#[derive(Debug, Fail)]
pub enum ImageHeaderError {
    #[fail(display = "Incompatible data ({:?}/{:?}", photometric_interpretation, bits_per_sample)]
    IncompatibleData { photometric_interpretation: PhotometricInterpretation, bits_per_sample: BitsPerSample },
}

#[derive(Debug, Clone)]
pub struct ImageHeader {
    width: u32,
    height: u32,
    compression: Compression,
    photometric_interpretation: PhotometricInterpretation,
    bits_per_sample: BitsPerSample,
}

impl ImageHeader {
    pub fn new(
        width: u32, 
        height: u32, 
        compression: Compression, 
        interpretation: PhotometricInterpretation,
        bits_per_sample: BitsPerSample) -> Result<ImageHeader, ImageHeaderError>
    {
        if !is_valid_color_type(interpretation, bits_per_sample) {
            return Err(ImageHeaderError::IncompatibleData { 
                photometric_interpretation: interpretation, 
                bits_per_sample: bits_per_sample,
            });
        }

        let header = ImageHeader {
            width: width,
            height: height,
            compression: compression,
            photometric_interpretation: interpretation,
            bits_per_sample: bits_per_sample,
        };

        Ok(header)
    }

    pub fn width(&self) -> u32 {
        self.width
    }

    pub fn height(&self) -> u32 {
        self.height
    }

    pub fn bits_per_sample(&self) -> BitsPerSample {
        self.bits_per_sample
    }

    pub fn compression(&self) -> Compression {
        self.compression
    }

    pub fn photometric_interpretation(&self) -> PhotometricInterpretation {
        self.photometric_interpretation
    }
}

#[derive(Debug)]
pub enum ImageData { 
    U8(Vec<u8>),
    U16(Vec<u16>),
}

#[derive(Debug)]
pub struct Image {
    header: ImageHeader,
    data: ImageData,
}

impl Image {
    pub fn new(header: ImageHeader, data: ImageData) -> Image {
        Image {
            header: header,
            data: data,
        }
    }

    pub fn header(&self) -> &ImageHeader {
        &self.header
    }

    pub fn data(&self) -> &ImageData {
        &self.data
    }
}

#[inline]
fn is_valid_color_type(photometric_interpretation: PhotometricInterpretation, bits_per_sample: BitsPerSample) -> bool {
    use self::PhotometricInterpretation::*;
    use self::BitsPerSample::*;

    match (photometric_interpretation, bits_per_sample) {
        (RGB, U8_3) | 
        (RGB, U8_4) | 
        (RGB, U16_3) | 
        (RGB, U16_4) |
        (CMYK, U8_4) | 
        (CMYK, U16_4) |
        (BlackIsZero, U8_1) | 
        (BlackIsZero, U16_1) |
        (WhiteIsZero, U8_1) | 
        (WhiteIsZero, U16_1) => true,
        _ => false
    }
}