Merge pull request #95 from ruffson/support_modern_jpeg

Support decoding of modern JPEG compression
This commit is contained in:
HeroicKatora authored and GitHub committed 2020-09-26 18:50:39 +02:00
commit e72401d4ac
7 files changed
+205 -5

No files matched your search

+1
View File
@@ -18,6 +18,7 @@ exclude = ["tests/images/*", "tests/fuzz_images/*"]
[dependencies]
weezl = "0.1.0"
jpeg = { package = "jpeg-decoder", version = "0.1.17", default-features = false }
[dependencies.miniz_oxide]
version = "0.4.1"
+30 -2
View File
@@ -10,13 +10,14 @@ use tags::{Tag, Type};
use {TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
use self::Value::{
Ascii, Double, Float, List, Rational, RationalBig, SRational, SRationalBig, Signed, SignedBig,
Unsigned, UnsignedBig,
Ascii, Byte, Double, Float, List, Rational, RationalBig, SRational, SRationalBig, Signed,
SignedBig, Unsigned, UnsignedBig,
};
#[allow(unused_qualifications)]
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
Byte(u8),
Signed(i32),
SignedBig(i64),
Unsigned(u32),
@@ -34,6 +35,13 @@ pub enum Value {
}
impl Value {
pub fn into_u8(self) -> TiffResult<u8> {
match self {
Byte(val) => Ok(val),
val => Err(TiffError::FormatError(TiffFormatError::ByteExpected(val))),
}
}
pub fn into_u32(self) -> TiffResult<u32> {
match self {
Unsigned(val) => Ok(val),
@@ -114,6 +122,23 @@ impl Value {
}
}
pub fn into_u8_vec(self) -> TiffResult<Vec<u8>> {
match self {
List(vec) => {
let mut new_vec = Vec::with_capacity(vec.len());
for v in vec {
new_vec.push(v.into_u8()?)
}
Ok(new_vec)
}
Byte(val) => Ok(vec![val]),
val => Err(TiffError::FormatError(
TiffFormatError::UnsignedIntegerExpected(val),
)),
}
}
pub fn into_i32_vec(self) -> TiffResult<Vec<i32>> {
match self {
List(vec) => {
@@ -426,6 +451,9 @@ impl Entry {
Ok(Ascii(string))
}
}
(Type::UNDEFINED, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(Byte(u8::from(decoder.read_byte()?)))
}),
_ => Err(TiffError::UnsupportedError(
TiffUnsupportedError::UnsupportedDataType,
)),
+71 -3
View File
@@ -9,7 +9,7 @@ use self::ifd::Directory;
use tags::{CompressionMethod, PhotometricInterpretation, Predictor, SampleFormat, Tag, Type};
use self::stream::{
ByteOrder, DeflateReader, EndianReader, LZWReader, PackBitsReader, SmartReader,
ByteOrder, DeflateReader, EndianReader, JpegReader, LZWReader, PackBitsReader, SmartReader,
};
pub mod ifd;
@@ -165,6 +165,9 @@ pub struct Limits {
/// The maximum size of any ifd value in bytes, the default is
/// 1MiB.
pub ifd_value_size: usize,
/// Maximum size for intermediate buffer which may be used to limit the amount of data read per
/// segment even if the entire image is decoded at once.
pub intermediate_buffer_size: usize,
/// The purpose of this is to prevent all the fields of the struct from
/// being public, as this would make adding new fields a major version
/// bump.
@@ -175,6 +178,7 @@ impl Default for Limits {
fn default() -> Limits {
Limits {
decoding_buffer_size: 256 * 1024 * 1024,
intermediate_buffer_size: 128 * 1024 * 1024,
ifd_value_size: 1024 * 1024,
_non_exhaustive: (),
}
@@ -638,7 +642,9 @@ impl<R: Read + Seek> Decoder<R> {
for _ in 0..num_tags {
let (tag, entry) = match self.read_entry()? {
Some(val) => val,
None => continue, // Unknown data type in tag, skip
None => {
continue;
} // Unknown data type in tag, skip
};
dir.insert(tag, entry);
}
@@ -746,6 +752,11 @@ impl<R: Read + Seek> Decoder<R> {
self.get_tag(tag)?.into_f64_vec()
}
/// Tries to retrieve a tag and convert it to a 8bit vector.
pub fn get_tag_u8_vec(&mut self, tag: Tag) -> TiffResult<Vec<u8>> {
self.get_tag(tag)?.into_u8_vec()
}
/// Decompresses the strip into the supplied buffer.
/// Returns the number of bytes read.
fn expand_strip<'a>(
@@ -923,6 +934,60 @@ impl<R: Read + Seek> Decoder<R> {
Ok(())
}
pub fn read_jpeg(&mut self) -> TiffResult<DecodingResult> {
let offsets = self.get_tag_u32_vec(Tag::StripOffsets)?;
let bytes = self.get_tag_u32_vec(Tag::StripByteCounts)?;
let jpeg_tables: Option<Vec<u8>> = match self.find_tag(Tag::JPEGTables) {
Ok(None) => None,
Ok(_) => Some(self.get_tag_u8_vec(Tag::JPEGTables)?),
Err(e) => return Err(e),
};
if offsets.len() == 0 {
return Err(TiffError::FormatError(TiffFormatError::RequiredTagEmpty(
Tag::StripOffsets,
)));
}
if offsets.len() != bytes.len() {
return Err(TiffError::FormatError(
TiffFormatError::InconsistentSizesEncountered,
));
}
let mut res_img = Vec::with_capacity(offsets[0] as usize);
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)?;
let jpeg_reader = JpegReader::new(&mut self.reader, bytes[idx], &jpeg_tables)?;
let mut decoder = jpeg::Decoder::new(jpeg_reader);
match decoder.decode() {
Ok(mut val) => res_img.append(&mut val),
Err(e) => {
return match e {
jpeg::Error::Io(io_err) => Err(TiffError::IoError(io_err)),
jpeg::Error::Format(fmt_err) => {
Err(TiffError::FormatError(TiffFormatError::Format(fmt_err)))
}
jpeg::Error::Unsupported(_) => Err(TiffError::UnsupportedError(
TiffUnsupportedError::UnknownInterpretation,
)),
jpeg::Error::Internal(_) => Err(TiffError::UnsupportedError(
TiffUnsupportedError::UnknownInterpretation,
)),
}
}
}
}
Ok(DecodingResult::U8(res_img))
}
pub fn read_strip_to_buffer(&mut self, mut buffer: DecodingBuffer) -> TiffResult<()> {
self.initialize_strip_decoder()?;
@@ -1045,6 +1110,10 @@ impl<R: Read + Seek> Decoder<R> {
/// Decodes the entire image and return it as a Vector
pub fn read_image(&mut self) -> TiffResult<DecodingResult> {
if self.compression_method == CompressionMethod::ModernJPEG {
return self.read_jpeg();
}
self.initialize_strip_decoder()?;
let rows_per_strip =
usize::try_from(self.get_tag_u32(Tag::RowsPerStrip).unwrap_or(self.height))?;
@@ -1057,7 +1126,6 @@ impl<R: Read + Seek> Decoder<R> {
for i in 0..usize::try_from(self.strip_count()?)? {
self.read_strip_to_buffer(result.as_buffer(samples_per_strip * i))?;
}
Ok(result)
}
}
+89
View File
@@ -187,6 +187,14 @@ pub trait EndianReader: Read {
}
}
///
/// # READERS
///
///
/// ## Deflate Reader
///
/// Reader that decompresses DEFLATE streams
pub struct DeflateReader {
buffer: io::Cursor<Vec<u8>>,
@@ -233,6 +241,10 @@ impl EndianReader for DeflateReader {
}
}
///
/// ## LZW Reader
///
/// Reader that decompresses LZW streams
pub struct LZWReader {
buffer: io::Cursor<Vec<u8>>,
@@ -306,6 +318,79 @@ impl EndianReader for LZWReader {
}
}
///
/// ## JPEG Reader (for "new-style" JPEG format (TIFF compression tag 7))
///
pub(crate) struct JpegReader {
buffer: io::Cursor<Vec<u8>>,
byte_order: ByteOrder,
}
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<R>(
reader: &mut SmartReader<R>,
length: u32,
jpeg_tables: &Option<Vec<u8>>,
) -> io::Result<JpegReader>
where
R: Read + Seek,
{
let order = reader.byte_order;
// Read jpeg image data
let mut segment = vec![0; length as usize];
reader.read_exact(&mut segment[..])?;
match jpeg_tables {
Some(tables) => {
let mut jpeg_data = tables.clone();
let truncated_length = jpeg_data.len() - 2;
jpeg_data.truncate(truncated_length);
jpeg_data.extend_from_slice(&mut segment[2..]);
Ok(JpegReader {
buffer: io::Cursor::new(jpeg_data),
byte_order: order,
})
}
None => Ok(JpegReader {
buffer: io::Cursor::new(segment),
byte_order: order,
}),
}
}
}
impl Read for JpegReader {
// #[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.buffer.read(buf)
}
}
impl EndianReader for JpegReader {
#[inline(always)]
fn byte_order(&self) -> ByteOrder {
self.byte_order
}
}
///
/// ## PackBits Reader
///
/// Reader that unpacks Apple's `PackBits` format
pub struct PackBitsReader {
buffer: io::Cursor<Vec<u8>>,
@@ -378,6 +463,10 @@ impl EndianReader for PackBitsReader {
}
}
///
/// ## SmartReader Reader
///
/// Reader that is aware of the byte order.
#[derive(Debug)]
pub struct SmartReader<R>
+6
View File
@@ -45,9 +45,12 @@ pub enum TiffFormatError {
InvalidTagValueType(Tag),
RequiredTagNotFound(Tag),
UnknownPredictor(u16),
ByteExpected(Value),
UnsignedIntegerExpected(Value),
SignedIntegerExpected(Value),
InflateError(InflateError),
Format(String),
RequiredTagEmpty(Tag),
#[doc(hidden)]
/// Do not match against this variant. It may get removed.
__NonExhaustive,
@@ -69,6 +72,7 @@ impl fmt::Display for TiffFormatError {
UnknownPredictor(ref predictor) => {
write!(fmt, "Unknown predictor “{}” encountered", predictor)
}
ByteExpected(ref val) => write!(fmt, "Expected byte, {:?} found.", val),
UnsignedIntegerExpected(ref val) => {
write!(fmt, "Expected unsigned integer, {:?} found.", val)
}
@@ -76,6 +80,8 @@ impl fmt::Display for TiffFormatError {
write!(fmt, "Expected signed integer, {:?} found.", val)
}
InflateError(_) => write!(fmt, "Failed to decode inflate data."),
Format(ref val) => write!(fmt, "Invalid format: {:?}.", val),
RequiredTagEmpty(ref val) => write!(fmt, "Required tag {:?} was empty.", val),
__NonExhaustive => unreachable!(),
}
}
+1
View File
@@ -6,6 +6,7 @@
//! # Related Links
//! * <https://www.adobe.io/open/standards/TIFF.html> - The TIFF specification
extern crate jpeg;
extern crate miniz_oxide;
extern crate weezl;
+7
View File
@@ -111,6 +111,8 @@ pub enum Tag(u16) unknown("A private or extension tag") {
Threshholding = 263, // TODO add support
XResolution = 282,
YResolution = 283,
// JPEG
JPEGTables = 347,
// Advanced tags
Predictor = 317,
TileWidth = 322,
@@ -130,6 +132,7 @@ pub enum Type(u16) {
LONG = 4,
RATIONAL = 5,
SBYTE = 6,
UNDEFINED = 7,
SSHORT = 8,
SLONG = 9,
SRATIONAL = 10,
@@ -141,6 +144,8 @@ pub enum Type(u16) {
}
tags! {
/// See [TIFF compression tags](https://www.awaresystems.be/imaging/tiff/tifftags/compression.html)
/// for reference.
pub enum CompressionMethod(u16) {
None = 1,
Huffman = 2,
@@ -148,6 +153,8 @@ pub enum CompressionMethod(u16) {
Fax4 = 4,
LZW = 5,
JPEG = 6,
// "Extended JPEG" or "new JPEG" style
ModernJPEG = 7,
Deflate = 8,
OldDeflate = 0x80B2,
PackBits = 0x8005,