Incremental decoding implemented
This commit is contained in:
1 parent
80be60dfd4
commit
6884558687
3 files changed
+156
-63
No files matched your search
+133
-63
@@ -27,6 +27,15 @@ pub enum DecodingResult {
|
|||||||
U16(Vec<u16>)
|
U16(Vec<u16>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DecodingResult {
|
||||||
|
fn to_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..]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// A buffer for image decoding
|
// A buffer for image decoding
|
||||||
enum DecodingBuffer<'a> {
|
enum DecodingBuffer<'a> {
|
||||||
/// A slice of unsigned bytes
|
/// A slice of unsigned bytes
|
||||||
@@ -35,6 +44,15 @@ enum DecodingBuffer<'a> {
|
|||||||
U16(&'a mut [u16])
|
U16(&'a mut [u16])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> DecodingBuffer<'a> {
|
||||||
|
fn copy<'b>(&'b mut self) -> DecodingBuffer<'b> where 'a: 'b {
|
||||||
|
match self {
|
||||||
|
DecodingBuffer::U8(ref mut buf) => DecodingBuffer::U8(buf),
|
||||||
|
DecodingBuffer::U16(ref mut buf) => DecodingBuffer::U16(buf),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, FromPrimitive)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, FromPrimitive)]
|
||||||
pub enum PhotometricInterpretation {
|
pub enum PhotometricInterpretation {
|
||||||
WhiteIsZero = 0,
|
WhiteIsZero = 0,
|
||||||
@@ -70,6 +88,13 @@ enum Predictor {
|
|||||||
Horizontal = 2
|
Horizontal = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct StripDecodeState {
|
||||||
|
strip_index: usize,
|
||||||
|
strip_offsets: Vec<u32>,
|
||||||
|
strip_bytes: Vec<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
/// The representation of a TIFF decoder
|
/// The representation of a TIFF decoder
|
||||||
///
|
///
|
||||||
/// Currently does not support decoding of interlaced images
|
/// Currently does not support decoding of interlaced images
|
||||||
@@ -84,7 +109,8 @@ pub struct Decoder<R> where R: Read + Seek {
|
|||||||
bits_per_sample: Vec<u8>,
|
bits_per_sample: Vec<u8>,
|
||||||
samples: u8,
|
samples: u8,
|
||||||
photometric_interpretation: PhotometricInterpretation,
|
photometric_interpretation: PhotometricInterpretation,
|
||||||
compression_method: CompressionMethod
|
compression_method: CompressionMethod,
|
||||||
|
strip_decoder: Option<StripDecodeState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
trait Wrapping {
|
trait Wrapping {
|
||||||
@@ -103,10 +129,9 @@ impl Wrapping for u16 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rev_hpredict_nsamp<T>(mut image: Vec<T>,
|
fn rev_hpredict_nsamp<T>(image: &mut [T],
|
||||||
size: (u32, u32),
|
size: (u32, u32),
|
||||||
samples: usize)
|
samples: usize)
|
||||||
-> Vec<T>
|
|
||||||
where T: Num + Copy + Wrapping {
|
where T: Num + Copy + Wrapping {
|
||||||
let width = size.0 as usize;
|
let width = size.0 as usize;
|
||||||
let height = size.1 as usize;
|
let height = size.1 as usize;
|
||||||
@@ -117,10 +142,9 @@ fn rev_hpredict_nsamp<T>(mut image: Vec<T>,
|
|||||||
*pixel = pixel.wrapping_add(prev_pixel);
|
*pixel = pixel.wrapping_add(prev_pixel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
image
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rev_hpredict(image: DecodingResult, size: (u32, u32), color_type: ColorType) -> TiffResult<DecodingResult> {
|
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) => 1,
|
||||||
ColorType::RGB(8) | ColorType::RGB(16) => 3,
|
ColorType::RGB(8) | ColorType::RGB(16) => 3,
|
||||||
@@ -128,11 +152,11 @@ fn rev_hpredict(image: DecodingResult, size: (u32, u32), color_type: ColorType)
|
|||||||
_ => return Err(TiffError::UnsupportedError(TiffUnsupportedError::HorizontalPredictor(color_type)))
|
_ => return Err(TiffError::UnsupportedError(TiffUnsupportedError::HorizontalPredictor(color_type)))
|
||||||
};
|
};
|
||||||
Ok(match image {
|
Ok(match image {
|
||||||
DecodingResult::U8(buf) => {
|
DecodingBuffer::U8(buf) => {
|
||||||
DecodingResult::U8(rev_hpredict_nsamp(buf, size, samples))
|
rev_hpredict_nsamp(buf, size, samples)
|
||||||
},
|
},
|
||||||
DecodingResult::U16(buf) => {
|
DecodingBuffer::U16(buf) => {
|
||||||
DecodingResult::U16(rev_hpredict_nsamp(buf, size, samples))
|
rev_hpredict_nsamp(buf, size, samples)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -150,7 +174,8 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
bits_per_sample: vec![1],
|
bits_per_sample: vec![1],
|
||||||
samples: 1,
|
samples: 1,
|
||||||
photometric_interpretation: PhotometricInterpretation::BlackIsZero,
|
photometric_interpretation: PhotometricInterpretation::BlackIsZero,
|
||||||
compression_method: CompressionMethod::None
|
compression_method: CompressionMethod::None,
|
||||||
|
strip_decoder: None,
|
||||||
}.init()
|
}.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,6 +234,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
self.ifd = Some(try!(self.read_ifd()));
|
self.ifd = Some(try!(self.read_ifd()));
|
||||||
self.width = try!(self.get_tag_u32(ifd::Tag::ImageWidth));
|
self.width = try!(self.get_tag_u32(ifd::Tag::ImageWidth));
|
||||||
self.height = try!(self.get_tag_u32(ifd::Tag::ImageLength));
|
self.height = try!(self.get_tag_u32(ifd::Tag::ImageLength));
|
||||||
|
self.strip_decoder = None;
|
||||||
self.photometric_interpretation = match FromPrimitive::from_u32(
|
self.photometric_interpretation = match FromPrimitive::from_u32(
|
||||||
try!(self.get_tag_u32(ifd::Tag::PhotometricInterpretation))
|
try!(self.get_tag_u32(ifd::Tag::PhotometricInterpretation))
|
||||||
) {
|
) {
|
||||||
@@ -440,68 +466,112 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decodes the entire image and return it as a Vector
|
/// Number of strips in image
|
||||||
pub fn read_image(&mut self) -> TiffResult<DecodingResult> {
|
pub fn strip_count(&mut self) -> TiffResult<u32> {
|
||||||
let bits_per_pixel: u8 = self.bits_per_sample.iter().cloned().sum();
|
let rows_per_strip = self.get_tag_u32(ifd::Tag::RowsPerStrip)
|
||||||
let scanline_size_bits = self.width as usize * bits_per_pixel as usize;
|
.unwrap_or(self.height);
|
||||||
let scanline_size = (scanline_size_bits + 7) / 8;
|
|
||||||
|
Ok((self.height + rows_per_strip - 1) / rows_per_strip)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn initialize_strip_decoder(&mut self) -> TiffResult<()> {
|
||||||
|
if self.strip_decoder.is_none() {
|
||||||
|
let strip_offsets = self.get_tag_u32_vec(ifd::Tag::StripOffsets)?;
|
||||||
|
let strip_bytes = self.get_tag_u32_vec(ifd::Tag::StripByteCounts)?;
|
||||||
|
|
||||||
|
self.strip_decoder = Some(StripDecodeState {
|
||||||
|
strip_index: 0,
|
||||||
|
strip_offsets,
|
||||||
|
strip_bytes,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_strip_to_buffer(&mut self, mut buffer: DecodingBuffer) -> TiffResult<()> {
|
||||||
|
self.initialize_strip_decoder()?;
|
||||||
|
|
||||||
|
let index = self.strip_decoder.as_ref().unwrap().strip_index;
|
||||||
|
let offset = self.strip_decoder.as_ref().unwrap().strip_offsets[index];
|
||||||
|
let byte_count = self.strip_decoder.as_ref().unwrap().strip_bytes[index];
|
||||||
|
|
||||||
let rows_per_strip = self.get_tag_u32(ifd::Tag::RowsPerStrip)
|
let rows_per_strip = self.get_tag_u32(ifd::Tag::RowsPerStrip)
|
||||||
.unwrap_or(self.height) as usize;
|
.unwrap_or(self.height) as usize;
|
||||||
let buffer_size =
|
|
||||||
self.width as usize
|
let strip_height = std::cmp::min(rows_per_strip, self.height as usize - index * rows_per_strip);
|
||||||
* self.height as usize
|
|
||||||
* self.bits_per_sample.iter().count();
|
let buffer_size = self.width as usize * strip_height * self.bits_per_sample.iter().count();
|
||||||
|
|
||||||
|
let units_read = self.expand_strip(buffer.copy(), offset, byte_count, buffer_size)?;
|
||||||
|
|
||||||
|
self.strip_decoder.as_mut().unwrap().strip_index += 1;
|
||||||
|
|
||||||
|
if index as u32 == self.strip_count()? {
|
||||||
|
self.strip_decoder = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if units_read < buffer_size {
|
||||||
|
return Err(TiffError::FormatError(TiffFormatError::InconsistentSizesEncountered));
|
||||||
|
}
|
||||||
|
if let Ok(predictor) = self.get_tag_u32(ifd::Tag::Predictor) {
|
||||||
|
match FromPrimitive::from_u32(predictor) {
|
||||||
|
Some(Predictor::None) => (),
|
||||||
|
Some(Predictor::Horizontal) => {
|
||||||
|
rev_hpredict(
|
||||||
|
buffer.copy(),
|
||||||
|
(self.width, strip_height as u32),
|
||||||
|
self.colortype()?
|
||||||
|
)?;
|
||||||
|
},
|
||||||
|
None => return Err(TiffError::FormatError(TiffFormatError::UnknownPredictor(predictor))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a single strip from the image and return it as a Vector
|
||||||
|
pub fn read_strip(&mut self) -> TiffResult<DecodingResult> {
|
||||||
|
self.initialize_strip_decoder()?;
|
||||||
|
let index = self.strip_decoder.as_ref().unwrap().strip_index;
|
||||||
|
|
||||||
|
let rows_per_strip = self.get_tag_u32(ifd::Tag::RowsPerStrip)
|
||||||
|
.unwrap_or(self.height) as usize;
|
||||||
|
|
||||||
|
let strip_height = std::cmp::min(rows_per_strip, self.height as usize - index * rows_per_strip);
|
||||||
|
|
||||||
|
let buffer_size = self.width as usize * strip_height * self.bits_per_sample.iter().count();
|
||||||
|
|
||||||
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::U8(vec![0; buffer_size]),
|
n if n <= 8 => DecodingResult::U8(vec![0; buffer_size]),
|
||||||
n if n <= 16 => DecodingResult::U16(vec![0; buffer_size]),
|
n if n <= 16 => DecodingResult::U16(vec![0; buffer_size]),
|
||||||
n => return Err(TiffError::UnsupportedError(TiffUnsupportedError::UnsupportedBitsPerChannel(n))),
|
n => return Err(TiffError::UnsupportedError(TiffUnsupportedError::UnsupportedBitsPerChannel(n))),
|
||||||
};
|
};
|
||||||
if let Ok(config) = self.get_tag_u32(ifd::Tag::PlanarConfiguration) {
|
|
||||||
match FromPrimitive::from_u32(config) {
|
|
||||||
Some(PlanarConfiguration::Chunky) => {},
|
|
||||||
config => return Err(TiffError::UnsupportedError(TiffUnsupportedError::UnsupportedPlanarConfig(config)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut units_read = 0;
|
|
||||||
for (i, (&offset, &byte_count)) in try!(self.get_tag_u32_vec(ifd::Tag::StripOffsets))
|
|
||||||
.iter().zip(try!(self.get_tag_u32_vec(ifd::Tag::StripByteCounts)).iter()).enumerate() {
|
|
||||||
let uncompressed_strip_size = scanline_size
|
|
||||||
* (self.height as usize - i * rows_per_strip).min(rows_per_strip);
|
|
||||||
|
|
||||||
units_read += match result {
|
self.read_strip_to_buffer(result.to_buffer(0))?;
|
||||||
DecodingResult::U8(ref mut buffer) => {
|
|
||||||
try!(self.expand_strip(
|
Ok(result)
|
||||||
DecodingBuffer::U8(&mut buffer[units_read..]),
|
}
|
||||||
offset, byte_count, uncompressed_strip_size
|
|
||||||
))
|
/// Decodes the entire image and return it as a Vector
|
||||||
},
|
pub fn read_image(&mut self) -> TiffResult<DecodingResult> {
|
||||||
DecodingResult::U16(ref mut buffer) => {
|
self.initialize_strip_decoder()?;
|
||||||
try!(self.expand_strip(
|
let rows_per_strip = self.get_tag_u32(ifd::Tag::RowsPerStrip)
|
||||||
DecodingBuffer::U16(&mut buffer[units_read..]),
|
.unwrap_or(self.height) as usize;
|
||||||
offset, byte_count, uncompressed_strip_size
|
|
||||||
))
|
let samples_per_strip = self.width as usize * rows_per_strip * self.bits_per_sample.iter().count();
|
||||||
},
|
|
||||||
};
|
let buffer_size = self.width as usize * self.height as usize * self.bits_per_sample.iter().count();
|
||||||
if units_read == buffer_size {
|
|
||||||
break
|
let mut result = match self.bits_per_sample.iter().cloned().max().unwrap_or(8) {
|
||||||
}
|
n if n <= 8 => DecodingResult::U8(vec![0; buffer_size]),
|
||||||
}
|
n if n <= 16 => DecodingResult::U16(vec![0; buffer_size]),
|
||||||
if units_read < buffer_size {
|
n => return Err(TiffError::UnsupportedError(TiffUnsupportedError::UnsupportedBitsPerChannel(n))),
|
||||||
return Err(TiffError::FormatError(TiffFormatError::InconsistentSizesEncountered));
|
};
|
||||||
}
|
|
||||||
if let Ok(predictor) = self.get_tag_u32(ifd::Tag::Predictor) {
|
for i in 0..self.strip_count()? as usize {
|
||||||
result = match FromPrimitive::from_u32(predictor) {
|
self.read_strip_to_buffer(result.to_buffer(samples_per_strip * i))?;
|
||||||
Some(Predictor::None) => result,
|
|
||||||
Some(Predictor::Horizontal) => {
|
|
||||||
try!(rev_hpredict(
|
|
||||||
result,
|
|
||||||
try!(self.dimensions()),
|
|
||||||
try!(self.colortype())
|
|
||||||
))
|
|
||||||
},
|
|
||||||
None => return Err(TiffError::FormatError(TiffFormatError::UnknownPredictor(predictor))),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,6 +63,29 @@ fn test_string_tags()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_decode_data() {
|
||||||
|
let mut image_data = Vec::new();
|
||||||
|
for x in 0..100 {
|
||||||
|
for y in 0..100u8 {
|
||||||
|
let val = x + y;
|
||||||
|
image_data.push(val);
|
||||||
|
image_data.push(val);
|
||||||
|
image_data.push(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let file = File::open("./tests/decodedata-rgb-3c-8b.tiff").unwrap();
|
||||||
|
let mut decoder = Decoder::new(file).unwrap();
|
||||||
|
assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(8));
|
||||||
|
assert_eq!(decoder.dimensions().unwrap(), (100, 100));
|
||||||
|
if let DecodingResult::U8(img_res) = decoder.read_image().unwrap() {
|
||||||
|
assert_eq!(image_data, img_res);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
panic!("Wrong data type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: GrayA support
|
// TODO: GrayA support
|
||||||
//#[test]
|
//#[test]
|
||||||
//fn test_gray_alpha_u8()
|
//fn test_gray_alpha_u8()
|
||||||
|
|||||||
Binary file not shown.
Reference in new issue
Block a user