Repository: image-rs/image-tiff (rust)

Issue:
Feature request: 32 bits per sample
Would you be able to add support for 32bit float Grayscale Tiffs?

Pull request description:
Support 32- and 64-bit integer sample grayscale and RGB(A) images
(This issue originally mentioned #19, but that is for floating point images.)

Matching the existing style, there was no attempt to DRY code here, even if some of this could be generic'd or macro'd away. There are a finite number of types, after all. But if we wanted to support CMYK(>8), etc., or floating point images that may need to be done.

Though a BigTIFF tag data type for 64 bit uints (`LONG8`) was added to satisfy some TiffValue bounds for 64-bit encoding, this data type never has to be written/read for 64-bit image data. `libtiff` and most libraries understand this tag data type value anyway.

Patch:
diff --git a/src/decoder/ifd.rs b/src/decoder/ifd.rs
index 2be016b..51553d2 100644
--- a/src/decoder/ifd.rs
+++ b/src/decoder/ifd.rs
@@ -122,6 +122,8 @@ pub enum Type(u16) {
     SSHORT = 8,
     SLONG = 9,
     SRATIONAL = 10,
+    /// BigTIFF 64-bit unsigned integer
+    LONG8 = 16,
 }
 }
 
diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs
index a3aff91..f879df6 100644
--- a/src/decoder/mod.rs
+++ b/src/decoder/mod.rs
@@ -19,6 +19,10 @@ pub enum DecodingResult {
     U8(Vec<u8>),
     /// A vector of unsigned words
     U16(Vec<u16>),
+    /// A vector of 32 bit unsigned ints
+    U32(Vec<u32>),
+    /// A vector of 64 bit unsigned ints
+    U64(Vec<u64>),
 }
 
 impl DecodingResult {
@@ -38,10 +42,28 @@ impl DecodingResult {
         }
     }
 
+    fn new_u32(size: usize, limits: &Limits) -> TiffResult<DecodingResult> {
+        if size > limits.decoding_buffer_size / 4 {
+            Err(TiffError::LimitsExceeded)
+        } else {
+            Ok(DecodingResult::U32(vec![0; size]))
+        }
+    }
+
+    fn new_u64(size: usize, limits: &Limits) -> TiffResult<DecodingResult> {
+        if size > limits.decoding_buffer_size / 8 {
+            Err(TiffError::LimitsExceeded)
+        } else {
+            Ok(DecodingResult::U64(vec![0; size]))
+        }
+    }
+
     pub fn as_buffer(&mut self, start: usize) -> DecodingBuffer {
         match *self {
             DecodingResult::U8(ref mut buf) => DecodingBuffer::U8(&mut buf[start..]),
             DecodingResult::U16(ref mut buf) => DecodingBuffer::U16(&mut buf[start..]),
+            DecodingResult::U32(ref mut buf) => DecodingBuffer::U32(&mut buf[start..]),
+            DecodingResult::U64(ref mut buf) => DecodingBuffer::U64(&mut buf[start..]),
         }
     }
 }
@@ -52,6 +74,10 @@ pub enum DecodingBuffer<'a> {
     U8(&'a mut [u8]),
     /// A slice of unsigned words
     U16(&'a mut [u16]),
+    /// A slice of 32 bit unsigned ints
+    U32(&'a mut [u32]),
+    /// A slice of 64 bit unsigned ints
+    U64(&'a mut [u64]),
 }
 
 impl<'a> DecodingBuffer<'a> {
@@ -59,6 +85,8 @@ impl<'a> DecodingBuffer<'a> {
         match *self {
             DecodingBuffer::U8(ref buf) => buf.len(),
             DecodingBuffer::U16(ref buf) => buf.len(),
+            DecodingBuffer::U32(ref buf) => buf.len(),
+            DecodingBuffer::U64(ref buf) => buf.len(),
         }
     }
 
@@ -66,6 +94,8 @@ impl<'a> DecodingBuffer<'a> {
         match *self {
             DecodingBuffer::U8(_) => 1,
             DecodingBuffer::U16(_) => 2,
+            DecodingBuffer::U32(_) => 4,
+            DecodingBuffer::U64(_) => 8,
         }
     }
 
@@ -76,6 +106,8 @@ impl<'a> DecodingBuffer<'a> {
         match *self {
             DecodingBuffer::U8(ref mut buf) => DecodingBuffer::U8(buf),
             DecodingBuffer::U16(ref mut buf) => DecodingBuffer::U16(buf),
+            DecodingBuffer::U32(ref mut buf) => DecodingBuffer::U32(buf),
+            DecodingBuffer::U64(ref mut buf) => DecodingBuffer::U64(buf),
         }
     }
 }
@@ -233,6 +265,18 @@ impl Wrapping for u16 {
     }
 }
 
+impl Wrapping for u32 {
+    fn wrapping_add(&self, other: Self) -> Self {
+        u32::wrapping_add(*self, other)
+    }
+}
+
+impl Wrapping for u64 {
+    fn wrapping_add(&self, other: Self) -> Self {
+        u64::wrapping_add(*self, other)
+    }
+}
+
 fn rev_hpredict_nsamp<T>(image: &mut [T], size: (u32, u32), samples: usize)
 where
     T: Copy + Wrapping,
@@ -250,9 +294,9 @@ where
 
 fn rev_hpredict(image: DecodingBuffer, size: (u32, u32), color_type: ColorType) -> TiffResult<()> {
     let samples = match color_type {
-        ColorType::Gray(8) | ColorType::Gray(16) => 1,
-        ColorType::RGB(8) | ColorType::RGB(16) => 3,
-        ColorType::RGBA(8) | ColorType::RGBA(16) | ColorType::CMYK(8) => 4,
+        ColorType::Gray(8) | ColorType::Gray(16) | ColorType::Gray(32) | ColorType::Gray(64) => 1,
+        ColorType::RGB(8) | ColorType::RGB(16) | ColorType::RGB(32) | ColorType::RGB(64) => 3,
+        ColorType::RGBA(8) | ColorType::RGBA(16) | ColorType::RGBA(32) | ColorType::RGBA(64) | ColorType::CMYK(8) => 4,
         _ => {
             return Err(TiffError::UnsupportedError(
                 TiffUnsupportedError::HorizontalPredictor(color_type),
@@ -266,6 +310,12 @@ fn rev_hpredict(image: DecodingBuffer, size: (u32, u32), color_type: ColorType)
         DecodingBuffer::U16(buf) => {
             rev_hpredict_nsamp(buf, size, samples);
         }
+        DecodingBuffer::U32(buf) => {
+            rev_hpredict_nsamp(buf, size, samples);
+        }
+        DecodingBuffer::U64(buf) => {
+            rev_hpredict_nsamp(buf, size, samples);
+        }
     }
     Ok(())
 }
@@ -301,19 +351,18 @@ impl<R: Read + Seek> Decoder<R> {
 
     pub fn colortype(&mut self) -> TiffResult<ColorType> {
         match self.photometric_interpretation {
-            // TODO: catch also [ 8, 8, 8, _] this does not work due to a bug in rust atm
-            PhotometricInterpretation::RGB if self.bits_per_sample == [8, 8, 8, 8] => {
-                Ok(ColorType::RGBA(8))
-            }
-            PhotometricInterpretation::RGB if self.bits_per_sample == [8, 8, 8] => {
-                Ok(ColorType::RGB(8))
-            }
-            PhotometricInterpretation::RGB if self.bits_per_sample == [16, 16, 16, 16] => {
-                Ok(ColorType::RGBA(16))
-            }
-            PhotometricInterpretation::RGB if self.bits_per_sample == [16, 16, 16] => {
-                Ok(ColorType::RGB(16))
-            }
+            PhotometricInterpretation::RGB  => {
+                match self.bits_per_sample[..] {
+                    [r, g, b] if [r, r] == [g, b] => Ok(ColorType::RGB(r)),
+                    [r, g, b, a] if [r, r, r] == [g, b, a] => Ok(ColorType::RGBA(r)),
+                    _ => Err(TiffError::UnsupportedError(
+                        TiffUnsupportedError::InterpretationWithBits(
+                            self.photometric_interpretation,
+                            self.bits_per_sample.clone(),
+                        ),
+                    )),
+                }
+            },
             PhotometricInterpretation::CMYK if self.bits_per_sample == [8, 8, 8, 8] => {
                 Ok(ColorType::CMYK(8))
             }
@@ -659,6 +708,34 @@ impl<R: Read + Seek> Decoder<R> {
                 reader.read_u16_into(&mut buffer[..bytes / 2])?;
                 bytes / 2
             }
+            (ColorType::RGBA(32), DecodingBuffer::U32(ref mut buffer))
+            | (ColorType::RGB(32), DecodingBuffer::U32(ref mut buffer)) => {
+                reader.read_u32_into(&mut buffer[..bytes / 4])?;
+                bytes / 4
+            }
+            (ColorType::RGBA(64), DecodingBuffer::U64(ref mut buffer))
+            | (ColorType::RGB(64), DecodingBuffer::U64(ref mut buffer)) => {
+                reader.read_u64_into(&mut buffer[..bytes / 8])?;
+                bytes / 8
+            }
+            (ColorType::Gray(64), DecodingBuffer::U64(ref mut buffer)) => {
+                reader.read_u64_into(&mut buffer[..bytes / 8])?;
+                if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
+                    for datum in buffer[..bytes / 8].iter_mut() {
+                        *datum = 0xffff_ffff_ffff_ffff - *datum
+                    }
+                }
+                bytes / 8
+            }
+            (ColorType::Gray(32), DecodingBuffer::U32(ref mut buffer)) => {
+                reader.read_u32_into(&mut buffer[..bytes / 4])?;
+                if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
+                    for datum in buffer[..bytes / 4].iter_mut() {
+                        *datum = 0xffff_ffff - *datum
+                    }
+                }
+                bytes / 4
+            }
             (ColorType::Gray(16), DecodingBuffer::U16(ref mut buffer)) => {
                 reader.read_u16_into(&mut buffer[..bytes / 2])?;
                 if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
@@ -806,6 +883,8 @@ impl<R: Read + Seek> Decoder<R> {
         let mut result = match self.bits_per_sample.iter().cloned().max().unwrap_or(8) {
             n if n <= 8 => DecodingResult::new_u8(buffer_size, &self.limits)?,
             n if n <= 16 => DecodingResult::new_u16(buffer_size, &self.limits)?,
+            n if n <= 32 => DecodingResult::new_u32(buffer_size, &self.limits)?,
+            n if n <= 64 => DecodingResult::new_u64(buffer_size, &self.limits)?,
             n => {
                 return Err(TiffError::UnsupportedError(
                     TiffUnsupportedError::UnsupportedBitsPerChannel(n),
@@ -834,6 +913,8 @@ impl<R: Read + Seek> Decoder<R> {
         let mut result = match self.bits_per_sample.iter().cloned().max().unwrap_or(8) {
             n if n <= 8 => DecodingResult::new_u8(buffer_size, &self.limits)?,
             n if n <= 16 => DecodingResult::new_u16(buffer_size, &self.limits)?,
+            n if n <= 32 => DecodingResult::new_u32(buffer_size, &self.limits)?,
+            n if n <= 64 => DecodingResult::new_u64(buffer_size, &self.limits)?,
             n => {
                 return Err(TiffError::UnsupportedError(
                     TiffUnsupportedError::UnsupportedBitsPerChannel(n),
diff --git a/src/decoder/stream.rs b/src/decoder/stream.rs
index fe5d20c..caf4e3c 100644
--- a/src/decoder/stream.rs
+++ b/src/decoder/stream.rs
@@ -59,6 +59,39 @@ pub trait EndianReader: Read {
         }
     }
 
+    #[inline(always)]
+    fn read_u32_into(&mut self, buffer: &mut [u32]) -> Result<(), io::Error> {
+        match self.byte_order() {
+            ByteOrder::LittleEndian => {
+                <Self as ReadBytesExt>::read_u32_into::<LittleEndian>(self, buffer)
+            }
+            ByteOrder::BigEndian => {
+                <Self as ReadBytesExt>::read_u32_into::<BigEndian>(self, buffer)
+            }
+        }
+    }
+
+    /// Reads an u64
+    #[inline(always)]
+    fn read_u64(&mut self) -> Result<u64, io::Error> {
+        match self.byte_order() {
+            ByteOrder::LittleEndian => <Self as ReadBytesExt>::read_u64::<LittleEndian>(self),
+            ByteOrder::BigEndian => <Self as ReadBytesExt>::read_u64::<BigEndian>(self),
+        }
+    }
+
+    #[inline(always)]
+    fn read_u64_into(&mut self, buffer: &mut [u64]) -> Result<(), io::Error> {
+        match self.byte_order() {
+            ByteOrder::LittleEndian => {
+                <Self as ReadBytesExt>::read_u64_into::<LittleEndian>(self, buffer)
+            }
+            ByteOrder::BigEndian => {
+                <Self as ReadBytesExt>::read_u64_into::<BigEndian>(self, buffer)
+            }
+        }
+    }
+
     /// Reads an i32
     #[inline(always)]
     fn read_i32(&mut self) -> Result<i32, io::Error> {
diff --git a/src/encoder/colortype.rs b/src/encoder/colortype.rs
index a348ee7..2f49fdb 100644
--- a/src/encoder/colortype.rs
+++ b/src/encoder/colortype.rs
@@ -24,6 +24,20 @@ impl ColorType for Gray16 {
     const BITS_PER_SAMPLE: &'static [u16] = &[16];
 }
 
+pub struct Gray32;
+impl ColorType for Gray32 {
+    type Inner = u32;
+    const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
+    const BITS_PER_SAMPLE: &'static [u16] = &[32];
+}
+
+pub struct Gray64;
+impl ColorType for Gray64 {
+    type Inner = u64;
+    const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
+    const BITS_PER_SAMPLE: &'static [u16] = &[64];
+}
+
 pub struct RGB8;
 impl ColorType for RGB8 {
     type Inner = u8;
@@ -38,6 +52,20 @@ impl ColorType for RGB16 {
     const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16];
 }
 
+pub struct RGB32;
+impl ColorType for RGB32 {
+    type Inner = u32;
+    const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
+    const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32];
+}
+
+pub struct RGB64;
+impl ColorType for RGB64 {
+    type Inner = u64;
+    const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
+    const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64];
+}
+
 pub struct RGBA8;
 impl ColorType for RGBA8 {
     type Inner = u8;
@@ -52,6 +80,20 @@ impl ColorType for RGBA16 {
     const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16, 16];
 }
 
+pub struct RGBA32;
+impl ColorType for RGBA32 {
+    type Inner = u16;
+    const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
+    const BITS_PER_SAMPLE: &'static [u16] = &[32, 32, 32, 32];
+}
+
+pub struct RGBA64;
+impl ColorType for RGBA64 {
+    type Inner = u16;
+    const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::RGB;
+    const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64, 64];
+}
+
 pub struct CMYK8;
 impl ColorType for CMYK8 {
     type Inner = u8;
diff --git a/src/encoder/mod.rs b/src/encoder/mod.rs
index f5b662c..9eda804 100644
--- a/src/encoder/mod.rs
+++ b/src/encoder/mod.rs
@@ -142,6 +142,23 @@ impl TiffValue for [i32] {
     }
 }
 
+impl TiffValue for [u64] {
+    const BYTE_LEN: u32 = 8;
+    const FIELD_TYPE: ifd::Type = ifd::Type::LONG8;
+
+    fn count(&self) -> u32 {
+        self.len() as u32
+    }
+
+    fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
+        // We write using nativeedian so this sould be safe
+        let slice =
+            unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * 8) };
+        writer.write_bytes(slice)?;
+        Ok(())
+    }
+}
+
 impl TiffValue for [Rational] {
     const BYTE_LEN: u32 = 8;
     const FIELD_TYPE: ifd::Type = ifd::Type::RATIONAL;
@@ -258,6 +275,20 @@ impl TiffValue for i32 {
     }
 }
 
+impl TiffValue for u64 {
+    const BYTE_LEN: u32 = 8;
+    const FIELD_TYPE: ifd::Type = ifd::Type::LONG8;
+
+    fn count(&self) -> u32 {
+        1
+    }
+
+    fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
+        writer.write_u64(*self)?;
+        Ok(())
+    }
+}
+
 impl TiffValue for Rational {
     const BYTE_LEN: u32 = 8;
     const FIELD_TYPE: ifd::Type = ifd::Type::RATIONAL;


Test patch:
diff --git a/tests/decode_images.rs b/tests/decode_images.rs
index 7c91988..910d43e 100644
--- a/tests/decode_images.rs
+++ b/tests/decode_images.rs
@@ -44,6 +44,18 @@ fn test_rgb_u8() {
     }
 }
 
+#[test]
+fn test_gray_u12() {
+    let img_file =
+        File::open("./tests/images/12bit.cropped.tiff").expect("Cannot find test image!");
+    let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
+    assert_eq!(decoder.colortype().unwrap(), ColorType::Gray(12));
+    assert!(match decoder.read_image() {
+        Err(tiff::TiffError::UnsupportedError(tiff::TiffUnsupportedError::UnsupportedColorType(_))) => true,
+        _ => false,
+    });
+}
+
 #[test]
 fn test_gray_u16() {
     let img_file =
@@ -64,6 +76,58 @@ fn test_gray_u16() {
     }
 }
 
+#[test]
+fn test_gray_u32() {
+    let img_file =
+        File::open("./tests/images/gradient-1c-32b.tiff").expect("Cannot find test image!");
+    let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
+    assert_eq!(decoder.colortype().unwrap(), ColorType::Gray(32));
+    let img_res = decoder.read_image().unwrap();
+    if let DecodingResult::U32(res) = img_res {
+        let mut res_sum: u64 = 0;
+        for x in res {
+            res_sum += x as u64;
+        }
+
+        assert_eq!(res_sum, 549892913787);
+    }
+    else {
+        panic!("Wrong bit depth")
+    }
+}
+
+#[test]
+fn test_gray_u64() {
+    let img_file =
+        File::open("./tests/images/gradient-1c-64b.tiff").expect("Cannot find test image!");
+    let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
+    assert_eq!(decoder.colortype().unwrap(), ColorType::Gray(64));
+    let img_res = decoder.read_image().unwrap();
+    if let DecodingResult::U64(res) = img_res {
+        let mut res_sum: u64 = 0;
+        for x in res {
+            res_sum += x as u64;
+        }
+
+        assert_eq!(res_sum, 549892913787);
+    }
+    else {
+        panic!("Wrong bit depth")
+    }
+}
+
+#[test]
+fn test_rgb_u12() {
+    let img_file =
+        File::open("./tests/images/12bit.cropped.rgb.tiff").expect("Cannot find test image!");
+    let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
+    assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(12));
+    assert!(match decoder.read_image() {
+        Err(tiff::TiffError::UnsupportedError(tiff::TiffUnsupportedError::UnsupportedColorType(_))) => true,
+        _ => false,
+    });
+}
+
 #[test]
 fn test_rgb_u16() {
     let img_file = File::open("./tests/images/rgb-3c-16b.tiff").expect("Cannot find test image!");
@@ -83,6 +147,44 @@ fn test_rgb_u16() {
     }
 }
 
+#[test]
+fn test_rgb_u32() {
+    let img_file = File::open("./tests/images/gradient-3c-32b.tiff").expect("Cannot find test image!");
+    let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
+    assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(32));
+    let img_res = decoder.read_image().unwrap();
+    if let DecodingResult::U32(res) = img_res {
+        let mut res_sum: u64 = 0;
+        for x in res {
+            res_sum += x as u64;
+        }
+
+        assert_eq!(res_sum, 2030834111716);
+    }
+    else {
+        panic!("Wrong bit depth")
+    }
+}
+
+#[test]
+fn test_rgb_u64() {
+    let img_file = File::open("./tests/images/gradient-3c-64b.tiff").expect("Cannot find test image!");
+    let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
+    assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(64));
+    let img_res = decoder.read_image().unwrap();
+    if let DecodingResult::U64(res) = img_res {
+        let mut res_sum: u64 = 0;
+        for x in res {
+            res_sum += x as u64;
+        }
+
+        assert_eq!(res_sum, 2030834111716);
+    }
+    else {
+        panic!("Wrong bit depth")
+    }
+}
+
 #[test]
 fn test_string_tags() {
     // these files have null-terminated strings for their Software tag. One has extra bytes after
diff --git a/tests/encode_images.rs b/tests/encode_images.rs
index 6256cec..f3ca92f 100644
--- a/tests/encode_images.rs
+++ b/tests/encode_images.rs
@@ -177,6 +177,128 @@ fn test_rgb_u16_roundtrip() {
     }
 }
 
+#[test]
+fn test_gray_u32_roundtrip() {
+    let img_file =
+        File::open("./tests/images/gradient-1c-32b.tiff").expect("Cannot find test image!");
+    let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
+    assert_eq!(decoder.colortype().unwrap(), ColorType::Gray(32));
+
+    let image_data = match decoder.read_image().unwrap() {
+        DecodingResult::U32(res) => res,
+        _ => panic!("Wrong data type"),
+    };
+
+    let mut file = tempfile::tempfile().unwrap();
+    {
+        let mut tiff = TiffEncoder::new(&mut file).unwrap();
+
+        let (width, height) = decoder.dimensions().unwrap();
+        tiff.write_image::<colortype::Gray32>(width, height, &image_data)
+            .unwrap();
+    }
+    file.seek(SeekFrom::Start(0)).unwrap();
+    {
+        let mut decoder = Decoder::new(&mut file).unwrap();
+        if let DecodingResult::U32(img_res) = decoder.read_image().unwrap() {
+            assert_eq!(image_data, img_res);
+        } else {
+            panic!("Wrong data type");
+        }
+    }
+}
+
+#[test]
+fn test_rgb_u32_roundtrip() {
+    let img_file = File::open("./tests/images/gradient-3c-32b.tiff").expect("Cannot find test image!");
+    let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
+    assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(32));
+
+    let image_data = match decoder.read_image().unwrap() {
+        DecodingResult::U32(res) => res,
+        _ => panic!("Wrong data type"),
+    };
+
+    let mut file = tempfile::tempfile().unwrap();
+    {
+        let mut tiff = TiffEncoder::new(&mut file).unwrap();
+
+        let (width, height) = decoder.dimensions().unwrap();
+        tiff.write_image::<colortype::RGB32>(width, height, &image_data)
+            .unwrap();
+    }
+    file.seek(SeekFrom::Start(0)).unwrap();
+    {
+        let mut decoder = Decoder::new(&mut file).unwrap();
+        if let DecodingResult::U32(img_res) = decoder.read_image().unwrap() {
+            assert_eq!(image_data, img_res);
+        } else {
+            panic!("Wrong data type");
+        }
+    }
+}
+
+#[test]
+fn test_gray_u64_roundtrip() {
+    let img_file =
+        File::open("./tests/images/gradient-1c-64b.tiff").expect("Cannot find test image!");
+    let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
+    assert_eq!(decoder.colortype().unwrap(), ColorType::Gray(64));
+
+    let image_data = match decoder.read_image().unwrap() {
+        DecodingResult::U64(res) => res,
+        _ => panic!("Wrong data type"),
+    };
+
+    let mut file = tempfile::tempfile().unwrap();
+    {
+        let mut tiff = TiffEncoder::new(&mut file).unwrap();
+
+        let (width, height) = decoder.dimensions().unwrap();
+        tiff.write_image::<colortype::Gray64>(width, height, &image_data)
+            .unwrap();
+    }
+    file.seek(SeekFrom::Start(0)).unwrap();
+    {
+        let mut decoder = Decoder::new(&mut file).unwrap();
+        if let DecodingResult::U64(img_res) = decoder.read_image().unwrap() {
+            assert_eq!(image_data, img_res);
+        } else {
+            panic!("Wrong data type");
+        }
+    }
+}
+
+#[test]
+fn test_rgb_u64_roundtrip() {
+    let img_file = File::open("./tests/images/gradient-3c-64b.tiff").expect("Cannot find test image!");
+    let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
+    assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(64));
+
+    let image_data = match decoder.read_image().unwrap() {
+        DecodingResult::U64(res) => res,
+        _ => panic!("Wrong data type"),
+    };
+
+    let mut file = tempfile::tempfile().unwrap();
+    {
+        let mut tiff = TiffEncoder::new(&mut file).unwrap();
+
+        let (width, height) = decoder.dimensions().unwrap();
+        tiff.write_image::<colortype::RGB64>(width, height, &image_data)
+            .unwrap();
+    }
+    file.seek(SeekFrom::Start(0)).unwrap();
+    {
+        let mut decoder = Decoder::new(&mut file).unwrap();
+        if let DecodingResult::U64(img_res) = decoder.read_image().unwrap() {
+            assert_eq!(image_data, img_res);
+        } else {
+            panic!("Wrong data type");
+        }
+    }
+}
+
 #[test]
 fn test_multiple_byte() {
     let mut data = Cursor::new(Vec::new());
@@ -273,10 +395,10 @@ fn test_multipage_image() {
 
         // write first grayscale image (2x2 16-bit)
         let img1: Vec<u16> = [1, 2, 3, 4].to_vec();
-        img_encoder.write_image::<colortype::Gray16>(2, 2, &img1[..]);
+        img_encoder.write_image::<colortype::Gray16>(2, 2, &img1[..]).unwrap();
         // write second grayscale image (3x3 8-bit)
         let img2: Vec<u8> = [9, 8, 7, 6, 5, 4, 3, 2, 1].to_vec();
-        img_encoder.write_image::<colortype::Gray8>(3, 3, &img2[..]);
+        img_encoder.write_image::<colortype::Gray8>(3, 3, &img2[..]).unwrap();
     }
 
     // seek to the beginning of the file, so that it can be decoded