From 68845586871577d705d208c686638cd36c0253b3 Mon Sep 17 00:00:00 2001 From: Birk Tjelmeland Date: Fri, 1 Feb 2019 00:58:49 +0100 Subject: [PATCH 1/2] Incremental decoding implemented --- src/decoder/mod.rs | 196 ++++++++++++++++++++++---------- tests/decode_images.rs | 23 ++++ tests/decodedata-rgb-3c-8b.tiff | Bin 0 -> 30180 bytes 3 files changed, 156 insertions(+), 63 deletions(-) create mode 100644 tests/decodedata-rgb-3c-8b.tiff diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index 72fbc70..393be17 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -27,6 +27,15 @@ pub enum DecodingResult { U16(Vec) } +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 enum DecodingBuffer<'a> { /// A slice of unsigned bytes @@ -35,6 +44,15 @@ enum DecodingBuffer<'a> { 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)] pub enum PhotometricInterpretation { WhiteIsZero = 0, @@ -70,6 +88,13 @@ enum Predictor { Horizontal = 2 } +#[derive(Debug)] +struct StripDecodeState { + strip_index: usize, + strip_offsets: Vec, + strip_bytes: Vec, +} + /// The representation of a TIFF decoder /// /// Currently does not support decoding of interlaced images @@ -84,7 +109,8 @@ pub struct Decoder where R: Read + Seek { bits_per_sample: Vec, samples: u8, photometric_interpretation: PhotometricInterpretation, - compression_method: CompressionMethod + compression_method: CompressionMethod, + strip_decoder: Option, } trait Wrapping { @@ -103,10 +129,9 @@ impl Wrapping for u16 { } } -fn rev_hpredict_nsamp(mut image: Vec, +fn rev_hpredict_nsamp(image: &mut [T], size: (u32, u32), samples: usize) - -> Vec where T: Num + Copy + Wrapping { let width = size.0 as usize; let height = size.1 as usize; @@ -117,10 +142,9 @@ fn rev_hpredict_nsamp(mut image: Vec, *pixel = pixel.wrapping_add(prev_pixel); } } - image } -fn rev_hpredict(image: DecodingResult, size: (u32, u32), color_type: ColorType) -> TiffResult { +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, @@ -128,11 +152,11 @@ fn rev_hpredict(image: DecodingResult, size: (u32, u32), color_type: ColorType) _ => return Err(TiffError::UnsupportedError(TiffUnsupportedError::HorizontalPredictor(color_type))) }; Ok(match image { - DecodingResult::U8(buf) => { - DecodingResult::U8(rev_hpredict_nsamp(buf, size, samples)) + DecodingBuffer::U8(buf) => { + rev_hpredict_nsamp(buf, size, samples) }, - DecodingResult::U16(buf) => { - DecodingResult::U16(rev_hpredict_nsamp(buf, size, samples)) + DecodingBuffer::U16(buf) => { + rev_hpredict_nsamp(buf, size, samples) } }) } @@ -150,7 +174,8 @@ impl Decoder { bits_per_sample: vec![1], samples: 1, photometric_interpretation: PhotometricInterpretation::BlackIsZero, - compression_method: CompressionMethod::None + compression_method: CompressionMethod::None, + strip_decoder: None, }.init() } @@ -209,6 +234,7 @@ impl Decoder { self.ifd = Some(try!(self.read_ifd())); self.width = try!(self.get_tag_u32(ifd::Tag::ImageWidth)); self.height = try!(self.get_tag_u32(ifd::Tag::ImageLength)); + self.strip_decoder = None; self.photometric_interpretation = match FromPrimitive::from_u32( try!(self.get_tag_u32(ifd::Tag::PhotometricInterpretation)) ) { @@ -440,68 +466,112 @@ impl Decoder { }) } - /// Decodes the entire image and return it as a Vector - pub fn read_image(&mut self) -> TiffResult { - let bits_per_pixel: u8 = self.bits_per_sample.iter().cloned().sum(); - let scanline_size_bits = self.width as usize * bits_per_pixel as usize; - let scanline_size = (scanline_size_bits + 7) / 8; + /// Number of strips in image + pub fn strip_count(&mut self) -> TiffResult { + let rows_per_strip = self.get_tag_u32(ifd::Tag::RowsPerStrip) + .unwrap_or(self.height); + + 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) .unwrap_or(self.height) as usize; - let buffer_size = - self.width as usize - * self.height as usize - * self.bits_per_sample.iter().count(); + + 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 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 { + 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) { n if n <= 8 => DecodingResult::U8(vec![0; buffer_size]), n if n <= 16 => DecodingResult::U16(vec![0; buffer_size]), 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 { - DecodingResult::U8(ref mut buffer) => { - try!(self.expand_strip( - DecodingBuffer::U8(&mut buffer[units_read..]), - offset, byte_count, uncompressed_strip_size - )) - }, - DecodingResult::U16(ref mut buffer) => { - try!(self.expand_strip( - DecodingBuffer::U16(&mut buffer[units_read..]), - offset, byte_count, uncompressed_strip_size - )) - }, - }; - if units_read == buffer_size { - break - } - } - if units_read < buffer_size { - return Err(TiffError::FormatError(TiffFormatError::InconsistentSizesEncountered)); - } - if let Ok(predictor) = self.get_tag_u32(ifd::Tag::Predictor) { - result = match FromPrimitive::from_u32(predictor) { - 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))), - } + self.read_strip_to_buffer(result.to_buffer(0))?; + + Ok(result) + } + + /// Decodes the entire image and return it as a Vector + pub fn read_image(&mut self) -> TiffResult { + self.initialize_strip_decoder()?; + let rows_per_strip = self.get_tag_u32(ifd::Tag::RowsPerStrip) + .unwrap_or(self.height) as usize; + + 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(); + + 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]), + n => return Err(TiffError::UnsupportedError(TiffUnsupportedError::UnsupportedBitsPerChannel(n))), + }; + + for i in 0..self.strip_count()? as usize { + self.read_strip_to_buffer(result.to_buffer(samples_per_strip * i))?; } + Ok(result) } } diff --git a/tests/decode_images.rs b/tests/decode_images.rs index 5881733..f34bcf8 100644 --- a/tests/decode_images.rs +++ b/tests/decode_images.rs @@ -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 //#[test] //fn test_gray_alpha_u8() diff --git a/tests/decodedata-rgb-3c-8b.tiff b/tests/decodedata-rgb-3c-8b.tiff new file mode 100644 index 0000000000000000000000000000000000000000..ad802921e270ae8b51fa3f64fa764a7038540875 GIT binary patch literal 30180 zcmeI5i8obk6vpp0i)5Zt2^kVHWGF>4We7!y%w?X*6qPY$%#e9jA|=v*%J_GEkB?dw z;@}SFocAp2E-$Xj`mJ-{v){e<^K2gGGxje~n>0M{jdIfwk$D*TB_%B_Eh8f%D=RA}Cnqm2f9%*X1qB5~MaARCk1HuD zoj7qqSy@>{MMYIrRZUGzU0q#6Lqk(jQ%g%rTU%R4M@LszS5Hq*Utj;^$&&^K2B%J) zI(_=|nKNe$4GqtpJ!@oSWNd6~Vq$Xc+&NQIQ!_I&b8~YG3ybsT&s$nrUbt|<%F4>x z+S(-%gfu_+sDVp*Vp&v&6|FHe*XUc0RaJlfq}Pf-3kf{ z3Jwkq2?@D<`}UnXcS1u$!@|Pu-o1P8-o5bf@cZ}gM?^$KMn*n(@E|HGDmpqkCMG5} zHa0FUjsryzRD66qcSTXTq5vu(A%T1A?fcf-0QKd~V|+-APVX1*0v zVqzk9*ZbF9ZyTtjq$Dmp*fl)Z2`V``nTw-##!e${6;PR(nOydOBB-paEW*<))Inuu zXOoq?n>2(0>gm&`?CWL`5~yd-p0TX$M|hx~KYz}q{wSe>Dlac*O(U7G zK~+>#u%qKn1fVJ_D_PK*C>l^zRaH#y;S?FD>gsAnH@k`wR836{bGw2?4C=*;7YuEm z7Cori+FB;|nTrL~%a<=1*N`tRP_JISVwQ42jG*f3>KG*P5HG0u`g*2lVZ;uqp`n2h zq9HnfYHVzzzq*PhpqiSRXf8ve52)tmW;*NjXa(x^>({guHqs4LOG^tq6`(W()!N!h zL#Zr1LAABD(M?lKTTt!o?X(hK(;3v8H*e^pQl~knj*bqR$oc6Hs`gK)rkSPI#_~ECV$*HYOx{N%nzy|Ngx&d^lMNYJ7ZL;D)7a1vN1-Ay{Wu z7K8fm;e!AzaM=xNa&l5oUcsyf_3`6JftV_j0P54HPe*s3CIr;f)YK7ey2%0c`Sa(a z_?Z(0YI=J5(8lkifts0_Ih;d2fuO#8`ErO+0%U@kot-`C%mrdWef|3NK*K3W2KDXR zH|kS72nY53`*-S7JTROBGZ!#Q01o*ueutkqY`Wn-4O3-!1;YXt&aN;lg%2m}CE=O~ zGeCIU!MY8OWH8!-e-vzy;PwL(8+fz8as*BqFo=Nf0p$EpsY84XO);dhP=G?%2)!O; zXi!%{Gz2XSBpy&MpdlZ9=4hWrS1_7g(Zh+>M0DJtkqrG&X!}7o3z{_0dw`NUD#a*( zqOON>6{;{OE+A))R4_7}NZcVGg)|GY2RIbttcN2EB69?sh@%i5;I4c@{C)Tek54r*?0j`|c2lCLO$nxCJiKE;FBx84S*g@py`Q#{DrqyB>W`SU0BDINrF z=37Di`t^(Y6c5twdfPz#{{5T!6c3`pgPouj7Z<5d@gOIT+7;B&(h~J49)tv({{pqV qyi9$H2MLk+9-vlMR;W+$VEqwhZ&0hNtJJ4>u=}jHFQ~P(wf_Kh8(N(J literal 0 HcmV?d00001 From 6aa5f3add5318572332c08d4989ac52d2f6348c6 Mon Sep 17 00:00:00 2001 From: Birk Tjelmeland Date: Fri, 8 Feb 2019 21:05:25 +0100 Subject: [PATCH 2/2] Made `DecodingBuffer` public Marked `DecodingBuffer` and `read_strip_to_buffer` as public to reduce copies in eventual `TiffReader`. --- src/decoder/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index 393be17..1cd8542 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -28,7 +28,7 @@ pub enum DecodingResult { } impl DecodingResult { - fn to_buffer(&mut self, start: usize) -> DecodingBuffer { + pub 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..]), @@ -37,7 +37,7 @@ impl DecodingResult { } // A buffer for image decoding -enum DecodingBuffer<'a> { +pub enum DecodingBuffer<'a> { /// A slice of unsigned bytes U8(&'a mut [u8]), /// A slice of unsigned words @@ -488,7 +488,7 @@ impl Decoder { Ok(()) } - fn read_strip_to_buffer(&mut self, mut buffer: DecodingBuffer) -> TiffResult<()> { + pub 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;