Compare commits

..
101 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
Andreas Molzer d38b65eeb2 Changelog for 0.7.4 2022-10-28 22:58:18 +02:00
HeroicKatora ee27553cf6 Merge pull request #180 from luksab/patch-1
Update link to the TIFF specification
2022-10-14 20:14:07 +02:00
Lukas Sabatschus 5a809a728e Update link to the TIFF specification
The old link is unavailable since mid-2017, so we link to archive.org
2022-10-14 13:53:29 +02:00
HeroicKatora b0b99fc536 Merge pull request #173 from micahsnyder/issue-168-handle-jpeg-decoder-errors
Handle possible JPEG decoder errors
2022-09-06 21:17:04 +02:00
Jonathan Behrens 3bce92781c Merge pull request #176 from fintelia/zero-size
Return an error for zero-size images
2022-09-05 13:10:30 -07:00
Jonathan Behrens 907bbee576 cargo fmt 2022-09-05 12:54:21 -07:00
Jonathan Behrens fc0dafcde2 Change error kind 2022-09-05 12:45:22 -07:00
Jonathan Behrens 945a02d1e1 Format 2022-09-05 12:44:42 -07:00
Jonathan Behrens ef3294d1fe Return an error for zero-size images 2022-09-05 12:44:42 -07:00
HeroicKatora 0311402999 Merge pull request #177 from fintelia/fix-warnings
Fix some warnings
2022-09-05 12:45:04 +02:00
Jonathan Behrens 361578df00 Fix some warnings 2022-09-04 12:35:15 -07:00
Jonathan Behrens bd23428f52 Validate that images contain the proper number of strips (#174) 2022-09-04 12:00:34 -07:00
Micah Snyder d32adf8ef4 Handle possible JPEG decoder errors
The JPEG decoder may fail and panic the TIFF decoder. This commit
converts the JPEG `Format(String)` error to a TIFF `Format(String)`
error.

Fixes #168
2022-09-02 15:02:23 -07:00
Jonathan Behrens 2ec1ab7489 Add changelog and metadata for 0.7.3 (#167) 2022-07-24 10:31:37 -07:00
Jonathan Behrens 80e7c46151 Merge pull request #163 from AlanRace/access-tile
Add ability to select image directory and tile using index
2022-07-23 15:44:45 -07:00
Jonathan Behrens 5003803c97 cargo fmt 2022-07-23 15:27:02 -07:00
Jonathan Behrens 7c4a6fae08 Update src/decoder/mod.rs 2022-07-18 19:36:35 -07:00
Jonathan Behrens 723ef6431a Update src/decoder/mod.rs 2022-07-18 19:36:28 -07:00
Alan Race 802949f6e1 Removed ChunkInfo and added additional functions on decoder 2022-07-18 17:15:44 +02:00
Alan Race 5cadbffebf Changed to use u32 for chunk_index 2022-07-11 08:56:35 +02:00
Alan Race e1f4161e0b Removed unused functions and make InvalidChunkIndex a UsageError 2022-07-08 11:08:30 +02:00
Alan Race 8c0c13f022 Added read_chunk_at and read_chunk_to_buffer functions 2022-07-07 15:25:01 +02:00
Alan Race d5e3f4aa8a Merge remote-tracking branch 'image-tiff/master' into access-tile 2022-07-07 15:06:56 +02:00
Jonathan Behrens 3c42a6e1f4 Merge pull request #165 from fintelia/improve-predictor
Floating point predictor
2022-06-27 07:36:45 -07:00
Jonathan Behrens 5e7702a874 Suggested edits 2022-06-26 20:45:21 -07:00
Jonathan Behrens 8e5c5b44c5 Support floating point predictor (predictor 3)
Based on work by @TomasKralCZ in #135
2022-06-22 20:14:25 -07:00
Jonathan Behrens 8a6e93f66e Merge fix_endianness and rev_hpredict methods 2022-06-22 20:13:55 -07:00
Alan Race 6e398ce7e3 Remove ChunkInfo from mod.rs that was added through merge 2022-05-09 09:58:51 +02:00
Alan Race 8bd02ac389 Merge remote-tracking branch 'origin/access-tile' into access-tile 2022-05-09 09:57:24 +02:00
Alan Race 0447d81b07 Updated to use new unified chunk handling 2022-05-09 09:56:46 +02:00
Alan Race 54664d553d Added comments for read_tile, read_strip and read_*_at 2022-05-09 09:17:39 +02:00
Alan Race 6bec36ab20 Reverted read_strip and read_tile to previous call signature
Added comments to ChunkInfo
2022-05-09 09:17:39 +02:00
Alan Race e57ca1a926 Added functionality for loading selected strip 2022-05-09 09:17:27 +02:00
Alan Race ca4da33dd7 Mirrored return value of read_tile to read_strip 2022-05-09 09:16:35 +02:00
Alan Race 6d4c4a49a1 read_tile now returns ChunkInfo 2022-05-09 09:16:13 +02:00
Alan Race d8520bde1f Now using the list of offsets to know how many IFDs we have seen 2022-05-09 09:14:56 +02:00
Alan Race 56c20c2c20 Added ability to select which image (IFD) to load 2022-05-09 09:14:56 +02:00
Alan Race 6efa89c249 Added ability to select which tile to load (based on index)
Also reduced repeated code when calculating padding etc.
2022-05-09 09:14:04 +02:00
HeroicKatora 68d677d1b3 Merge pull request #164 from fintelia/unify-strip-tile
Unify strip and tile decoding
2022-05-05 18:35:53 +02:00
Alan Race 72ca636625 Added comments for read_tile, read_strip and read_*_at 2022-05-03 09:27:23 +02:00
Alan Race 1f501b5acb Reverted read_strip and read_tile to previous call signature
Added comments to ChunkInfo
2022-05-03 09:21:23 +02:00
Jonathan Behrens 697d2877a1 Cargo fmt 2022-05-02 20:37:37 -07:00
Jonathan Behrens 805a1e5cef Rename chunk_byte_range method 2022-05-02 19:47:41 -07:00
Jonathan Behrens dc8311fb99 Merge read_tiled_image and read_stripped_image 2022-05-02 19:46:28 -07:00
Jonathan Behrens f25530adec Unify expand_strip and expand_tile 2022-05-02 19:37:00 -07:00
Alan Race dd8c9e4f2b Added functionality for loading selected strip 2022-05-02 15:59:48 +02:00
Alan Race 8f34748d01 Mirrored return value of read_tile to read_strip 2022-05-02 15:39:14 +02:00
Alan Race 4ce3c2f41f read_tile now returns ChunkInfo 2022-05-02 15:35:48 +02:00
Alan Race f809d7d1bb Fixed compilation issue after merging 2022-05-02 08:47:05 +02:00
Alan Race 902f945375 Merge branch 'tiled-jpeg' into access-tile 2022-05-02 08:41:29 +02:00
Jonathan Behrens d437c2708b Merge pull request #162 from AlanRace/tiled-jpeg
Added ability to decompress tiled jpeg files
2022-05-01 22:31:14 -07:00
Alan Race a133056287 Removed passing of PhotometricInterpretation to create_reader
as this is now handled for JPEG globally
2022-04-30 14:55:52 +02:00
Alan Race ce00bb5a28 Removed old code 2022-04-30 14:35:33 +02:00
Alan Race 623a3d957d Avoid cloning jpeg_tables and instead pass Arc<Vec<u8>> 2022-04-30 14:33:12 +02:00
Alan Race 9e6f9771d3 Updated create_reader to only require &mut SmartReader and not &mut Self 2022-04-28 08:54:32 +02:00
Alan Race b196d19238 Now using the list of offsets to know how many IFDs we have seen 2022-04-27 17:50:34 +02:00
Alan Race 436cfaf69f Added ability to select which image (IFD) to load 2022-04-27 17:38:44 +02:00
Alan Race 8fcf49deac Added ability to select which tile to load (based on index)
Also reduced repeated code when calculating padding etc.
2022-04-27 16:46:48 +02:00
Alan Race 2cf33d634d Clean up read_image 2022-04-27 15:00:22 +02:00
Alan Race eeb79ee07a Reduce number of clones when processing jpeg data 2022-04-27 14:46:52 +02:00
Alan Race 592405268e Added ability to decompress tiled jpeg files 2022-04-27 14:05:41 +02:00
HeroicKatora 9f2508e37e Merge pull request #160 from fintelia/stream-pack-bits
Make PackBitsReader incrementally decompress output
2022-04-16 22:22:28 +02:00
Jonathan Behrens 4f46bacfda Fixes to PackBitsReader 2022-04-16 16:10:09 -04:00
Jonathan Behrens b286457337 Remove dbg! print 2022-04-16 15:52:09 -04:00
Jonathan Behrens 7881291032 Make PackBitsReader incrementally decompress output 2022-04-15 12:25:24 -04:00
HeroicKatora 167088b33d Merge pull request #159 from fintelia/stream-lzw
Make LZWReader incrementally decompress input
2022-04-15 17:35:43 +02:00
Jonathan Behrens ff33343372 Fix formatting 2022-04-15 11:28:56 -04:00
Jonathan Behrens 4097bf37b1 Rename test oom_2 -> fuzzer_testcase4 2022-04-15 10:04:05 -04:00
Jonathan Behrens c910ef8534 Remove redundant limit on LZWReader output length 2022-04-15 10:02:33 -04:00
Jonathan Behrens 1063d6dd27 Have LZWReader internally use a BufReader<Take<R>> 2022-04-15 09:49:18 -04:00
Jonathan Behrens e4eac36c1c Remove now unneeded limit check 2022-04-14 21:40:19 -04:00
Jonathan Behrens 9394f6d043 Make LZWReader incrementally decompress input 2022-04-14 21:00:36 -04:00
HeroicKatora 3399f93620 Merge pull request #158 from image-rs/release-0.7.2
Add changelog and metadata for 0.7.2
2022-04-11 18:48:10 +02:00
19 changed files with 961 additions and 854 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:
+64
View File
@@ -1,3 +1,67 @@
# 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
New features:
* Creating an encoder for invalid, zero-sized images is now rejected.
Fixes:
* Fix panic, in a case where decoding jpeg encoded images did not expect the
jpeg decoder to return an error.
* Fix panic by validating `rows_per_strip` better, fixing a division-by-zero.
# Version 0.7.3
New features:
* Allow decoder to access specific tiles by index.
* Add support for floating point predictor.
* Tiled jpeg file support.
Changes:
* Various refactoring and performance improvements.
# Version 0.7.2 # Version 0.7.2
New features: New features:
+12 -13
View File
@@ -1,25 +1,24 @@
[package] [package]
name = "tiff" name = "tiff"
version = "0.7.2" version = "0.9.0"
authors = [ edition = "2018"
"ccgn", resolver = "2"
"bvssvni <bvssvni@gmail.com>",
"nwin", # note: when changed, also update test runner in `.github/workflows/rust.yml`
"TyOverby <ty@pre-alpha.com>", rust-version = "1.61.0"
"HeroicKatora",
"Calum",
"CensoredUsername <cens.username@gmail.com>",
"Robzz",
"birktj"]
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]
+3 -9
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,15 +441,14 @@ 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!(),
}); });
} }
// Case 3: There is more than one value, but it fits in the offset field. // Case 3: There is more than one value, but it fits in the offset field.
if value_bytes <= 4 || bigtiff && value_bytes <= 8 { if value_bytes <= 4 || bigtiff && value_bytes <= 8 {
match self.type_ { match self.type_ {
Type::BYTE => return Ok(offset_to_bytes(self.count as usize, self)?), Type::BYTE => return offset_to_bytes(self.count as usize, self),
Type::SBYTE => return Ok(offset_to_sbytes(self.count as usize, self)?), Type::SBYTE => return offset_to_sbytes(self.count as usize, self),
Type::ASCII => { Type::ASCII => {
let mut buf = vec![0; self.count as usize]; let mut buf = vec![0; self.count as usize];
self.r(bo).read_exact(&mut buf)?; self.r(bo).read_exact(&mut buf)?;
@@ -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!(),
} }
} }
+340 -29
View File
@@ -1,11 +1,13 @@
use super::ifd::{Directory, Value}; use super::ifd::{Directory, Value};
use super::stream::{ByteOrder, DeflateReader, JpegReader, LZWReader, PackBitsReader};
use super::tag_reader::TagReader; use super::tag_reader::TagReader;
use super::Limits; use super::{fp_predict_f32, fp_predict_f64, DecodingBuffer, Limits};
use super::{stream::SmartReader, ChunkType}; use super::{stream::SmartReader, ChunkType};
use crate::tags::{CompressionMethod, PhotometricInterpretation, Predictor, SampleFormat, Tag}; use crate::tags::{CompressionMethod, PhotometricInterpretation, Predictor, SampleFormat, Tag};
use crate::{TiffError, TiffFormatError, TiffResult, TiffUnsupportedError}; use crate::{ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError, UsageError};
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
use std::io::{Read, Seek}; use std::io::{self, Cursor, Read, Seek};
use std::sync::Arc;
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct StripDecodeState { pub(crate) struct StripDecodeState {
@@ -17,7 +19,6 @@ pub(crate) struct StripDecodeState {
pub(crate) struct TileAttributes { pub(crate) struct TileAttributes {
pub image_width: usize, pub image_width: usize,
pub image_height: usize, pub image_height: usize,
pub samples_per_pixel: usize,
pub tile_width: usize, pub tile_width: usize,
pub tile_length: usize, pub tile_length: usize,
@@ -31,30 +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 row_samples(&self) -> usize {
self.tile_width * self.samples_per_pixel
}
pub fn tile_samples(&self) -> usize {
self.tile_length * self.tile_width * self.samples_per_pixel
}
fn tile_strip_samples(&self) -> usize {
(self.tile_samples() * self.tiles_across())
- (self.padding_right() * self.tile_length * self.samples_per_pixel)
}
/// Returns the tile offset in the result buffer, counted in samples
pub fn get_offset(&self, tile: usize) -> usize {
let row = tile / self.tiles_across();
let column = tile % self.tiles_across();
(row * self.tile_strip_samples()) + (column * self.row_samples())
}
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();
@@ -87,7 +69,7 @@ pub(crate) struct Image {
pub photometric_interpretation: PhotometricInterpretation, pub photometric_interpretation: PhotometricInterpretation,
pub compression_method: CompressionMethod, pub compression_method: CompressionMethod,
pub predictor: Predictor, pub predictor: Predictor,
pub jpeg_tables: Option<Vec<u8>>, pub jpeg_tables: Option<Arc<Vec<u8>>>,
pub chunk_type: ChunkType, pub chunk_type: ChunkType,
pub strip_decoder: Option<StripDecodeState>, pub strip_decoder: Option<StripDecodeState>,
pub tile_attributes: Option<TileAttributes>, pub tile_attributes: Option<TileAttributes>,
@@ -111,6 +93,11 @@ impl Image {
let width = tag_reader.require_tag(Tag::ImageWidth)?.into_u32()?; let width = tag_reader.require_tag(Tag::ImageWidth)?.into_u32()?;
let height = tag_reader.require_tag(Tag::ImageLength)?.into_u32()?; let height = tag_reader.require_tag(Tag::ImageLength)?.into_u32()?;
if width == 0 || height == 0 {
return Err(TiffError::FormatError(TiffFormatError::InvalidDimensions(
width, height,
)));
}
let photometric_interpretation = tag_reader let photometric_interpretation = tag_reader
.find_tag(Tag::PhotometricInterpretation)? .find_tag(Tag::PhotometricInterpretation)?
@@ -139,7 +126,8 @@ impl Image {
TiffFormatError::InvalidTagValueType(Tag::JPEGTables), TiffFormatError::InvalidTagValueType(Tag::JPEGTables),
)); ));
} }
Some(vec)
Some(Arc::new(vec))
} else { } else {
None None
}; };
@@ -216,7 +204,11 @@ impl Image {
strip_decoder = Some(StripDecodeState { rows_per_strip }); strip_decoder = Some(StripDecodeState { rows_per_strip });
tile_attributes = None; tile_attributes = None;
if chunk_offsets.len() != chunk_bytes.len() { if chunk_offsets.len() != chunk_bytes.len()
|| rows_per_strip == 0
|| u32::try_from(chunk_offsets.len())?
!= height.saturating_sub(1) / rows_per_strip + 1
{
return Err(TiffError::FormatError( return Err(TiffError::FormatError(
TiffFormatError::InconsistentSizesEncountered, TiffFormatError::InconsistentSizesEncountered,
)); ));
@@ -240,7 +232,6 @@ impl Image {
tile_attributes = Some(TileAttributes { tile_attributes = Some(TileAttributes {
image_width: usize::try_from(width)?, image_width: usize::try_from(width)?,
image_height: usize::try_from(height)?, image_height: usize::try_from(height)?,
samples_per_pixel: bits_per_sample.len(),
tile_width, tile_width,
tile_length, tile_length,
}); });
@@ -287,4 +278,324 @@ impl Image {
chunk_bytes, chunk_bytes,
}) })
} }
pub(crate) fn colortype(&self) -> TiffResult<ColorType> {
match self.photometric_interpretation {
PhotometricInterpretation::RGB => match self.bits_per_sample[..] {
[r, g, b] if [r, r] == [g, b] => Ok(ColorType::RGB(r)),
[r, g, b, a] if [r, r, r] == [g, b, a] => Ok(ColorType::RGBA(r)),
// FIXME: We should _ignore_ other components. In particular:
// > Beware of extra components. Some TIFF files may have more components per pixel
// than you think. A Baseline TIFF reader must skip over them gracefully,using the
// values of the SamplesPerPixel and BitsPerSample fields.
// > -- TIFF 6.0 Specification, Section 7, Additional Baseline requirements.
_ => Err(TiffError::UnsupportedError(
TiffUnsupportedError::InterpretationWithBits(
self.photometric_interpretation,
self.bits_per_sample.clone(),
),
)),
},
PhotometricInterpretation::CMYK => match self.bits_per_sample[..] {
[c, m, y, k] if [c, c, c] == [m, y, k] => Ok(ColorType::CMYK(c)),
_ => Err(TiffError::UnsupportedError(
TiffUnsupportedError::InterpretationWithBits(
self.photometric_interpretation,
self.bits_per_sample.clone(),
),
)),
},
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
if self.bits_per_sample.len() == 1 =>
{
Ok(ColorType::Gray(self.bits_per_sample[0]))
}
// TODO: this is bad we should not fail at this point
_ => Err(TiffError::UnsupportedError(
TiffUnsupportedError::InterpretationWithBits(
self.photometric_interpretation,
self.bits_per_sample.clone(),
),
)),
}
}
fn create_reader<'r, R: 'r + Read>(
reader: R,
photometric_interpretation: PhotometricInterpretation,
compression_method: CompressionMethod,
compressed_length: u64,
jpeg_tables: Option<Arc<Vec<u8>>>,
) -> TiffResult<Box<dyn Read + 'r>> {
Ok(match compression_method {
CompressionMethod::None => Box::new(reader),
CompressionMethod::LZW => {
Box::new(LZWReader::new(reader, usize::try_from(compressed_length)?))
}
CompressionMethod::PackBits => Box::new(PackBitsReader::new(reader, compressed_length)),
CompressionMethod::Deflate | CompressionMethod::OldDeflate => {
Box::new(DeflateReader::new(reader))
}
CompressionMethod::ModernJPEG => {
if jpeg_tables.is_some() && compressed_length < 2 {
return Err(TiffError::FormatError(
TiffFormatError::InvalidTagValueType(Tag::JPEGTables),
));
}
let jpeg_reader = JpegReader::new(reader, compressed_length, jpeg_tables)?;
let mut decoder = jpeg::Decoder::new(jpeg_reader);
match photometric_interpretation {
PhotometricInterpretation::RGB => {
decoder.set_color_transform(jpeg::ColorTransform::RGB)
}
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))
}
method => {
return Err(TiffError::UnsupportedError(
TiffUnsupportedError::UnsupportedCompressionMethod(method),
))
}
})
}
pub(crate) fn chunk_file_range(&self, chunk: u32) -> TiffResult<(u64, u64)> {
let file_offset = self
.chunk_offsets
.get(chunk as usize)
.ok_or(TiffError::FormatError(
TiffFormatError::InconsistentSizesEncountered,
))?;
let compressed_bytes =
self.chunk_bytes
.get(chunk as usize)
.ok_or(TiffError::FormatError(
TiffFormatError::InconsistentSizesEncountered,
))?;
Ok((*file_offset, *compressed_bytes))
}
pub(crate) fn chunk_dimensions(&self) -> TiffResult<(u32, u32)> {
match self.chunk_type {
ChunkType::Strip => {
let strip_attrs = self.strip_decoder.as_ref().unwrap();
Ok((self.width, strip_attrs.rows_per_strip))
}
ChunkType::Tile => {
let tile_attrs = self.tile_attributes.as_ref().unwrap();
Ok((
u32::try_from(tile_attrs.tile_width)?,
u32::try_from(tile_attrs.tile_length)?,
))
}
}
}
pub(crate) fn chunk_data_dimensions(&self, chunk_index: u32) -> TiffResult<(u32, u32)> {
let dims = self.chunk_dimensions()?;
match self.chunk_type {
ChunkType::Strip => {
let strip_height_without_padding = chunk_index
.checked_mul(dims.1)
.and_then(|x| self.height.checked_sub(x))
.ok_or(TiffError::UsageError(UsageError::InvalidChunkIndex(
chunk_index,
)))?;
// Ignore potential vertical padding on the bottommost strip
let strip_height = dims.1.min(strip_height_without_padding);
Ok((dims.0, strip_height))
}
ChunkType::Tile => {
let tile_attrs = self.tile_attributes.as_ref().unwrap();
let (padding_right, padding_down) = tile_attrs.get_padding(chunk_index as usize);
let tile_width = tile_attrs.tile_width - padding_right;
let tile_length = tile_attrs.tile_length - padding_down;
Ok((u32::try_from(tile_width)?, u32::try_from(tile_length)?))
}
}
}
pub(crate) fn expand_chunk(
&self,
reader: impl Read,
mut buffer: DecodingBuffer,
output_width: usize,
byte_order: ByteOrder,
chunk_index: u32,
) -> TiffResult<()> {
// Validate that the provided buffer is of the expected type.
let color_type = self.colortype()?;
match (color_type, &buffer) {
(ColorType::RGB(n), _)
| (ColorType::RGBA(n), _)
| (ColorType::CMYK(n), _)
| (ColorType::YCbCr(n), _)
| (ColorType::Gray(n), _)
if usize::from(n) == buffer.byte_len() * 8 => {}
(ColorType::Gray(n), DecodingBuffer::U8(_)) if n < 8 => match self.predictor {
Predictor::None => {}
Predictor::Horizontal => {
return Err(TiffError::UnsupportedError(
TiffUnsupportedError::HorizontalPredictor(color_type),
))
}
Predictor::FloatingPoint => {
return Err(TiffError::UnsupportedError(
TiffUnsupportedError::FloatingPointPredictor(color_type),
));
}
},
(type_, _) => {
return Err(TiffError::UnsupportedError(
TiffUnsupportedError::UnsupportedColorType(type_),
))
}
}
// Validate that the predictor is supported for the sample type.
match (self.predictor, &buffer) {
(Predictor::Horizontal, DecodingBuffer::F32(_))
| (Predictor::Horizontal, DecodingBuffer::F64(_)) => {
return Err(TiffError::UnsupportedError(
TiffUnsupportedError::HorizontalPredictor(color_type),
));
}
(Predictor::FloatingPoint, DecodingBuffer::F32(_))
| (Predictor::FloatingPoint, DecodingBuffer::F64(_)) => {}
(Predictor::FloatingPoint, _) => {
return Err(TiffError::UnsupportedError(
TiffUnsupportedError::FloatingPointPredictor(color_type),
));
}
_ => {}
}
let compressed_bytes =
self.chunk_bytes
.get(chunk_index as usize)
.ok_or(TiffError::FormatError(
TiffFormatError::InconsistentSizesEncountered,
))?;
let byte_len = buffer.byte_len();
let compression_method = self.compression_method;
let photometric_interpretation = self.photometric_interpretation;
let predictor = self.predictor;
let samples = self.bits_per_sample.len();
let chunk_dims = self.chunk_dimensions()?;
let data_dims = self.chunk_data_dimensions(chunk_index)?;
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,
)?;
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 tile = &mut buffer.as_bytes_mut()[..total_samples * byte_len];
reader.read_exact(tile)?;
for row in 0..data_dims.1 as usize {
let row_start = row as usize * output_width as usize * samples;
let row_end = (row + 1) * output_width as usize * samples;
let row = buffer.subrange(row_start..row_end);
super::fix_endianness_and_predict(row, samples, byte_order, predictor);
}
if photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
super::invert_colors(&mut buffer.subrange(0..total_samples), color_type);
}
} else if padding_right > 0 && self.predictor == Predictor::FloatingPoint {
// The floating point predictor shuffles the padding bytes into the encoded output, so
// this case is handled specially when needed.
let mut encoded = vec![0u8; chunk_dims.0 as usize * samples * byte_len];
for row in 0..data_dims.1 as usize {
let row_start = row * output_width as usize * samples;
let row_end = row_start + data_dims.0 as usize * samples;
reader.read_exact(&mut encoded)?;
match buffer.subrange(row_start..row_end) {
DecodingBuffer::F32(buf) => fp_predict_f32(&mut encoded, buf, samples),
DecodingBuffer::F64(buf) => fp_predict_f64(&mut encoded, buf, samples),
_ => unreachable!(),
}
if photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
super::invert_colors(&mut buffer.subrange(row_start..row_end), color_type);
}
}
} else {
for row in 0..data_dims.1 as usize {
let row_start = row * output_width as usize * samples;
let row_end = row_start + data_dims.0 as usize * samples;
let row = &mut buffer.as_bytes_mut()[(row_start * byte_len)..(row_end * byte_len)];
reader.read_exact(row)?;
// Skip horizontal padding
if padding_right > 0 {
let len = u64::try_from(padding_right as usize * samples * byte_len)?;
io::copy(&mut reader.by_ref().take(len), &mut io::sink())?;
}
let mut row = buffer.subrange(row_start..row_end);
super::fix_endianness_and_predict(row.copy(), samples, byte_order, predictor);
if photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
super::invert_colors(&mut row, color_type);
}
}
}
Ok(())
}
} }
+281 -647
View File
File diff suppressed because it is too large. Load diff
+136 -101
View File
@@ -1,6 +1,8 @@
//! All IO functionality needed for TIFF decoding //! All IO functionality needed for TIFF decoding
use std::io::{self, Read, Seek}; use std::convert::TryFrom;
use std::io::{self, BufRead, BufReader, Read, Seek, SeekFrom, Take};
use std::sync::Arc;
/// Byte order of the TIFF file. /// Byte order of the TIFF file.
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
@@ -124,70 +126,60 @@ pub trait EndianReader: Read {
/// ## Deflate Reader /// ## Deflate Reader
/// ///
pub type DeflateReader<'r, R> = flate2::read::ZlibDecoder<&'r mut SmartReader<R>>; pub type DeflateReader<R> = flate2::read::ZlibDecoder<R>;
/// ///
/// ## LZW Reader /// ## LZW Reader
/// ///
/// Reader that decompresses LZW streams /// Reader that decompresses LZW streams
pub struct LZWReader { pub struct LZWReader<R: Read> {
buffer: io::Cursor<Vec<u8>>, reader: BufReader<Take<R>>,
decoder: weezl::decode::Decoder,
} }
impl LZWReader { impl<R: Read> LZWReader<R> {
/// Wraps a reader /// Wraps a reader
pub fn new<R>( pub fn new(reader: R, compressed_length: usize) -> LZWReader<R> {
reader: &mut SmartReader<R>, Self {
compressed_length: usize, reader: BufReader::with_capacity(
max_uncompressed_length: usize, (32 * 1024).min(compressed_length),
) -> io::Result<(usize, LZWReader)> reader.take(u64::try_from(compressed_length).unwrap()),
where ),
R: Read + Seek, decoder: weezl::decode::Decoder::with_tiff_size_switch(weezl::BitOrder::Msb, 8),
{ }
let mut compressed = vec![0; compressed_length as usize]; }
reader.read_exact(&mut compressed[..])?; }
let mut uncompressed = vec![0; max_uncompressed_length];
let mut decoder = weezl::decode::Decoder::with_tiff_size_switch(weezl::BitOrder::Msb, 8);
let mut bytes_read = 0; impl<R: Read> Read for LZWReader<R> {
let mut bytes_written = 0; fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
loop {
while bytes_written < max_uncompressed_length { let result = self.decoder.decode_bytes(self.reader.fill_buf()?, buf);
let result = decoder.decode_bytes( self.reader.consume(result.consumed_in);
&compressed[bytes_read..],
&mut uncompressed[bytes_written..],
);
bytes_read += result.consumed_in;
bytes_written += result.consumed_out;
match result.status { match result.status {
Ok(weezl::LzwStatus::Ok) => {} Ok(weezl::LzwStatus::Ok) => {
Ok(weezl::LzwStatus::Done) => break, if result.consumed_out == 0 {
continue;
} else {
return Ok(result.consumed_out);
}
}
Ok(weezl::LzwStatus::NoProgress) => { Ok(weezl::LzwStatus::NoProgress) => {
assert_eq!(result.consumed_in, 0);
assert_eq!(result.consumed_out, 0);
assert!(self.reader.buffer().is_empty());
return Err(io::Error::new( return Err(io::Error::new(
io::ErrorKind::UnexpectedEof, io::ErrorKind::UnexpectedEof,
"no lzw end code found", "no lzw end code found",
)) ));
}
Ok(weezl::LzwStatus::Done) => {
return Ok(result.consumed_out);
} }
Err(err) => return Err(io::Error::new(io::ErrorKind::InvalidData, err)), Err(err) => return Err(io::Error::new(io::ErrorKind::InvalidData, err)),
} }
} }
uncompressed.resize(bytes_written, 0);
Ok((
uncompressed.len(),
LZWReader {
buffer: io::Cursor::new(uncompressed),
},
))
}
}
impl Read for LZWReader {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.buffer.read(buf)
} }
} }
@@ -196,8 +188,13 @@ impl Read for LZWReader {
/// ///
pub(crate) struct JpegReader { pub(crate) struct JpegReader {
jpeg_tables: Option<Arc<Vec<u8>>>,
buffer: io::Cursor<Vec<u8>>, buffer: io::Cursor<Vec<u8>>,
offset: usize,
} }
impl JpegReader { impl JpegReader {
/// Constructs new JpegReader wrapping a SmartReader. /// Constructs new JpegReader wrapping a SmartReader.
/// Because JPEG compression in TIFF allows to save quantization and/or huffman tables in one /// Because JPEG compression in TIFF allows to save quantization and/or huffman tables in one
@@ -210,25 +207,22 @@ impl JpegReader {
/// bytes of the remaining JPEG data is removed because it follows `jpeg_tables`. /// 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, /// 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`). /// this has to be removed as well (last two bytes of `jpeg_tables`).
pub fn new<R: Read>(
pub fn new<R>( mut reader: R,
reader: &mut SmartReader<R>, length: u64,
length: u32, jpeg_tables: Option<Arc<Vec<u8>>>,
jpeg_tables: &Option<Vec<u8>>, ) -> io::Result<JpegReader> {
) -> io::Result<JpegReader>
where
R: Read + Seek,
{
// Read jpeg image data // Read jpeg image data
let mut segment = vec![0; length as usize]; let mut segment = vec![0; length as usize];
reader.read_exact(&mut segment[..])?; reader.read_exact(&mut segment[..])?;
match jpeg_tables { match jpeg_tables {
Some(tables) => { Some(jpeg_tables) => {
assert!( assert!(
tables.len() >= 2, jpeg_tables.len() >= 2,
"jpeg_tables, if given, must be at least 2 bytes long. Got {:?}", "jpeg_tables, if given, must be at least 2 bytes long. Got {:?}",
tables jpeg_tables
); );
assert!( assert!(
@@ -237,17 +231,20 @@ impl JpegReader {
length length
); );
let mut jpeg_data = tables.clone(); let mut buffer = io::Cursor::new(segment);
let truncated_length = jpeg_data.len() - 2; // Skip the first two bytes (marker bytes)
jpeg_data.truncate(truncated_length); buffer.seek(SeekFrom::Start(2))?;
jpeg_data.extend_from_slice(&segment[2..]);
Ok(JpegReader { Ok(JpegReader {
buffer: io::Cursor::new(jpeg_data), buffer,
jpeg_tables: Some(jpeg_tables),
offset: 0,
}) })
} }
None => Ok(JpegReader { None => Ok(JpegReader {
buffer: io::Cursor::new(segment), buffer: io::Cursor::new(segment),
jpeg_tables: None,
offset: 0,
}), }),
} }
} }
@@ -256,7 +253,31 @@ impl JpegReader {
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> {
self.buffer.read(buf) 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)
} }
} }
@@ -264,56 +285,70 @@ impl Read for JpegReader {
/// ## PackBits Reader /// ## PackBits Reader
/// ///
/// Reader that unpacks Apple's `PackBits` format enum PackBitsReaderState {
pub struct PackBitsReader { Header,
buffer: io::Cursor<Vec<u8>>, Literal,
Repeat { value: u8 },
} }
impl PackBitsReader { /// Reader that unpacks Apple's `PackBits` format
pub struct PackBitsReader<R: Read> {
reader: Take<R>,
state: PackBitsReaderState,
count: usize,
}
impl<R: Read> PackBitsReader<R> {
/// Wraps a reader /// Wraps a reader
pub fn new<R: Read + Seek>( pub fn new(reader: R, length: u64) -> Self {
mut reader: R, Self {
length: usize, reader: reader.take(length),
) -> io::Result<(usize, PackBitsReader)> { state: PackBitsReaderState::Header,
let mut buffer = Vec::new(); count: 0,
let mut header: [u8; 1] = [0]; }
let mut data: [u8; 1] = [0]; }
}
let mut bytes_read = 0;
while bytes_read < length {
reader.read_exact(&mut header)?;
bytes_read += 1;
impl<R: Read> Read for PackBitsReader<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
while let PackBitsReaderState::Header = self.state {
if self.reader.limit() == 0 {
return Ok(0);
}
let mut header: [u8; 1] = [0];
self.reader.read_exact(&mut header)?;
let h = header[0] as i8; let h = header[0] as i8;
if h >= -127 && h <= -1 { if h >= -127 && h <= -1 {
let new_len = buffer.len() + (1 - h as isize) as usize; let mut data: [u8; 1] = [0];
reader.read_exact(&mut data)?; self.reader.read_exact(&mut data)?;
buffer.resize(new_len, data[0]); self.state = PackBitsReaderState::Repeat { value: data[0] };
bytes_read += 1; self.count = (1 - h as isize) as usize;
} else if h >= 0 { } else if h >= 0 {
let num_vals = h as usize + 1; self.state = PackBitsReaderState::Literal;
let start = buffer.len(); self.count = h as usize + 1;
buffer.resize(start + num_vals, 0);
reader.read_exact(&mut buffer[start..])?;
bytes_read += num_vals
} else { } else {
// h = -128 is a no-op. // h = -128 is a no-op.
} }
} }
Ok(( let length = buf.len().min(self.count);
buffer.len(), let actual = match self.state {
PackBitsReader { PackBitsReaderState::Literal => self.reader.read(&mut buf[..length])?,
buffer: io::Cursor::new(buffer), PackBitsReaderState::Repeat { value } => {
}, for b in &mut buf[..length] {
)) *b = value;
} }
}
impl Read for PackBitsReader { length
#[inline] }
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { PackBitsReaderState::Header => unreachable!(),
self.buffer.read(buf) };
self.count -= actual;
if self.count == 0 {
self.state = PackBitsReaderState::Header;
}
return Ok(actual);
} }
} }
@@ -386,7 +421,7 @@ mod test {
let encoded_len = encoded.len(); let encoded_len = encoded.len();
let buff = io::Cursor::new(encoded); let buff = io::Cursor::new(encoded);
let (_, mut decoder) = PackBitsReader::new(buff, encoded_len).unwrap(); let mut decoder = PackBitsReader::new(buff, encoded_len as u64);
let mut decoded = Vec::new(); let mut decoded = Vec::new();
decoder.read_to_end(&mut decoded).unwrap(); decoder.read_to_end(&mut decoded).unwrap();
+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];
}
+1 -1
View File
@@ -53,7 +53,7 @@ impl Compression for Deflate {
impl CompressionAlgorithm for Deflate { impl CompressionAlgorithm for Deflate {
fn write_to<W: Write>(&mut self, writer: &mut W, bytes: &[u8]) -> Result<u64, io::Error> { fn write_to<W: Write>(&mut self, writer: &mut W, bytes: &[u8]) -> Result<u64, io::Error> {
let mut encoder = ZlibEncoder::new(writer, self.level); let mut encoder = ZlibEncoder::new(writer, self.level);
encoder.write_all(&bytes)?; encoder.write_all(bytes)?;
encoder.try_finish()?; encoder.try_finish()?;
Ok(encoder.total_out()) Ok(encoder.total_out())
} }
+2 -2
View File
@@ -157,7 +157,7 @@ mod tests {
let mut compressed_data = Vec::<u8>::new(); let mut compressed_data = Vec::<u8>::new();
let mut writer = Cursor::new(&mut compressed_data); let mut writer = Cursor::new(&mut compressed_data);
Packbits::default() Packbits::default()
.write_to(&mut writer, &UNCOMPRESSED_DATA) .write_to(&mut writer, UNCOMPRESSED_DATA)
.unwrap(); .unwrap();
assert_eq!(compressed_data, EXPECTED_COMPRESSED_DATA); assert_eq!(compressed_data, EXPECTED_COMPRESSED_DATA);
} }
@@ -207,7 +207,7 @@ mod tests {
let mut compressed_data = Vec::<u8>::new(); let mut compressed_data = Vec::<u8>::new();
let mut writer = Cursor::new(&mut compressed_data); let mut writer = Cursor::new(&mut compressed_data);
Packbits::default() Packbits::default()
.write_to(&mut writer, &TEST_DATA) .write_to(&mut writer, TEST_DATA)
.unwrap(); .unwrap();
assert_eq!(compressed_data, EXPECTED_COMPRESSED_DATA); assert_eq!(compressed_data, EXPECTED_COMPRESSED_DATA);
} }
+7
View File
@@ -13,6 +13,7 @@ use std::{
use crate::{ use crate::{
error::TiffResult, error::TiffResult,
tags::{CompressionMethod, ResolutionUnit, Tag}, tags::{CompressionMethod, ResolutionUnit, Tag},
TiffError, TiffFormatError,
}; };
pub mod colortype; pub mod colortype;
@@ -349,6 +350,12 @@ impl<'a, W: 'a + Write + Seek, T: ColorType, K: TiffKind, D: Compression>
height: u32, height: u32,
compression: D, compression: D,
) -> TiffResult<Self> { ) -> TiffResult<Self> {
if width == 0 || height == 0 {
return Err(TiffError::FormatError(TiffFormatError::InvalidDimensions(
width, height,
)));
}
let row_samples = u64::from(width) * u64::try_from(<T>::BITS_PER_SAMPLE.len())?; let row_samples = u64::from(width) * u64::try_from(<T>::BITS_PER_SAMPLE.len())?;
let row_bytes = row_samples * u64::from(<T::Inner>::BYTE_LEN); let row_bytes = row_samples * u64::from(<T::Inner>::BYTE_LEN);
+73 -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,
@@ -56,6 +61,7 @@ pub enum TiffFormatError {
actual_samples: usize, actual_samples: usize,
required_samples: usize, required_samples: usize,
}, },
InvalidDimensions(u32, u32),
InvalidTag, InvalidTag,
InvalidTagValueType(Tag), InvalidTagValueType(Tag),
RequiredTagNotFound(Tag), RequiredTagNotFound(Tag),
@@ -67,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 {
@@ -100,6 +104,7 @@ impl fmt::Display for TiffFormatError {
actual_samples, required_samples actual_samples, required_samples
) )
} }
InvalidDimensions(width, height) => write!(fmt, "Invalid dimensions: {}x{}.", width, height),
InvalidTag => write!(fmt, "Image contains invalid tag."), InvalidTag => write!(fmt, "Image contains invalid tag."),
InvalidTagValueType(ref tag) => { InvalidTagValueType(ref tag) => {
write!(fmt, "Tag `{:?}` did not have the expected value type.", tag) write!(fmt, "Tag `{:?}` did not have the expected value type.", tag)
@@ -119,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),
} }
} }
} }
@@ -133,7 +138,9 @@ 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),
HorizontalPredictor(ColorType), HorizontalPredictor(ColorType),
InterpretationWithBits(PhotometricInterpretation, Vec<u8>), InterpretationWithBits(PhotometricInterpretation, Vec<u8>),
UnknownInterpretation, UnknownInterpretation,
@@ -145,15 +152,19 @@ 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 {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
use self::TiffUnsupportedError::*; use self::TiffUnsupportedError::*;
match *self { match *self {
FloatingPointPredictor(color_type) => write!(
fmt,
"Floating point predictor for {:?} is unsupported.",
color_type
),
HorizontalPredictor(color_type) => write!( HorizontalPredictor(color_type) => write!(
fmt, fmt,
"Horizontal predictor for {:?} is unsupported.", "Horizontal predictor for {:?} is unsupported.",
@@ -188,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)
}
} }
} }
} }
@@ -199,6 +219,7 @@ impl fmt::Display for TiffUnsupportedError {
#[derive(Debug)] #[derive(Debug)]
pub enum UsageError { pub enum UsageError {
InvalidChunkType(ChunkType, ChunkType), InvalidChunkType(ChunkType, ChunkType),
InvalidChunkIndex(u32),
} }
impl fmt::Display for UsageError { impl fmt::Display for UsageError {
@@ -212,6 +233,7 @@ impl fmt::Display for UsageError {
expected, actual expected, actual
) )
} }
InvalidChunkIndex(index) => write!(fmt, "Image chunk index ({}) requested.", index),
} }
} }
} }
@@ -284,6 +306,12 @@ impl From<TiffUnsupportedError> for TiffError {
} }
} }
impl From<UsageError> for TiffError {
fn from(err: UsageError) -> TiffError {
TiffError::UsageError(err)
}
}
impl From<std::num::TryFromIntError> for TiffError { impl From<std::num::TryFromIntError> for TiffError {
fn from(_err: std::num::TryFromIntError) -> TiffError { fn from(_err: std::num::TryFromIntError) -> TiffError {
TiffError::IntSizeError TiffError::IntSizeError
@@ -300,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>;
+4 -1
View File
@@ -4,7 +4,7 @@
//! lossless and lossy compression. //! lossless and lossy compression.
//! //!
//! # Related Links //! # Related Links
//! * <https://www.adobe.io/open/standards/TIFF.html> - The TIFF specification //! * <https://web.archive.org/web/20210108073850/https://www.adobe.io/open/standards/TIFF.html> - The TIFF specification
extern crate jpeg; extern crate jpeg;
extern crate weezl; extern crate weezl;
@@ -37,4 +37,7 @@ pub enum ColorType {
/// Pixel is CMYK /// Pixel is CMYK
CMYK(u8), CMYK(u8),
/// Pixel is YCbCr
YCbCr(u8),
} }
+2 -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!(),
} }
} }
} }
@@ -214,6 +211,7 @@ tags! {
pub enum Predictor(u16) { pub enum Predictor(u16) {
None = 1, None = 1,
Horizontal = 2, Horizontal = 2,
FloatingPoint = 3,
} }
} }
+17 -36
View File
@@ -275,7 +275,7 @@ fn test_tiled_incremental() {
assert_eq!(tiles as usize, sums.len()); assert_eq!(tiles as usize, sums.len());
for tile in 0..tiles { for tile in 0..tiles {
match decoder.read_tile().unwrap() { match decoder.read_chunk(tile).unwrap() {
DecodingResult::U8(res) => { DecodingResult::U8(res) => {
let sum: u64 = res.into_iter().map(<u64>::from).sum(); let sum: u64 = res.into_iter().map(<u64>::from).sum();
assert_eq!(sum, sums[tile as usize]); assert_eq!(sum, sums[tile as usize]);
@@ -324,7 +324,7 @@ fn test_too_many_value_bytes() {
} }
#[test] #[test]
fn test_bad_strip_count() { fn fuzzer_testcase5() {
let image = [ let image = [
73, 73, 42, 0, 8, 0, 0, 0, 8, 0, 0, 1, 4, 0, 1, 0, 0, 0, 100, 0, 0, 0, 1, 1, 4, 0, 1, 0, 0, 73, 73, 42, 0, 8, 0, 0, 0, 8, 0, 0, 1, 4, 0, 1, 0, 0, 0, 100, 0, 0, 0, 1, 1, 4, 0, 1, 0, 0,
0, 158, 0, 0, 251, 3, 1, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, 6, 1, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 158, 0, 0, 251, 3, 1, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, 6, 1, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0,
@@ -335,12 +335,7 @@ fn test_bad_strip_count() {
let mut decoder = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap(); let mut decoder = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap();
let err = decoder.read_image().unwrap_err(); let _ = decoder.read_image().unwrap_err();
match err {
tiff::TiffError::IntSizeError => {}
unexpected => panic!("Unexpected error {}", unexpected),
}
} }
#[test] #[test]
@@ -385,18 +380,11 @@ fn oom() {
0, 0, 0, 40, 0, 0, 0, 23, 1, 4, 0, 1, 0, 0, 0, 178, 48, 178, 178, 178, 178, 162, 178, 0, 0, 0, 40, 0, 0, 0, 23, 1, 4, 0, 1, 0, 0, 0, 178, 48, 178, 178, 178, 178, 162, 178,
]; ];
let mut decoder = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap(); let _ = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap_err();
let err = decoder.read_image().unwrap_err();
match err {
tiff::TiffError::LimitsExceeded => {}
unexpected => panic!("Unexpected error {}", unexpected),
}
} }
#[test] #[test]
fn oom_2() { fn fuzzer_testcase4() {
let image = [ let image = [
73, 73, 42, 0, 8, 0, 0, 0, 8, 0, 0, 1, 4, 0, 1, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 40, 1, 0, 0, 73, 73, 42, 0, 8, 0, 0, 0, 8, 0, 0, 1, 4, 0, 1, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 40, 1, 0, 0,
0, 158, 0, 0, 251, 3, 1, 3, 0, 1, 0, 0, 0, 5, 0, 0, 0, 6, 1, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 158, 0, 0, 251, 3, 1, 3, 0, 1, 0, 0, 0, 5, 0, 0, 0, 6, 1, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0,
@@ -404,14 +392,7 @@ fn oom_2() {
0, 0, 0, 40, 0, 0, 0, 23, 1, 4, 0, 1, 0, 0, 0, 48, 178, 178, 178, 0, 1, 0, 13, 13, 0, 0, 0, 40, 0, 0, 0, 23, 1, 4, 0, 1, 0, 0, 0, 48, 178, 178, 178, 0, 1, 0, 13, 13,
]; ];
let mut decoder = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap(); let _ = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap_err();
let err = decoder.read_image().unwrap_err();
match err {
tiff::TiffError::LimitsExceeded => {}
unexpected => panic!("Unexpected error {}", unexpected),
}
} }
#[test] #[test]
@@ -432,9 +413,6 @@ fn fuzzer_testcase2() {
#[test] #[test]
fn invalid_jpeg_tag_2() { fn invalid_jpeg_tag_2() {
use tiff::tags;
use tiff::{TiffError, TiffFormatError};
let image = [ let image = [
73, 73, 42, 0, 8, 0, 0, 0, 16, 0, 254, 0, 4, 0, 1, 0, 0, 0, 0, 0, 0, 242, 0, 1, 4, 0, 1, 0, 73, 73, 42, 0, 8, 0, 0, 0, 16, 0, 254, 0, 4, 0, 1, 0, 0, 0, 0, 0, 0, 242, 0, 1, 4, 0, 1, 0,
0, 0, 0, 129, 16, 0, 1, 1, 4, 0, 1, 0, 0, 0, 214, 0, 0, 248, 253, 1, 3, 0, 1, 0, 0, 0, 64, 0, 0, 0, 129, 16, 0, 1, 1, 4, 0, 1, 0, 0, 0, 214, 0, 0, 248, 253, 1, 3, 0, 1, 0, 0, 0, 64,
@@ -446,14 +424,7 @@ fn invalid_jpeg_tag_2() {
0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 36, 73, 73, 0, 42, 36, 36, 36, 36, 0, 0, 8, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 36, 73, 73, 0, 42, 36, 36, 36, 36, 0, 0, 8, 0,
]; ];
let mut decoder = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap(); let _ = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap_err();
let err = decoder.read_image().unwrap_err();
match err {
TiffError::FormatError(TiffFormatError::InvalidTagValueType(tags::Tag::JPEGTables)) => {}
unexpected => panic!("Unexpected error {}", unexpected),
}
} }
#[test] #[test]
@@ -496,3 +467,13 @@ fn timeout() {
fn test_no_rows_per_strip() { fn test_no_rows_per_strip() {
test_image_sum_u8("no_rows_per_strip.tiff", ColorType::RGB(8), 99448840); test_image_sum_u8("no_rows_per_strip.tiff", ColorType::RGB(8), 99448840);
} }
#[test]
fn test_predictor_3_rgb_f32() {
test_image_sum_f32("predictor-3-rgb-f32.tif", ColorType::RGB(32), 54004.33);
}
#[test]
fn test_predictor_3_gray_f32() {
test_image_sum_f32("predictor-3-gray-f32.tif", ColorType::Gray(32), 20008.275);
}
+9 -1
View File
@@ -94,6 +94,7 @@ fn test_encode_ifd() {
{ {
let mut tiff = TiffEncoder::new(&mut data).unwrap(); let mut tiff = TiffEncoder::new(&mut data).unwrap();
let mut image_encoder = tiff.new_image::<colortype::Gray8>(1, 1).unwrap(); let mut image_encoder = tiff.new_image::<colortype::Gray8>(1, 1).unwrap();
image_encoder.write_strip(&[1]).unwrap();
let encoder = image_encoder.encoder(); let encoder = image_encoder.encoder();
// Use the "reusable" tags section as per the TIFF6 spec // Use the "reusable" tags section as per the TIFF6 spec
@@ -263,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>;
@@ -320,6 +326,7 @@ fn test_multiple_byte() {
{ {
let mut tiff = TiffEncoder::new(&mut data).unwrap(); let mut tiff = TiffEncoder::new(&mut data).unwrap();
let mut image_encoder = tiff.new_image::<colortype::Gray8>(1, 1).unwrap(); let mut image_encoder = tiff.new_image::<colortype::Gray8>(1, 1).unwrap();
image_encoder.write_strip(&[1]).unwrap();
let encoder = image_encoder.encoder(); let encoder = image_encoder.encoder();
encoder.write_tag(Tag::Unknown(65000), &[1_u8][..]).unwrap(); encoder.write_tag(Tag::Unknown(65000), &[1_u8][..]).unwrap();
@@ -360,6 +367,7 @@ fn test_signed() {
{ {
let mut tiff = TiffEncoder::new(&mut data).unwrap(); let mut tiff = TiffEncoder::new(&mut data).unwrap();
let mut image_encoder = tiff.new_image::<colortype::Gray8>(1, 1).unwrap(); let mut image_encoder = tiff.new_image::<colortype::Gray8>(1, 1).unwrap();
image_encoder.write_strip(&[1]).unwrap();
let encoder = image_encoder.encoder(); let encoder = image_encoder.encoder();
//Use the "reusable" tags section as per the TIFF6 spec //Use the "reusable" tags section as per the TIFF6 spec
@@ -512,7 +520,7 @@ fn test_rows_per_strip() {
for i in 0..50 { for i in 0..50 {
let img2 = [i; 2 * 100]; let img2 = [i; 2 * 100];
match decoder.read_strip().unwrap() { match decoder.read_chunk(i as u32).unwrap() {
DecodingResult::U8(data) => assert_eq!(&img2[..], &data[..]), DecodingResult::U8(data) => assert_eq!(&img2[..], &data[..]),
other => panic!("Incorrect strip type {:?}", other), other => panic!("Incorrect strip type {:?}", other),
} }
Binary file not shown.
Binary file not shown.
Binary file not shown.