Compare commits

..
120 Commits
Author SHA1 Message Date
Andreas Molzer b8459f413a Add fuzzing work and bugs to release notes 2021-09-22 01:20:00 +02:00
Andreas Molzer 26e760e303 Add notes on MSRV 2021-09-22 01:13:10 +02:00
Andreas Molzer a4878e9658 Release notes for 0.7.1 2021-09-22 01:05:25 +02:00
HeroicKatora 73285481cd Merge pull request #141 from 5225225/oom
Fix out of memory issues / misc other decoding bugs.
2021-09-18 15:51:33 +02:00
5225225 b065539c07 Add fuzzing instructions to readme, bump CI version to 1.48 2021-09-18 14:42:53 +01:00
5225225 1c5800daa0 Infinitely try to read off images rather than only trying 128 2021-09-18 12:14:17 +01:00
5225225 885d762def Implement cycle checking in IFD offsets 2021-09-08 18:08:10 +01:00
5225225 2c9e02d347 Merge remote-tracking branch 'origin/master' into oom 2021-09-06 17:53:16 +01:00
Jonathan Behrens b318481c55 Merge pull request #143 from Christopher22/reset_tag_values
Do not carry tag values from previous images
2021-08-15 11:43:44 -04:00
5225225 1774b362d3 x 2021-07-15 18:08:40 +01:00
Jonathan Behrens f2fec92283 Merge pull request #146 from fintelia/encode-signed-int
Support encoding signed integer formats
2021-06-20 18:39:08 -04:00
Jonathan Behrens be0619d6a1 Support encoding signed integer formats 2021-06-20 14:33:13 -04:00
Tomáš Král e92cf219a9 Fix the mandrill.tiff regression (#145)
* fix mandrill.tiff regression from upstream

* use contains_key(), added StripTileTagConflict error

* updated test_div_zero, added test_no_rows_per_strip
2021-06-17 09:22:07 -04:00
Christopher Gundler 3e5f977460 Do not carry tag values from previous images 2021-06-14 20:21:51 +02:00
5225225 f0666aebbf Fix overflow when calculating strip height for multiple images 2021-05-29 22:52:42 +01:00
5225225 65a1fbe8ae Test for short JPEG tag 2021-05-29 22:12:35 +01:00
5225225 03b6ed413b Fix out of bounds subtraction with invalid JPEG table 2021-05-29 22:12:35 +01:00
5225225 5b7814c994 Check for OOM when creating LZWReader 2021-05-29 22:12:35 +01:00
5225225 a15d619471 Fix OOM in Jpeg decoding buffer allocation 2021-05-29 22:12:35 +01:00
HeroicKatora 458a713827 Merge pull request #140 from 5225225/fuzzer
Add fuzzer with 1MB size limits decoding image
2021-05-29 23:12:04 +02:00
5225225 2f6ad07f28 Try to read multiple images in fuzzer 2021-05-29 22:00:36 +01:00
HeroicKatora 153ed48ee7 Merge pull request #136 from TomasKralCZ/padding_skip
remove padding_buffer
2021-05-29 15:13:20 +02:00
5225225 420667955a Add fuzzer with 1MB size limits decoding image 2021-05-29 13:18:35 +01:00
HeroicKatora 3bf19822a2 Merge pull request #139 from 5225225/value-bytes-overflow
Check for various arithmetic overflows
2021-05-29 13:48:03 +02:00
5225225 2bb37a4ecd Do checked multiplication for samples per strip 2021-05-29 12:37:15 +01:00
5225225 b41ac105ab Use checked multiplication when calculating buffer size 2021-05-29 12:36:59 +01:00
5225225 1e10b9803b Add check for an invalid strip count 2021-05-29 12:36:50 +01:00
5225225 2f4e491874 Check for where tag count * tag size overflows 2021-05-28 20:32:30 +01:00
HeroicKatora 285b881e27 Merge pull request #138 from 5225225/divide_by_zero
Fix division by zero issue with malformed file
2021-05-28 19:59:37 +02:00
5225225 bdfe2f55fd Check error returned from parsing invalid image 2021-05-28 15:46:55 +01:00
5225225 eda5a8ac30 Fix division by zero issue with malformed file 2021-05-28 14:44:50 +01:00
HeroicKatora bd1e37d78b Merge pull request #130 from phil-opp/release
Prepare 0.7.0 release
2021-05-25 21:03:44 +02:00
Andreas Molzer e8bc935618 Mention changed interpretation of MinIsWhite 2021-05-25 20:27:46 +02:00
Philipp Oppermann 150ff8303b Add changes of #132 and #134 2021-05-25 20:19:01 +02:00
Philipp Oppermann e44b98ddf9 Prepare 0.7.0 release 2021-05-25 20:19:01 +02:00
Tomáš Král ad0f75222a remove padding_buffer 2021-05-21 18:06:05 +02:00
HeroicKatora 4df26011d9 Merge pull request #133 from TomasKralCZ/inversion
expand_strip() and expand_tile() cleanup
2021-05-18 18:37:25 +02:00
Tomáš Král 4dc7c2a986 add comment in expand_strip() 2021-05-18 18:31:17 +02:00
Tomáš Král b7c4f668ae remove redundant subrange and outdated comments 2021-05-16 12:00:15 +02:00
Jonathan Behrens 9fe9b2e0a0 Merge pull request #134 from RReverser/remove-inflate-error
Remove InflateError
2021-05-15 16:50:36 -04:00
Ingvar Stepanyan b7e8f210d1 Remove InflateError
It was left in but marked as deprecated as part of #132 to avoid breaking API changes.

However, #130 is going to mark a 0.7.0 release, so we can remove it altogether for simplicity.
2021-05-15 21:43:42 +01:00
Tomáš Král 371a9b53c3 fix u64::MAX error on older compilers 2021-05-15 21:31:11 +02:00
Tomáš Král d0e4f37a28 implement subrange for DecodingBuffer 2021-05-15 21:14:00 +02:00
Tomáš Král 46c7efd836 remove unneeded mutability 2021-05-15 19:55:48 +02:00
Tomáš Král 65832e01bd Merge branch 'master' into inversion 2021-05-15 19:49:01 +02:00
Tomáš Král b9590e0842 remove WhiteIsZero i8 test 2021-05-15 18:15:24 +02:00
Tomáš Král 521b58dc07 expand_strip and expand_tile cleanup 2021-05-15 18:12:34 +02:00
Jonathan Behrens e70121efbb Merge pull request #132 from RReverser/deflate-improvements
Improve deflate support
2021-05-14 17:58:11 -04:00
Ingvar Stepanyan a6fb855121 Improve deflate support
- Switch to streaming decompression via `flate2`. Aside from performance improvements and lower RAM consumption, this fixes a bug where `max_uncompressed_length` was precalculated for a single tile but then used as a hard limit on the whole data, failing to decompress any tiled images.
 - Deprecate `InflateError` - this was used for wrapping `miniz_oxide` raw status, but `flate2` already reports them as `io::Error` instead. This makes errors from both Lzw and Deflate modes more consistent - they're all now just `io::Error`.
 - Add support for new `Deflate` tag in addition to `OldDeflate` - format-wise it's same Zlib, and on practice decodes successfully with the same underlying code.
 - Slightly simplify partial reads from the `Reader` by using the `std::io::copy` helper instead of doing it manually.

Fixes #131.
2021-05-08 15:34:14 +01:00
HeroicKatora a874b9c3a5 Merge pull request #125 from TomasKralCZ/tiles
Added basic tile decoding
2021-04-18 18:26:53 +02:00
Tomáš Král d4ce174236 Merge remote-tracking branch 'upstream/master' into tiles 2021-04-14 13:32:56 +02:00
HeroicKatora a43aa1fbc7 Merge pull request #126 from fintelia/better-decompress
Simplify de-compression logic
2021-04-12 18:36:07 +02:00
HeroicKatora 69c34ac33c Merge pull request #128 from fintelia/simplify-expand3
Simplify expand_strip
2021-04-12 18:35:48 +02:00
Tomáš Král 00e0eb9006 incremental tile decoding, refactoring 2021-04-12 15:09:39 +02:00
Tomáš Král ebd0e266f7 Merge remote-tracking branch 'upstream/master' into tiles 2021-04-12 14:52:38 +02:00
HeroicKatora 9b94e9de88 Merge pull request #122 from phil-opp/encode-bigtiff
Add support for encoding BigTiff
2021-04-12 12:46:59 +02:00
Philipp Oppermann 47dfa0aa3e Merge branch 'master' into encode-bigtiff 2021-04-12 11:34:41 +02:00
Jonathan Behrens 1eb9c4fab7 fix borrow check issue on rustc 1.34.2 2021-04-10 21:45:04 -04:00
Jonathan Behrens 201b95f749 Improve expand_strip and remove read_{u16, u32, ...}_into 2021-04-10 21:34:57 -04:00
Jonathan Behrens 7641dcbd3f Refactoring to simplify expand_strip function 2021-04-10 15:02:16 -04:00
Jonathan Behrens 60c6e0cd90 Simplify de-compression logic 2021-04-10 14:49:04 -04:00
Tomáš Král 65a1c553af TileAttributes doc comments, padding buffer limit check 2021-04-06 20:42:48 +02:00
Tomáš Král fddc048849 changed test photos to avoid coyright, added whiteiszero test 2021-04-06 13:58:33 +02:00
Tomáš Král 68f2182918 fixed WhiteIsZero processing 2021-04-04 19:34:14 +02:00
Tomáš Král b2a7e35c85 added basic tiled-based decoding 2021-04-04 18:15:28 +02:00
HeroicKatora 91fe529859 Merge pull request #123 from kalcutter/add-s-minmax-samplevalue-tags
Add `SMinSampleValue` and `SMaxSampleValue` tags
2021-04-03 18:53:27 +02:00
Kal Conley 5ca2730416 Add SMinSampleValue and SMaxSampleValue tags 2021-04-03 18:45:01 +02:00
HeroicKatora 8fcacc829f Merge pull request #119 from kalcutter/missing-tags-and-i64
Implement missing tag types and signed 64-bit support
2021-03-24 12:25:01 +01:00
Philipp Oppermann 59d0b7389d Run rustfmt 2021-03-24 11:11:56 +01:00
Philipp Oppermann 0ac444b63f Improve documentation 2021-03-24 11:10:42 +01:00
Philipp Oppermann ce1ee6776f Move TiffValue trait to separate module 2021-03-24 10:47:23 +01:00
Philipp Oppermann 29a020e5ec Clarify TODO 2021-03-23 14:44:39 +01:00
Philipp Oppermann 9021985477 Provide documentation for TiffKind helper trait 2021-03-23 14:42:05 +01:00
Philipp Oppermann d4c958f915 Add a test for bigtiff encoding 2021-03-23 14:29:56 +01:00
Philipp Oppermann dc04282290 Fix: Use the correctly parametrized directory and image encoder types 2021-03-23 14:29:45 +01:00
Philipp Oppermann 4cfed1c7b1 Make encoders generic to support BigTiff too 2021-03-23 13:49:54 +01:00
Philipp Oppermann 745ed62808 Change associated const types of TiffValue to usize/u8
This allows us to reuse the trait for BigTiff encoding. It also makes things safer for normal Tiff since we properly error on 32 bit overflows now.
2021-03-23 10:56:33 +01:00
HeroicKatora 6fe9b78509 Merge pull request #121 from emmanuelantony2000/ascii_tag_validation
Validation for ASCII tag type
2021-03-20 13:28:32 +01:00
Emmanuel Antony f5754159cb Modified the validation for ascii tag type 2021-03-20 08:47:47 +05:30
HeroicKatora 53d0256585 Merge pull request #120 from atouchet/ga
Replace Travis badge with GitHub Actions
2021-03-20 00:45:18 +01:00
Alex Touchet 09aa28d699 Replace Travis badge with GitHub Actions 2021-03-19 16:33:30 -07:00
Kal Conley d086e289dd Implement missing tag types and signed 64-bit support 2021-03-17 01:24:32 +01:00
Jonathan Behrens 2cba04dfe7 Merge pull request #118 from satarsa/master
Open tif files containing i32 arrays
2021-02-24 09:44:37 -05:00
Vadim Dyadkin 3de87a7001 Open tif files containing i32 arrays 2021-02-20 18:25:29 +01:00
HeroicKatora 4900c82871 Merge pull request #114 from HagaiHargil/int16_support
Int8/16 support
2020-12-14 00:21:51 +01:00
HagaiHargil 8a8e3ed753 Rustfmt 2020-12-13 12:03:53 +02:00
HagaiHargil cd2f4f6462 Added tests and finalized i8 support 2020-12-13 12:00:48 +02:00
HagaiHargil aa13e31894 Initial i8 support 2020-12-13 11:37:56 +02:00
HeroicKatora 6c211b9179 Merge pull request #115 from image-rs/release-0.6.1
Change lot and meta data for 0.6.1
2020-12-12 20:24:40 +01:00
Andreas Molzer 49f9b3941d Change lot and meta data for 0.6.1 2020-12-12 19:49:19 +01:00
Andreas Molzer 332ab1dc4d Use WhiteIsZero interpretation of libtiff
Note that the variants are even named `MINISWHITE` in their code.
Address the remaining review nits.
2020-12-11 20:36:33 +01:00
HagaiHargil ec98e32a3b Removed unused wrapping f32 and f64 2020-12-08 13:46:09 +02:00
HagaiHargil 7ec2b75c70 Added int16 tests 2020-12-08 13:42:08 +02:00
HagaiHargil 9788085786 Int16 support 2020-12-08 13:33:25 +02:00
Jonathan Behrens fc80c5e3dd Merge pull request #111 from tnl-rwa/large_sttrip_size
Large strip size
2020-12-07 20:50:17 -05:00
Robert Wassens 95f0a34bcc Update comment 2020-12-07 12:14:36 +01:00
Robert Wassens 3e042df438 Set default strip size to 1MB 2020-12-07 11:44:21 +01:00
Robert Wassens 5346a441ae Processed review comment 2020-12-07 11:43:09 +01:00
Robert Wassens 76173069a3 round trip test 2020-12-06 23:34:24 +01:00
HeroicKatora 6cdd340933 Merge pull request #112 from image-rs/unlimited
Add a constant unlimited limit
2020-12-06 21:24:54 +01:00
HeroicKatora ef004cee48 Merge pull request #108 from image-rs/short-read-in-lzw
Fix lzw decoding stopping short of full decode
2020-12-06 02:52:06 +01:00
Andreas Molzer 6d7d92c605 Adjust field names based on review 2020-12-06 02:34:59 +01:00
HeroicKatora 53437dd7b4 Merge pull request #110 from fintelia/entry-val-refactor
Refactor Entry::val function
2020-12-05 21:35:37 +01:00
Andreas Molzer 0c06d32ee4 Add a constant unlimited limit 2020-12-05 18:20:23 +01:00
Andreas Molzer 4d8c64bf1e Fix lzw decoding stopping short of full decode
When the required amount of data was just a tiny bit larger than full a
page size (one buffer increment), the decoder would stop since all
compressed data had been read. However, the lzw decoder contains a small
internal bit buffer for code decoding. That is, some remaining bytes had
been retained and were still left to be decoded.
2020-12-05 14:57:28 +01:00
Robert Wassens 7da8f01f75 make strip size configurable 2020-12-05 01:39:37 +01:00
Jonathan Behrens 67105fe4ec Refactor Entry::val function 2020-12-04 18:59:51 -05:00
Robert Wassens 1b38ff9410 strip size = image height 2020-12-04 12:45:46 +01:00
HeroicKatora c9703dcb8a Merge pull request #100 from ruffson/feature_geotiff
Feature GeoTIFF
2020-11-16 19:07:29 +01:00
ruffson e2caf86f6a Add intos for Short 2020-11-16 19:01:18 +01:00
ruffson 923b37d647 Remove unnecessary line 2020-11-16 18:57:50 +01:00
ruffson 9f4799912f Remove geokey parsing 2020-11-16 18:57:48 +01:00
ruffson 4b218be4ac Add geodir hashmap to decoder 2020-11-16 18:57:24 +01:00
ruffson 9a0041286e Remove print outs 2020-11-16 18:57:24 +01:00
ruffson 0a4b51055e Add gdal_nodata tag 2020-11-16 18:57:24 +01:00
ruffson 96d6d52830 Move geo code in its own dir 2020-11-16 18:57:24 +01:00
ruffson 62a1a500a5 Add support for GeoTiff geokey dir 2020-11-16 18:57:21 +01:00
ruffson 3953bd31e1 Read out short vecs and ascii from ifds 2020-11-16 18:52:04 +01:00
HeroicKatora 83004c8344 Merge pull request #104 from image-rs/edition2018
Convert to edition 2018
2020-10-31 20:31:03 +01:00
Andreas Molzer 8ab9d30eff Convert to edition 2018
Notably, This enables NLL even on older compilers such as 1.34.
2020-10-31 20:13:30 +01:00
30 changed files with 2739 additions and 1117 deletions

No files matched your search

+2 -2
View File
@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
rust: ["1.34.2", stable, beta, nightly]
rust: ["1.48", stable, beta, nightly]
command: [build, test]
steps:
- uses: actions/checkout@v2
@@ -20,7 +20,7 @@ jobs:
- name: test
run: >
cargo test --tests --benches --no-default-features --features "$FEATURES"
if: ${{ matrix.rust != '1.34.2' }}
if: ${{ matrix.rust != '1.48' }}
env:
FEATURES: ${{ matrix.features }}
rustfmt:
+81
View File
@@ -1,3 +1,84 @@
# Version 0.7.1
New features:
* Encoding signed integer formats is now supported.
* Extensive fuzzing with `cargo fuzz`.
Changes:
* Tile decoding should be a little faster, requires one less intermediate buffer.
* Images whose IFDs form a cycle due to their offsets will now raise an error
when the cycle would be entered (jumping back should still be supported).
Fixes:
* Fixed a regression that caused conflict between strips and tile images,
causing errors in decoding some images.
* Use checked integer arithmetic in limit calculations, fixes overflows.
* IFD Tags are now always cleared between images.
* Found by fuzzing: Several memory limit overflows; JPEG now correctly
validates offsets and a minimum size of its table; Check upper limit of strip
byte size correctly;
Notes:
Our CI has warned that this version no longer builds on `1.34.2` out of the
box. We're still committed to the MSRV on this major version yet one
dependency—`flate2`—has already bumped it in a SemVer compatible version of its
own. This is out-of-our-control (cargo's dependency resolution does not allow
us to address this in a reasonable manner).
This can be address this by pinning the version of `flate2` to `1.0.21` in your
own files. However, you should understand that this puts you in considerable
maintenance debt as you will no longer receive any updates for this dependency
and any package that _requires_ a new version of the `1.0` series would be
incompatible with this requirement (cargo might yell at you very loudly).
# Version 0.7.0
New features:
* Support for encoding BigTiff ([#122](https://github.com/image-rs/image-tiff/pull/122))
* _Breaking:_ Encoder types now have a generic parameter to differentiate BigTiff and standard Tiff encoding. Defaults to standard Tiff.
* Basic tile decoding ([#125](https://github.com/image-rs/image-tiff/pull/125))
* _Breaking:_ There is a new `TiffError::UsageError` variant.
* Support for datatypes `Int8` and `Int16` ([#114](https://github.com/image-rs/image-tiff/pull/114))
* _Breaking:_ `DecodingResult` and `DecodingBuffer` have the two new variants `I8` and `I16`.
* Support for `i32` arrays ([#118](https://github.com/image-rs/image-tiff/pull/118/files))
* _Breaking:_ `DecodingResult` and `DecodingBuffer` have a new `I32` variant.
* Support for `Ifd` and `IfdBig` tag types and `I64` data type ([#119](https://github.com/image-rs/image-tiff/pull/119))
* _Breaking:_ `DecodingResult` and `DecodingBuffer` have a new `I64` variant.
* Add `SMinSampleValue` and `SMaxSampleValue` ([#123](https://github.com/image-rs/image-tiff/pull/123))
Changes:
* Improve deflate support ([#132](https://github.com/image-rs/image-tiff/pull/132))
* Switch to streaming decompression via `flate2`. Aside from performance improvements and lower RAM consumption, this fixes a bug where `max_uncompressed_length` was precalculated for a single tile but then used as a hard limit on the whole data, failing to decompress any tiled images.
* Add support for new `Deflate` tag in addition to `OldDeflate`.
* _Breaking:_ Remove `InflateError`, which is no longer needed with `flate2` ([#134](https://github.com/image-rs/image-tiff/pull/134))
* _Breaking:_ Support for `MinIsWhite` is restricted to unsigned and floating
point values. This is expected to be be re-added once some contradictory
interpretation regarding semantics for signed values is resolved.
Fixes:
* Validate that ASCII tags are valid ASCII and end with a null byte ([#121](https://github.com/image-rs/image-tiff/pull/121))
Internal:
* Simplify decompression logic ([#126](https://github.com/image-rs/image-tiff/pull/126))
* Simplify `expand_strip` ([#128](https://github.com/image-rs/image-tiff/pull/128))
# Version 0.6.1
New features:
* Support for reading `u16` and ascii string tags.
* Added `Limits::unlimited` for disabling all limits.
* Added `ImageEncoder::rows_per_strip` to overwrite the default.
Changes:
* The default strip size for chunked encoding is now 1MB, up from 8KB. This
should lead to more efficient decoding and compression.
Fixes:
* Fixed a bug where LZW compressed strips could not be decoded, instead
returning an error `Inconsistent sizes encountered`.
* Reading a tag with a complex type and a single value returns the proper Value
variant, instead of a vector with one entry.
# Version 0.6.0
New features:
+3 -5
View File
@@ -1,6 +1,6 @@
[package]
name = "tiff"
version = "0.6.0"
version = "0.7.1"
authors = [
"ccgn",
"bvssvni <bvssvni@gmail.com>",
@@ -15,14 +15,12 @@ license = "MIT"
description = "TIFF decoding and encoding library in pure Rust"
repository = "https://github.com/image-rs/image-tiff"
exclude = ["tests/images/*", "tests/fuzz_images/*"]
edition = "2018"
[dependencies]
weezl = "0.1.0"
jpeg = { package = "jpeg-decoder", version = "0.1.17", default-features = false }
[dependencies.miniz_oxide]
version = "0.4.1"
features = ["no_extern_crate_alloc"]
flate2 = "1.0.20"
[dev-dependencies]
criterion = "0.3.1"
+9 -1
View File
@@ -1,5 +1,5 @@
# image-tiff
[![Build Status](https://travis-ci.org/image-rs/image-tiff.svg?branch=master)](https://travis-ci.org/image-rs/image-tiff)
[![Build Status](https://github.com/image-rs/image-tiff/workflows/Rust%20CI/badge.svg)](https://github.com/image-rs/image-tiff/actions)
[![Documentation](https://docs.rs/tiff/badge.svg)](https://docs.rs/tiff)
[![Further crate info](https://img.shields.io/crates/v/tiff.svg)](https://crates.io/crates/tiff)
@@ -44,3 +44,11 @@ Formats and interpretations not listed above or with empty entries are unsupport
- Baseline tags
- `ExtraSamples`
- Extension tags
## Fuzzing
This crate uses [cargo-fuzz](https://github.com/rust-fuzz/cargo-fuzz) in order to test the image parser.
After installing it with `cargo install cargo-fuzz` on a nightly rustc, the
fuzzing harness can be run with recommended settings using
`cargo fuzz run decode_image -snone -- -timeout=5`.
+3
View File
@@ -0,0 +1,3 @@
target
corpus
artifacts
+25
View File
@@ -0,0 +1,25 @@
[package]
name = "tiff-fuzz"
version = "0.0.0"
authors = ["Automatically generated"]
publish = false
edition = "2018"
[package.metadata]
cargo-fuzz = true
[dependencies]
libfuzzer-sys = "0.4"
[dependencies.tiff]
path = ".."
# Prevent this from interfering with workspaces
[workspace]
members = ["."]
[[bin]]
name = "decode_image"
path = "fuzz_targets/decode_image.rs"
test = false
doc = false
+31
View File
@@ -0,0 +1,31 @@
#![no_main]
use libfuzzer_sys::fuzz_target;
fuzz_target!(|data: &[u8]| {
let mut decoder = if let Ok(d) = tiff::decoder::Decoder::new(std::io::Cursor::new(data)) {
d
} else {
return;
};
let mut limits = tiff::decoder::Limits::default();
limits.decoding_buffer_size = 1_000_000;
limits.ifd_value_size = 1_000_000;
limits.intermediate_buffer_size = 1_000_000;
decoder = decoder.with_limits(limits);
loop {
if let Err(_) = decoder.read_image() {
break;
}
if !decoder.more_images() {
break;
}
if let Err(_) = decoder.next_image() {
break;
}
}
});
+4 -3
View File
@@ -23,11 +23,12 @@ macro_rules! integral_slice_as_bytes{($int:ty, $const:ident $(,$mut:ident)*) =>
})*
}}
integral_slice_as_bytes!(i8, i8_as_ne_bytes);
integral_slice_as_bytes!(i8, i8_as_ne_bytes, i8_as_ne_mut_bytes);
integral_slice_as_bytes!(u16, u16_as_ne_bytes, u16_as_ne_mut_bytes);
integral_slice_as_bytes!(i16, i16_as_ne_bytes);
integral_slice_as_bytes!(i16, i16_as_ne_bytes, i16_as_ne_mut_bytes);
integral_slice_as_bytes!(u32, u32_as_ne_bytes, u32_as_ne_mut_bytes);
integral_slice_as_bytes!(i32, i32_as_ne_bytes);
integral_slice_as_bytes!(i32, i32_as_ne_bytes, i32_as_ne_mut_bytes);
integral_slice_as_bytes!(u64, u64_as_ne_bytes, u64_as_ne_mut_bytes);
integral_slice_as_bytes!(i64, i64_as_ne_bytes, i64_as_ne_mut_bytes);
integral_slice_as_bytes!(f32, f32_as_ne_bytes, f32_as_ne_mut_bytes);
integral_slice_as_bytes!(f64, f64_as_ne_bytes, f64_as_ne_mut_bytes);
+302 -165
View File
@@ -4,20 +4,22 @@ use std::collections::HashMap;
use std::convert::{TryFrom, TryInto};
use std::io::{self, Read, Seek};
use std::mem;
use std::str;
use super::stream::{ByteOrder, EndianReader, SmartReader};
use tags::{Tag, Type};
use {TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
use crate::tags::{Tag, Type};
use crate::{TiffError, TiffFormatError, TiffResult};
use self::Value::{
Ascii, Byte, Double, Float, List, Rational, RationalBig, SRational, SRationalBig, Signed,
SignedBig, Unsigned, UnsignedBig,
Ascii, Byte, Double, Float, Ifd, IfdBig, List, Rational, RationalBig, SRational, SRationalBig,
Short, Signed, SignedBig, Unsigned, UnsignedBig,
};
#[allow(unused_qualifications)]
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
Byte(u8),
Short(u16),
Signed(i32),
SignedBig(i64),
Unsigned(u32),
@@ -30,6 +32,8 @@ pub enum Value {
SRational(i32, i32),
SRationalBig(i64, i64),
Ascii(String),
Ifd(u32),
IfdBig(u64),
#[doc(hidden)] // Do not match against this.
__NonExhaustive,
}
@@ -42,10 +46,24 @@ impl Value {
}
}
pub fn into_u16(self) -> TiffResult<u16> {
match self {
Short(val) => Ok(val),
Unsigned(val) => Ok(u16::try_from(val)?),
UnsignedBig(val) => Ok(u16::try_from(val)?),
val => Err(TiffError::FormatError(
TiffFormatError::UnsignedIntegerExpected(val),
)),
}
}
pub fn into_u32(self) -> TiffResult<u32> {
match self {
Short(val) => Ok(val.into()),
Unsigned(val) => Ok(val),
UnsignedBig(val) => Ok(u32::try_from(val)?),
Ifd(val) => Ok(val),
IfdBig(val) => Ok(u32::try_from(val)?),
val => Err(TiffError::FormatError(
TiffFormatError::UnsignedIntegerExpected(val),
)),
@@ -64,8 +82,11 @@ impl Value {
pub fn into_u64(self) -> TiffResult<u64> {
match self {
Short(val) => Ok(val.into()),
Unsigned(val) => Ok(val.into()),
UnsignedBig(val) => Ok(val),
Ifd(val) => Ok(val.into()),
IfdBig(val) => Ok(val),
val => Err(TiffError::FormatError(
TiffFormatError::UnsignedIntegerExpected(val),
)),
@@ -100,6 +121,15 @@ impl Value {
}
}
pub fn into_string(self) -> TiffResult<String> {
match self {
Ascii(val) => Ok(val),
val => Err(TiffError::FormatError(
TiffFormatError::SignedIntegerExpected(val),
)),
}
}
pub fn into_u32_vec(self) -> TiffResult<Vec<u32>> {
match self {
List(vec) => {
@@ -115,6 +145,8 @@ impl Value {
RationalBig(numerator, denominator) => {
Ok(vec![u32::try_from(numerator)?, u32::try_from(denominator)?])
}
Ifd(val) => Ok(vec![val]),
IfdBig(val) => Ok(vec![u32::try_from(val)?]),
Ascii(val) => Ok(val.chars().map(u32::from).collect()),
val => Err(TiffError::FormatError(
TiffFormatError::UnsignedIntegerExpected(val),
@@ -139,6 +171,22 @@ impl Value {
}
}
pub fn into_u16_vec(self) -> TiffResult<Vec<u16>> {
match self {
List(vec) => {
let mut new_vec = Vec::with_capacity(vec.len());
for v in vec {
new_vec.push(v.into_u16()?)
}
Ok(new_vec)
}
Short(val) => Ok(vec![val]),
val => Err(TiffError::FormatError(
TiffFormatError::UnsignedIntegerExpected(val),
)),
}
}
pub fn into_i32_vec(self) -> TiffResult<Vec<i32>> {
match self {
List(vec) => {
@@ -215,6 +263,8 @@ impl Value {
UnsignedBig(val) => Ok(vec![val]),
Rational(numerator, denominator) => Ok(vec![numerator.into(), denominator.into()]),
RationalBig(numerator, denominator) => Ok(vec![numerator, denominator]),
Ifd(val) => Ok(vec![val.into()]),
IfdBig(val) => Ok(vec![val]),
Ascii(val) => Ok(val.chars().map(u32::from).map(u64::from).collect()),
val => Err(TiffError::FormatError(
TiffFormatError::UnsignedIntegerExpected(val),
@@ -293,171 +343,258 @@ impl Entry {
limits: &super::Limits,
decoder: &mut super::Decoder<R>,
) -> TiffResult<Value> {
// Case 1: there are no values so we can return immediately.
if self.count == 0 {
return Ok(List(Vec::new()));
}
let bo = decoder.byte_order();
let bigtiff_value = if decoder.bigtiff {
match (self.type_, self.count) {
(Type::BYTE, 5) => Some(offset_to_bytes(5, self)?),
(Type::BYTE, 6) => Some(offset_to_bytes(6, self)?),
(Type::BYTE, 7) => Some(offset_to_bytes(7, self)?),
(Type::BYTE, 8) => Some(offset_to_bytes(8, self)?),
(Type::SBYTE, 5) => Some(offset_to_sbytes(5, self)?),
(Type::SBYTE, 6) => Some(offset_to_sbytes(6, self)?),
(Type::SBYTE, 7) => Some(offset_to_sbytes(7, self)?),
(Type::SBYTE, 8) => Some(offset_to_sbytes(8, self)?),
(Type::SHORT, 3) => {
let mut r = self.r(bo);
Some(List(vec![
Unsigned(u32::from(r.read_u16()?)),
Unsigned(u32::from(r.read_u16()?)),
Unsigned(u32::from(r.read_u16()?)),
]))
}
(Type::SSHORT, 3) => {
let mut r = self.r(bo);
Some(List(vec![
Signed(i32::from(r.read_i16()?)),
Signed(i32::from(r.read_i16()?)),
Signed(i32::from(r.read_i16()?)),
]))
}
(Type::SHORT, 4) => {
let mut r = self.r(bo);
Some(List(vec![
Unsigned(u32::from(r.read_u16()?)),
Unsigned(u32::from(r.read_u16()?)),
Unsigned(u32::from(r.read_u16()?)),
Unsigned(u32::from(r.read_u16()?)),
]))
}
(Type::SSHORT, 4) => {
let mut r = self.r(bo);
Some(List(vec![
Signed(i32::from(r.read_i16()?)),
Signed(i32::from(r.read_i16()?)),
Signed(i32::from(r.read_i16()?)),
Signed(i32::from(r.read_i16()?)),
]))
}
(Type::LONG, 2) => {
let mut r = self.r(bo);
Some(List(vec![Unsigned(r.read_u32()?), Unsigned(r.read_u32()?)]))
}
(Type::SLONG, 2) => {
let mut r = self.r(bo);
Some(List(vec![Signed(r.read_i32()?), Signed(r.read_i32()?)]))
}
(Type::RATIONAL, 1) => {
let mut r = self.r(bo);
let numerator = r.read_u32()?;
let denominator = r.read_u32()?;
Some(Rational(numerator, denominator))
}
(Type::SRATIONAL, 1) => {
let mut r = self.r(bo);
let numerator = r.read_i32()?;
let denominator = r.read_i32()?;
Some(SRational(numerator, denominator))
}
_ => None,
}
} else {
None
let tag_size = match self.type_ {
Type::BYTE | Type::SBYTE | Type::ASCII | Type::UNDEFINED => 1,
Type::SHORT | Type::SSHORT => 2,
Type::LONG | Type::SLONG | Type::FLOAT | Type::IFD => 4,
Type::LONG8
| Type::SLONG8
| Type::DOUBLE
| Type::RATIONAL
| Type::SRATIONAL
| Type::IFD8 => 8,
Type::__NonExhaustive => unreachable!(),
};
if let Some(v) = bigtiff_value {
Ok(v)
} else {
match (self.type_, self.count) {
// TODO check if this could give wrong results
// at a different endianess of file/computer.
(Type::BYTE, 1) => Ok(Unsigned(u32::from(self.offset[0]))),
(Type::BYTE, 2) => offset_to_bytes(2, self),
(Type::BYTE, 3) => offset_to_bytes(3, self),
(Type::BYTE, 4) => offset_to_bytes(4, self),
(Type::BYTE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(UnsignedBig(u64::from(decoder.read_byte()?)))
}),
(Type::SBYTE, 1) => Ok(Signed(i32::from(self.offset[0] as i8))),
(Type::SBYTE, 2) => offset_to_sbytes(2, self),
(Type::SBYTE, 3) => offset_to_sbytes(3, self),
(Type::SBYTE, 4) => offset_to_sbytes(4, self),
(Type::SBYTE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(SignedBig(i64::from(decoder.read_byte()? as i8)))
}),
(Type::SHORT, 1) => Ok(Unsigned(u32::from(self.r(bo).read_u16()?))),
(Type::SSHORT, 1) => Ok(Signed(i32::from(self.r(bo).read_i16()?))),
(Type::SHORT, 2) => {
let mut r = self.r(bo);
Ok(List(vec![
Unsigned(u32::from(r.read_u16()?)),
Unsigned(u32::from(r.read_u16()?)),
]))
}
(Type::SSHORT, 2) => {
let mut r = self.r(bo);
Ok(List(vec![
Signed(i32::from(r.read_i16()?)),
Signed(i32::from(r.read_i16()?)),
]))
}
(Type::SHORT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(UnsignedBig(u64::from(decoder.read_short()?)))
}),
(Type::SSHORT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(SignedBig(i64::from(decoder.read_sshort()?)))
}),
(Type::LONG, 1) => Ok(Unsigned(self.r(bo).read_u32()?)),
(Type::SLONG, 1) => Ok(Signed(self.r(bo).read_i32()?)),
(Type::LONG, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(Unsigned(decoder.read_long()?))
}),
(Type::SLONG, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(Signed(decoder.read_slong()?))
}),
(Type::FLOAT, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(Float(decoder.read_float()?))
}),
(Type::DOUBLE, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(Double(decoder.read_double()?))
}),
(Type::RATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(Rational(decoder.read_long()?, decoder.read_long()?))
}),
(Type::SRATIONAL, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(SRational(decoder.read_slong()?, decoder.read_slong()?))
}),
(Type::LONG8, n) => self.decode_offset(n, bo, limits, decoder, |decoder| {
Ok(UnsignedBig(decoder.read_long8()?))
}),
(Type::ASCII, n) => {
let n = usize::try_from(n)?;
if n > limits.decoding_buffer_size {
return Err(TiffError::LimitsExceeded);
}
if (n <= 4 && !decoder.bigtiff) || (n <= 8 && decoder.bigtiff) {
let mut buf = vec![0; n];
self.r(bo).read_exact(&mut buf)?;
let v = String::from_utf8(buf)?;
let v = v.trim_matches(char::from(0));
Ok(Ascii(v.into()))
} else {
if decoder.bigtiff {
decoder.goto_offset_u64(self.r(bo).read_u64()?)?
} else {
decoder.goto_offset(self.r(bo).read_u32()?)?
}
let string = decoder.read_string(n)?;
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,
)),
let value_bytes = match self.count.checked_mul(tag_size) {
Some(n) => n,
None => {
return Err(TiffError::LimitsExceeded);
}
};
// Case 2: there is one value.
if self.count == 1 {
// 2a: the value is 5-8 bytes and we're in BigTiff mode.
if decoder.bigtiff && value_bytes > 4 && value_bytes <= 8 {
return Ok(match self.type_ {
Type::LONG8 => UnsignedBig(self.r(bo).read_u64()?),
Type::SLONG8 => SignedBig(self.r(bo).read_i64()?),
Type::DOUBLE => Double(self.r(bo).read_f64()?),
Type::RATIONAL => {
let mut r = self.r(bo);
Rational(r.read_u32()?, r.read_u32()?)
}
Type::SRATIONAL => {
let mut r = self.r(bo);
SRational(r.read_i32()?, r.read_i32()?)
}
Type::IFD8 => IfdBig(self.r(bo).read_u64()?),
Type::BYTE
| Type::SBYTE
| Type::ASCII
| Type::UNDEFINED
| Type::SHORT
| Type::SSHORT
| Type::LONG
| Type::SLONG
| Type::FLOAT
| Type::IFD => unreachable!(),
Type::__NonExhaustive => unreachable!(),
});
}
// 2b: the value is at most 4 bytes or doesn't fit in the offset field.
return Ok(match self.type_ {
Type::BYTE => Unsigned(u32::from(self.offset[0])),
Type::SBYTE => Signed(i32::from(self.offset[0] as i8)),
Type::UNDEFINED => Byte(self.offset[0]),
Type::SHORT => Unsigned(u32::from(self.r(bo).read_u16()?)),
Type::SSHORT => Signed(i32::from(self.r(bo).read_i16()?)),
Type::LONG => Unsigned(self.r(bo).read_u32()?),
Type::SLONG => Signed(self.r(bo).read_i32()?),
Type::FLOAT => Float(self.r(bo).read_f32()?),
Type::ASCII => {
if self.offset[0] == 0 {
Ascii("".to_string())
} else {
return Err(TiffError::FormatError(TiffFormatError::InvalidTag));
}
}
Type::LONG8 => {
decoder.goto_offset(self.r(bo).read_u32()?)?;
UnsignedBig(decoder.read_long8()?)
}
Type::SLONG8 => {
decoder.goto_offset(self.r(bo).read_u32()?)?;
SignedBig(decoder.read_slong8()?)
}
Type::DOUBLE => {
decoder.goto_offset(self.r(bo).read_u32()?)?;
Double(decoder.read_double()?)
}
Type::RATIONAL => {
decoder.goto_offset(self.r(bo).read_u32()?)?;
Rational(decoder.read_long()?, decoder.read_long()?)
}
Type::SRATIONAL => {
decoder.goto_offset(self.r(bo).read_u32()?)?;
SRational(decoder.read_slong()?, decoder.read_slong()?)
}
Type::IFD => Ifd(self.r(bo).read_u32()?),
Type::IFD8 => {
decoder.goto_offset(self.r(bo).read_u32()?)?;
IfdBig(decoder.read_long8()?)
}
Type::__NonExhaustive => unreachable!(),
});
}
// Case 3: There is more than one value, but it fits in the offset field.
if value_bytes <= 4 || decoder.bigtiff && value_bytes <= 8 {
match self.type_ {
Type::BYTE => return Ok(offset_to_bytes(self.count as usize, self)?),
Type::SBYTE => return Ok(offset_to_sbytes(self.count as usize, self)?),
Type::ASCII => {
let mut buf = vec![0; self.count as usize];
self.r(bo).read_exact(&mut buf)?;
if buf.is_ascii() && buf.ends_with(&[0]) {
let v = str::from_utf8(&buf)?;
let v = v.trim_matches(char::from(0));
return Ok(Ascii(v.into()));
} else {
return Err(TiffError::FormatError(TiffFormatError::InvalidTag));
}
}
Type::UNDEFINED => {
return Ok(List(
self.offset[0..self.count as usize]
.iter()
.map(|&b| Byte(b))
.collect(),
));
}
Type::SHORT => {
let mut r = self.r(bo);
let mut v = Vec::new();
for _ in 0..self.count {
v.push(Short(r.read_u16()?));
}
return Ok(List(v));
}
Type::SSHORT => {
let mut r = self.r(bo);
let mut v = Vec::new();
for _ in 0..self.count {
v.push(Signed(i32::from(r.read_i16()?)));
}
return Ok(List(v));
}
Type::LONG => {
let mut r = self.r(bo);
let mut v = Vec::new();
for _ in 0..self.count {
v.push(Unsigned(r.read_u32()?));
}
return Ok(List(v));
}
Type::SLONG => {
let mut r = self.r(bo);
let mut v = Vec::new();
for _ in 0..self.count {
v.push(Signed(r.read_i32()?));
}
return Ok(List(v));
}
Type::FLOAT => {
let mut r = self.r(bo);
let mut v = Vec::new();
for _ in 0..self.count {
v.push(Float(r.read_f32()?));
}
return Ok(List(v));
}
Type::IFD => {
let mut r = self.r(bo);
let mut v = Vec::new();
for _ in 0..self.count {
v.push(Ifd(r.read_u32()?));
}
return Ok(List(v));
}
Type::LONG8
| Type::SLONG8
| Type::RATIONAL
| Type::SRATIONAL
| Type::DOUBLE
| Type::IFD8 => {
unreachable!()
}
Type::__NonExhaustive => unreachable!(),
}
}
// Case 4: there is more than one value, and it doesn't fit in the offset field.
match self.type_ {
// TODO check if this could give wrong results
// at a different endianess of file/computer.
Type::BYTE => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
Ok(UnsignedBig(u64::from(decoder.read_byte()?)))
}),
Type::SBYTE => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
Ok(SignedBig(i64::from(decoder.read_byte()? as i8)))
}),
Type::SHORT => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
Ok(UnsignedBig(u64::from(decoder.read_short()?)))
}),
Type::SSHORT => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
Ok(SignedBig(i64::from(decoder.read_sshort()?)))
}),
Type::LONG => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
Ok(Unsigned(decoder.read_long()?))
}),
Type::SLONG => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
Ok(Signed(decoder.read_slong()?))
}),
Type::FLOAT => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
Ok(Float(decoder.read_float()?))
}),
Type::DOUBLE => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
Ok(Double(decoder.read_double()?))
}),
Type::RATIONAL => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
Ok(Rational(decoder.read_long()?, decoder.read_long()?))
}),
Type::SRATIONAL => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
Ok(SRational(decoder.read_slong()?, decoder.read_slong()?))
}),
Type::LONG8 => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
Ok(UnsignedBig(decoder.read_long8()?))
}),
Type::SLONG8 => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
Ok(SignedBig(decoder.read_slong8()?))
}),
Type::IFD => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
Ok(Ifd(decoder.read_long()?))
}),
Type::IFD8 => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
Ok(IfdBig(decoder.read_long8()?))
}),
Type::UNDEFINED => self.decode_offset(self.count, bo, limits, decoder, |decoder| {
Ok(Byte(u8::from(decoder.read_byte()?)))
}),
Type::ASCII => {
let n = usize::try_from(self.count)?;
if n > limits.decoding_buffer_size {
return Err(TiffError::LimitsExceeded);
}
if decoder.bigtiff {
decoder.goto_offset_u64(self.r(bo).read_u64()?)?
} else {
decoder.goto_offset(self.r(bo).read_u32()?)?
}
let string = decoder.read_string(n)?;
Ok(Ascii(string))
}
Type::__NonExhaustive => unreachable!(),
}
}
+811 -254
View File
File diff suppressed because it is too large. Load diff
+66 -213
View File
@@ -1,8 +1,5 @@
//! All IO functionality needed for TIFF decoding
use crate::bytecast;
use crate::error::{TiffError, TiffResult};
use miniz_oxide::inflate;
use std::io::{self, Read, Seek};
/// Byte order of the TIFF file.
@@ -30,22 +27,15 @@ pub trait EndianReader: Read {
})
}
/// Reads an i8
#[inline(always)]
fn read_u16_into(&mut self, buffer: &mut [u16]) -> Result<(), io::Error> {
self.read_exact(bytecast::u16_as_ne_mut_bytes(buffer))?;
match self.byte_order() {
ByteOrder::LittleEndian => {
for n in buffer {
*n = u16::from_le(*n);
}
}
ByteOrder::BigEndian => {
for n in buffer {
*n = u16::from_be(*n);
}
}
}
Ok(())
fn read_i8(&mut self) -> Result<i8, io::Error> {
let mut n = [0u8; 1];
self.read_exact(&mut n)?;
Ok(match self.byte_order() {
ByteOrder::LittleEndian => i8::from_le_bytes(n),
ByteOrder::BigEndian => i8::from_be_bytes(n),
})
}
/// Reads an i16
@@ -70,24 +60,6 @@ pub trait EndianReader: Read {
})
}
#[inline(always)]
fn read_u32_into(&mut self, buffer: &mut [u32]) -> Result<(), io::Error> {
self.read_exact(bytecast::u32_as_ne_mut_bytes(buffer))?;
match self.byte_order() {
ByteOrder::LittleEndian => {
for n in buffer {
*n = u32::from_le(*n);
}
}
ByteOrder::BigEndian => {
for n in buffer {
*n = u32::from_be(*n);
}
}
}
Ok(())
}
/// Reads an i32
#[inline(always)]
fn read_i32(&mut self) -> Result<i32, io::Error> {
@@ -110,22 +82,15 @@ pub trait EndianReader: Read {
})
}
/// Reads an i64
#[inline(always)]
fn read_u64_into(&mut self, buffer: &mut [u64]) -> Result<(), io::Error> {
self.read_exact(bytecast::u64_as_ne_mut_bytes(buffer))?;
match self.byte_order() {
ByteOrder::LittleEndian => {
for n in buffer {
*n = u64::from_le(*n);
}
}
ByteOrder::BigEndian => {
for n in buffer {
*n = u64::from_be(*n);
}
}
}
Ok(())
fn read_i64(&mut self) -> Result<i64, io::Error> {
let mut n = [0u8; 8];
self.read_exact(&mut n)?;
Ok(match self.byte_order() {
ByteOrder::LittleEndian => i64::from_le_bytes(n),
ByteOrder::BigEndian => i64::from_be_bytes(n),
})
}
/// Reads an f32
@@ -139,24 +104,6 @@ pub trait EndianReader: Read {
}))
}
#[inline(always)]
fn read_f32_into(&mut self, buffer: &mut [f32]) -> Result<(), io::Error> {
self.read_exact(bytecast::f32_as_ne_mut_bytes(buffer))?;
match self.byte_order() {
ByteOrder::LittleEndian => {
for n in buffer {
*n = f32::from_bits(u32::from_le(n.to_bits()));
}
}
ByteOrder::BigEndian => {
for n in buffer {
*n = f32::from_bits(u32::from_be(n.to_bits()));
}
}
}
Ok(())
}
/// Reads an f64
#[inline(always)]
fn read_f64(&mut self) -> Result<f64, io::Error> {
@@ -167,24 +114,6 @@ pub trait EndianReader: Read {
ByteOrder::BigEndian => u64::from_be_bytes(n),
}))
}
#[inline(always)]
fn read_f64_into(&mut self, buffer: &mut [f64]) -> Result<(), io::Error> {
self.read_exact(bytecast::f64_as_ne_mut_bytes(buffer))?;
match self.byte_order() {
ByteOrder::LittleEndian => {
for n in buffer {
*n = f64::from_bits(u64::from_le(n.to_bits()));
}
}
ByteOrder::BigEndian => {
for n in buffer {
*n = f64::from_bits(u64::from_be(n.to_bits()));
}
}
}
Ok(())
}
}
///
@@ -195,51 +124,7 @@ pub trait EndianReader: Read {
/// ## Deflate Reader
///
/// Reader that decompresses DEFLATE streams
pub struct DeflateReader {
buffer: io::Cursor<Vec<u8>>,
byte_order: ByteOrder,
}
impl DeflateReader {
pub fn new<R: Read + Seek>(
reader: &mut SmartReader<R>,
max_uncompressed_length: usize,
) -> TiffResult<(usize, Self)> {
let byte_order = reader.byte_order;
let mut compressed = Vec::new();
reader.read_to_end(&mut compressed)?;
// TODO: Implement streaming compression, and remove this (temporary) and somewhat
// misleading workaround.
if compressed.len() > max_uncompressed_length {
return Err(TiffError::LimitsExceeded);
}
let uncompressed =
inflate::decompress_to_vec_zlib(&compressed).map_err(TiffError::from_inflate_status)?;
Ok((
uncompressed.len(),
Self {
byte_order,
buffer: io::Cursor::new(uncompressed),
},
))
}
}
impl Read for DeflateReader {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.buffer.read(buf)
}
}
impl EndianReader for DeflateReader {
fn byte_order(&self) -> ByteOrder {
self.byte_order
}
}
pub type DeflateReader<'r, R> = flate2::read::ZlibDecoder<&'r mut SmartReader<R>>;
///
/// ## LZW Reader
@@ -248,7 +133,6 @@ impl EndianReader for DeflateReader {
/// Reader that decompresses LZW streams
pub struct LZWReader {
buffer: io::Cursor<Vec<u8>>,
byte_order: ByteOrder,
}
impl LZWReader {
@@ -261,24 +145,21 @@ impl LZWReader {
where
R: Read + Seek,
{
let order = reader.byte_order;
let mut compressed = vec![0; compressed_length as usize];
reader.read_exact(&mut compressed[..])?;
let mut uncompressed = Vec::with_capacity(max_uncompressed_length);
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;
while bytes_read < compressed_length && uncompressed.len() < max_uncompressed_length {
let bytes_written = uncompressed.len();
uncompressed.reserve(1 << 12);
let buffer_space = uncompressed.capacity();
uncompressed.resize(buffer_space, 0u8);
let mut bytes_read = 0;
let mut bytes_written = 0;
while bytes_written < max_uncompressed_length {
let result = decoder.decode_bytes(
&compressed[bytes_read..],
&mut uncompressed[bytes_written..],
);
bytes_read += result.consumed_in;
uncompressed.truncate(bytes_written + result.consumed_out);
bytes_written += result.consumed_out;
match result.status {
Ok(weezl::LzwStatus::Ok) => {}
@@ -293,12 +174,11 @@ impl LZWReader {
}
}
let bytes = uncompressed.len();
uncompressed.resize(bytes_written, 0);
Ok((
bytes,
uncompressed.len(),
LZWReader {
buffer: io::Cursor::new(uncompressed),
byte_order: order,
},
))
}
@@ -311,20 +191,12 @@ impl Read for LZWReader {
}
}
impl EndianReader for LZWReader {
#[inline(always)]
fn byte_order(&self) -> ByteOrder {
self.byte_order
}
}
///
/// ## 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.
@@ -347,27 +219,35 @@ impl 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) => {
assert!(
tables.len() >= 2,
"jpeg_tables, if given, must be at least 2 bytes long. Got {:?}",
tables
);
assert!(
length >= 2,
"if jpeg_tables is given, length must be at least 2 bytes long, got {}",
length
);
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..]);
jpeg_data.extend_from_slice(&segment[2..]);
Ok(JpegReader {
buffer: io::Cursor::new(jpeg_data),
byte_order: order,
})
}
None => Ok(JpegReader {
buffer: io::Cursor::new(segment),
byte_order: order,
}),
}
}
@@ -380,13 +260,6 @@ impl Read for JpegReader {
}
}
impl EndianReader for JpegReader {
#[inline(always)]
fn byte_order(&self) -> ByteOrder {
self.byte_order
}
}
///
/// ## PackBits Reader
///
@@ -394,58 +267,46 @@ impl EndianReader for JpegReader {
/// Reader that unpacks Apple's `PackBits` format
pub struct PackBitsReader {
buffer: io::Cursor<Vec<u8>>,
byte_order: ByteOrder,
}
impl PackBitsReader {
/// Wraps a reader
pub fn new<R: Read + Seek>(
mut reader: R,
byte_order: ByteOrder,
length: usize,
) -> io::Result<(usize, PackBitsReader)> {
let mut buffer = Vec::new();
let mut read: usize = 0;
while read < length {
let lread = read_packbits_run(&mut reader, &mut buffer)?;
if lread == 0 {
return Err(io::ErrorKind::UnexpectedEof.into());
}
read += lread;
}
Ok((
buffer.len(),
PackBitsReader {
buffer: io::Cursor::new(buffer),
byte_order,
},
))
}
}
let mut header: [u8; 1] = [0];
let mut data: [u8; 1] = [0];
fn read_packbits_run<R: Read + Seek>(reader: &mut R, buffer: &mut Vec<u8>) -> io::Result<usize> {
let mut header: [u8; 1] = [0];
let mut bytes_read = 0;
while bytes_read < length {
reader.read_exact(&mut header)?;
bytes_read += 1;
let bytes = reader.read(&mut header)?;
match bytes {
0 => Ok(0),
_ => match header[0] as i8 {
-128 => Ok(1),
h if h >= -127 && h <= -1 => {
let h = header[0] as i8;
if h >= -127 && h <= -1 {
let new_len = buffer.len() + (1 - h as isize) as usize;
reader.read_exact(&mut header)?;
buffer.resize(new_len, header[0]);
Ok(2)
}
h => {
reader.read_exact(&mut data)?;
buffer.resize(new_len, data[0]);
bytes_read += 1;
} else if h >= 0 {
let num_vals = h as usize + 1;
let start = buffer.len();
buffer.resize(start + num_vals, 0);
reader.read_exact(&mut buffer[start..])?;
Ok(num_vals + 1)
bytes_read += num_vals
} else {
// h = -128 is a no-op.
}
},
}
Ok((
buffer.len(),
PackBitsReader {
buffer: io::Cursor::new(buffer),
},
))
}
}
@@ -456,13 +317,6 @@ impl Read for PackBitsReader {
}
}
impl EndianReader for PackBitsReader {
#[inline(always)]
fn byte_order(&self) -> ByteOrder {
self.byte_order
}
}
///
/// ## SmartReader Reader
///
@@ -471,7 +325,7 @@ impl EndianReader for PackBitsReader {
#[derive(Debug)]
pub struct SmartReader<R>
where
R: Read + Seek,
R: Read,
{
reader: R,
pub byte_order: ByteOrder,
@@ -479,7 +333,7 @@ where
impl<R> SmartReader<R>
where
R: Read + Seek,
R: Read,
{
/// Wraps a reader
pub fn wrap(reader: R, byte_order: ByteOrder) -> SmartReader<R> {
@@ -489,7 +343,7 @@ where
impl<R> EndianReader for SmartReader<R>
where
R: Read + Seek,
R: Read,
{
#[inline(always)]
fn byte_order(&self) -> ByteOrder {
@@ -497,7 +351,7 @@ where
}
}
impl<R: Read + Seek> Read for SmartReader<R> {
impl<R: Read> Read for SmartReader<R> {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.reader.read(buf)
@@ -524,8 +378,7 @@ mod test {
let encoded_len = encoded.len();
let buff = io::Cursor::new(encoded);
let (_, mut decoder) =
PackBitsReader::new(buff, ByteOrder::LittleEndian, encoded_len).unwrap();
let (_, mut decoder) = PackBitsReader::new(buff, encoded_len).unwrap();
let mut decoded = Vec::new();
decoder.read_to_end(&mut decoded).unwrap();
+33 -1
View File
@@ -1,4 +1,4 @@
use tags::{PhotometricInterpretation, SampleFormat};
use crate::tags::{PhotometricInterpretation, SampleFormat};
/// Trait for different colortypes that can be encoded.
pub trait ColorType {
@@ -20,6 +20,14 @@ impl ColorType for Gray8 {
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint];
}
pub struct GrayI8;
impl ColorType for GrayI8 {
type Inner = i8;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
const BITS_PER_SAMPLE: &'static [u16] = &[8];
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Int];
}
pub struct Gray16;
impl ColorType for Gray16 {
type Inner = u16;
@@ -28,6 +36,14 @@ impl ColorType for Gray16 {
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint];
}
pub struct GrayI16;
impl ColorType for GrayI16 {
type Inner = i16;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
const BITS_PER_SAMPLE: &'static [u16] = &[16];
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Int];
}
pub struct Gray32;
impl ColorType for Gray32 {
type Inner = u32;
@@ -36,6 +52,14 @@ impl ColorType for Gray32 {
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint];
}
pub struct GrayI32;
impl ColorType for GrayI32 {
type Inner = i32;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
const BITS_PER_SAMPLE: &'static [u16] = &[32];
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Int];
}
pub struct Gray32Float;
impl ColorType for Gray32Float {
type Inner = f32;
@@ -52,6 +76,14 @@ impl ColorType for Gray64 {
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint];
}
pub struct GrayI64;
impl ColorType for GrayI64 {
type Inner = i64;
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::BlackIsZero;
const BITS_PER_SAMPLE: &'static [u16] = &[64];
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Int];
}
pub struct Gray64Float;
impl ColorType for Gray64Float {
type Inner = f64;
+255 -439
View File
@@ -1,402 +1,31 @@
use std::collections::BTreeMap;
use std::convert::TryFrom;
use std::io::{Seek, Write};
use std::{cmp, io, mem};
pub use tiff_value::*;
use bytecast;
use error::{TiffError, TiffFormatError, TiffResult};
use tags::{self, ResolutionUnit, Tag, Type};
use std::{
cmp,
collections::BTreeMap,
convert::{TryFrom, TryInto},
io::{self, Seek, Write},
marker::PhantomData,
mem,
num::TryFromIntError,
};
use crate::{
error::TiffResult,
tags::{self, ResolutionUnit, Tag},
};
pub mod colortype;
mod tiff_value;
mod writer;
use self::colortype::*;
use self::writer::*;
/// Type to represent tiff values of type `RATIONAL`
#[derive(Clone)]
pub struct Rational {
pub n: u32,
pub d: u32,
}
/// Type to represent tiff values of type `SRATIONAL`
pub struct SRational {
pub n: i32,
pub d: i32,
}
/// Trait for types that can be encoded in a tiff file
pub trait TiffValue {
const BYTE_LEN: u32;
const FIELD_TYPE: Type;
fn count(&self) -> u32;
fn bytes(&self) -> u32 {
self.count() * Self::BYTE_LEN
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()>;
}
impl TiffValue for [u8] {
const BYTE_LEN: u32 = 1;
const FIELD_TYPE: Type = Type::BYTE;
fn count(&self) -> u32 {
self.len() as u32
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_bytes(self)?;
Ok(())
}
}
impl TiffValue for [i8] {
const BYTE_LEN: u32 = 1;
const FIELD_TYPE: Type = Type::SBYTE;
fn count(&self) -> u32 {
self.len() as u32
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
let slice = bytecast::i8_as_ne_bytes(self);
writer.write_bytes(slice)?;
Ok(())
}
}
impl TiffValue for [u16] {
const BYTE_LEN: u32 = 2;
const FIELD_TYPE: Type = Type::SHORT;
fn count(&self) -> u32 {
self.len() as u32
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
let slice = bytecast::u16_as_ne_bytes(self);
writer.write_bytes(slice)?;
Ok(())
}
}
impl TiffValue for [i16] {
const BYTE_LEN: u32 = 2;
const FIELD_TYPE: Type = Type::SSHORT;
fn count(&self) -> u32 {
self.len() as u32
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
let slice = bytecast::i16_as_ne_bytes(self);
writer.write_bytes(slice)?;
Ok(())
}
}
impl TiffValue for [u32] {
const BYTE_LEN: u32 = 4;
const FIELD_TYPE: Type = Type::LONG;
fn count(&self) -> u32 {
self.len() as u32
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
let slice = bytecast::u32_as_ne_bytes(self);
writer.write_bytes(slice)?;
Ok(())
}
}
impl TiffValue for [i32] {
const BYTE_LEN: u32 = 4;
const FIELD_TYPE: Type = Type::SLONG;
fn count(&self) -> u32 {
self.len() as u32
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
let slice = bytecast::i32_as_ne_bytes(self);
writer.write_bytes(slice)?;
Ok(())
}
}
impl TiffValue for [u64] {
const BYTE_LEN: u32 = 8;
const FIELD_TYPE: Type = Type::LONG8;
fn count(&self) -> u32 {
self.len() as u32
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
let slice = bytecast::u64_as_ne_bytes(self);
writer.write_bytes(slice)?;
Ok(())
}
}
impl TiffValue for [f32] {
const BYTE_LEN: u32 = 4;
const FIELD_TYPE: Type = Type::FLOAT;
fn count(&self) -> u32 {
self.len() as u32
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
// We write using nativeedian so this sould be safe
let slice = bytecast::f32_as_ne_bytes(self);
writer.write_bytes(slice)?;
Ok(())
}
}
impl TiffValue for [f64] {
const BYTE_LEN: u32 = 8;
const FIELD_TYPE: Type = Type::DOUBLE;
fn count(&self) -> u32 {
self.len() as u32
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
// We write using nativeedian so this sould be safe
let slice = bytecast::f64_as_ne_bytes(self);
writer.write_bytes(slice)?;
Ok(())
}
}
impl TiffValue for [Rational] {
const BYTE_LEN: u32 = 8;
const FIELD_TYPE: Type = Type::RATIONAL;
fn count(&self) -> u32 {
self.len() as u32
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
for x in self {
x.write(writer)?;
}
Ok(())
}
}
impl TiffValue for [SRational] {
const BYTE_LEN: u32 = 8;
const FIELD_TYPE: Type = Type::SRATIONAL;
fn count(&self) -> u32 {
self.len() as u32
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
for x in self {
x.write(writer)?;
}
Ok(())
}
}
impl TiffValue for u8 {
const BYTE_LEN: u32 = 1;
const FIELD_TYPE: Type = Type::BYTE;
fn count(&self) -> u32 {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_u8(*self)?;
Ok(())
}
}
impl TiffValue for i8 {
const BYTE_LEN: u32 = 1;
const FIELD_TYPE: Type = Type::SBYTE;
fn count(&self) -> u32 {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_i8(*self)?;
Ok(())
}
}
impl TiffValue for u16 {
const BYTE_LEN: u32 = 2;
const FIELD_TYPE: Type = Type::SHORT;
fn count(&self) -> u32 {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_u16(*self)?;
Ok(())
}
}
impl TiffValue for i16 {
const BYTE_LEN: u32 = 2;
const FIELD_TYPE: Type = Type::SSHORT;
fn count(&self) -> u32 {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_i16(*self)?;
Ok(())
}
}
impl TiffValue for u32 {
const BYTE_LEN: u32 = 4;
const FIELD_TYPE: Type = Type::LONG;
fn count(&self) -> u32 {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_u32(*self)?;
Ok(())
}
}
impl TiffValue for i32 {
const BYTE_LEN: u32 = 4;
const FIELD_TYPE: Type = Type::SLONG;
fn count(&self) -> u32 {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_i32(*self)?;
Ok(())
}
}
impl TiffValue for u64 {
const BYTE_LEN: u32 = 8;
const FIELD_TYPE: Type = Type::LONG8;
fn count(&self) -> u32 {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_u64(*self)?;
Ok(())
}
}
impl TiffValue for f32 {
const BYTE_LEN: u32 = 4;
const FIELD_TYPE: Type = Type::FLOAT;
fn count(&self) -> u32 {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_f32(*self)?;
Ok(())
}
}
impl TiffValue for f64 {
const BYTE_LEN: u32 = 8;
const FIELD_TYPE: Type = Type::DOUBLE;
fn count(&self) -> u32 {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_f64(*self)?;
Ok(())
}
}
impl TiffValue for Rational {
const BYTE_LEN: u32 = 8;
const FIELD_TYPE: Type = Type::RATIONAL;
fn count(&self) -> u32 {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_u32(self.n)?;
writer.write_u32(self.d)?;
Ok(())
}
}
impl TiffValue for SRational {
const BYTE_LEN: u32 = 8;
const FIELD_TYPE: Type = Type::SRATIONAL;
fn count(&self) -> u32 {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_i32(self.n)?;
writer.write_i32(self.d)?;
Ok(())
}
}
impl TiffValue for str {
const BYTE_LEN: u32 = 1;
const FIELD_TYPE: Type = Type::ASCII;
fn count(&self) -> u32 {
self.len() as u32 + 1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
if self.is_ascii() && !self.bytes().any(|b| b == 0) {
writer.write_bytes(self.as_bytes())?;
writer.write_u8(0)?;
Ok(())
} else {
Err(TiffError::FormatError(TiffFormatError::InvalidTag))
}
}
}
impl<'a, T: TiffValue + ?Sized> TiffValue for &'a T {
const BYTE_LEN: u32 = T::BYTE_LEN;
const FIELD_TYPE: Type = T::FIELD_TYPE;
fn count(&self) -> u32 {
(*self).count()
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
(*self).write(writer)
}
}
/// Tiff encoder.
/// Encoder for Tiff and BigTiff files.
///
/// With this type you can get a `DirectoryEncoder` or a `ImageEncoder`
/// to encode tiff ifd directories with images.
/// to encode Tiff/BigTiff ifd directories with images.
///
/// See `DirectoryEncoder` and `ImageEncoder`.
///
@@ -408,39 +37,68 @@ impl<'a, T: TiffValue + ?Sized> TiffValue for &'a T {
/// # let image_data = vec![0; 100*100*3];
/// use tiff::encoder::*;
///
/// // create a standard Tiff file
/// let mut tiff = TiffEncoder::new(&mut file).unwrap();
///
/// tiff.write_image::<colortype::RGB8>(100, 100, &image_data).unwrap();
///
/// // create a BigTiff file
/// let mut bigtiff = TiffEncoder::new_big(&mut file).unwrap();
/// bigtiff.write_image::<colortype::RGB8>(100, 100, &image_data).unwrap();
///
/// # }
/// ```
pub struct TiffEncoder<W> {
pub struct TiffEncoder<W, K: TiffKind = TiffKindStandard> {
writer: TiffWriter<W>,
kind: PhantomData<K>,
}
/// Constructor functions to create standard Tiff files.
impl<W: Write + Seek> TiffEncoder<W> {
pub fn new(writer: W) -> TiffResult<TiffEncoder<W>> {
/// Creates a new encoder for standard Tiff files.
///
/// To create BigTiff files, use [`new_big`][TiffEncoder::new_big] or
/// [`new_generic`][TiffEncoder::new_generic].
pub fn new(writer: W) -> TiffResult<TiffEncoder<W, TiffKindStandard>> {
TiffEncoder::new_generic(writer)
}
}
/// Constructor functions to create BigTiff files.
impl<W: Write + Seek> TiffEncoder<W, TiffKindBig> {
/// Creates a new encoder for BigTiff files.
///
/// To create standard Tiff files, use [`new`][TiffEncoder::new] or
/// [`new_generic`][TiffEncoder::new_generic].
pub fn new_big(writer: W) -> TiffResult<Self> {
TiffEncoder::new_generic(writer)
}
}
/// Generic functions that are available for both Tiff and BigTiff encoders.
impl<W: Write + Seek, K: TiffKind> TiffEncoder<W, K> {
/// Creates a new Tiff or BigTiff encoder, inferred from the return type.
pub fn new_generic(writer: W) -> TiffResult<Self> {
let mut encoder = TiffEncoder {
writer: TiffWriter::new(writer),
kind: PhantomData,
};
write_tiff_header(&mut encoder.writer)?;
// blank the IFD offset location
encoder.writer.write_u32(0)?;
K::write_header(&mut encoder.writer)?;
Ok(encoder)
}
/// Create a `DirectoryEncoder` to encode an ifd directory.
pub fn new_directory(&mut self) -> TiffResult<DirectoryEncoder<W>> {
/// Create a [`DirectoryEncoder`] to encode an ifd directory.
pub fn new_directory(&mut self) -> TiffResult<DirectoryEncoder<W, K>> {
DirectoryEncoder::new(&mut self.writer)
}
/// Create an 'ImageEncoder' to encode an image one slice at a time.
/// Create an [`ImageEncoder`] to encode an image one slice at a time.
pub fn new_image<C: ColorType>(
&mut self,
width: u32,
height: u32,
) -> TiffResult<ImageEncoder<W, C>> {
) -> TiffResult<ImageEncoder<W, C, K>> {
let encoder = DirectoryEncoder::new(&mut self.writer)?;
ImageEncoder::new(encoder, width, height)
}
@@ -456,7 +114,7 @@ impl<W: Write + Seek> TiffEncoder<W> {
[C::Inner]: TiffValue,
{
let encoder = DirectoryEncoder::new(&mut self.writer)?;
let image: ImageEncoder<W, C> = ImageEncoder::new(encoder, width, height)?;
let image: ImageEncoder<W, C, K> = ImageEncoder::new(encoder, width, height)?;
image.write_data(data)
}
}
@@ -465,19 +123,19 @@ impl<W: Write + Seek> TiffEncoder<W> {
///
/// You should call `finish` on this when you are finished with it.
/// Encoding can silently fail while this is dropping.
pub struct DirectoryEncoder<'a, W: 'a + Write + Seek> {
pub struct DirectoryEncoder<'a, W: 'a + Write + Seek, K: TiffKind> {
writer: &'a mut TiffWriter<W>,
dropped: bool,
// We use BTreeMap to make sure tags are written in correct order
ifd_pointer_pos: u64,
ifd: BTreeMap<u16, (u16, u32, Vec<u8>)>,
ifd: BTreeMap<u16, DirectoryEntry<K::OffsetType>>,
}
impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
fn new(writer: &'a mut TiffWriter<W>) -> TiffResult<DirectoryEncoder<'a, W>> {
impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
fn new(writer: &'a mut TiffWriter<W>) -> TiffResult<Self> {
// the previous word is the IFD offset position
let ifd_pointer_pos = writer.offset() - mem::size_of::<u32>() as u64;
writer.pad_word_boundary()?;
let ifd_pointer_pos = writer.offset() - mem::size_of::<K::OffsetType>() as u64;
writer.pad_word_boundary()?; // TODO: Do we need to adjust this for BigTiff?
Ok(DirectoryEncoder {
writer,
dropped: false,
@@ -488,8 +146,7 @@ impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
/// Write a single ifd tag.
pub fn write_tag<T: TiffValue>(&mut self, tag: Tag, value: T) -> TiffResult<()> {
let len = <T>::BYTE_LEN * value.count();
let mut bytes = Vec::with_capacity(usize::try_from(len)?);
let mut bytes = Vec::with_capacity(value.bytes());
{
let mut writer = TiffWriter::new(&mut bytes);
value.write(&mut writer)?;
@@ -497,7 +154,11 @@ impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
self.ifd.insert(
tag.to_u16(),
(<T>::FIELD_TYPE.to_u16(), value.count(), bytes),
DirectoryEntry {
data_type: <T>::FIELD_TYPE.to_u16(),
count: value.count().try_into()?,
data: bytes,
},
);
Ok(())
@@ -505,15 +166,21 @@ impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
fn write_directory(&mut self) -> TiffResult<u64> {
// Start by writing out all values
for &mut (_, _, ref mut bytes) in self.ifd.values_mut() {
if bytes.len() > 4 {
for &mut DirectoryEntry {
data: ref mut bytes,
..
} in self.ifd.values_mut()
{
let data_bytes = mem::size_of::<K::OffsetType>();
if bytes.len() > data_bytes {
let offset = self.writer.offset();
self.writer.write_bytes(bytes)?;
*bytes = vec![0, 0, 0, 0];
*bytes = vec![0; data_bytes];
let mut writer = TiffWriter::new(bytes as &mut [u8]);
writer.write_u32(u32::try_from(offset)?)?;
K::write_offset(&mut writer, offset)?;
} else {
while bytes.len() < 4 {
while bytes.len() < data_bytes {
bytes.push(0);
}
}
@@ -521,11 +188,19 @@ impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
let offset = self.writer.offset();
self.writer.write_u16(u16::try_from(self.ifd.len())?)?;
for (tag, &(ref field_type, ref count, ref offset)) in self.ifd.iter() {
K::write_entry_count(&mut self.writer, self.ifd.len())?;
for (
tag,
&DirectoryEntry {
data_type: ref field_type,
ref count,
data: ref offset,
},
) in self.ifd.iter()
{
self.writer.write_u16(*tag)?;
self.writer.write_u16(*field_type)?;
self.writer.write_u32(*count)?;
(*count).write(&mut self.writer)?;
self.writer.write_bytes(offset)?;
}
@@ -546,9 +221,9 @@ impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
let curr_pos = self.writer.offset();
self.writer.goto_offset(self.ifd_pointer_pos)?;
self.writer.write_u32(u32::try_from(ifd_pointer)?)?;
K::write_offset(&mut self.writer, ifd_pointer)?;
self.writer.goto_offset(curr_pos)?;
self.writer.write_u32(0)?;
K::write_offset(&mut self.writer, 0)?;
self.dropped = true;
@@ -561,7 +236,7 @@ impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
}
}
impl<'a, W: Write + Seek> Drop for DirectoryEncoder<'a, W> {
impl<'a, W: Write + Seek, K: TiffKind> Drop for DirectoryEncoder<'a, W, K> {
fn drop(&mut self) {
if !self.dropped {
let _ = self.finish_internal();
@@ -589,6 +264,9 @@ impl<'a, W: Write + Seek> Drop for DirectoryEncoder<'a, W> {
/// // You can encode tags here
/// image.encoder().write_tag(Tag::Artist, "Image-tiff").unwrap();
///
/// // Strip size can be configured before writing data
/// image.rows_per_strip(2).unwrap();
///
/// let mut idx = 0;
/// while image.next_strip_sample_count() > 0 {
/// let sample_count = image.next_strip_sample_count() as usize;
@@ -599,31 +277,28 @@ impl<'a, W: Write + Seek> Drop for DirectoryEncoder<'a, W> {
/// # }
/// ```
/// You can also call write_data function wich will encode by strip and finish
pub struct ImageEncoder<'a, W: 'a + Write + Seek, C: ColorType> {
encoder: DirectoryEncoder<'a, W>,
pub struct ImageEncoder<'a, W: 'a + Write + Seek, C: ColorType, K: TiffKind> {
encoder: DirectoryEncoder<'a, W, K>,
strip_idx: u64,
strip_count: u64,
row_samples: u64,
width: u32,
height: u32,
rows_per_strip: u64,
strip_offsets: Vec<u32>,
strip_byte_count: Vec<u32>,
strip_offsets: Vec<K::OffsetType>,
strip_byte_count: Vec<K::OffsetType>,
dropped: bool,
_phantom: ::std::marker::PhantomData<C>,
}
impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
fn new(
mut encoder: DirectoryEncoder<'a, W>,
width: u32,
height: u32,
) -> TiffResult<ImageEncoder<'a, W, T>> {
impl<'a, W: 'a + Write + Seek, T: ColorType, K: TiffKind> ImageEncoder<'a, W, T, K> {
fn new(mut encoder: DirectoryEncoder<'a, W, K>, width: u32, height: u32) -> TiffResult<Self> {
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);
// As per tiff spec each strip should be about 8k long
let rows_per_strip = (8000 + row_bytes - 1) / row_bytes;
// Limit the strip size to prevent potential memory and security issues.
// Also keep the multiple strip handling 'oiled'
let rows_per_strip = (1_000_000 + row_bytes - 1) / row_bytes;
let strip_count = (u64::from(height) + rows_per_strip - 1) / rows_per_strip;
@@ -690,8 +365,8 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
}
let offset = self.encoder.write_data(value)?;
self.strip_offsets.push(u32::try_from(offset)?);
self.strip_byte_count.push(value.bytes());
self.strip_offsets.push(K::convert_offset(offset)?);
self.strip_byte_count.push(value.bytes().try_into()?);
self.strip_idx += 1;
Ok(())
@@ -755,18 +430,42 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
self.encoder.write_tag(Tag::YResolution, value).unwrap();
}
/// Set image number of lines per strip
///
/// This function needs to be called before any calls to `write_data` or
/// `write_strip` and will return an error otherwise.
pub fn rows_per_strip(&mut self, value: u32) -> TiffResult<()> {
if self.strip_idx != 0 {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Cannot change strip size after data was written",
)
.into());
}
// Write tag as 32 bits
self.encoder.write_tag(Tag::RowsPerStrip, value)?;
let value: u64 = value as u64;
self.strip_count = (self.height as u64 + value - 1) / value;
self.rows_per_strip = value;
Ok(())
}
fn finish_internal(&mut self) -> TiffResult<()> {
self.encoder
.write_tag(Tag::StripOffsets, &*self.strip_offsets)?;
self.encoder
.write_tag(Tag::StripByteCounts, &*self.strip_byte_count)?;
.write_tag(Tag::StripOffsets, K::convert_slice(&self.strip_offsets))?;
self.encoder.write_tag(
Tag::StripByteCounts,
K::convert_slice(&self.strip_byte_count),
)?;
self.dropped = true;
self.encoder.finish_internal()
}
/// Get a reference of the underlying `DirectoryEncoder`
pub fn encoder(&mut self) -> &mut DirectoryEncoder<'a, W> {
pub fn encoder(&mut self) -> &mut DirectoryEncoder<'a, W, K> {
&mut self.encoder
}
@@ -776,10 +475,127 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
}
}
impl<'a, W: Write + Seek, C: ColorType> Drop for ImageEncoder<'a, W, C> {
impl<'a, W: Write + Seek, C: ColorType, K: TiffKind> Drop for ImageEncoder<'a, W, C, K> {
fn drop(&mut self) {
if !self.dropped {
let _ = self.finish_internal();
}
}
}
struct DirectoryEntry<S> {
data_type: u16,
count: S,
data: Vec<u8>,
}
/// Trait to abstract over Tiff/BigTiff differences.
///
/// Implemented for [`TiffKindStandard`] and [`TiffKindBig`].
pub trait TiffKind {
/// The type of offset fields, `u32` for normal Tiff, `u64` for BigTiff.
type OffsetType: TryFrom<usize, Error = TryFromIntError> + Into<u64> + TiffValue;
/// Needed for the `convert_slice` method.
type OffsetArrayType: ?Sized + TiffValue;
/// Write the (Big)Tiff header.
fn write_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()>;
/// Convert a file offset to `Self::OffsetType`.
///
/// This returns an error for normal Tiff if the offset is larger than `u32::MAX`.
fn convert_offset(offset: u64) -> TiffResult<Self::OffsetType>;
/// Write an offset value to the given writer.
///
/// Like `convert_offset`, this errors if `offset > u32::MAX` for normal Tiff.
fn write_offset<W: Write>(writer: &mut TiffWriter<W>, offset: u64) -> TiffResult<()>;
/// Write the IFD entry count field with the given `count` value.
///
/// The entry count field is an `u16` for normal Tiff and `u64` for BigTiff. Errors
/// if the given `usize` is larger than the representable values.
fn write_entry_count<W: Write>(writer: &mut TiffWriter<W>, count: usize) -> TiffResult<()>;
/// Internal helper method for satisfying Rust's type checker.
///
/// The `TiffValue` trait is implemented for both primitive values (e.g. `u8`, `u32`) and
/// slices of primitive values (e.g. `[u8]`, `[u32]`). However, this is not represented in
/// the type system, so there is no guarantee that that for all `T: TiffValue` there is also
/// an implementation of `TiffValue` for `[T]`. This method works around that problem by
/// providing a conversion from `[T]` to some value that implements `TiffValue`, thereby
/// making all slices of `OffsetType` usable with `write_tag` and similar methods.
///
/// Implementations of this trait should always set `OffsetArrayType` to `[OffsetType]`.
fn convert_slice(slice: &[Self::OffsetType]) -> &Self::OffsetArrayType;
}
/// Create a standard Tiff file.
pub struct TiffKindStandard;
impl TiffKind for TiffKindStandard {
type OffsetType = u32;
type OffsetArrayType = [u32];
fn write_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()> {
write_tiff_header(writer)?;
// blank the IFD offset location
writer.write_u32(0)?;
Ok(())
}
fn convert_offset(offset: u64) -> TiffResult<Self::OffsetType> {
Ok(Self::OffsetType::try_from(offset)?)
}
fn write_offset<W: Write>(writer: &mut TiffWriter<W>, offset: u64) -> TiffResult<()> {
writer.write_u32(u32::try_from(offset)?)?;
Ok(())
}
fn write_entry_count<W: Write>(writer: &mut TiffWriter<W>, count: usize) -> TiffResult<()> {
writer.write_u16(u16::try_from(count)?)?;
Ok(())
}
fn convert_slice(slice: &[Self::OffsetType]) -> &Self::OffsetArrayType {
slice
}
}
/// Create a BigTiff file.
pub struct TiffKindBig;
impl TiffKind for TiffKindBig {
type OffsetType = u64;
type OffsetArrayType = [u64];
fn write_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()> {
write_bigtiff_header(writer)?;
// blank the IFD offset location
writer.write_u64(0)?;
Ok(())
}
fn convert_offset(offset: u64) -> TiffResult<Self::OffsetType> {
Ok(offset)
}
fn write_offset<W: Write>(writer: &mut TiffWriter<W>, offset: u64) -> TiffResult<()> {
writer.write_u64(offset)?;
Ok(())
}
fn write_entry_count<W: Write>(writer: &mut TiffWriter<W>, count: usize) -> TiffResult<()> {
writer.write_u64(u64::try_from(count)?)?;
Ok(())
}
fn convert_slice(slice: &[Self::OffsetType]) -> &Self::OffsetArrayType {
slice
}
}
+483
View File
@@ -0,0 +1,483 @@
use std::io::Write;
use crate::{bytecast, tags::Type, TiffError, TiffFormatError, TiffResult};
use super::writer::TiffWriter;
/// Trait for types that can be encoded in a tiff file
pub trait TiffValue {
const BYTE_LEN: u8;
const FIELD_TYPE: Type;
fn count(&self) -> usize;
fn bytes(&self) -> usize {
self.count() * usize::from(Self::BYTE_LEN)
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()>;
}
impl TiffValue for [u8] {
const BYTE_LEN: u8 = 1;
const FIELD_TYPE: Type = Type::BYTE;
fn count(&self) -> usize {
self.len()
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_bytes(self)?;
Ok(())
}
}
impl TiffValue for [i8] {
const BYTE_LEN: u8 = 1;
const FIELD_TYPE: Type = Type::SBYTE;
fn count(&self) -> usize {
self.len()
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
let slice = bytecast::i8_as_ne_bytes(self);
writer.write_bytes(slice)?;
Ok(())
}
}
impl TiffValue for [u16] {
const BYTE_LEN: u8 = 2;
const FIELD_TYPE: Type = Type::SHORT;
fn count(&self) -> usize {
self.len()
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
let slice = bytecast::u16_as_ne_bytes(self);
writer.write_bytes(slice)?;
Ok(())
}
}
impl TiffValue for [i16] {
const BYTE_LEN: u8 = 2;
const FIELD_TYPE: Type = Type::SSHORT;
fn count(&self) -> usize {
self.len()
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
let slice = bytecast::i16_as_ne_bytes(self);
writer.write_bytes(slice)?;
Ok(())
}
}
impl TiffValue for [u32] {
const BYTE_LEN: u8 = 4;
const FIELD_TYPE: Type = Type::LONG;
fn count(&self) -> usize {
self.len()
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
let slice = bytecast::u32_as_ne_bytes(self);
writer.write_bytes(slice)?;
Ok(())
}
}
impl TiffValue for [i32] {
const BYTE_LEN: u8 = 4;
const FIELD_TYPE: Type = Type::SLONG;
fn count(&self) -> usize {
self.len()
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
let slice = bytecast::i32_as_ne_bytes(self);
writer.write_bytes(slice)?;
Ok(())
}
}
impl TiffValue for [u64] {
const BYTE_LEN: u8 = 8;
const FIELD_TYPE: Type = Type::LONG8;
fn count(&self) -> usize {
self.len()
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
let slice = bytecast::u64_as_ne_bytes(self);
writer.write_bytes(slice)?;
Ok(())
}
}
impl TiffValue for [i64] {
const BYTE_LEN: u8 = 8;
const FIELD_TYPE: Type = Type::SLONG8;
fn count(&self) -> usize {
self.len()
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
let slice = bytecast::i64_as_ne_bytes(self);
writer.write_bytes(slice)?;
Ok(())
}
}
impl TiffValue for [f32] {
const BYTE_LEN: u8 = 4;
const FIELD_TYPE: Type = Type::FLOAT;
fn count(&self) -> usize {
self.len()
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
// We write using nativeedian so this sould be safe
let slice = bytecast::f32_as_ne_bytes(self);
writer.write_bytes(slice)?;
Ok(())
}
}
impl TiffValue for [f64] {
const BYTE_LEN: u8 = 8;
const FIELD_TYPE: Type = Type::DOUBLE;
fn count(&self) -> usize {
self.len()
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
// We write using nativeedian so this sould be safe
let slice = bytecast::f64_as_ne_bytes(self);
writer.write_bytes(slice)?;
Ok(())
}
}
impl TiffValue for [Ifd] {
const BYTE_LEN: u8 = 4;
const FIELD_TYPE: Type = Type::IFD;
fn count(&self) -> usize {
self.len()
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
for x in self {
x.write(writer)?;
}
Ok(())
}
}
impl TiffValue for [Ifd8] {
const BYTE_LEN: u8 = 8;
const FIELD_TYPE: Type = Type::IFD8;
fn count(&self) -> usize {
self.len()
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
for x in self {
x.write(writer)?;
}
Ok(())
}
}
impl TiffValue for [Rational] {
const BYTE_LEN: u8 = 8;
const FIELD_TYPE: Type = Type::RATIONAL;
fn count(&self) -> usize {
self.len()
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
for x in self {
x.write(writer)?;
}
Ok(())
}
}
impl TiffValue for [SRational] {
const BYTE_LEN: u8 = 8;
const FIELD_TYPE: Type = Type::SRATIONAL;
fn count(&self) -> usize {
self.len()
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
for x in self {
x.write(writer)?;
}
Ok(())
}
}
impl TiffValue for u8 {
const BYTE_LEN: u8 = 1;
const FIELD_TYPE: Type = Type::BYTE;
fn count(&self) -> usize {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_u8(*self)?;
Ok(())
}
}
impl TiffValue for i8 {
const BYTE_LEN: u8 = 1;
const FIELD_TYPE: Type = Type::SBYTE;
fn count(&self) -> usize {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_i8(*self)?;
Ok(())
}
}
impl TiffValue for u16 {
const BYTE_LEN: u8 = 2;
const FIELD_TYPE: Type = Type::SHORT;
fn count(&self) -> usize {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_u16(*self)?;
Ok(())
}
}
impl TiffValue for i16 {
const BYTE_LEN: u8 = 2;
const FIELD_TYPE: Type = Type::SSHORT;
fn count(&self) -> usize {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_i16(*self)?;
Ok(())
}
}
impl TiffValue for u32 {
const BYTE_LEN: u8 = 4;
const FIELD_TYPE: Type = Type::LONG;
fn count(&self) -> usize {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_u32(*self)?;
Ok(())
}
}
impl TiffValue for i32 {
const BYTE_LEN: u8 = 4;
const FIELD_TYPE: Type = Type::SLONG;
fn count(&self) -> usize {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_i32(*self)?;
Ok(())
}
}
impl TiffValue for u64 {
const BYTE_LEN: u8 = 8;
const FIELD_TYPE: Type = Type::LONG8;
fn count(&self) -> usize {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_u64(*self)?;
Ok(())
}
}
impl TiffValue for i64 {
const BYTE_LEN: u8 = 8;
const FIELD_TYPE: Type = Type::SLONG8;
fn count(&self) -> usize {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_i64(*self)?;
Ok(())
}
}
impl TiffValue for f32 {
const BYTE_LEN: u8 = 4;
const FIELD_TYPE: Type = Type::FLOAT;
fn count(&self) -> usize {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_f32(*self)?;
Ok(())
}
}
impl TiffValue for f64 {
const BYTE_LEN: u8 = 8;
const FIELD_TYPE: Type = Type::DOUBLE;
fn count(&self) -> usize {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_f64(*self)?;
Ok(())
}
}
impl TiffValue for Ifd {
const BYTE_LEN: u8 = 4;
const FIELD_TYPE: Type = Type::IFD;
fn count(&self) -> usize {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_u32(self.0)?;
Ok(())
}
}
impl TiffValue for Ifd8 {
const BYTE_LEN: u8 = 8;
const FIELD_TYPE: Type = Type::IFD8;
fn count(&self) -> usize {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_u64(self.0)?;
Ok(())
}
}
impl TiffValue for Rational {
const BYTE_LEN: u8 = 8;
const FIELD_TYPE: Type = Type::RATIONAL;
fn count(&self) -> usize {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_u32(self.n)?;
writer.write_u32(self.d)?;
Ok(())
}
}
impl TiffValue for SRational {
const BYTE_LEN: u8 = 8;
const FIELD_TYPE: Type = Type::SRATIONAL;
fn count(&self) -> usize {
1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
writer.write_i32(self.n)?;
writer.write_i32(self.d)?;
Ok(())
}
}
impl TiffValue for str {
const BYTE_LEN: u8 = 1;
const FIELD_TYPE: Type = Type::ASCII;
fn count(&self) -> usize {
self.len() + 1
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
if self.is_ascii() && !self.bytes().any(|b| b == 0) {
writer.write_bytes(self.as_bytes())?;
writer.write_u8(0)?;
Ok(())
} else {
Err(TiffError::FormatError(TiffFormatError::InvalidTag))
}
}
}
impl<'a, T: TiffValue + ?Sized> TiffValue for &'a T {
const BYTE_LEN: u8 = T::BYTE_LEN;
const FIELD_TYPE: Type = T::FIELD_TYPE;
fn count(&self) -> usize {
(*self).count()
}
fn write<W: Write>(&self, writer: &mut TiffWriter<W>) -> TiffResult<()> {
(*self).write(writer)
}
}
/// Type to represent tiff values of type `IFD`
#[derive(Clone)]
pub struct Ifd(pub u32);
/// Type to represent tiff values of type `IFD8`
#[derive(Clone)]
pub struct Ifd8(pub u64);
/// Type to represent tiff values of type `RATIONAL`
#[derive(Clone)]
pub struct Rational {
pub n: u32,
pub d: u32,
}
/// Type to represent tiff values of type `SRATIONAL`
#[derive(Clone)]
pub struct SRational {
pub n: i32,
pub d: i32,
}
+33 -1
View File
@@ -1,4 +1,4 @@
use error::TiffResult;
use crate::error::TiffResult;
use std::io::{self, Seek, SeekFrom, Write};
pub fn write_tiff_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()> {
@@ -14,6 +14,31 @@ pub fn write_tiff_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()>
Ok(())
}
/// Writes a BigTiff header, excluding the IFD offset field.
///
/// Writes the byte order, version number, offset byte size, and zero constant fields. Does
// _not_ write the offset to the first IFD, this should be done by the caller.
pub fn write_bigtiff_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()> {
#[cfg(target_endian = "little")]
let boi: u8 = 0x49;
#[cfg(not(target_endian = "little"))]
let boi: u8 = 0x4d;
// byte order indication
writer.writer.write_all(&[boi, boi])?;
// version number
writer.writer.write_all(&43u16.to_ne_bytes())?;
// bytesize of offsets (pointer size)
writer.writer.write_all(&8u16.to_ne_bytes())?;
// always 0
writer.writer.write_all(&0u16.to_ne_bytes())?;
// we wrote 8 bytes, so set the internal offset accordingly
writer.offset += 8;
Ok(())
}
pub struct TiffWriter<W> {
writer: W,
offset: u64,
@@ -81,6 +106,13 @@ impl<W: Write> TiffWriter<W> {
Ok(())
}
pub fn write_i64(&mut self, n: i64) -> Result<(), io::Error> {
self.writer.write_all(&n.to_ne_bytes())?;
self.offset += 8;
Ok(())
}
pub fn write_f32(&mut self, n: f32) -> Result<(), io::Error> {
self.writer.write_all(&u32::to_ne_bytes(n.to_bits()))?;
self.offset += 4;
+72 -24
View File
@@ -1,12 +1,14 @@
use std::error::Error;
use std::fmt;
use std::io;
use std::str;
use std::string;
use decoder::ifd::Value;
use miniz_oxide::inflate::TINFLStatus;
use tags::{CompressionMethod, PhotometricInterpretation, PlanarConfiguration, SampleFormat, Tag};
use ColorType;
use crate::decoder::{ifd::Value, ChunkType};
use crate::tags::{
CompressionMethod, PhotometricInterpretation, PlanarConfiguration, SampleFormat, Tag,
};
use crate::ColorType;
/// Tiff error kinds.
#[derive(Debug)]
@@ -26,6 +28,9 @@ pub enum TiffError {
/// An integer conversion to or from a platform size failed, either due to
/// limits of the platform size or limits of the format.
IntSizeError,
/// The image does not support the requested operation
UsageError(UsageError),
}
/// The image is not formatted properly.
@@ -41,6 +46,14 @@ pub enum TiffFormatError {
TiffSignatureInvalid,
ImageFileDirectoryNotFound,
InconsistentSizesEncountered,
UnexpectedCompressedData {
actual_bytes: usize,
required_bytes: usize,
},
InconsistentStripSamples {
actual_samples: usize,
required_samples: usize,
},
InvalidTag,
InvalidTagValueType(Tag),
RequiredTagNotFound(Tag),
@@ -48,9 +61,10 @@ pub enum TiffFormatError {
ByteExpected(Value),
UnsignedIntegerExpected(Value),
SignedIntegerExpected(Value),
InflateError(InflateError),
Format(String),
RequiredTagEmpty(Tag),
StripTileTagConflict,
CycleInOffsets,
#[doc(hidden)]
/// Do not match against this variant. It may get removed.
__NonExhaustive,
@@ -64,6 +78,26 @@ impl fmt::Display for TiffFormatError {
TiffSignatureInvalid => write!(fmt, "TIFF signature invalid."),
ImageFileDirectoryNotFound => write!(fmt, "Image file directory not found."),
InconsistentSizesEncountered => write!(fmt, "Inconsistent sizes encountered."),
UnexpectedCompressedData {
actual_bytes,
required_bytes,
} => {
write!(
fmt,
"Decompression returned different amount of bytes than expected: got {}, expected {}.",
actual_bytes, required_bytes
)
}
InconsistentStripSamples {
actual_samples,
required_samples,
} => {
write!(
fmt,
"Inconsistent elements in strip: got {}, expected {}.",
actual_samples, required_samples
)
}
InvalidTag => write!(fmt, "Image contains invalid tag."),
InvalidTagValueType(ref tag) => {
write!(fmt, "Tag `{:?}` did not have the expected value type.", tag)
@@ -79,32 +113,15 @@ impl fmt::Display for TiffFormatError {
SignedIntegerExpected(ref val) => {
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),
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"),
__NonExhaustive => unreachable!(),
}
}
}
/// Decompression failed due to faulty compressed data.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct InflateError {
status: TINFLStatus,
}
impl InflateError {
pub(crate) fn new(status: TINFLStatus) -> Self {
Self { status }
}
}
impl TiffError {
pub(crate) fn from_inflate_status(status: TINFLStatus) -> Self {
TiffError::FormatError(TiffFormatError::InflateError(InflateError::new(status)))
}
}
/// The Decoder does not support features required by the image.
///
/// This only captures known failures for which the standard either does not require support or an
@@ -174,6 +191,29 @@ impl fmt::Display for TiffUnsupportedError {
}
}
/// User attempted to use the Decoder in a way that is incompatible with a specific image.
///
/// For example: attempting to read a tile from a stripped image.
#[derive(Debug)]
pub enum UsageError {
InvalidChunkType(ChunkType, ChunkType),
}
impl fmt::Display for UsageError {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
use self::UsageError::*;
match *self {
InvalidChunkType(expected, actual) => {
write!(
fmt,
"Requested operation is only valid for images with chunk encoding of type: {:?}, got {:?}.",
expected, actual
)
}
}
}
}
impl fmt::Display for TiffError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
@@ -187,6 +227,7 @@ impl fmt::Display for TiffError {
TiffError::IoError(ref e) => e.fmt(fmt),
TiffError::LimitsExceeded => write!(fmt, "The Decoder limits are exceeded"),
TiffError::IntSizeError => write!(fmt, "Platform or format size limits exceeded"),
TiffError::UsageError(ref e) => write!(fmt, "Usage error: {}", e),
}
}
}
@@ -199,6 +240,7 @@ impl Error for TiffError {
TiffError::IoError(..) => "IO error",
TiffError::LimitsExceeded => "Decoder limits exceeded",
TiffError::IntSizeError => "Platform or format size limits exceeded",
TiffError::UsageError(..) => "Invalid usage",
}
}
@@ -216,6 +258,12 @@ impl From<io::Error> for TiffError {
}
}
impl From<str::Utf8Error> for TiffError {
fn from(_err: str::Utf8Error) -> TiffError {
TiffError::FormatError(TiffFormatError::InvalidTag)
}
}
impl From<string::FromUtf8Error> for TiffError {
fn from(_err: string::FromUtf8Error) -> TiffError {
TiffError::FormatError(TiffFormatError::InvalidTag)
+1 -2
View File
@@ -7,7 +7,6 @@
//! * <https://www.adobe.io/open/standards/TIFF.html> - The TIFF specification
extern crate jpeg;
extern crate miniz_oxide;
extern crate weezl;
mod bytecast;
@@ -16,7 +15,7 @@ pub mod encoder;
mod error;
pub mod tags;
pub use self::error::{InflateError, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
pub use self::error::{TiffError, TiffFormatError, TiffResult, TiffUnsupportedError, UsageError};
/// An enumeration over supported color types and their bit depths
#[derive(Copy, PartialEq, Eq, Debug, Clone, Hash)]
+31 -2
View File
@@ -111,35 +111,64 @@ 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,
TileLength = 323,
TileOffsets = 324,
TileByteCounts = 325,
// Data Sample Format
SampleFormat = 339,
SMinSampleValue = 340, // TODO add support
SMaxSampleValue = 341, // TODO add support
// JPEG
JPEGTables = 347,
// GeoTIFF
ModelPixelScaleTag = 33550, // (SoftDesk)
ModelTransformationTag = 34264, // (JPL Carto Group)
ModelTiepointTag = 33922, // (Intergraph)
GeoKeyDirectoryTag = 34735, // (SPOT)
GeoDoubleParamsTag = 34736, // (SPOT)
GeoAsciiParamsTag = 34737, // (SPOT)
GdalNodata = 42113, // Contains areas with missing data
}
}
tags! {
/// The type of an IFD entry (a 2 byte field).
pub enum Type(u16) {
/// 8-bit unsigned integer
BYTE = 1,
/// 8-bit byte that contains a 7-bit ASCII code; the last byte must be zero
ASCII = 2,
/// 16-bit unsigned integer
SHORT = 3,
/// 32-bit unsigned integer
LONG = 4,
/// Fraction stored as two 32-bit unsigned integers
RATIONAL = 5,
/// 8-bit signed integer
SBYTE = 6,
/// 8-bit byte that may contain anything, depending on the field
UNDEFINED = 7,
/// 16-bit signed integer
SSHORT = 8,
/// 32-bit signed integer
SLONG = 9,
/// Fraction stored as two 32-bit signed integers
SRATIONAL = 10,
/// 32-bit IEEE floating point
FLOAT = 11,
/// 64-bit IEEE floating point
DOUBLE = 12,
/// 32-bit unsigned integer (offset)
IFD = 13,
/// BigTIFF 64-bit unsigned integer
LONG8 = 16,
/// BigTIFF 64-bit signed integer
SLONG8 = 17,
/// BigTIFF 64-bit unsigned integer (offset)
IFD8 = 18,
}
}
+330
View File
@@ -29,7 +29,9 @@ macro_rules! test_image_sum {
}
test_image_sum!(test_image_sum_u8, U8, u64);
test_image_sum!(test_image_sum_i8, I8, i64);
test_image_sum!(test_image_sum_u16, U16, u64);
test_image_sum!(test_image_sum_i16, I16, i64);
test_image_sum!(test_image_sum_u32, U32, u64);
test_image_sum!(test_image_sum_u64, U64, u64);
test_image_sum!(test_image_sum_f32, F32, f32);
@@ -135,6 +137,26 @@ fn test_rgb_f32() {
test_image_sum_f32("gradient-3c-32b-float.tiff", ColorType::RGB(32), 472.8405);
}
#[test]
fn test_int8() {
test_image_sum_i8("int8.tif", ColorType::Gray(8), 3111)
}
#[test]
fn test_int8_rgb() {
test_image_sum_i8("int8_rgb.tif", ColorType::RGB(8), -10344)
}
#[test]
fn test_int16() {
test_image_sum_i16("int16.tif", ColorType::Gray(16), 354396);
}
#[test]
fn test_int16_rgb() {
test_image_sum_i16("int16_rgb.tif", ColorType::RGB(16), 1063188);
}
#[test]
fn test_string_tags() {
// these files have null-terminated strings for their Software tag. One has extra bytes after
@@ -193,3 +215,311 @@ fn issue_69() {
//let img_res = decoder.read_image();
//assert!(img_res.is_ok());
//}
#[test]
fn test_tiled_rgb_u8() {
test_image_sum_u8("tiled-rgb-u8.tif", ColorType::RGB(8), 39528948);
}
#[test]
fn test_tiled_rect_rgb_u8() {
test_image_sum_u8("tiled-rect-rgb-u8.tif", ColorType::RGB(8), 62081032);
}
/* #[test]
fn test_tiled_jpeg_rgb_u8() {
test_image_sum_u8("tiled-jpeg-rgb-u8.tif", ColorType::RGB(8), 93031606);
} */
#[test]
fn test_tiled_oversize_gray_i8() {
test_image_sum_i8("tiled-oversize-gray-i8.tif", ColorType::Gray(8), 1214996);
}
#[test]
fn test_tiled_cmyk_i8() {
test_image_sum_i8("tiled-cmyk-i8.tif", ColorType::CMYK(8), 1759101);
}
#[test]
fn test_tiled_incremental() {
let file = "tiled-rgb-u8.tif";
let expected_type = ColorType::RGB(8);
let sums = [
188760, 195639, 108148, 81986, 665088, 366140, 705317, 423366, 172033, 324455, 244102,
81853, 181258, 247971, 129486, 55600, 565625, 422102, 730888, 379271, 232142, 292549,
244045, 86866, 188141, 115036, 150785, 84389, 353170, 459325, 719619, 329594, 278663,
220474, 243048, 113563, 189152, 109684, 179391, 122188, 279651, 622093, 724682, 302459,
268428, 204499, 224255, 124674, 170668, 121868, 192768, 183367, 378029, 585651, 657712,
296790, 241444, 197083, 198429, 134869, 182318, 86034, 203655, 182338, 297255, 601284,
633813, 242531, 228578, 206441, 193552, 125412, 181527, 165439, 202531, 159538, 268388,
565790, 611382, 272967, 236497, 215154, 158881, 90806, 106114, 182342, 191824, 186138,
215174, 393193, 701228, 198866, 227944, 193830, 166330, 49008, 55719, 122820, 197316,
161969, 203152, 170986, 624427, 188605, 186187, 111064, 115192, 39538, 48626, 163929,
144682, 135796, 194141, 154198, 584125, 180255, 153524, 121433, 132641, 35743, 47798,
152343, 162874, 167664, 160175, 133038, 659882, 138339, 166470, 124173, 118929, 51317,
45267, 155776, 161331, 161006, 130052, 137618, 337291, 106481, 161999, 127343, 87724,
59540, 63907, 155677, 140668, 141523, 108061, 168657, 186482, 98599, 147614, 139963, 90444,
56602, 92547, 125644, 134212, 126569, 144153, 179800, 174516, 133969, 129399, 117681,
83305, 55075, 110737, 115108, 128572, 128911, 130922, 179986, 143288, 145884, 155856,
96683, 94057, 56238, 79649, 71651, 70182, 75010, 77009, 98855, 78979, 74341, 83482, 53403,
59842, 30305,
];
let path = PathBuf::from(TEST_IMAGE_DIR).join(file);
let img_file = File::open(path).expect("Cannot find test image!");
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
assert_eq!(decoder.colortype().unwrap(), expected_type);
let tiles = decoder.tile_count().unwrap();
assert_eq!(tiles as usize, sums.len());
for tile in 0..tiles {
match decoder.read_tile().unwrap() {
DecodingResult::U8(res) => {
let sum: u64 = res.into_iter().map(<u64>::from).sum();
assert_eq!(sum, sums[tile as usize]);
}
_ => panic!("Wrong bit depth"),
}
}
}
#[test]
fn test_div_zero() {
use tiff::{TiffError, TiffFormatError};
let image = [
73, 73, 42, 0, 8, 0, 0, 0, 8, 0, 0, 1, 4, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 40, 1, 0, 0,
0, 158, 0, 0, 251, 3, 1, 3, 0, 1, 0, 0, 0, 1, 0, 0, 39, 6, 1, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0,
17, 1, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 3, 0, 1, 0, 0, 0, 158, 0, 0, 251, 67, 1, 3, 0,
1, 0, 0, 0, 40, 0, 0, 0, 66, 1, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 178, 178, 178, 178,
178, 178, 178,
];
let err = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap_err();
match err {
TiffError::FormatError(TiffFormatError::StripTileTagConflict) => {}
unexpected => panic!("Unexpected error {}", unexpected),
}
}
#[test]
fn test_too_many_value_bytes() {
let image = [
73, 73, 43, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 8, 0, 0, 0,
23, 0, 12, 0, 0, 65, 4, 0, 1, 6, 0, 0, 1, 16, 0, 1, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0,
0, 0, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, 59, 73, 84, 186, 202, 83, 240, 66, 1, 53, 22, 56, 47,
0, 0, 0, 0, 0, 0, 1, 222, 4, 0, 58, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 4, 0, 0, 100, 0,
0, 89, 89, 89, 89, 89, 89, 89, 89, 96, 1, 20, 89, 89, 89, 89, 18,
];
let error = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap_err();
match error {
tiff::TiffError::LimitsExceeded => {}
unexpected => panic!("Unexpected error {}", unexpected),
}
}
#[test]
fn test_bad_strip_count() {
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,
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,
17, 1, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 1, 3, 0, 0, 0, 0, 0, 246, 16, 0, 0, 22, 1, 4, 0, 1,
0, 0, 0, 40, 0, 251, 255, 23, 1, 4, 0, 1, 0, 0, 0, 48, 178, 178, 178, 178, 178, 178, 178,
178, 178, 178,
];
let mut decoder = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap();
let err = decoder.read_image().unwrap_err();
match err {
tiff::TiffError::IntSizeError => {}
unexpected => panic!("Unexpected error {}", unexpected),
}
}
#[test]
fn test_too_big_buffer_size() {
let image = [
73, 73, 42, 0, 8, 0, 0, 0, 8, 0, 0, 1, 4, 0, 1, 0, 0, 0, 99, 255, 255, 254, 1, 1, 4, 0, 1,
0, 0, 0, 158, 0, 0, 251, 3, 1, 3, 255, 254, 255, 255, 0, 1, 0, 0, 0, 6, 1, 3, 0, 1, 0, 0,
0, 0, 0, 0, 0, 17, 1, 4, 0, 9, 0, 0, 0, 0, 0, 0, 0, 2, 1, 3, 0, 2, 0, 0, 0, 63, 0, 0, 0,
22, 1, 4, 0, 1, 0, 0, 0, 44, 0, 0, 0, 23, 1, 4, 0, 0, 0, 0, 0, 0, 0, 2, 1, 3, 1, 0, 178,
178,
];
let mut decoder = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap();
let err = decoder.read_image().unwrap_err();
match err {
tiff::TiffError::LimitsExceeded => {}
unexpected => panic!("Unexpected error {}", unexpected),
}
}
#[test]
fn test_stripped_image_overflow() {
let image = [
73, 73, 42, 0, 8, 0, 0, 0, 8, 0, 0, 1, 4, 0, 1, 0, 0, 0, 100, 0, 0, 148, 1, 1, 4, 0, 1, 0,
0, 0, 158, 0, 0, 251, 3, 1, 3, 255, 254, 255, 255, 0, 1, 0, 0, 0, 6, 1, 3, 0, 1, 0, 0, 0,
0, 0, 0, 0, 17, 1, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 1, 3, 0, 2, 0, 0, 0, 63, 0, 0, 0, 22,
1, 4, 0, 1, 0, 0, 0, 44, 0, 248, 255, 23, 1, 4, 0, 1, 0, 0, 0, 178, 178, 178, 0, 1, 178,
178, 178,
];
let mut decoder = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap();
let err = decoder.read_image().unwrap_err();
match err {
tiff::TiffError::LimitsExceeded => {}
unexpected => panic!("Unexpected error {}", unexpected),
}
}
#[test]
fn oom() {
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,
0, 158, 0, 0, 251, 3, 1, 3, 0, 1, 0, 0, 0, 7, 0, 0, 0, 6, 1, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0,
17, 1, 4, 0, 1, 0, 0, 0, 3, 77, 0, 0, 1, 1, 3, 0, 1, 0, 0, 0, 3, 128, 0, 0, 22, 1, 4, 0, 1,
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 err = decoder.read_image().unwrap_err();
match err {
tiff::TiffError::LimitsExceeded => {}
unexpected => panic!("Unexpected error {}", unexpected),
}
}
#[test]
fn oom_2() {
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,
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,
17, 1, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 3, 0, 1, 0, 0, 0, 3, 128, 0, 0, 22, 1, 4, 0, 1,
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 err = decoder.read_image().unwrap_err();
match err {
tiff::TiffError::LimitsExceeded => {}
unexpected => panic!("Unexpected error {}", unexpected),
}
}
#[test]
fn invalid_jpeg_tag() {
use tiff::tags;
use tiff::{TiffError, TiffFormatError};
let image = [
73, 73, 42, 0, 8, 0, 0, 0, 15, 0, 0, 254, 44, 1, 0, 0, 0, 0, 0, 32, 0, 0, 0, 1, 4, 0, 1, 0,
0, 0, 0, 1, 0, 0, 91, 1, 1, 0, 0, 0, 0, 0, 242, 4, 0, 0, 0, 22, 0, 56, 77, 0, 77, 1, 0, 0,
73, 42, 0, 1, 4, 0, 1, 0, 0, 0, 4, 0, 8, 0, 0, 1, 4, 0, 1, 0, 0, 0, 158, 0, 0, 251, 3, 1,
3, 0, 1, 0, 0, 0, 7, 0, 0, 0, 6, 1, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, 17, 1, 4, 0, 1, 0, 0, 0,
0, 0, 0, 0, 1, 1, 3, 0, 1, 0, 0, 0, 0, 0, 0, 4, 61, 1, 18, 0, 1, 0, 0, 0, 202, 0, 0, 0, 17,
1, 100, 0, 129, 0, 0, 0, 0, 0, 0, 0, 232, 254, 252, 255, 254, 255, 255, 255, 1, 29, 0, 0,
22, 1, 3, 0, 1, 0, 0, 0, 16, 0, 0, 0, 23, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 123, 73, 254, 0,
73,
];
let mut decoder = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap();
let err = decoder.read_image().unwrap_err();
match err {
TiffError::FormatError(TiffFormatError::InvalidTagValueType(tags::Tag::JPEGTables)) => {}
unexpected => panic!("Unexpected error {}", unexpected),
}
}
#[test]
fn invalid_jpeg_tag_2() {
use tiff::tags;
use tiff::{TiffError, TiffFormatError};
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,
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, 3, 1, 3, 0, 1, 0, 0, 0, 7, 0, 0, 0, 6, 1, 3, 0, 1, 0, 0, 0, 1, 0, 0, 64, 14, 1, 0,
2, 0, 0, 148, 0, 206, 0, 0, 0, 17, 1, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
1, 0, 0, 0, 22, 1, 4, 0, 17, 0, 0, 201, 1, 0, 0, 0, 23, 1, 2, 0, 20, 0, 0, 0, 194, 0, 0, 0,
91, 1, 7, 0, 5, 0, 0, 0, 64, 0, 0, 0, 237, 254, 65, 255, 255, 255, 255, 255, 1, 0, 0, 0,
22, 1, 4, 0, 1, 0, 0, 0, 42, 0, 0, 0, 23, 1, 255, 255, 255, 255, 255, 36, 36, 0, 0, 0, 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 err = decoder.read_image().unwrap_err();
match err {
TiffError::FormatError(TiffFormatError::InvalidTagValueType(tags::Tag::JPEGTables)) => {}
unexpected => panic!("Unexpected error {}", unexpected),
}
}
#[test]
fn multiple_images_strip_height() {
use tiff::TiffError;
let image = [
73, 73, 42, 0, 8, 0, 0, 0, 8, 0, 0, 1, 4, 0, 1, 0, 0, 0, 2, 0, 0, 0, 61, 1, 9, 0, 46, 22,
128, 0, 0, 0, 0, 1, 6, 1, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 17, 1, 4, 0, 27, 0, 0, 0, 0, 0, 0,
0, 1, 1, 3, 0, 1, 0, 0, 0, 17, 1, 0, 231, 22, 1, 1, 0, 1, 0, 0, 0, 130, 0, 0, 0, 23, 1, 4,
0, 14, 0, 0, 0, 0, 0, 0, 0, 133, 133, 133, 77, 77, 77, 0, 0, 22, 128, 0, 255, 255, 255,
255, 255,
];
let mut decoder = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap();
let _ = decoder.read_image().unwrap();
let err = decoder.read_image().unwrap_err();
match err {
TiffError::IntSizeError => {}
unexpected => panic!("Unexpected error {}", unexpected),
}
}
#[test]
fn timeout() {
use tiff::{TiffError, TiffFormatError};
let image = [
73, 73, 42, 0, 8, 0, 0, 0, 16, 0, 254, 0, 4, 0, 1, 68, 0, 0, 0, 2, 0, 32, 254, 252, 0, 109,
0, 129, 0, 0, 0, 32, 0, 58, 0, 1, 4, 0, 1, 0, 6, 0, 0, 0, 8, 0, 0, 1, 73, 73, 42, 0, 8, 0,
0, 0, 8, 0, 0, 1, 4, 0, 1, 0, 0, 0, 21, 0, 0, 0, 61, 1, 255, 128, 9, 0, 0, 8, 0, 1, 113, 2,
3, 1, 3, 0, 1, 0, 0, 0, 5, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 112, 0, 0, 36, 0, 0,
0, 112, 56, 200, 0, 5, 0, 0, 64, 0, 0, 1, 0, 4, 0, 0, 0, 2, 0, 6, 1, 3, 0, 1, 0, 0, 0, 0,
0, 0, 4, 17, 1, 1, 0, 93, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 3, 6, 0, 231, 22, 1,
1, 0, 1, 0, 0, 0, 2, 64, 118, 36, 23, 1, 1, 0, 43, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 4, 0, 8,
0, 0, 73, 73, 42, 0, 8, 0, 0, 0, 0, 0, 32,
];
let error = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap_err();
match error {
TiffError::FormatError(TiffFormatError::CycleInOffsets) => {}
e => panic!("Unexpected error {:?}", e),
}
}
#[test]
fn test_no_rows_per_strip() {
test_image_sum_u8("no_rows_per_strip.tiff", ColorType::RGB(8), 99448840);
}
+164 -5
View File
@@ -1,7 +1,7 @@
extern crate tiff;
use tiff::decoder::{ifd, Decoder, DecodingResult};
use tiff::encoder::{colortype, SRational, TiffEncoder};
use tiff::encoder::{colortype, Ifd, Ifd8, SRational, TiffEncoder};
use tiff::tags::Tag;
use tiff::ColorType;
@@ -48,6 +48,92 @@ fn encode_decode() {
}
}
#[test]
fn encode_decode_big() {
let mut image_data = Vec::new();
for x in 0..100 {
for y in 0..100u8 {
let val = x + y;
image_data.push(val);
image_data.push(val);
image_data.push(val);
}
}
let mut file = Cursor::new(Vec::new());
{
let mut tiff = TiffEncoder::new_big(&mut file).unwrap();
let mut image = tiff.new_image::<colortype::RGB8>(100, 100).unwrap();
image
.encoder()
.write_tag(Tag::Artist, "Image-tiff")
.unwrap();
image.write_data(&image_data).unwrap();
}
{
file.seek(SeekFrom::Start(0)).unwrap();
let mut decoder = Decoder::new(&mut file).unwrap();
assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(8));
assert_eq!(decoder.dimensions().unwrap(), (100, 100));
assert_eq!(
decoder.get_tag(Tag::Artist).unwrap(),
ifd::Value::Ascii("Image-tiff".into())
);
if let DecodingResult::U8(img_res) = decoder.read_image().unwrap() {
assert_eq!(image_data, img_res);
} else {
panic!("Wrong data type");
}
}
}
#[test]
fn test_encode_ifd() {
let mut data = Cursor::new(Vec::new());
{
let mut tiff = TiffEncoder::new(&mut data).unwrap();
let mut image_encoder = tiff.new_image::<colortype::Gray8>(1, 1).unwrap();
let encoder = image_encoder.encoder();
// Use the "reusable" tags section as per the TIFF6 spec
encoder.write_tag(Tag::Unknown(65000), Ifd(42u32)).unwrap();
encoder
.write_tag(Tag::Unknown(65001), &[Ifd(100u32)][..])
.unwrap();
encoder
.write_tag(Tag::Unknown(65002), &[Ifd(1u32), Ifd(2u32), Ifd(3u32)][..])
.unwrap();
encoder.write_tag(Tag::Unknown(65010), Ifd8(43u64)).unwrap();
encoder
.write_tag(Tag::Unknown(65011), &[Ifd8(100u64)][..])
.unwrap();
encoder
.write_tag(
Tag::Unknown(65012),
&[Ifd8(1u64), Ifd8(2u64), Ifd8(3u64)][..],
)
.unwrap();
}
// Rewind the cursor for reading
data.set_position(0);
{
let mut decoder = Decoder::new(&mut data).unwrap();
assert_eq!(decoder.assert_tag_u32(65000), 42);
assert_eq!(decoder.assert_tag_u32_vec(65000), [42]);
assert_eq!(decoder.assert_tag_u32_vec(65001), [100]);
assert_eq!(decoder.assert_tag_u32_vec(65002), [1, 2, 3]);
assert_eq!(decoder.assert_tag_u64(65010), 43);
assert_eq!(decoder.assert_tag_u64_vec(65010), [43]);
assert_eq!(decoder.assert_tag_u64_vec(65011), [100]);
assert_eq!(decoder.assert_tag_u64_vec(65012), [1, 2, 3]);
}
}
#[test]
/// Test that attempting to encode when the input buffer is undersized returns
/// an error rather than panicking.
@@ -182,6 +268,10 @@ trait AssertDecode {
fn assert_tag_u32_vec(&mut self, tag: u16) -> Vec<u32>;
fn assert_tag_i32(&mut self, tag: u16) -> i32;
fn assert_tag_i32_vec(&mut self, tag: u16) -> Vec<i32>;
fn assert_tag_u64(&mut self, tag: u16) -> u64;
fn assert_tag_u64_vec(&mut self, tag: u16) -> Vec<u64>;
fn assert_tag_i64(&mut self, tag: u16) -> i64;
fn assert_tag_i64_vec(&mut self, tag: u16) -> Vec<i64>;
}
impl<R: std::io::Read + std::io::Seek> AssertDecode for Decoder<R> {
@@ -203,6 +293,24 @@ impl<R: std::io::Read + std::io::Seek> AssertDecode for Decoder<R> {
.into_i32_vec()
.unwrap()
}
fn assert_tag_u64(&mut self, tag: u16) -> u64 {
self.get_tag(Tag::Unknown(tag)).unwrap().into_u64().unwrap()
}
fn assert_tag_u64_vec(&mut self, tag: u16) -> Vec<u64> {
self.get_tag(Tag::Unknown(tag))
.unwrap()
.into_u64_vec()
.unwrap()
}
fn assert_tag_i64(&mut self, tag: u16) -> i64 {
self.get_tag(Tag::Unknown(tag)).unwrap().into_i64().unwrap()
}
fn assert_tag_i64_vec(&mut self, tag: u16) -> Vec<i64> {
self.get_tag(Tag::Unknown(tag))
.unwrap()
.into_i64_vec()
.unwrap()
}
}
#[test]
@@ -289,12 +397,20 @@ fn test_signed() {
.write_tag(Tag::Unknown(65022), &[-1_i32, 2][..])
.unwrap();
encoder.write_tag(Tag::Unknown(65030), -1_i64).unwrap();
encoder
.write_tag(Tag::Unknown(65030), make_srational(-1))
.write_tag(Tag::Unknown(65031), &[-1_i64][..])
.unwrap();
encoder
.write_tag(Tag::Unknown(65032), &[-1_i64, 2][..])
.unwrap();
encoder
.write_tag(Tag::Unknown(65040), make_srational(-1))
.unwrap();
encoder
.write_tag(
Tag::Unknown(65031),
Tag::Unknown(65041),
&[make_srational(-1), make_srational(2)][..],
)
.unwrap();
@@ -321,8 +437,12 @@ fn test_signed() {
assert_eq!(decoder.assert_tag_i32_vec(65021), [-1]);
assert_eq!(decoder.assert_tag_i32_vec(65022), [-1, 2]);
assert_eq!(decoder.assert_tag_i32_vec(65030), [-1, 100]);
assert_eq!(decoder.assert_tag_i32_vec(65031), [-1_i32, 100, 2, 100]);
assert_eq!(decoder.assert_tag_i64(65030), -1);
assert_eq!(decoder.assert_tag_i64_vec(65031), [-1]);
assert_eq!(decoder.assert_tag_i64_vec(65032), [-1, 2]);
assert_eq!(decoder.assert_tag_i32_vec(65040), [-1, 100]);
assert_eq!(decoder.assert_tag_i32_vec(65041), [-1_i32, 100, 2, 100]);
}
}
@@ -360,3 +480,42 @@ fn test_multipage_image() {
assert_eq!(img_decoder.dimensions().unwrap(), (3, 3));
}
}
#[test]
/// verify rows per strip setting
fn test_rows_per_strip() {
let mut file = Cursor::new(Vec::new());
{
let mut img_encoder = TiffEncoder::new(&mut file).unwrap();
let mut image = img_encoder.new_image::<colortype::Gray8>(100, 100).unwrap();
assert_eq!(image.next_strip_sample_count(), 100 * 100);
image.rows_per_strip(2).unwrap();
assert_eq!(image.next_strip_sample_count(), 2 * 100);
let img2: Vec<u8> = vec![0; 2 * 100];
image.write_strip(&img2[..]).unwrap();
assert!(image.rows_per_strip(5).is_err());
for i in 1..50 {
let img2: Vec<u8> = vec![i; 2 * 100];
image.write_strip(&img2[..]).unwrap();
}
assert!(image.write_strip(&img2[..]).is_err());
image.finish().unwrap();
}
file.seek(SeekFrom::Start(0)).unwrap();
{
let mut decoder = Decoder::new(&mut file).unwrap();
assert_eq!(decoder.get_tag_u64(Tag::RowsPerStrip).unwrap(), 2);
assert_eq!(decoder.strip_count().unwrap(), 50);
for i in 0..50 {
let img2 = [i; 2 * 100];
match decoder.read_strip().unwrap() {
DecodingResult::U8(data) => assert_eq!(&img2[..], &data[..]),
other => panic!("Incorrect strip type {:?}", other),
}
}
}
}
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.