Merge pull request #66 from aschampion/features/32-bit
Support 32- and 64-bit integer sample grayscale and RGB(A) images
This commit is contained in:
13 files changed
+431
-18
No files matched your search
@@ -122,6 +122,8 @@ pub enum Type(u16) {
|
|||||||
SSHORT = 8,
|
SSHORT = 8,
|
||||||
SLONG = 9,
|
SLONG = 9,
|
||||||
SRATIONAL = 10,
|
SRATIONAL = 10,
|
||||||
|
/// BigTIFF 64-bit unsigned integer
|
||||||
|
LONG8 = 16,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+97
-16
@@ -19,6 +19,10 @@ pub enum DecodingResult {
|
|||||||
U8(Vec<u8>),
|
U8(Vec<u8>),
|
||||||
/// A vector of unsigned words
|
/// A vector of unsigned words
|
||||||
U16(Vec<u16>),
|
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 {
|
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 {
|
pub fn as_buffer(&mut self, start: usize) -> DecodingBuffer {
|
||||||
match *self {
|
match *self {
|
||||||
DecodingResult::U8(ref mut buf) => DecodingBuffer::U8(&mut buf[start..]),
|
DecodingResult::U8(ref mut buf) => DecodingBuffer::U8(&mut buf[start..]),
|
||||||
DecodingResult::U16(ref mut buf) => DecodingBuffer::U16(&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]),
|
U8(&'a mut [u8]),
|
||||||
/// A slice of unsigned words
|
/// A slice of unsigned words
|
||||||
U16(&'a mut [u16]),
|
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> {
|
impl<'a> DecodingBuffer<'a> {
|
||||||
@@ -59,6 +85,8 @@ impl<'a> DecodingBuffer<'a> {
|
|||||||
match *self {
|
match *self {
|
||||||
DecodingBuffer::U8(ref buf) => buf.len(),
|
DecodingBuffer::U8(ref buf) => buf.len(),
|
||||||
DecodingBuffer::U16(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 {
|
match *self {
|
||||||
DecodingBuffer::U8(_) => 1,
|
DecodingBuffer::U8(_) => 1,
|
||||||
DecodingBuffer::U16(_) => 2,
|
DecodingBuffer::U16(_) => 2,
|
||||||
|
DecodingBuffer::U32(_) => 4,
|
||||||
|
DecodingBuffer::U64(_) => 8,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,6 +106,8 @@ impl<'a> DecodingBuffer<'a> {
|
|||||||
match *self {
|
match *self {
|
||||||
DecodingBuffer::U8(ref mut buf) => DecodingBuffer::U8(buf),
|
DecodingBuffer::U8(ref mut buf) => DecodingBuffer::U8(buf),
|
||||||
DecodingBuffer::U16(ref mut buf) => DecodingBuffer::U16(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)
|
fn rev_hpredict_nsamp<T>(image: &mut [T], size: (u32, u32), samples: usize)
|
||||||
where
|
where
|
||||||
T: Copy + Wrapping,
|
T: Copy + Wrapping,
|
||||||
@@ -250,9 +294,9 @@ where
|
|||||||
|
|
||||||
fn rev_hpredict(image: DecodingBuffer, size: (u32, u32), color_type: ColorType) -> TiffResult<()> {
|
fn rev_hpredict(image: DecodingBuffer, size: (u32, u32), color_type: ColorType) -> TiffResult<()> {
|
||||||
let samples = match color_type {
|
let samples = match color_type {
|
||||||
ColorType::Gray(8) | ColorType::Gray(16) => 1,
|
ColorType::Gray(8) | ColorType::Gray(16) | ColorType::Gray(32) | ColorType::Gray(64) => 1,
|
||||||
ColorType::RGB(8) | ColorType::RGB(16) => 3,
|
ColorType::RGB(8) | ColorType::RGB(16) | ColorType::RGB(32) | ColorType::RGB(64) => 3,
|
||||||
ColorType::RGBA(8) | ColorType::RGBA(16) | ColorType::CMYK(8) => 4,
|
ColorType::RGBA(8) | ColorType::RGBA(16) | ColorType::RGBA(32) | ColorType::RGBA(64) | ColorType::CMYK(8) => 4,
|
||||||
_ => {
|
_ => {
|
||||||
return Err(TiffError::UnsupportedError(
|
return Err(TiffError::UnsupportedError(
|
||||||
TiffUnsupportedError::HorizontalPredictor(color_type),
|
TiffUnsupportedError::HorizontalPredictor(color_type),
|
||||||
@@ -266,6 +310,12 @@ fn rev_hpredict(image: DecodingBuffer, size: (u32, u32), color_type: ColorType)
|
|||||||
DecodingBuffer::U16(buf) => {
|
DecodingBuffer::U16(buf) => {
|
||||||
rev_hpredict_nsamp(buf, size, samples);
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -301,19 +351,18 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
|
|
||||||
pub fn colortype(&mut self) -> TiffResult<ColorType> {
|
pub fn colortype(&mut self) -> TiffResult<ColorType> {
|
||||||
match self.photometric_interpretation {
|
match self.photometric_interpretation {
|
||||||
// TODO: catch also [ 8, 8, 8, _] this does not work due to a bug in rust atm
|
PhotometricInterpretation::RGB => {
|
||||||
PhotometricInterpretation::RGB if self.bits_per_sample == [8, 8, 8, 8] => {
|
match self.bits_per_sample[..] {
|
||||||
Ok(ColorType::RGBA(8))
|
[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)),
|
||||||
PhotometricInterpretation::RGB if self.bits_per_sample == [8, 8, 8] => {
|
_ => Err(TiffError::UnsupportedError(
|
||||||
Ok(ColorType::RGB(8))
|
TiffUnsupportedError::InterpretationWithBits(
|
||||||
}
|
self.photometric_interpretation,
|
||||||
PhotometricInterpretation::RGB if self.bits_per_sample == [16, 16, 16, 16] => {
|
self.bits_per_sample.clone(),
|
||||||
Ok(ColorType::RGBA(16))
|
),
|
||||||
}
|
)),
|
||||||
PhotometricInterpretation::RGB if self.bits_per_sample == [16, 16, 16] => {
|
}
|
||||||
Ok(ColorType::RGB(16))
|
},
|
||||||
}
|
|
||||||
PhotometricInterpretation::CMYK if self.bits_per_sample == [8, 8, 8, 8] => {
|
PhotometricInterpretation::CMYK if self.bits_per_sample == [8, 8, 8, 8] => {
|
||||||
Ok(ColorType::CMYK(8))
|
Ok(ColorType::CMYK(8))
|
||||||
}
|
}
|
||||||
@@ -659,6 +708,34 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
reader.read_u16_into(&mut buffer[..bytes / 2])?;
|
reader.read_u16_into(&mut buffer[..bytes / 2])?;
|
||||||
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)) => {
|
(ColorType::Gray(16), DecodingBuffer::U16(ref mut buffer)) => {
|
||||||
reader.read_u16_into(&mut buffer[..bytes / 2])?;
|
reader.read_u16_into(&mut buffer[..bytes / 2])?;
|
||||||
if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
|
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) {
|
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 <= 8 => DecodingResult::new_u8(buffer_size, &self.limits)?,
|
||||||
n if n <= 16 => DecodingResult::new_u16(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 => {
|
n => {
|
||||||
return Err(TiffError::UnsupportedError(
|
return Err(TiffError::UnsupportedError(
|
||||||
TiffUnsupportedError::UnsupportedBitsPerChannel(n),
|
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) {
|
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 <= 8 => DecodingResult::new_u8(buffer_size, &self.limits)?,
|
||||||
n if n <= 16 => DecodingResult::new_u16(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 => {
|
n => {
|
||||||
return Err(TiffError::UnsupportedError(
|
return Err(TiffError::UnsupportedError(
|
||||||
TiffUnsupportedError::UnsupportedBitsPerChannel(n),
|
TiffUnsupportedError::UnsupportedBitsPerChannel(n),
|
||||||
|
|||||||
@@ -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
|
/// Reads an i32
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn read_i32(&mut self) -> Result<i32, io::Error> {
|
fn read_i32(&mut self) -> Result<i32, io::Error> {
|
||||||
|
|||||||
@@ -24,6 +24,20 @@ impl ColorType for Gray16 {
|
|||||||
const BITS_PER_SAMPLE: &'static [u16] = &[16];
|
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;
|
pub struct RGB8;
|
||||||
impl ColorType for RGB8 {
|
impl ColorType for RGB8 {
|
||||||
type Inner = u8;
|
type Inner = u8;
|
||||||
@@ -38,6 +52,20 @@ impl ColorType for RGB16 {
|
|||||||
const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16];
|
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;
|
pub struct RGBA8;
|
||||||
impl ColorType for RGBA8 {
|
impl ColorType for RGBA8 {
|
||||||
type Inner = u8;
|
type Inner = u8;
|
||||||
@@ -52,6 +80,20 @@ impl ColorType for RGBA16 {
|
|||||||
const BITS_PER_SAMPLE: &'static [u16] = &[16, 16, 16, 16];
|
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;
|
pub struct CMYK8;
|
||||||
impl ColorType for CMYK8 {
|
impl ColorType for CMYK8 {
|
||||||
type Inner = u8;
|
type Inner = u8;
|
||||||
|
|||||||
@@ -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] {
|
impl TiffValue for [Rational] {
|
||||||
const BYTE_LEN: u32 = 8;
|
const BYTE_LEN: u32 = 8;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::RATIONAL;
|
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 {
|
impl TiffValue for Rational {
|
||||||
const BYTE_LEN: u32 = 8;
|
const BYTE_LEN: u32 = 8;
|
||||||
const FIELD_TYPE: ifd::Type = ifd::Type::RATIONAL;
|
const FIELD_TYPE: ifd::Type = ifd::Type::RATIONAL;
|
||||||
|
|||||||
@@ -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]
|
#[test]
|
||||||
fn test_gray_u16() {
|
fn test_gray_u16() {
|
||||||
let img_file =
|
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]
|
#[test]
|
||||||
fn test_rgb_u16() {
|
fn test_rgb_u16() {
|
||||||
let img_file = File::open("./tests/images/rgb-3c-16b.tiff").expect("Cannot find test image!");
|
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]
|
#[test]
|
||||||
fn test_string_tags() {
|
fn test_string_tags() {
|
||||||
// these files have null-terminated strings for their Software tag. One has extra bytes after
|
// these files have null-terminated strings for their Software tag. One has extra bytes after
|
||||||
|
|||||||
+124
-2
@@ -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]
|
#[test]
|
||||||
fn test_multiple_byte() {
|
fn test_multiple_byte() {
|
||||||
let mut data = Cursor::new(Vec::new());
|
let mut data = Cursor::new(Vec::new());
|
||||||
@@ -273,10 +395,10 @@ fn test_multipage_image() {
|
|||||||
|
|
||||||
// write first grayscale image (2x2 16-bit)
|
// write first grayscale image (2x2 16-bit)
|
||||||
let img1: Vec<u16> = [1, 2, 3, 4].to_vec();
|
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)
|
// write second grayscale image (3x3 8-bit)
|
||||||
let img2: Vec<u8> = [9, 8, 7, 6, 5, 4, 3, 2, 1].to_vec();
|
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
|
// seek to the beginning of the file, so that it can be decoded
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in new issue
Block a user