Compare commits

..
28 Commits
Author SHA1 Message Date
Andreas Molzer fcfcecb2df Add changelog and metadata for 0.8.2 2023-02-06 23:37:12 +01:00
Jonathan Behrens 0da9770916 Merge pull request #190 from kurtkuehnert/YCbCr_support
Added support for the YCbCr PhotometricInterpretation
2023-01-06 23:54:16 -08:00
Kurt Kühnert c30ea911a2 fixed ci 2023-01-05 10:00:10 +01:00
Kurt Kühnert da07c0f4f5 added round trip test
fixed ci
added colortype
2023-01-05 09:34:54 +01:00
Jonathan Behrens 289fab2fcd Merge pull request #197 from pka/fix-padding
Fix padding when image size is multiple of chunk size
2022-12-16 15:49:58 -08:00
Pirmin Kalberer 21252d9d89 Fix padding when image size is multiple of chunk size 2022-12-16 23:37:38 +01:00
Andreas Molzer ffaccf2b52 Merge pull request #196 from image-rs/release-0.8.1
Changelog and metadata for 0.8.1
2022-12-10 16:57:28 +01:00
Andreas Molzer 4dea7c4c81 Changelog and metadata for 0.8.1 2022-12-10 08:25:57 +01:00
Andreas Molzer aa42fcb1bb Merge pull request #193 from image-rs/issue-192-desync-packbits
Fix read in packbits decoder
2022-12-03 18:49:51 +01:00
Andreas Molzer 1f7b25ef55 Fix read in packbits decoder
This assumed that a `read` would succeed for the full length, thus
interpreting in a subsequent call some data bits as a header.

For instance, consider 77 bytes of literal were left and a buffer of
1024 bytes was provided. Then the `read` call might return 60 which was
ignored and assumed to be 77. Thus the next 17 bytes of literal data
were instead interpreted as a header due to setting `self.count` to 0.
2022-12-03 18:42:08 +01:00
Andreas Molzer 6c65e66440 Merge pull request #170 from AlanRace/jpeg-unwrap
Removed `unwrap` and set colour transform with Jpeg compressed tiffs
2022-11-22 16:17:40 +01:00
Alan Race a733ff13d9 Changed to using private constructor rather than From<> JpegDecoderError 2022-11-22 15:53:42 +01:00
Alan Race b1cd459330 Updated error handling 2022-11-22 15:44:10 +01:00
Alan Race 4f1c88e35f Added unsupported error for interpretations not supported by JPEG 2022-11-22 14:38:27 +01:00
Alan Race 7307e96782 Removed unwrap and set colour transform with Jpeg compressed tiffs 2022-11-22 14:31:33 +01:00
Kurt Kühnert a08604b015 added support for the YCbCr PhotometricInterpretation 2022-11-16 17:33:39 +01:00
HeroicKatora 9333d99c92 Merge pull request #187 from image-rs/release-0.8.0
Changelog for 0.8
2022-11-10 21:02:40 +01:00
Andreas Molzer 65bc24d5d9 Add deprecation removals to changelog 2022-11-10 17:49:32 +01:00
Andreas Molzer 7fc75f194a Changelog for 0.8 2022-11-10 17:28:12 +01:00
HeroicKatora 7679be19c1 Merge pull request #189 from image-rs/non-exhaustive-enumeration
Non exhaustive enumerations
2022-11-10 17:27:31 +01:00
Andreas Molzer 0e9f8e3b5b Mark more tag enums as non_exhaustive 2022-11-10 12:45:04 +01:00
Andreas Molzer c900129f2c Change to non_exhaustive attributes 2022-11-10 12:45:04 +01:00
HeroicKatora 61234c0005 Merge pull request #188 from fintelia/decoder-deprecated
Remove deprecated methods on Decoder
2022-11-05 20:22:38 +01:00
Jonathan Behrens 1416af1018 Remove deprecated methods on Decoder 2022-11-05 10:15:31 -07:00
HeroicKatora 87d652ae5e Merge pull request #186 from micahsnyder/bump-jpeg-decoder-dependency
Bump jpeg-decoder version pin to 0.3.0
2022-11-03 08:28:43 +01:00
Micah Snyder b5a78fbec4 CI: Correct the MSRV from 1.48 to 1.61 2022-11-02 15:00:29 -07:00
Micah Snyder 30640d50ff Bump jpeg-decoder version pin to 0.3.0
The jpeg-decoder project has a new version with a number of improvements
plus a fix for a possible panic.
Semantic versioning considers 0.2.4 to be incompatible with 0.3.X because
of the leading zero, so this change bumps the pin to 0.3.0.
2022-11-02 14:25:18 -07:00
HeroicKatora b87770225f Merge pull request #185 from image-rs/release-0.7.4
Changelog for 0.7.4
2022-10-29 12:44:25 +02:00
13 changed files with 191 additions and 170 deletions

No files matched your search

+2 -2
View File
@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
rust: ["1.48", stable, beta, nightly] rust: ["1.61", stable, beta, nightly]
command: [build, test] command: [build, test]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
@@ -20,7 +20,7 @@ jobs:
- name: test - name: test
run: > run: >
cargo test --tests --benches --no-default-features --features "$FEATURES" cargo test --tests --benches --no-default-features --features "$FEATURES"
if: ${{ matrix.rust != '1.48' }} if: ${{ matrix.rust != '1.61' }}
env: env:
FEATURES: ${{ matrix.features }} FEATURES: ${{ matrix.features }}
rustfmt: rustfmt:
+44
View File
@@ -1,3 +1,47 @@
# Version 0.9.0
New features:
* Added support for photometric interpretation `YCbCr` and added related
`ColorType`.
Fixes:
* Decoding tiled images calculates padding correctly when image width or height
is a multiple of tile size. It could previously corrupt the last tile per row
by skipping over data.
# Version 0.8.1
Changes:
* The jpeg decoder gained to ability to utilize the Photometric Interpretation
directly instead of relying on a custom APP segment.
Fixes:
* A spurious error within the PackBits decoder lead to the incorrect results
(wrong bits or errors), based on the maximum size of reads from the
underlying reader.
* Removed a panic path in jpeg decoding, when a feature such as photometric
interpretation is not supported. An error is returned instead.
# Version 0.8.0
Changes:
* The minimum supported rust version is now indicated in `Cargo.toml`.
* The enums `TiffFormatError` and `TiffUnsupportedError` are now
marked with the `#[non_exhaustive]` attribute.
* Additionally, tag related enums `Value`, `Tags`, `Type`, `CompressionMethod`,
`PhotometricInterpretation`, `PlanarConfiguration`, `Predictor`,
`ResolutionUnit`, `SampleFormat` are also changed.
Removals:
* Removed deprecated methods of `Decoder`: `init`, `read_jpeg`,
`read_strip_to_buffer`, `read_strip`, `read_tile`. The implicit chunk (row or
tile) index order could not be easily tracked by the caller. New separate
utility interfaces may be introduced at a later point, for now callers are
obligated to choose their own.
Fixes:
* Update to `jpeg_decoder = 0.3`.
# Version 0.7.4 # Version 0.7.4
New features: New features:
+12 -4
View File
@@ -1,16 +1,24 @@
[package] [package]
name = "tiff" name = "tiff"
version = "0.7.4" version = "0.9.0"
authors = ["The image-rs Developers"] edition = "2018"
resolver = "2"
# note: when changed, also update test runner in `.github/workflows/rust.yml`
rust-version = "1.61.0"
license = "MIT" license = "MIT"
description = "TIFF decoding and encoding library in pure Rust" description = "TIFF decoding and encoding library in pure Rust"
authors = ["The image-rs Developers"]
repository = "https://github.com/image-rs/image-tiff" repository = "https://github.com/image-rs/image-tiff"
categories = ["multimedia::images", "multimedia::encoding"]
exclude = ["tests/images/*", "tests/fuzz_images/*"] exclude = ["tests/images/*", "tests/fuzz_images/*"]
edition = "2018"
[dependencies] [dependencies]
weezl = "0.1.0" weezl = "0.1.0"
jpeg = { package = "jpeg-decoder", version = "0.2.4", default-features = false } jpeg = { package = "jpeg-decoder", version = "0.3.0", default-features = false }
flate2 = "1.0.20" flate2 = "1.0.20"
[dev-dependencies] [dev-dependencies]
+1 -7
View File
@@ -17,6 +17,7 @@ use self::Value::{
#[allow(unused_qualifications)] #[allow(unused_qualifications)]
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum Value { pub enum Value {
Byte(u8), Byte(u8),
Short(u16), Short(u16),
@@ -34,8 +35,6 @@ pub enum Value {
Ascii(String), Ascii(String),
Ifd(u32), Ifd(u32),
IfdBig(u64), IfdBig(u64),
#[doc(hidden)] // Do not match against this.
__NonExhaustive,
} }
impl Value { impl Value {
@@ -361,7 +360,6 @@ impl Entry {
| Type::RATIONAL | Type::RATIONAL
| Type::SRATIONAL | Type::SRATIONAL
| Type::IFD8 => 8, | Type::IFD8 => 8,
Type::__NonExhaustive => unreachable!(),
}; };
let value_bytes = match self.count.checked_mul(tag_size) { let value_bytes = match self.count.checked_mul(tag_size) {
@@ -398,7 +396,6 @@ impl Entry {
| Type::SLONG | Type::SLONG
| Type::FLOAT | Type::FLOAT
| Type::IFD => unreachable!(), | Type::IFD => unreachable!(),
Type::__NonExhaustive => unreachable!(),
}); });
} }
@@ -444,7 +441,6 @@ impl Entry {
reader.goto_offset(self.r(bo).read_u32()?.into())?; reader.goto_offset(self.r(bo).read_u32()?.into())?;
IfdBig(reader.read_u64()?) IfdBig(reader.read_u64()?)
} }
Type::__NonExhaustive => unreachable!(),
}); });
} }
@@ -528,7 +524,6 @@ impl Entry {
| Type::IFD8 => { | Type::IFD8 => {
unreachable!() unreachable!()
} }
Type::__NonExhaustive => unreachable!(),
} }
} }
@@ -611,7 +606,6 @@ impl Entry {
} }
Ok(Ascii(String::from_utf8(out)?)) Ok(Ascii(String::from_utf8(out)?))
} }
Type::__NonExhaustive => unreachable!(),
} }
} }
+51 -23
View File
@@ -1,8 +1,5 @@
use super::ifd::{Directory, Value}; use super::ifd::{Directory, Value};
use super::stream::{ use super::stream::{ByteOrder, DeflateReader, JpegReader, LZWReader, PackBitsReader};
add_app14segment, ByteOrder, DeflateReader, JpegReader, JpegTagApp14Transform, LZWReader,
PackBitsReader,
};
use super::tag_reader::TagReader; use super::tag_reader::TagReader;
use super::{fp_predict_f32, fp_predict_f64, DecodingBuffer, Limits}; use super::{fp_predict_f32, fp_predict_f64, DecodingBuffer, Limits};
use super::{stream::SmartReader, ChunkType}; use super::{stream::SmartReader, ChunkType};
@@ -35,12 +32,11 @@ impl TileAttributes {
(self.image_height + self.tile_length - 1) / self.tile_length (self.image_height + self.tile_length - 1) / self.tile_length
} }
fn padding_right(&self) -> usize { fn padding_right(&self) -> usize {
self.tile_width - self.image_width % self.tile_width (self.tile_width - self.image_width % self.tile_width) % self.tile_width
} }
fn padding_down(&self) -> usize { fn padding_down(&self) -> usize {
self.tile_length - self.image_height % self.tile_length (self.tile_length - self.image_height % self.tile_length) % self.tile_length
} }
pub fn get_padding(&self, tile: usize) -> (usize, usize) { pub fn get_padding(&self, tile: usize) -> (usize, usize) {
let row = tile / self.tiles_across(); let row = tile / self.tiles_across();
let column = tile % self.tiles_across(); let column = tile % self.tiles_across();
@@ -121,7 +117,7 @@ impl Image {
let jpeg_tables = if compression_method == CompressionMethod::ModernJPEG let jpeg_tables = if compression_method == CompressionMethod::ModernJPEG
&& ifd.contains_key(&Tag::JPEGTables) && ifd.contains_key(&Tag::JPEGTables)
{ {
let mut vec = tag_reader let vec = tag_reader
.find_tag(Tag::JPEGTables)? .find_tag(Tag::JPEGTables)?
.unwrap() .unwrap()
.into_u8_vec()?; .into_u8_vec()?;
@@ -131,11 +127,6 @@ impl Image {
)); ));
} }
// TODO: Can we avoid this somehow?
if photometric_interpretation == PhotometricInterpretation::RGB {
add_app14segment(&mut vec, JpegTagApp14Transform::App14TransformUnknown)
}
Some(Arc::new(vec)) Some(Arc::new(vec))
} else { } else {
None None
@@ -314,6 +305,15 @@ impl Image {
), ),
)), )),
}, },
PhotometricInterpretation::YCbCr => match self.bits_per_sample[..] {
[y, cb, cr] if [y, y] == [cb, cr] => Ok(ColorType::YCbCr(y)),
_ => Err(TiffError::UnsupportedError(
TiffUnsupportedError::InterpretationWithBits(
self.photometric_interpretation,
self.bits_per_sample.clone(),
),
)),
},
PhotometricInterpretation::BlackIsZero | PhotometricInterpretation::WhiteIsZero PhotometricInterpretation::BlackIsZero | PhotometricInterpretation::WhiteIsZero
if self.bits_per_sample.len() == 1 => if self.bits_per_sample.len() == 1 =>
{ {
@@ -332,6 +332,7 @@ impl Image {
fn create_reader<'r, R: 'r + Read>( fn create_reader<'r, R: 'r + Read>(
reader: R, reader: R,
photometric_interpretation: PhotometricInterpretation,
compression_method: CompressionMethod, compression_method: CompressionMethod,
compressed_length: u64, compressed_length: u64,
jpeg_tables: Option<Arc<Vec<u8>>>, jpeg_tables: Option<Arc<Vec<u8>>>,
@@ -354,14 +355,36 @@ impl Image {
let jpeg_reader = JpegReader::new(reader, compressed_length, jpeg_tables)?; let jpeg_reader = JpegReader::new(reader, compressed_length, jpeg_tables)?;
let mut decoder = jpeg::Decoder::new(jpeg_reader); let mut decoder = jpeg::Decoder::new(jpeg_reader);
let data = match decoder.decode() {
Ok(data) => data, match photometric_interpretation {
Err(e) => { PhotometricInterpretation::RGB => {
return Err(TiffError::FormatError(TiffFormatError::Format( decoder.set_color_transform(jpeg::ColorTransform::RGB)
e.to_string(),
)))
} }
}; PhotometricInterpretation::WhiteIsZero => {
decoder.set_color_transform(jpeg::ColorTransform::None)
}
PhotometricInterpretation::BlackIsZero => {
decoder.set_color_transform(jpeg::ColorTransform::None)
}
PhotometricInterpretation::TransparencyMask => {
decoder.set_color_transform(jpeg::ColorTransform::None)
}
PhotometricInterpretation::CMYK => {
decoder.set_color_transform(jpeg::ColorTransform::CMYK)
}
PhotometricInterpretation::YCbCr => {
decoder.set_color_transform(jpeg::ColorTransform::YCbCr)
}
photometric_interpretation => {
return Err(TiffError::UnsupportedError(
TiffUnsupportedError::UnsupportedInterpretation(
photometric_interpretation,
),
));
}
}
let data = decoder.decode()?;
Box::new(Cursor::new(data)) Box::new(Cursor::new(data))
} }
@@ -450,6 +473,7 @@ impl Image {
(ColorType::RGB(n), _) (ColorType::RGB(n), _)
| (ColorType::RGBA(n), _) | (ColorType::RGBA(n), _)
| (ColorType::CMYK(n), _) | (ColorType::CMYK(n), _)
| (ColorType::YCbCr(n), _)
| (ColorType::Gray(n), _) | (ColorType::Gray(n), _)
if usize::from(n) == buffer.byte_len() * 8 => {} if usize::from(n) == buffer.byte_len() * 8 => {}
(ColorType::Gray(n), DecodingBuffer::U8(_)) if n < 8 => match self.predictor { (ColorType::Gray(n), DecodingBuffer::U8(_)) if n < 8 => match self.predictor {
@@ -464,7 +488,6 @@ impl Image {
TiffUnsupportedError::FloatingPointPredictor(color_type), TiffUnsupportedError::FloatingPointPredictor(color_type),
)); ));
} }
Predictor::__NonExhaustive => unreachable!(),
}, },
(type_, _) => { (type_, _) => {
return Err(TiffError::UnsupportedError( return Err(TiffError::UnsupportedError(
@@ -510,8 +533,13 @@ impl Image {
let padding_right = chunk_dims.0 - data_dims.0; let padding_right = chunk_dims.0 - data_dims.0;
let jpeg_tables = self.jpeg_tables.clone(); let jpeg_tables = self.jpeg_tables.clone();
let mut reader = let mut reader = Self::create_reader(
Self::create_reader(reader, compression_method, *compressed_bytes, jpeg_tables)?; reader,
photometric_interpretation,
compression_method,
*compressed_bytes,
jpeg_tables,
)?;
if output_width == data_dims.0 as usize && padding_right == 0 { if output_width == data_dims.0 as usize && padding_right == 0 {
let total_samples = data_dims.0 as usize * data_dims.1 as usize * samples; let total_samples = data_dims.0 as usize * data_dims.1 as usize * samples;
-63
View File
@@ -301,7 +301,6 @@ where
reader: SmartReader<R>, reader: SmartReader<R>,
bigtiff: bool, bigtiff: bool,
limits: Limits, limits: Limits,
current_chunk: u32,
next_ifd: Option<u64>, next_ifd: Option<u64>,
ifd_offsets: Vec<u64>, ifd_offsets: Vec<u64>,
seen_ifds: HashSet<u64>, seen_ifds: HashSet<u64>,
@@ -430,7 +429,6 @@ fn fix_endianness_and_predict(
_ => unreachable!("Caller should have validated arguments. Please file a bug."), _ => unreachable!("Caller should have validated arguments. Please file a bug."),
} }
} }
Predictor::__NonExhaustive => unreachable!(),
} }
} }
@@ -586,7 +584,6 @@ impl<R: Read + Seek> Decoder<R> {
chunk_offsets: Vec::new(), chunk_offsets: Vec::new(),
chunk_bytes: Vec::new(), chunk_bytes: Vec::new(),
}, },
current_chunk: 0,
}; };
decoder.next_image()?; decoder.next_image()?;
Ok(decoder) Ok(decoder)
@@ -638,7 +635,6 @@ impl<R: Read + Seek> Decoder<R> {
if let Some(ifd_offset) = self.ifd_offsets.get(ifd_index) { if let Some(ifd_offset) = self.ifd_offsets.get(ifd_index) {
let (ifd, _next_ifd) = Self::read_ifd(&mut self.reader, self.bigtiff, *ifd_offset)?; let (ifd, _next_ifd) = Self::read_ifd(&mut self.reader, self.bigtiff, *ifd_offset)?;
self.current_chunk = 0;
self.image = Image::from_reader(&mut self.reader, ifd, &self.limits, self.bigtiff)?; self.image = Image::from_reader(&mut self.reader, ifd, &self.limits, self.bigtiff)?;
Ok(()) Ok(())
@@ -649,13 +645,6 @@ impl<R: Read + Seek> Decoder<R> {
} }
} }
/// Reset the decoder.
#[deprecated = "Never should have been public. Only use Decoder::new()"]
pub fn init(self) -> TiffResult<Decoder<R>> {
let Self { reader, .. } = self;
Self::new(reader.into_inner())
}
fn next_ifd(&mut self) -> TiffResult<(Directory, Option<u64>)> { fn next_ifd(&mut self) -> TiffResult<(Directory, Option<u64>)> {
if self.next_ifd.is_none() { if self.next_ifd.is_none() {
return Err(TiffError::FormatError( return Err(TiffError::FormatError(
@@ -686,7 +675,6 @@ impl<R: Read + Seek> Decoder<R> {
pub fn next_image(&mut self) -> TiffResult<()> { pub fn next_image(&mut self) -> TiffResult<()> {
let (ifd, _next_ifd) = self.next_ifd()?; let (ifd, _next_ifd) = self.next_ifd()?;
self.current_chunk = 0;
self.image = Image::from_reader(&mut self.reader, ifd, &self.limits, self.bigtiff)?; self.image = Image::from_reader(&mut self.reader, ifd, &self.limits, self.bigtiff)?;
Ok(()) Ok(())
} }
@@ -1037,33 +1025,6 @@ impl<R: Read + Seek> Decoder<R> {
Ok(u32::try_from(self.image().chunk_offsets.len())?) Ok(u32::try_from(self.image().chunk_offsets.len())?)
} }
#[deprecated = "Use read_image instead"]
pub fn read_jpeg(&mut self) -> TiffResult<DecodingResult> {
self.read_image()
}
#[deprecated = "Use read_chunk_to_buffer instead"]
pub fn read_strip_to_buffer(&mut self, mut buffer: DecodingBuffer) -> TiffResult<()> {
self.check_chunk_type(ChunkType::Strip)?;
let offset = self.image.chunk_file_range(self.current_chunk)?.0;
self.goto_offset_u64(offset)?;
let byte_order = self.reader.byte_order;
let output_width = usize::try_from(self.image().width)?;
self.image.expand_chunk(
&mut self.reader,
buffer.copy(),
output_width,
byte_order,
self.current_chunk,
)?;
self.current_chunk += 1;
Ok(())
}
pub fn read_chunk_to_buffer( pub fn read_chunk_to_buffer(
&mut self, &mut self,
mut buffer: DecodingBuffer, mut buffer: DecodingBuffer,
@@ -1139,30 +1100,6 @@ impl<R: Read + Seek> Decoder<R> {
} }
} }
/// Read a single strip from the image and return it as a Vector. This method does not return
/// information on the size of the strip read and so the caller must take care of the final
/// strip in the image as the data read may be less than the expected strip size. This interface
/// may change at a future date to reflect this.
#[deprecated = "Use read_chunk instead"]
pub fn read_strip(&mut self) -> TiffResult<DecodingResult> {
let result = self.read_chunk(self.current_chunk)?;
Ok(result)
}
/// Read a single tile from the image and return it as a Vector. This method does not return
/// information on the size of the tile read and so the caller must take care of tiles on the
/// rightmost column and the bottommost row in the image as the data read may be less than the
/// expected tile size. This interface may change at a future date to reflect this.
#[deprecated = "Use read_chunk instead"]
pub fn read_tile(&mut self) -> TiffResult<DecodingResult> {
let result = self.read_chunk(self.current_chunk)?;
self.current_chunk += 1;
Ok(result)
}
/// Read the specified chunk (at index `chunk_index`) and return the binary data as a Vector. /// Read the specified chunk (at index `chunk_index`) and return the binary data as a Vector.
pub fn read_chunk(&mut self, chunk_index: u32) -> TiffResult<DecodingResult> { pub fn read_chunk(&mut self, chunk_index: u32) -> TiffResult<DecodingResult> {
let data_dims = self.image().chunk_data_dimensions(chunk_index)?; let data_dims = self.image().chunk_data_dimensions(chunk_index)?;
+7 -59
View File
@@ -250,58 +250,6 @@ impl JpegReader {
} }
} }
#[repr(u8)]
pub(crate) enum JpegTagApp14Transform {
// App14TransformYCCK = 2,
// App14TransformYCbCr = 1,
App14TransformUnknown = 0,
}
pub(crate) fn add_app14segment(jpeg_tables: &mut Vec<u8>, transform: JpegTagApp14Transform) {
// Add JPEG Tag APP14 Adobe segment to jpeg_tables.
// This segment stores image encoding information for DCT filters.
// When `transform` value is 0 which is defined as Unknown, jpeg-decoder interpret the
// color-space of image as RGB(3 channels) or CMYK(4).
let mut app14_offset = None;
let mut dht_offset = None;
for (offset, window) in jpeg_tables.windows(2).enumerate() {
if window == [0xff, 0xee] {
app14_offset = Some(offset);
break;
}
if window == [0xff, 0xc4] {
dht_offset = Some(offset);
}
}
match (app14_offset, dht_offset) {
(Some(i), _) => {
jpeg_tables[i + 16] = transform as u8;
}
(None, Some(sos_offset)) => {
let app14segment: [u8; 16] = [
0xff,
0xee,
0x00,
0x0e,
0x41,
0x64,
0x6f,
0x62,
0x65,
0x00,
0x64,
0x00,
0x00,
0x00,
0x00,
transform as u8,
];
jpeg_tables.splice(sos_offset..sos_offset, app14segment.iter().copied());
}
(None, None) => {}
}
}
impl Read for JpegReader { impl Read for JpegReader {
// #[inline] // #[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
@@ -384,23 +332,23 @@ impl<R: Read> Read for PackBitsReader<R> {
} }
let length = buf.len().min(self.count); let length = buf.len().min(self.count);
match self.state { let actual = match self.state {
PackBitsReaderState::Literal => { PackBitsReaderState::Literal => self.reader.read(&mut buf[..length])?,
self.reader.read(&mut buf[..length])?;
}
PackBitsReaderState::Repeat { value } => { PackBitsReaderState::Repeat { value } => {
for b in &mut buf[..length] { for b in &mut buf[..length] {
*b = value; *b = value;
} }
length
} }
PackBitsReaderState::Header => unreachable!(), PackBitsReaderState::Header => unreachable!(),
} };
self.count -= length; self.count -= actual;
if self.count == 0 { if self.count == 0 {
self.state = PackBitsReaderState::Header; self.state = PackBitsReaderState::Header;
} }
return Ok(length); return Ok(actual);
} }
} }
+8
View File
@@ -235,3 +235,11 @@ impl ColorType for CMYK64Float {
const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64, 64]; const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64, 64];
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::IEEEFP; 4]; const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::IEEEFP; 4];
} }
pub struct YCbCr8;
impl ColorType for YCbCr8 {
type Inner = u8;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::YCbCr;
const BITS_PER_SAMPLE: &'static [u16] = &[8, 8, 8];
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 3];
}
+57 -8
View File
@@ -1,8 +1,12 @@
use std::error::Error; use std::error::Error;
use std::fmt; use std::fmt;
use std::fmt::Display;
use std::io; use std::io;
use std::str; use std::str;
use std::string; use std::string;
use std::sync::Arc;
use jpeg::UnsupportedFeature;
use crate::decoder::{ifd::Value, ChunkType}; use crate::decoder::{ifd::Value, ChunkType};
use crate::tags::{ use crate::tags::{
@@ -43,6 +47,7 @@ pub enum TiffError {
/// The list of variants may grow to incorporate errors of future features. Matching against this /// The list of variants may grow to incorporate errors of future features. Matching against this
/// exhaustively is not covered by interface stability guarantees. /// exhaustively is not covered by interface stability guarantees.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum TiffFormatError { pub enum TiffFormatError {
TiffSignatureNotFound, TiffSignatureNotFound,
TiffSignatureInvalid, TiffSignatureInvalid,
@@ -68,9 +73,7 @@ pub enum TiffFormatError {
RequiredTagEmpty(Tag), RequiredTagEmpty(Tag),
StripTileTagConflict, StripTileTagConflict,
CycleInOffsets, CycleInOffsets,
#[doc(hidden)] JpegDecoder(JpegDecoderError),
/// Do not match against this variant. It may get removed.
__NonExhaustive,
} }
impl fmt::Display for TiffFormatError { impl fmt::Display for TiffFormatError {
@@ -121,7 +124,7 @@ impl fmt::Display for TiffFormatError {
RequiredTagEmpty(ref val) => write!(fmt, "Required tag {:?} was empty.", val), RequiredTagEmpty(ref val) => write!(fmt, "Required tag {:?} was empty.", val),
StripTileTagConflict => write!(fmt, "File should contain either (StripByteCounts and StripOffsets) or (TileByteCounts and TileOffsets), other combination was found."), StripTileTagConflict => write!(fmt, "File should contain either (StripByteCounts and StripOffsets) or (TileByteCounts and TileOffsets), other combination was found."),
CycleInOffsets => write!(fmt, "File contained a cycle in the list of IFDs"), CycleInOffsets => write!(fmt, "File contained a cycle in the list of IFDs"),
__NonExhaustive => unreachable!(), JpegDecoder(ref error) => write!(fmt, "{}", error),
} }
} }
} }
@@ -135,6 +138,7 @@ impl fmt::Display for TiffFormatError {
/// The list of variants may grow. Matching against this exhaustively is not covered by interface /// The list of variants may grow. Matching against this exhaustively is not covered by interface
/// stability guarantees. /// stability guarantees.
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum TiffUnsupportedError { pub enum TiffUnsupportedError {
FloatingPointPredictor(ColorType), FloatingPointPredictor(ColorType),
HorizontalPredictor(ColorType), HorizontalPredictor(ColorType),
@@ -148,9 +152,8 @@ pub enum TiffUnsupportedError {
UnsupportedBitsPerChannel(u8), UnsupportedBitsPerChannel(u8),
UnsupportedPlanarConfig(Option<PlanarConfiguration>), UnsupportedPlanarConfig(Option<PlanarConfiguration>),
UnsupportedDataType, UnsupportedDataType,
#[doc(hidden)] UnsupportedInterpretation(PhotometricInterpretation),
/// Do not match against this variant. It may get removed. UnsupportedJpegFeature(UnsupportedFeature),
__NonExhaustive,
} }
impl fmt::Display for TiffUnsupportedError { impl fmt::Display for TiffUnsupportedError {
@@ -196,7 +199,16 @@ impl fmt::Display for TiffUnsupportedError {
write!(fmt, "Unsupported planar configuration “{:?}”.", config) write!(fmt, "Unsupported planar configuration “{:?}”.", config)
} }
UnsupportedDataType => write!(fmt, "Unsupported data type."), UnsupportedDataType => write!(fmt, "Unsupported data type."),
__NonExhaustive => unreachable!(), UnsupportedInterpretation(interpretation) => {
write!(
fmt,
"Unsupported photometric interpretation \"{:?}\".",
interpretation
)
}
UnsupportedJpegFeature(ref unsupported_feature) => {
write!(fmt, "Unsupported JPEG feature {:?}", unsupported_feature)
}
} }
} }
} }
@@ -316,5 +328,42 @@ impl From<LzwError> for TiffError {
} }
} }
#[derive(Debug, Clone)]
pub struct JpegDecoderError {
inner: Arc<jpeg::Error>,
}
impl JpegDecoderError {
fn new(error: jpeg::Error) -> Self {
Self {
inner: Arc::new(error),
}
}
}
impl PartialEq for JpegDecoderError {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.inner, &other.inner)
}
}
impl Display for JpegDecoderError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.fmt(f)
}
}
impl From<JpegDecoderError> for TiffError {
fn from(error: JpegDecoderError) -> Self {
TiffError::FormatError(TiffFormatError::JpegDecoder(error))
}
}
impl From<jpeg::Error> for TiffError {
fn from(error: jpeg::Error) -> Self {
JpegDecoderError::new(error).into()
}
}
/// Result of an image decoding/encoding process /// Result of an image decoding/encoding process
pub type TiffResult<T> = Result<T, TiffError>; pub type TiffResult<T> = Result<T, TiffError>;
+3
View File
@@ -37,4 +37,7 @@ pub enum ColorType {
/// Pixel is CMYK /// Pixel is CMYK
CMYK(u8), CMYK(u8),
/// Pixel is YCbCr
YCbCr(u8),
} }
+1 -4
View File
@@ -9,11 +9,9 @@ macro_rules! tags {
} => { } => {
$( #[$enum_attr] )* $( #[$enum_attr] )*
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
#[non_exhaustive]
pub enum $name { pub enum $name {
$($(#[$ident_attr])* $tag,)* $($(#[$ident_attr])* $tag,)*
// FIXME: switch to non_exhaustive once stabilized and compiler requirement new enough
#[doc(hidden)]
__NonExhaustive,
$( $(
#[doc = $unknown_doc] #[doc = $unknown_doc]
Unknown($ty), Unknown($ty),
@@ -34,7 +32,6 @@ macro_rules! tags {
match *self { match *self {
$( $name::$tag => $val, )* $( $name::$tag => $val, )*
$( $name::Unknown(n) => { $unknown_doc; n }, )* $( $name::Unknown(n) => { $unknown_doc; n }, )*
$name::__NonExhaustive => unreachable!(),
} }
} }
} }
+5
View File
@@ -264,6 +264,11 @@ fn test_gray_f64_roundtrip() {
test_f64_roundtrip::<colortype::Gray64Float>("gradient-1c-64b-float.tiff", ColorType::Gray(64)); test_f64_roundtrip::<colortype::Gray64Float>("gradient-1c-64b-float.tiff", ColorType::Gray(64));
} }
#[test]
fn test_ycbcr_u8_roundtrip() {
test_u8_roundtrip::<colortype::YCbCr8>("tiled-jpeg-ycbcr.tif", ColorType::YCbCr(8));
}
trait AssertDecode { trait AssertDecode {
fn assert_tag_u32(&mut self, tag: u16) -> u32; fn assert_tag_u32(&mut self, tag: u16) -> u32;
fn assert_tag_u32_vec(&mut self, tag: u16) -> Vec<u32>; fn assert_tag_u32_vec(&mut self, tag: u16) -> Vec<u32>;
Binary file not shown.