diff --git a/src/decoder/image.rs b/src/decoder/image.rs index 6f97b01..8086b29 100644 --- a/src/decoder/image.rs +++ b/src/decoder/image.rs @@ -1,5 +1,5 @@ use super::ifd::{Directory, Value}; -use super::stream::{ByteOrder, DeflateReader, JpegReader, LZWReader, PackBitsReader}; +use super::stream::{ByteOrder, DeflateReader, LZWReader, PackBitsReader}; use super::tag_reader::TagReader; use super::{fp_predict_f32, fp_predict_f64, DecodingBuffer, Limits}; use super::{stream::SmartReader, ChunkType}; @@ -326,7 +326,7 @@ impl Image { photometric_interpretation: PhotometricInterpretation, compression_method: CompressionMethod, compressed_length: u64, - jpeg_tables: Option>>, + jpeg_tables: Option<&[u8]>, ) -> TiffResult> { Ok(match compression_method { CompressionMethod::None => Box::new(reader), @@ -344,7 +344,29 @@ impl Image { )); } - let jpeg_reader = JpegReader::new(reader, compressed_length, jpeg_tables)?; + // Construct new jpeg_reader wrapping a SmartReader. + // + // JPEG compression in TIFF allows saving quantization and/or huffman tables in one + // central location. These `jpeg_tables` are simply prepended to the remaining jpeg image data. + // Because these `jpeg_tables` start with a `SOI` (HEX: `0xFFD8`) or __start of image__ marker + // which is also at the beginning of the remaining JPEG image data and would + // confuse the JPEG renderer, one of these has to be taken off. In this case the first two + // bytes of the remaining JPEG data is removed because it follows `jpeg_tables`. + // Similary, `jpeg_tables` ends with a `EOI` (HEX: `0xFFD9`) or __end of image__ marker, + // this has to be removed as well (last two bytes of `jpeg_tables`). + let jpeg_reader = match jpeg_tables { + Some(jpeg_tables) => { + let mut reader = reader.take(compressed_length); + reader.read_exact(&mut [0; 2])?; + + Box::new( + Cursor::new(&jpeg_tables[..jpeg_tables.len() - 2]) + .chain(reader.take(compressed_length)), + ) as Box + } + None => Box::new(reader.take(compressed_length)), + }; + let mut decoder = jpeg::Decoder::new(jpeg_reader); match photometric_interpretation { @@ -522,13 +544,12 @@ impl Image { let padding_right = chunk_dims.0 - data_dims.0; - let jpeg_tables = self.jpeg_tables.clone(); let mut reader = Self::create_reader( reader, photometric_interpretation, compression_method, *compressed_bytes, - jpeg_tables, + self.jpeg_tables.as_deref().map(|a| &**a), )?; if output_width == data_dims.0 as usize && padding_right == 0 { diff --git a/src/decoder/stream.rs b/src/decoder/stream.rs index e0323c2..54dee50 100644 --- a/src/decoder/stream.rs +++ b/src/decoder/stream.rs @@ -1,8 +1,7 @@ //! All IO functionality needed for TIFF decoding use std::convert::TryFrom; -use std::io::{self, BufRead, BufReader, Read, Seek, SeekFrom, Take}; -use std::sync::Arc; +use std::io::{self, BufRead, BufReader, Read, Seek, Take}; /// Byte order of the TIFF file. #[derive(Clone, Copy, Debug)] @@ -183,104 +182,6 @@ impl Read for LZWReader { } } -/// -/// ## JPEG Reader (for "new-style" JPEG format (TIFF compression tag 7)) -/// - -pub(crate) struct JpegReader { - jpeg_tables: Option>>, - - buffer: io::Cursor>, - - offset: usize, -} - -impl JpegReader { - /// Constructs new JpegReader wrapping a SmartReader. - /// Because JPEG compression in TIFF allows to save quantization and/or huffman tables in one - /// central location, the constructor accepts this data as `jpeg_tables` here containing either - /// or both. - /// These `jpeg_tables` are simply prepended to the remaining jpeg image data. - /// Because these `jpeg_tables` start with a `SOI` (HEX: `0xFFD8`) or __start of image__ marker - /// which is also at the beginning of the remaining JPEG image data and would - /// confuse the JPEG renderer, one of these has to be taken off. In this case the first two - /// bytes of the remaining JPEG data is removed because it follows `jpeg_tables`. - /// Similary, `jpeg_tables` ends with a `EOI` (HEX: `0xFFD9`) or __end of image__ marker, - /// this has to be removed as well (last two bytes of `jpeg_tables`). - pub fn new( - mut reader: R, - length: u64, - jpeg_tables: Option>>, - ) -> io::Result { - // Read jpeg image data - let mut segment = vec![0; length as usize]; - - reader.read_exact(&mut segment[..])?; - - match jpeg_tables { - Some(jpeg_tables) => { - assert!( - jpeg_tables.len() >= 2, - "jpeg_tables, if given, must be at least 2 bytes long. Got {:?}", - jpeg_tables - ); - - assert!( - length >= 2, - "if jpeg_tables is given, length must be at least 2 bytes long, got {}", - length - ); - - let mut buffer = io::Cursor::new(segment); - // Skip the first two bytes (marker bytes) - buffer.seek(SeekFrom::Start(2))?; - - Ok(JpegReader { - buffer, - jpeg_tables: Some(jpeg_tables), - offset: 0, - }) - } - None => Ok(JpegReader { - buffer: io::Cursor::new(segment), - jpeg_tables: None, - offset: 0, - }), - } - } -} - -impl Read for JpegReader { - // #[inline] - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let mut start = 0; - - if let Some(jpeg_tables) = &self.jpeg_tables { - if jpeg_tables.len() - 2 > self.offset { - // Read (rest of) jpeg_tables to buf (without the last two bytes) - let size_remaining = jpeg_tables.len() - self.offset - 2; - let to_copy = size_remaining.min(buf.len()); - - buf[start..start + to_copy] - .copy_from_slice(&jpeg_tables[self.offset..self.offset + to_copy]); - - self.offset += to_copy; - - if to_copy == buf.len() { - return Ok(to_copy); - } - - start += to_copy; - } - } - - let read = self.buffer.read(&mut buf[start..])?; - self.offset += read; - - Ok(read + start) - } -} - /// /// ## PackBits Reader ///