Merge pull request #125 from TomasKralCZ/tiles
Added basic tile decoding
This commit is contained in:
10 files changed
+659
-29
No files matched your search
+553
-27
@@ -3,7 +3,9 @@ use std::collections::HashMap;
|
|||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::io::{self, Read, Seek};
|
use std::io::{self, Read, Seek};
|
||||||
|
|
||||||
use crate::{bytecast, ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
use crate::{
|
||||||
|
bytecast, ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError, UsageError,
|
||||||
|
};
|
||||||
|
|
||||||
use self::ifd::Directory;
|
use self::ifd::Directory;
|
||||||
use crate::tags::{
|
use crate::tags::{
|
||||||
@@ -238,6 +240,73 @@ struct StripDecodeState {
|
|||||||
strip_bytes: Vec<u64>,
|
strip_bytes: Vec<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
/// Computed values useful for tile decoding
|
||||||
|
struct TileAttributes {
|
||||||
|
tile_width: usize,
|
||||||
|
tile_length: usize,
|
||||||
|
tiles_down: usize,
|
||||||
|
tiles_across: usize,
|
||||||
|
/// Length of padding for rightmost tiles in pixels
|
||||||
|
padding_right: usize,
|
||||||
|
/// length of padding for bottommost tile in pixels
|
||||||
|
padding_down: usize,
|
||||||
|
/// A simple buffer right paddding is read into
|
||||||
|
tile_samples: usize,
|
||||||
|
/// Sample count of one row of one tile
|
||||||
|
row_samples: usize,
|
||||||
|
/// Sample count of one row of tiles
|
||||||
|
tile_strip_samples: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TileAttributes {
|
||||||
|
/// Returns the tile offset in the result buffer, counted in samples
|
||||||
|
fn get_offset(&self, tile: usize) -> usize {
|
||||||
|
let row = tile / self.tiles_across;
|
||||||
|
let column = tile % self.tiles_across;
|
||||||
|
|
||||||
|
(row * self.tile_strip_samples) + (column * self.row_samples)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_padding(&self, tile: usize) -> (usize, usize) {
|
||||||
|
let row = tile / self.tiles_across;
|
||||||
|
let column = tile % self.tiles_across;
|
||||||
|
|
||||||
|
let padding_right = if column == self.tiles_across - 1 {
|
||||||
|
self.padding_right
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
let padding_down = if row == self.tiles_down - 1 {
|
||||||
|
self.padding_down
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
(padding_right, padding_down)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
/// Stateful variables for tile decoding
|
||||||
|
struct TileDecodeState {
|
||||||
|
current_tile: usize,
|
||||||
|
tile_offsets: Vec<u64>,
|
||||||
|
tile_bytes: Vec<u64>,
|
||||||
|
/// Buffer used for skipping horizontal padding
|
||||||
|
padding_buffer: Vec<u8>,
|
||||||
|
/// Pixel width of one row of the decoding result (tile / whole image)
|
||||||
|
result_width: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
/// Chunk type of the internal representation
|
||||||
|
pub enum ChunkType {
|
||||||
|
Strip,
|
||||||
|
Tile,
|
||||||
|
}
|
||||||
|
|
||||||
/// Decoding limits
|
/// Decoding limits
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Limits {
|
pub struct Limits {
|
||||||
@@ -308,7 +377,10 @@ where
|
|||||||
sample_format: Vec<SampleFormat>,
|
sample_format: Vec<SampleFormat>,
|
||||||
photometric_interpretation: PhotometricInterpretation,
|
photometric_interpretation: PhotometricInterpretation,
|
||||||
compression_method: CompressionMethod,
|
compression_method: CompressionMethod,
|
||||||
|
chunk_type: ChunkType,
|
||||||
strip_decoder: Option<StripDecodeState>,
|
strip_decoder: Option<StripDecodeState>,
|
||||||
|
tile_decoder: Option<TileDecodeState>,
|
||||||
|
tile_attributes: Option<TileAttributes>,
|
||||||
}
|
}
|
||||||
|
|
||||||
trait Wrapping {
|
trait Wrapping {
|
||||||
@@ -363,7 +435,12 @@ impl Wrapping for i64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rev_hpredict_nsamp<T>(image: &mut [T], size: (u32, u32), samples: usize) -> TiffResult<()>
|
fn rev_hpredict_nsamp<T>(
|
||||||
|
image: &mut [T],
|
||||||
|
size: (u32, u32), // Size of the block
|
||||||
|
img_width: usize, // Width of the image (this distinction is needed for tiles)
|
||||||
|
samples: usize,
|
||||||
|
) -> TiffResult<()>
|
||||||
where
|
where
|
||||||
T: Copy + Wrapping,
|
T: Copy + Wrapping,
|
||||||
{
|
{
|
||||||
@@ -371,15 +448,21 @@ where
|
|||||||
let height = usize::try_from(size.1)?;
|
let height = usize::try_from(size.1)?;
|
||||||
for row in 0..height {
|
for row in 0..height {
|
||||||
for col in samples..width * samples {
|
for col in samples..width * samples {
|
||||||
let prev_pixel = image[(row * width * samples + col - samples)];
|
let prev_pixel = image[(row * img_width * samples + col - samples)];
|
||||||
let pixel = &mut image[(row * width * samples + col)];
|
let pixel = &mut image[(row * img_width * samples + col)];
|
||||||
*pixel = pixel.wrapping_add(prev_pixel);
|
*pixel = pixel.wrapping_add(prev_pixel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rev_hpredict(image: DecodingBuffer, size: (u32, u32), color_type: ColorType) -> TiffResult<()> {
|
fn rev_hpredict(
|
||||||
|
image: DecodingBuffer,
|
||||||
|
size: (u32, u32),
|
||||||
|
img_width: usize,
|
||||||
|
color_type: ColorType,
|
||||||
|
) -> TiffResult<()> {
|
||||||
|
// TODO: use bits_per_sample.len() after implementing type 3 predictor
|
||||||
let samples = match color_type {
|
let samples = match color_type {
|
||||||
ColorType::Gray(8) | ColorType::Gray(16) | ColorType::Gray(32) | ColorType::Gray(64) => 1,
|
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::RGB(8) | ColorType::RGB(16) | ColorType::RGB(32) | ColorType::RGB(64) => 3,
|
||||||
@@ -397,23 +480,24 @@ fn rev_hpredict(image: DecodingBuffer, size: (u32, u32), color_type: ColorType)
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match image {
|
match image {
|
||||||
DecodingBuffer::U8(buf) => {
|
DecodingBuffer::U8(buf) => {
|
||||||
rev_hpredict_nsamp(buf, size, samples)?;
|
rev_hpredict_nsamp(buf, size, img_width, samples)?;
|
||||||
}
|
}
|
||||||
DecodingBuffer::U16(buf) => {
|
DecodingBuffer::U16(buf) => {
|
||||||
rev_hpredict_nsamp(buf, size, samples)?;
|
rev_hpredict_nsamp(buf, size, img_width, samples)?;
|
||||||
}
|
}
|
||||||
DecodingBuffer::U32(buf) => {
|
DecodingBuffer::U32(buf) => {
|
||||||
rev_hpredict_nsamp(buf, size, samples)?;
|
rev_hpredict_nsamp(buf, size, img_width, samples)?;
|
||||||
}
|
}
|
||||||
DecodingBuffer::U64(buf) => {
|
DecodingBuffer::U64(buf) => {
|
||||||
rev_hpredict_nsamp(buf, size, samples)?;
|
rev_hpredict_nsamp(buf, size, img_width, samples)?;
|
||||||
}
|
}
|
||||||
DecodingBuffer::F32(_buf) => {
|
DecodingBuffer::F32(_buf) => {
|
||||||
// FIXME: check how this is defined.
|
// FIXME: check how this is defined.
|
||||||
// See issue #89.
|
// See issue #89.
|
||||||
// rev_hpredict_nsamp(buf, size, samples)?;
|
// rev_hpredict_nsamp(buf, size, img_width,samples)?;
|
||||||
return Err(TiffError::UnsupportedError(
|
return Err(TiffError::UnsupportedError(
|
||||||
TiffUnsupportedError::HorizontalPredictor(color_type),
|
TiffUnsupportedError::HorizontalPredictor(color_type),
|
||||||
));
|
));
|
||||||
@@ -421,27 +505,77 @@ fn rev_hpredict(image: DecodingBuffer, size: (u32, u32), color_type: ColorType)
|
|||||||
DecodingBuffer::F64(_buf) => {
|
DecodingBuffer::F64(_buf) => {
|
||||||
//FIXME: check how this is defined.
|
//FIXME: check how this is defined.
|
||||||
// See issue #89.
|
// See issue #89.
|
||||||
// rev_hpredict_nsamp(buf, size, samples)?;
|
// rev_hpredict_nsamp(buf, size, img_width,samples)?;
|
||||||
return Err(TiffError::UnsupportedError(
|
return Err(TiffError::UnsupportedError(
|
||||||
TiffUnsupportedError::HorizontalPredictor(color_type),
|
TiffUnsupportedError::HorizontalPredictor(color_type),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
DecodingBuffer::I8(buf) => {
|
DecodingBuffer::I8(buf) => {
|
||||||
rev_hpredict_nsamp(buf, size, samples)?;
|
rev_hpredict_nsamp(buf, size, img_width, samples)?;
|
||||||
}
|
}
|
||||||
DecodingBuffer::I16(buf) => {
|
DecodingBuffer::I16(buf) => {
|
||||||
rev_hpredict_nsamp(buf, size, samples)?;
|
rev_hpredict_nsamp(buf, size, img_width, samples)?;
|
||||||
}
|
}
|
||||||
DecodingBuffer::I32(buf) => {
|
DecodingBuffer::I32(buf) => {
|
||||||
rev_hpredict_nsamp(buf, size, samples)?;
|
rev_hpredict_nsamp(buf, size, img_width, samples)?;
|
||||||
}
|
}
|
||||||
DecodingBuffer::I64(buf) => {
|
DecodingBuffer::I64(buf) => {
|
||||||
rev_hpredict_nsamp(buf, size, samples)?;
|
rev_hpredict_nsamp(buf, size, img_width, samples)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn process_photometry_u64(buffer: &mut [u64]) {
|
||||||
|
for datum in buffer.iter_mut() {
|
||||||
|
*datum = 0xffff_ffff_ffff_ffff - *datum
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_photometry_u32(buffer: &mut [u32]) {
|
||||||
|
for datum in buffer.iter_mut() {
|
||||||
|
*datum = 0xffff_ffff - *datum
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_photometry_u16(buffer: &mut [u16]) {
|
||||||
|
for datum in buffer.iter_mut() {
|
||||||
|
*datum = 0xffff - *datum
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_photometry_i16(buffer: &mut [i16]) {
|
||||||
|
for datum in buffer.iter_mut() {
|
||||||
|
*datum = !*datum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_photometry_i8(buffer: &mut [i8]) {
|
||||||
|
for datum in buffer.iter_mut() {
|
||||||
|
*datum = !*datum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_photometry_u8(buffer: &mut [u8]) {
|
||||||
|
for byte in buffer.iter_mut() {
|
||||||
|
*byte = 0xff - *byte
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_photometry_f64(buffer: &mut [f64]) {
|
||||||
|
for datum in buffer.iter_mut() {
|
||||||
|
// FIXME: assumes [0, 1) range for floats
|
||||||
|
*datum = 1.0 - *datum
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_photometry_f32(buffer: &mut [f32]) {
|
||||||
|
for datum in buffer.iter_mut() {
|
||||||
|
// FIXME: assumes [0, 1) range for floats
|
||||||
|
*datum = 1.0 - *datum
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<R: Read + Seek> Decoder<R> {
|
impl<R: Read + Seek> Decoder<R> {
|
||||||
/// Create a new decoder that decodes from the stream ```r```
|
/// Create a new decoder that decodes from the stream ```r```
|
||||||
pub fn new(r: R) -> TiffResult<Decoder<R>> {
|
pub fn new(r: R) -> TiffResult<Decoder<R>> {
|
||||||
@@ -459,7 +593,10 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
sample_format: vec![SampleFormat::Uint],
|
sample_format: vec![SampleFormat::Uint],
|
||||||
photometric_interpretation: PhotometricInterpretation::BlackIsZero,
|
photometric_interpretation: PhotometricInterpretation::BlackIsZero,
|
||||||
compression_method: CompressionMethod::None,
|
compression_method: CompressionMethod::None,
|
||||||
|
chunk_type: ChunkType::Strip,
|
||||||
strip_decoder: None,
|
strip_decoder: None,
|
||||||
|
tile_decoder: None,
|
||||||
|
tile_attributes: None,
|
||||||
}
|
}
|
||||||
.init()
|
.init()
|
||||||
}
|
}
|
||||||
@@ -614,6 +751,23 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
_ => return Err(TiffUnsupportedError::UnsupportedSampleDepth(self.samples).into()),
|
_ => return Err(TiffUnsupportedError::UnsupportedSampleDepth(self.samples).into()),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.chunk_type =
|
||||||
|
match (
|
||||||
|
self.get_tag_u32(Tag::RowsPerStrip),
|
||||||
|
self.get_tag_u32(Tag::TileWidth),
|
||||||
|
self.get_tag_u32(Tag::TileLength),
|
||||||
|
) {
|
||||||
|
(Ok(_), Err(_), Err(_)) => ChunkType::Strip,
|
||||||
|
(Err(_), Ok(_), Ok(_)) => ChunkType::Tile,
|
||||||
|
// TODO: The spec says not to use both strip-oriented fields and tile-oriented fields.
|
||||||
|
// We can relax this later if it becomes a problem
|
||||||
|
_ => return Err(TiffError::FormatError(TiffFormatError::Format(
|
||||||
|
String::from(
|
||||||
|
"Neither strips nor tiles were found or both were used in the same file",
|
||||||
|
),
|
||||||
|
))),
|
||||||
|
};
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1058,8 +1212,207 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Decompresses the tile into the supplied buffer.
|
||||||
|
/// Returns the number of bytes read.
|
||||||
|
fn expand_tile<'a>(
|
||||||
|
&mut self,
|
||||||
|
mut buffer: DecodingBuffer<'a>,
|
||||||
|
offset: u64,
|
||||||
|
compressed_length: u64,
|
||||||
|
tile: usize,
|
||||||
|
) -> TiffResult<()> {
|
||||||
|
let color_type = self.colortype()?;
|
||||||
|
let byte_len = buffer.byte_len();
|
||||||
|
|
||||||
|
let tile_attrs = self.tile_attributes.as_mut().unwrap();
|
||||||
|
let (padding_right, padding_down) = tile_attrs.get_padding(tile);
|
||||||
|
let tile_samples = tile_attrs.tile_samples;
|
||||||
|
let tile_length = tile_attrs.tile_length;
|
||||||
|
let row_samples = tile_attrs.row_samples;
|
||||||
|
let padding_right_samples = padding_right * self.bits_per_sample.len();
|
||||||
|
|
||||||
|
self.goto_offset_u64(offset)?;
|
||||||
|
|
||||||
|
let tile_decoder = self.tile_decoder.as_mut().unwrap();
|
||||||
|
let line_samples = tile_decoder.result_width * self.bits_per_sample.len();
|
||||||
|
let padding_buffer = &mut tile_decoder.padding_buffer;
|
||||||
|
|
||||||
|
let mut reader = Self::create_reader(
|
||||||
|
&mut self.reader,
|
||||||
|
self.compression_method,
|
||||||
|
compressed_length,
|
||||||
|
tile_samples,
|
||||||
|
byte_len,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
for row in 0..(tile_length - padding_down) {
|
||||||
|
let buf = match &mut buffer {
|
||||||
|
DecodingBuffer::U8(buf) => &mut *buf,
|
||||||
|
DecodingBuffer::I8(buf) => bytecast::i8_as_ne_mut_bytes(buf),
|
||||||
|
DecodingBuffer::U16(buf) => bytecast::u16_as_ne_mut_bytes(buf),
|
||||||
|
DecodingBuffer::I16(buf) => bytecast::i16_as_ne_mut_bytes(buf),
|
||||||
|
DecodingBuffer::U32(buf) => bytecast::u32_as_ne_mut_bytes(buf),
|
||||||
|
DecodingBuffer::I32(buf) => bytecast::i32_as_ne_mut_bytes(buf),
|
||||||
|
DecodingBuffer::U64(buf) => bytecast::u64_as_ne_mut_bytes(buf),
|
||||||
|
DecodingBuffer::I64(buf) => bytecast::i64_as_ne_mut_bytes(buf),
|
||||||
|
DecodingBuffer::F32(buf) => bytecast::f32_as_ne_mut_bytes(buf),
|
||||||
|
DecodingBuffer::F64(buf) => bytecast::f64_as_ne_mut_bytes(buf),
|
||||||
|
};
|
||||||
|
|
||||||
|
let row_start = row * line_samples;
|
||||||
|
let row_end = row_start + row_samples - padding_right_samples;
|
||||||
|
|
||||||
|
let row = &mut buf[(row_start * byte_len)..(row_end * byte_len)];
|
||||||
|
reader.read_exact(row)?;
|
||||||
|
// Skip horizontal padding
|
||||||
|
// TODO: find a better way of skipping the padding
|
||||||
|
if padding_right > 0 {
|
||||||
|
reader.read_exact(padding_buffer)?
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fix byte order if necessary
|
||||||
|
match self.byte_order {
|
||||||
|
ByteOrder::LittleEndian => match &mut buffer {
|
||||||
|
DecodingBuffer::U8(_) | DecodingBuffer::I8(_) => {}
|
||||||
|
DecodingBuffer::U16(b) => b[row_start..row_end]
|
||||||
|
.iter_mut()
|
||||||
|
.for_each(|v| *v = u16::from_le(*v)),
|
||||||
|
DecodingBuffer::I16(b) => b[row_start..row_end]
|
||||||
|
.iter_mut()
|
||||||
|
.for_each(|v| *v = i16::from_le(*v)),
|
||||||
|
DecodingBuffer::U32(b) => b[row_start..row_end]
|
||||||
|
.iter_mut()
|
||||||
|
.for_each(|v| *v = u32::from_le(*v)),
|
||||||
|
DecodingBuffer::I32(b) => b[row_start..row_end]
|
||||||
|
.iter_mut()
|
||||||
|
.for_each(|v| *v = i32::from_le(*v)),
|
||||||
|
DecodingBuffer::U64(b) => b[row_start..row_end]
|
||||||
|
.iter_mut()
|
||||||
|
.for_each(|v| *v = u64::from_le(*v)),
|
||||||
|
DecodingBuffer::I64(b) => b[row_start..row_end]
|
||||||
|
.iter_mut()
|
||||||
|
.for_each(|v| *v = i64::from_le(*v)),
|
||||||
|
DecodingBuffer::F32(b) => b[row_start..row_end]
|
||||||
|
.iter_mut()
|
||||||
|
.for_each(|v| *v = f32::from_bits(u32::from_le(v.to_bits()))),
|
||||||
|
DecodingBuffer::F64(b) => b[row_start..row_end]
|
||||||
|
.iter_mut()
|
||||||
|
.for_each(|v| *v = f64::from_bits(u64::from_le(v.to_bits()))),
|
||||||
|
},
|
||||||
|
ByteOrder::BigEndian => match &mut buffer {
|
||||||
|
DecodingBuffer::U8(_) | DecodingBuffer::I8(_) => {}
|
||||||
|
DecodingBuffer::U16(b) => b[row_start..row_end]
|
||||||
|
.iter_mut()
|
||||||
|
.for_each(|v| *v = u16::from_be(*v)),
|
||||||
|
DecodingBuffer::I16(b) => b[row_start..row_end]
|
||||||
|
.iter_mut()
|
||||||
|
.for_each(|v| *v = i16::from_be(*v)),
|
||||||
|
DecodingBuffer::U32(b) => b[row_start..row_end]
|
||||||
|
.iter_mut()
|
||||||
|
.for_each(|v| *v = u32::from_be(*v)),
|
||||||
|
DecodingBuffer::I32(b) => b[row_start..row_end]
|
||||||
|
.iter_mut()
|
||||||
|
.for_each(|v| *v = i32::from_be(*v)),
|
||||||
|
DecodingBuffer::U64(b) => b[row_start..row_end]
|
||||||
|
.iter_mut()
|
||||||
|
.for_each(|v| *v = u64::from_be(*v)),
|
||||||
|
DecodingBuffer::I64(b) => b[row_start..row_end]
|
||||||
|
.iter_mut()
|
||||||
|
.for_each(|v| *v = i64::from_be(*v)),
|
||||||
|
DecodingBuffer::F32(b) => b[row_start..row_end]
|
||||||
|
.iter_mut()
|
||||||
|
.for_each(|v| *v = f32::from_bits(u32::from_be(v.to_bits()))),
|
||||||
|
DecodingBuffer::F64(b) => b[row_start..row_end]
|
||||||
|
.iter_mut()
|
||||||
|
.for_each(|v| *v = f64::from_bits(u64::from_be(v.to_bits()))),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Invert colors if necessary.
|
||||||
|
if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
|
||||||
|
match (color_type, &mut buffer) {
|
||||||
|
(ColorType::Gray(64), DecodingBuffer::U64(ref mut buffer)) => {
|
||||||
|
process_photometry_u64(&mut buffer[row_start..row_end]);
|
||||||
|
}
|
||||||
|
(ColorType::Gray(32), DecodingBuffer::U32(ref mut buffer)) => {
|
||||||
|
process_photometry_u32(&mut buffer[row_start..row_end]);
|
||||||
|
}
|
||||||
|
(ColorType::Gray(16), DecodingBuffer::U16(ref mut buffer)) => {
|
||||||
|
process_photometry_u16(&mut buffer[row_start..row_end]);
|
||||||
|
}
|
||||||
|
(ColorType::Gray(n), DecodingBuffer::U8(ref mut buffer)) if n <= 8 => {
|
||||||
|
process_photometry_u8(&mut buffer[row_start..row_end]);
|
||||||
|
}
|
||||||
|
(ColorType::Gray(32), DecodingBuffer::F32(ref mut buffer)) => {
|
||||||
|
process_photometry_f32(&mut buffer[row_start..row_end]);
|
||||||
|
}
|
||||||
|
(ColorType::Gray(64), DecodingBuffer::F64(ref mut buffer)) => {
|
||||||
|
process_photometry_f64(&mut buffer[row_start..row_end]);
|
||||||
|
}
|
||||||
|
(ColorType::Gray(n), DecodingBuffer::I8(ref mut buffer)) if n <= 8 => {
|
||||||
|
process_photometry_i8(&mut buffer[row_start..row_end]);
|
||||||
|
}
|
||||||
|
(ColorType::Gray(16), DecodingBuffer::I16(ref mut buffer)) => {
|
||||||
|
process_photometry_i16(&mut buffer[row_start..row_end]);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_reader<'r>(
|
||||||
|
reader: &'r mut SmartReader<R>,
|
||||||
|
compression_method: CompressionMethod,
|
||||||
|
compressed_length: u64,
|
||||||
|
samples: usize, // Expected chunk length in samples
|
||||||
|
byte_len: usize, // Byte length of the result buffer
|
||||||
|
) -> TiffResult<Box<dyn Read + 'r>> {
|
||||||
|
Ok(match compression_method {
|
||||||
|
CompressionMethod::None => Box::new(reader),
|
||||||
|
CompressionMethod::LZW => Box::new(
|
||||||
|
LZWReader::new(
|
||||||
|
reader,
|
||||||
|
usize::try_from(compressed_length)?,
|
||||||
|
samples * byte_len,
|
||||||
|
)?
|
||||||
|
.1,
|
||||||
|
),
|
||||||
|
CompressionMethod::PackBits => {
|
||||||
|
Box::new(PackBitsReader::new(reader, usize::try_from(compressed_length)?)?.1)
|
||||||
|
}
|
||||||
|
CompressionMethod::OldDeflate => {
|
||||||
|
Box::new(DeflateReader::new(reader, samples * byte_len)?.1)
|
||||||
|
}
|
||||||
|
method => {
|
||||||
|
return Err(TiffError::UnsupportedError(
|
||||||
|
TiffUnsupportedError::UnsupportedCompressionMethod(method),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_chunk_type(&self, expected: ChunkType) -> TiffResult<()> {
|
||||||
|
if expected != self.chunk_type {
|
||||||
|
return Err(TiffError::UsageError(UsageError::InvalidChunkType(
|
||||||
|
expected,
|
||||||
|
self.chunk_type,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The chunk type (Strips / Tiles) of the image
|
||||||
|
pub fn get_chunk_type(&self) -> ChunkType {
|
||||||
|
self.chunk_type
|
||||||
|
}
|
||||||
|
|
||||||
/// Number of strips in image
|
/// Number of strips in image
|
||||||
pub fn strip_count(&mut self) -> TiffResult<u32> {
|
pub fn strip_count(&mut self) -> TiffResult<u32> {
|
||||||
|
self.check_chunk_type(ChunkType::Strip)?;
|
||||||
let rows_per_strip = self.get_tag_u32(Tag::RowsPerStrip).unwrap_or(self.height);
|
let rows_per_strip = self.get_tag_u32(Tag::RowsPerStrip).unwrap_or(self.height);
|
||||||
|
|
||||||
if rows_per_strip == 0 {
|
if rows_per_strip == 0 {
|
||||||
@@ -1069,6 +1422,16 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
Ok((self.height + rows_per_strip - 1) / rows_per_strip)
|
Ok((self.height + rows_per_strip - 1) / rows_per_strip)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Number of tiles in image
|
||||||
|
pub fn tile_count(&mut self) -> TiffResult<u32> {
|
||||||
|
self.check_chunk_type(ChunkType::Tile)?;
|
||||||
|
self.init_tile_attributes()?;
|
||||||
|
let tile_attrs = self.tile_attributes.as_ref().unwrap();
|
||||||
|
Ok(u32::try_from(
|
||||||
|
tile_attrs.tiles_across * tile_attrs.tiles_down,
|
||||||
|
)?)
|
||||||
|
}
|
||||||
|
|
||||||
fn initialize_strip_decoder(&mut self) -> TiffResult<()> {
|
fn initialize_strip_decoder(&mut self) -> TiffResult<()> {
|
||||||
if self.strip_decoder.is_none() {
|
if self.strip_decoder.is_none() {
|
||||||
let strip_offsets = self.get_tag_u64_vec(Tag::StripOffsets)?;
|
let strip_offsets = self.get_tag_u64_vec(Tag::StripOffsets)?;
|
||||||
@@ -1083,6 +1446,67 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn init_tile_attributes(&mut self) -> TiffResult<()> {
|
||||||
|
if self.tile_attributes.is_none() {
|
||||||
|
let tile_width = usize::try_from(self.get_tag_u32(Tag::TileWidth)?)?;
|
||||||
|
let tile_length = usize::try_from(self.get_tag_u32(Tag::TileLength)?)?;
|
||||||
|
|
||||||
|
let tiles_across = (usize::try_from(self.width)? + tile_width - 1) / tile_width;
|
||||||
|
let tiles_down = (usize::try_from(self.height)? + tile_length - 1) / tile_length;
|
||||||
|
|
||||||
|
let samples_per_pixel = self.bits_per_sample.len();
|
||||||
|
|
||||||
|
let tile_samples = tile_length * tile_width * samples_per_pixel;
|
||||||
|
let padding_right = (tiles_across * tile_width) - usize::try_from(self.width)?;
|
||||||
|
let tile_strip_samples =
|
||||||
|
(tile_samples * tiles_across) - (padding_right * tile_length * samples_per_pixel);
|
||||||
|
|
||||||
|
self.tile_attributes = Some(TileAttributes {
|
||||||
|
tile_width,
|
||||||
|
tile_length,
|
||||||
|
tiles_across,
|
||||||
|
tiles_down,
|
||||||
|
tile_samples,
|
||||||
|
padding_right,
|
||||||
|
padding_down: (tiles_down * tile_length) - usize::try_from(self.height)?,
|
||||||
|
row_samples: (tile_width * samples_per_pixel),
|
||||||
|
tile_strip_samples,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_tile_decoder(
|
||||||
|
&mut self,
|
||||||
|
result_width: usize,
|
||||||
|
buffer_byte_len: usize,
|
||||||
|
) -> TiffResult<()> {
|
||||||
|
let samples_per_pixel = self.bits_per_sample.len();
|
||||||
|
|
||||||
|
if self.tile_decoder.is_none() {
|
||||||
|
let tile_attrs = self.tile_attributes.as_ref().unwrap();
|
||||||
|
|
||||||
|
let padding_buffer_size =
|
||||||
|
tile_attrs.padding_right * samples_per_pixel * buffer_byte_len;
|
||||||
|
if padding_buffer_size > self.limits.intermediate_buffer_size {
|
||||||
|
return Err(TiffError::LimitsExceeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tile_decoder = Some(TileDecodeState {
|
||||||
|
current_tile: 0,
|
||||||
|
tile_offsets: self.get_tag_u64_vec(Tag::TileOffsets)?,
|
||||||
|
tile_bytes: self.get_tag_u64_vec(Tag::TileByteCounts)?,
|
||||||
|
padding_buffer: vec![0; padding_buffer_size],
|
||||||
|
result_width: 0, // needs to be updated for differently padded_tiles, see below
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tile_decoder.as_mut().unwrap().result_width = result_width;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn read_jpeg(&mut self) -> TiffResult<DecodingResult> {
|
pub fn read_jpeg(&mut self) -> TiffResult<DecodingResult> {
|
||||||
let offsets = self.get_tag_u32_vec(Tag::StripOffsets)?;
|
let offsets = self.get_tag_u32_vec(Tag::StripOffsets)?;
|
||||||
let bytes = self.get_tag_u32_vec(Tag::StripByteCounts)?;
|
let bytes = self.get_tag_u32_vec(Tag::StripByteCounts)?;
|
||||||
@@ -1107,10 +1531,6 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
let mut res_img = Vec::with_capacity(offsets[0] as usize);
|
let mut res_img = Vec::with_capacity(offsets[0] as usize);
|
||||||
|
|
||||||
for (idx, offset) in offsets.iter().enumerate() {
|
for (idx, offset) in offsets.iter().enumerate() {
|
||||||
if bytes[idx] as usize > self.limits.intermediate_buffer_size {
|
|
||||||
return Err(TiffError::LimitsExceeded);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.goto_offset(*offset)?;
|
self.goto_offset(*offset)?;
|
||||||
let jpeg_reader = JpegReader::new(&mut self.reader, bytes[idx], &jpeg_tables)?;
|
let jpeg_reader = JpegReader::new(&mut self.reader, bytes[idx], &jpeg_tables)?;
|
||||||
let mut decoder = jpeg::Decoder::new(jpeg_reader);
|
let mut decoder = jpeg::Decoder::new(jpeg_reader);
|
||||||
@@ -1164,6 +1584,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
let sized_width = usize::try_from(self.width)?;
|
let sized_width = usize::try_from(self.width)?;
|
||||||
let sized_height = usize::try_from(self.height)?;
|
let sized_height = usize::try_from(self.height)?;
|
||||||
|
|
||||||
|
// Ignore potential vertical padding on the bottommost strip
|
||||||
let strip_height = cmp::min(rows_per_strip, sized_height - index * rows_per_strip);
|
let strip_height = cmp::min(rows_per_strip, sized_height - index * rows_per_strip);
|
||||||
|
|
||||||
let buffer_size = sized_width * strip_height * self.bits_per_sample.len();
|
let buffer_size = sized_width * strip_height * self.bits_per_sample.len();
|
||||||
@@ -1187,6 +1608,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
rev_hpredict(
|
rev_hpredict(
|
||||||
buffer.copy(),
|
buffer.copy(),
|
||||||
(self.width, u32::try_from(strip_height)?),
|
(self.width, u32::try_from(strip_height)?),
|
||||||
|
usize::try_from(self.width)?,
|
||||||
self.colortype()?,
|
self.colortype()?,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
@@ -1201,8 +1623,62 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn result_buffer(&self, height: usize) -> TiffResult<DecodingResult> {
|
fn read_tile_to_buffer(&mut self, result: &mut DecodingBuffer, tile: usize) -> TiffResult<()> {
|
||||||
let buffer_size = usize::try_from(self.width)? * height * self.bits_per_sample.len();
|
let file_offset = *self
|
||||||
|
.tile_decoder
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.tile_offsets
|
||||||
|
.get(tile)
|
||||||
|
.ok_or(TiffError::FormatError(
|
||||||
|
TiffFormatError::InconsistentSizesEncountered,
|
||||||
|
))?;
|
||||||
|
|
||||||
|
let compressed_bytes = *self
|
||||||
|
.tile_decoder
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.tile_bytes
|
||||||
|
.get(tile)
|
||||||
|
.ok_or(TiffError::FormatError(
|
||||||
|
TiffFormatError::InconsistentSizesEncountered,
|
||||||
|
))?;
|
||||||
|
|
||||||
|
let tile_attrs = self.tile_attributes.as_ref().unwrap();
|
||||||
|
let tile_width = tile_attrs.tile_width;
|
||||||
|
let tile_length = tile_attrs.tile_length;
|
||||||
|
|
||||||
|
let (padding_right, padding_down) = tile_attrs.get_padding(tile);
|
||||||
|
|
||||||
|
self.expand_tile(result.copy(), file_offset, compressed_bytes, tile)?;
|
||||||
|
|
||||||
|
if let Ok(predictor) = self.get_tag_unsigned(Tag::Predictor) {
|
||||||
|
match Predictor::from_u16(predictor) {
|
||||||
|
Some(Predictor::None) => (),
|
||||||
|
Some(Predictor::Horizontal) => {
|
||||||
|
rev_hpredict(
|
||||||
|
result.copy(),
|
||||||
|
(
|
||||||
|
u32::try_from(tile_width - padding_right)?,
|
||||||
|
u32::try_from(tile_length - padding_down)?,
|
||||||
|
),
|
||||||
|
self.tile_decoder.as_ref().unwrap().result_width,
|
||||||
|
self.colortype()?,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return Err(TiffError::FormatError(TiffFormatError::UnknownPredictor(
|
||||||
|
predictor,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
Some(Predictor::__NonExhaustive) => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn result_buffer(&self, width: usize, height: usize) -> TiffResult<DecodingResult> {
|
||||||
|
let buffer_size = width * height * self.bits_per_sample.len();
|
||||||
|
|
||||||
let max_sample_bits = self.bits_per_sample.iter().cloned().max().unwrap_or(8);
|
let max_sample_bits = self.bits_per_sample.iter().cloned().max().unwrap_or(8);
|
||||||
match self.sample_format.first().unwrap_or(&SampleFormat::Uint) {
|
match self.sample_format.first().unwrap_or(&SampleFormat::Uint) {
|
||||||
@@ -1239,6 +1715,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
|
|
||||||
/// Read a single strip from the image and return it as a Vector
|
/// Read a single strip from the image and return it as a Vector
|
||||||
pub fn read_strip(&mut self) -> TiffResult<DecodingResult> {
|
pub fn read_strip(&mut self) -> TiffResult<DecodingResult> {
|
||||||
|
self.check_chunk_type(ChunkType::Strip)?;
|
||||||
self.initialize_strip_decoder()?;
|
self.initialize_strip_decoder()?;
|
||||||
let index = self.strip_decoder.as_ref().unwrap().strip_index;
|
let index = self.strip_decoder.as_ref().unwrap().strip_index;
|
||||||
|
|
||||||
@@ -1250,18 +1727,55 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
usize::try_from(self.height)? - index * rows_per_strip,
|
usize::try_from(self.height)? - index * rows_per_strip,
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut result = self.result_buffer(strip_height)?;
|
let mut result = self.result_buffer(usize::try_from(self.width)?, strip_height)?;
|
||||||
self.read_strip_to_buffer(result.as_buffer(0))?;
|
self.read_strip_to_buffer(result.as_buffer(0))?;
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decodes the entire image and return it as a Vector
|
/// Read a single tile from the image and return it as a Vector
|
||||||
pub fn read_image(&mut self) -> TiffResult<DecodingResult> {
|
pub fn read_tile(&mut self) -> TiffResult<DecodingResult> {
|
||||||
if self.compression_method == CompressionMethod::ModernJPEG {
|
self.check_chunk_type(ChunkType::Tile)?;
|
||||||
return self.read_jpeg();
|
self.init_tile_attributes()?;
|
||||||
|
|
||||||
|
let tile = self.tile_decoder.as_ref().map_or(0, |d| d.current_tile);
|
||||||
|
|
||||||
|
let tile_attrs = self.tile_attributes.as_ref().unwrap();
|
||||||
|
let (padding_right, padding_down) = tile_attrs.get_padding(tile);
|
||||||
|
|
||||||
|
let tile_width = tile_attrs.tile_width - padding_right;
|
||||||
|
let tile_length = tile_attrs.tile_length - padding_down;
|
||||||
|
|
||||||
|
let mut result = self.result_buffer(tile_width, tile_length)?;
|
||||||
|
self.update_tile_decoder(tile_width, result.as_buffer(0).byte_len())?;
|
||||||
|
|
||||||
|
self.read_tile_to_buffer(&mut result.as_buffer(0), tile)?;
|
||||||
|
|
||||||
|
self.tile_decoder.as_mut().unwrap().current_tile += 1;
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_tiled_image(&mut self) -> TiffResult<DecodingResult> {
|
||||||
|
let width = usize::try_from(self.width)?;
|
||||||
|
let mut result = self.result_buffer(width, usize::try_from(self.height)?)?;
|
||||||
|
|
||||||
|
self.init_tile_attributes()?;
|
||||||
|
self.update_tile_decoder(width, result.as_buffer(0).byte_len())?;
|
||||||
|
|
||||||
|
let tile_attrs = self.tile_attributes.as_ref().unwrap();
|
||||||
|
let tiles_across = tile_attrs.tiles_across;
|
||||||
|
let tiles_down = tile_attrs.tiles_down;
|
||||||
|
|
||||||
|
for tile in 0..(tiles_across * tiles_down) {
|
||||||
|
let buffer_offset = self.tile_attributes.as_ref().unwrap().get_offset(tile);
|
||||||
|
self.read_tile_to_buffer(&mut result.as_buffer(buffer_offset), tile)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_stripped_image(&mut self) -> TiffResult<DecodingResult> {
|
||||||
self.initialize_strip_decoder()?;
|
self.initialize_strip_decoder()?;
|
||||||
let rows_per_strip =
|
let rows_per_strip =
|
||||||
usize::try_from(self.get_tag_u32(Tag::RowsPerStrip).unwrap_or(self.height))?;
|
usize::try_from(self.get_tag_u32(Tag::RowsPerStrip).unwrap_or(self.height))?;
|
||||||
@@ -1269,7 +1783,8 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
let samples_per_strip =
|
let samples_per_strip =
|
||||||
usize::try_from(self.width)? * rows_per_strip * self.bits_per_sample.len();
|
usize::try_from(self.width)? * rows_per_strip * self.bits_per_sample.len();
|
||||||
|
|
||||||
let mut result = self.result_buffer(usize::try_from(self.height)?)?;
|
let mut result =
|
||||||
|
self.result_buffer(usize::try_from(self.width)?, usize::try_from(self.height)?)?;
|
||||||
|
|
||||||
for i in 0..usize::try_from(self.strip_count()?)? {
|
for i in 0..usize::try_from(self.strip_count()?)? {
|
||||||
let r = result.as_buffer(samples_per_strip * i);
|
let r = result.as_buffer(samples_per_strip * i);
|
||||||
@@ -1277,4 +1792,15 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
}
|
}
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Decodes the entire image and return it as a Vector
|
||||||
|
pub fn read_image(&mut self) -> TiffResult<DecodingResult> {
|
||||||
|
let result = match (self.chunk_type, self.compression_method) {
|
||||||
|
(_, CompressionMethod::ModernJPEG) => self.read_jpeg()?,
|
||||||
|
(ChunkType::Strip, _) => self.read_stripped_image()?,
|
||||||
|
(ChunkType::Tile, _) => self.read_tiled_image()?,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+29
-1
@@ -4,7 +4,7 @@ use std::io;
|
|||||||
use std::str;
|
use std::str;
|
||||||
use std::string;
|
use std::string;
|
||||||
|
|
||||||
use crate::decoder::ifd::Value;
|
use crate::decoder::{ifd::Value, ChunkType};
|
||||||
use crate::tags::{
|
use crate::tags::{
|
||||||
CompressionMethod, PhotometricInterpretation, PlanarConfiguration, SampleFormat, Tag,
|
CompressionMethod, PhotometricInterpretation, PlanarConfiguration, SampleFormat, Tag,
|
||||||
};
|
};
|
||||||
@@ -29,6 +29,9 @@ pub enum TiffError {
|
|||||||
/// An integer conversion to or from a platform size failed, either due to
|
/// An integer conversion to or from a platform size failed, either due to
|
||||||
/// limits of the platform size or limits of the format.
|
/// limits of the platform size or limits of the format.
|
||||||
IntSizeError,
|
IntSizeError,
|
||||||
|
|
||||||
|
/// The image does not support the requested operation
|
||||||
|
UsageError(UsageError),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The image is not formatted properly.
|
/// The image is not formatted properly.
|
||||||
@@ -205,6 +208,29 @@ impl fmt::Display for TiffUnsupportedError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// User attempted to use the Decoder in a way that is incompatible with a specific image.
|
||||||
|
///
|
||||||
|
/// For example: attempting to read a tile from a stripped image.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum UsageError {
|
||||||
|
InvalidChunkType(ChunkType, ChunkType),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for UsageError {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
use self::UsageError::*;
|
||||||
|
match *self {
|
||||||
|
InvalidChunkType(expected, actual) => {
|
||||||
|
write!(
|
||||||
|
fmt,
|
||||||
|
"Requested operation is only valid for images with chunk encoding of type: {:?}, got {:?}.",
|
||||||
|
expected, actual
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for TiffError {
|
impl fmt::Display for TiffError {
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
match *self {
|
match *self {
|
||||||
@@ -218,6 +244,7 @@ impl fmt::Display for TiffError {
|
|||||||
TiffError::IoError(ref e) => e.fmt(fmt),
|
TiffError::IoError(ref e) => e.fmt(fmt),
|
||||||
TiffError::LimitsExceeded => write!(fmt, "The Decoder limits are exceeded"),
|
TiffError::LimitsExceeded => write!(fmt, "The Decoder limits are exceeded"),
|
||||||
TiffError::IntSizeError => write!(fmt, "Platform or format size limits exceeded"),
|
TiffError::IntSizeError => write!(fmt, "Platform or format size limits exceeded"),
|
||||||
|
TiffError::UsageError(ref e) => write!(fmt, "Usage error: {}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -230,6 +257,7 @@ impl Error for TiffError {
|
|||||||
TiffError::IoError(..) => "IO error",
|
TiffError::IoError(..) => "IO error",
|
||||||
TiffError::LimitsExceeded => "Decoder limits exceeded",
|
TiffError::LimitsExceeded => "Decoder limits exceeded",
|
||||||
TiffError::IntSizeError => "Platform or format size limits exceeded",
|
TiffError::IntSizeError => "Platform or format size limits exceeded",
|
||||||
|
TiffError::UsageError(..) => "Invalid usage",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -16,7 +16,9 @@ pub mod encoder;
|
|||||||
mod error;
|
mod error;
|
||||||
pub mod tags;
|
pub mod tags;
|
||||||
|
|
||||||
pub use self::error::{InflateError, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
|
pub use self::error::{
|
||||||
|
InflateError, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError, UsageError,
|
||||||
|
};
|
||||||
|
|
||||||
/// An enumeration over supported color types and their bit depths
|
/// An enumeration over supported color types and their bit depths
|
||||||
#[derive(Copy, PartialEq, Eq, Debug, Clone, Hash)]
|
#[derive(Copy, PartialEq, Eq, Debug, Clone, Hash)]
|
||||||
|
|||||||
@@ -215,3 +215,77 @@ fn issue_69() {
|
|||||||
//let img_res = decoder.read_image();
|
//let img_res = decoder.read_image();
|
||||||
//assert!(img_res.is_ok());
|
//assert!(img_res.is_ok());
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tiled_rgb_u8() {
|
||||||
|
test_image_sum_u8("tiled-rgb-u8.tif", ColorType::RGB(8), 39528948);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tiled_rect_rgb_u8() {
|
||||||
|
test_image_sum_u8("tiled-rect-rgb-u8.tif", ColorType::RGB(8), 62081032);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* #[test]
|
||||||
|
fn test_tiled_jpeg_rgb_u8() {
|
||||||
|
test_image_sum_u8("tiled-jpeg-rgb-u8.tif", ColorType::RGB(8), 93031606);
|
||||||
|
} */
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tiled_oversize_gray_i8() {
|
||||||
|
test_image_sum_i8("tiled-oversize-gray-i8.tif", ColorType::Gray(8), 1214996);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tiled_whiteiszero_gray_i8() {
|
||||||
|
test_image_sum_i8("tiled-whiteiszero-gray-i8.tif", ColorType::Gray(8), 1214996);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tiled_cmyk_i8() {
|
||||||
|
test_image_sum_i8("tiled-cmyk-i8.tif", ColorType::CMYK(8), 1759101);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tiled_incremental() {
|
||||||
|
let file = "tiled-rgb-u8.tif";
|
||||||
|
let expected_type = ColorType::RGB(8);
|
||||||
|
let sums = [
|
||||||
|
188760, 195639, 108148, 81986, 665088, 366140, 705317, 423366, 172033, 324455, 244102,
|
||||||
|
81853, 181258, 247971, 129486, 55600, 565625, 422102, 730888, 379271, 232142, 292549,
|
||||||
|
244045, 86866, 188141, 115036, 150785, 84389, 353170, 459325, 719619, 329594, 278663,
|
||||||
|
220474, 243048, 113563, 189152, 109684, 179391, 122188, 279651, 622093, 724682, 302459,
|
||||||
|
268428, 204499, 224255, 124674, 170668, 121868, 192768, 183367, 378029, 585651, 657712,
|
||||||
|
296790, 241444, 197083, 198429, 134869, 182318, 86034, 203655, 182338, 297255, 601284,
|
||||||
|
633813, 242531, 228578, 206441, 193552, 125412, 181527, 165439, 202531, 159538, 268388,
|
||||||
|
565790, 611382, 272967, 236497, 215154, 158881, 90806, 106114, 182342, 191824, 186138,
|
||||||
|
215174, 393193, 701228, 198866, 227944, 193830, 166330, 49008, 55719, 122820, 197316,
|
||||||
|
161969, 203152, 170986, 624427, 188605, 186187, 111064, 115192, 39538, 48626, 163929,
|
||||||
|
144682, 135796, 194141, 154198, 584125, 180255, 153524, 121433, 132641, 35743, 47798,
|
||||||
|
152343, 162874, 167664, 160175, 133038, 659882, 138339, 166470, 124173, 118929, 51317,
|
||||||
|
45267, 155776, 161331, 161006, 130052, 137618, 337291, 106481, 161999, 127343, 87724,
|
||||||
|
59540, 63907, 155677, 140668, 141523, 108061, 168657, 186482, 98599, 147614, 139963, 90444,
|
||||||
|
56602, 92547, 125644, 134212, 126569, 144153, 179800, 174516, 133969, 129399, 117681,
|
||||||
|
83305, 55075, 110737, 115108, 128572, 128911, 130922, 179986, 143288, 145884, 155856,
|
||||||
|
96683, 94057, 56238, 79649, 71651, 70182, 75010, 77009, 98855, 78979, 74341, 83482, 53403,
|
||||||
|
59842, 30305,
|
||||||
|
];
|
||||||
|
|
||||||
|
let path = PathBuf::from(TEST_IMAGE_DIR).join(file);
|
||||||
|
let img_file = File::open(path).expect("Cannot find test image!");
|
||||||
|
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
|
||||||
|
assert_eq!(decoder.colortype().unwrap(), expected_type);
|
||||||
|
|
||||||
|
let tiles = decoder.tile_count().unwrap();
|
||||||
|
assert_eq!(tiles as usize, sums.len());
|
||||||
|
|
||||||
|
for tile in 0..tiles {
|
||||||
|
match decoder.read_tile().unwrap() {
|
||||||
|
DecodingResult::U8(res) => {
|
||||||
|
let sum: u64 = res.into_iter().map(<u64>::from).sum();
|
||||||
|
assert_eq!(sum, sums[tile as usize]);
|
||||||
|
}
|
||||||
|
_ => panic!("Wrong bit depth"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
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