Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0ba4a6788 | ||
|
|
4a2e76858b | ||
|
|
a67f50568c | ||
|
|
d6f6b8a01e | ||
|
|
8ec91b64f6 | ||
|
|
60f866c119 | ||
|
|
daa0e3eabf | ||
|
|
24d203c7c3 | ||
|
|
473868a7bb | ||
|
|
aefea7786f | ||
|
|
d1ad9b2664 | ||
|
|
bd1d085189 | ||
|
|
fcfcecb2df | ||
|
|
aa10d27a28 | ||
|
|
8fa175c371 | ||
|
|
2a15aa6b7e | ||
|
|
42feeba8da | ||
|
|
0da9770916 | ||
|
|
c30ea911a2 | ||
|
|
da07c0f4f5 | ||
|
|
289fab2fcd | ||
|
|
21252d9d89 | ||
|
|
ffaccf2b52 | ||
|
|
4dea7c4c81 | ||
|
|
aa42fcb1bb | ||
|
|
1f7b25ef55 | ||
|
|
6c65e66440 | ||
|
|
a733ff13d9 | ||
|
|
b1cd459330 | ||
|
|
4f1c88e35f | ||
|
|
7307e96782 | ||
|
|
a08604b015 | ||
|
|
9333d99c92 |
No files matched your search
+38
-17
@@ -9,32 +9,53 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
rust: ["1.61", stable, beta, nightly]
|
rust: ["1.61.0", stable, beta, nightly]
|
||||||
command: [build, test]
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- run: rustup default ${{ matrix.rust }}
|
- uses: dtolnay/rust-toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: ${{ matrix.rust }}
|
||||||
|
- name: Cache Cargo Dependencies
|
||||||
|
uses: Swatinem/rust-cache@v2
|
||||||
|
with:
|
||||||
|
cache-on-failure: true
|
||||||
|
- name: Install MSRV Cargo.lock
|
||||||
|
if: ${{ matrix.rust == '1.61.0' }}
|
||||||
|
run: mv Cargo.lock.msrv Cargo.lock
|
||||||
- name: build
|
- name: build
|
||||||
run: >
|
run: cargo build -v
|
||||||
cargo build --verbose --no-default-features --features "$FEATURES"
|
|
||||||
- name: test
|
- name: test
|
||||||
run: >
|
if: ${{ matrix.rust != '1.61.0' }}
|
||||||
cargo test --tests --benches --no-default-features --features "$FEATURES"
|
run: cargo test -v && cargo doc -v
|
||||||
if: ${{ matrix.rust != '1.61' }}
|
|
||||||
env:
|
|
||||||
FEATURES: ${{ matrix.features }}
|
|
||||||
rustfmt:
|
rustfmt:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions-rs/toolchain@v1
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
toolchain: stable
|
|
||||||
override: true
|
|
||||||
components: rustfmt
|
components: rustfmt
|
||||||
- name: Run rustfmt check
|
- name: Run rustfmt check
|
||||||
uses: actions-rs/cargo@v1
|
run: cargo fmt -- --check
|
||||||
with:
|
|
||||||
command: fmt
|
|
||||||
args: -- --check
|
|
||||||
|
|
||||||
|
clippy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
components: clippy
|
||||||
|
- run: cargo clippy --all-features # TODO: enable "-- -D warnings" once all outstanding clippy lints are fixed
|
||||||
|
|
||||||
|
verify_msrv:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Install or use cached `cargo-msrv`
|
||||||
|
uses: baptiste0928/cargo-install@v1
|
||||||
|
with:
|
||||||
|
crate: cargo-msrv
|
||||||
|
- name: Install MSRV Cargo.lock
|
||||||
|
run: mv Cargo.lock.msrv Cargo.lock
|
||||||
|
- name: Verify Minimum Rust Version
|
||||||
|
run: cargo-msrv verify
|
||||||
+33
@@ -1,3 +1,36 @@
|
|||||||
|
# Version 0.9.1
|
||||||
|
|
||||||
|
New features:
|
||||||
|
- Basic support for planar configuration.
|
||||||
|
- Allow arbitrary number of samples as long as all have the same bit depth.
|
||||||
|
|
||||||
|
Fixes:
|
||||||
|
- Don't panic when parsing metadata when custom compression is used.
|
||||||
|
|
||||||
|
# Version 0.9.0
|
||||||
|
|
||||||
|
New features:
|
||||||
|
* Added support for photometric interpretation `YCbCr` and added related
|
||||||
|
`ColorType`.
|
||||||
|
|
||||||
|
Fixes:
|
||||||
|
* Decoding tiled images calculates padding correctly when image width or height
|
||||||
|
is a multiple of tile size. It could previously corrupt the last tile per row
|
||||||
|
by skipping over data.
|
||||||
|
|
||||||
|
# Version 0.8.1
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
* The jpeg decoder gained to ability to utilize the Photometric Interpretation
|
||||||
|
directly instead of relying on a custom APP segment.
|
||||||
|
|
||||||
|
Fixes:
|
||||||
|
* A spurious error within the PackBits decoder lead to the incorrect results
|
||||||
|
(wrong bits or errors), based on the maximum size of reads from the
|
||||||
|
underlying reader.
|
||||||
|
* Removed a panic path in jpeg decoding, when a feature such as photometric
|
||||||
|
interpretation is not supported. An error is returned instead.
|
||||||
|
|
||||||
# Version 0.8.0
|
# Version 0.8.0
|
||||||
|
|
||||||
Changes:
|
Changes:
|
||||||
|
|||||||
+638
@@ -0,0 +1,638 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "adler"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atty"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi 0.1.19",
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bumpalo"
|
||||||
|
version = "3.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cast"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "2.34.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"textwrap",
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc32fast"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "criterion"
|
||||||
|
version = "0.3.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f"
|
||||||
|
dependencies = [
|
||||||
|
"atty",
|
||||||
|
"cast",
|
||||||
|
"clap",
|
||||||
|
"criterion-plot",
|
||||||
|
"csv",
|
||||||
|
"itertools",
|
||||||
|
"lazy_static",
|
||||||
|
"num-traits",
|
||||||
|
"oorandom",
|
||||||
|
"plotters",
|
||||||
|
"rayon",
|
||||||
|
"regex",
|
||||||
|
"serde",
|
||||||
|
"serde_cbor",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"tinytemplate",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "criterion-plot"
|
||||||
|
version = "0.4.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876"
|
||||||
|
dependencies = [
|
||||||
|
"cast",
|
||||||
|
"itertools",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-channel"
|
||||||
|
version = "0.5.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-deque"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"crossbeam-epoch",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-epoch"
|
||||||
|
version = "0.9.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"cfg-if",
|
||||||
|
"crossbeam-utils",
|
||||||
|
"memoffset",
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.8.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "csv"
|
||||||
|
version = "1.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "626ae34994d3d8d668f4269922248239db4ae42d538b14c398b74a52208e8086"
|
||||||
|
dependencies = [
|
||||||
|
"csv-core",
|
||||||
|
"itoa",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "csv-core"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "flate2"
|
||||||
|
version = "1.0.26"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743"
|
||||||
|
dependencies = [
|
||||||
|
"crc32fast",
|
||||||
|
"miniz_oxide",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "half"
|
||||||
|
version = "1.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.1.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.10.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jpeg-decoder"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "js-sys"
|
||||||
|
version = "0.3.64"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a"
|
||||||
|
dependencies = [
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.147"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memoffset"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "miniz_oxide"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
|
||||||
|
dependencies = [
|
||||||
|
"adler",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_cpus"
|
||||||
|
version = "1.16.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi 0.3.2",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.18.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "oorandom"
|
||||||
|
version = "11.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "plotters"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
"plotters-backend",
|
||||||
|
"plotters-svg",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "plotters-backend"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "plotters-svg"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab"
|
||||||
|
dependencies = [
|
||||||
|
"plotters-backend",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.66"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.33"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon"
|
||||||
|
version = "1.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"rayon-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon-core"
|
||||||
|
version = "1.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-channel",
|
||||||
|
"crossbeam-deque",
|
||||||
|
"crossbeam-utils",
|
||||||
|
"num_cpus",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.9.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.3.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "same-file"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.183"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_cbor"
|
||||||
|
version = "0.11.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5"
|
||||||
|
dependencies = [
|
||||||
|
"half",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.183"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.105"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "textwrap"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tiff"
|
||||||
|
version = "0.9.0"
|
||||||
|
dependencies = [
|
||||||
|
"criterion",
|
||||||
|
"flate2",
|
||||||
|
"jpeg-decoder",
|
||||||
|
"weezl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tinytemplate"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "walkdir"
|
||||||
|
version = "2.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698"
|
||||||
|
dependencies = [
|
||||||
|
"same-file",
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen"
|
||||||
|
version = "0.2.87"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"wasm-bindgen-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-backend"
|
||||||
|
version = "0.2.87"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro"
|
||||||
|
version = "0.2.87"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"wasm-bindgen-macro-support",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro-support"
|
||||||
|
version = "0.2.87"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-backend",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-shared"
|
||||||
|
version = "0.2.87"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "web-sys"
|
||||||
|
version = "0.3.64"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "weezl"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-util"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "tiff"
|
name = "tiff"
|
||||||
version = "0.8.0"
|
version = "0.9.1"
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
# note: when changed, also update test runner in `.github/workflows/rust.yml`
|
# note: when changed, also update test runner in `.github/workflows/rust.yml`
|
||||||
|
|||||||
+1
-1
@@ -537,7 +537,7 @@ impl Entry {
|
|||||||
Ok(UnsignedBig(u64::from(buf[0])))
|
Ok(UnsignedBig(u64::from(buf[0])))
|
||||||
}),
|
}),
|
||||||
Type::SBYTE => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| {
|
Type::SBYTE => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| {
|
||||||
Ok(SignedBig(i64::from(reader.read_i8()? as i8)))
|
Ok(SignedBig(i64::from(reader.read_i8()?)))
|
||||||
}),
|
}),
|
||||||
Type::SHORT => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| {
|
Type::SHORT => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| {
|
||||||
Ok(UnsignedBig(u64::from(reader.read_u16()?)))
|
Ok(UnsignedBig(u64::from(reader.read_u16()?)))
|
||||||
|
|||||||
+171
-59
@@ -1,14 +1,13 @@
|
|||||||
use super::ifd::{Directory, Value};
|
use super::ifd::{Directory, Value};
|
||||||
use super::stream::{
|
use super::stream::{ByteOrder, DeflateReader, LZWReader, PackBitsReader};
|
||||||
add_app14segment, ByteOrder, DeflateReader, JpegReader, JpegTagApp14Transform, LZWReader,
|
|
||||||
PackBitsReader,
|
|
||||||
};
|
|
||||||
use super::tag_reader::TagReader;
|
use super::tag_reader::TagReader;
|
||||||
use super::{fp_predict_f32, fp_predict_f64, DecodingBuffer, Limits};
|
use super::{fp_predict_f32, fp_predict_f64, DecodingBuffer, Limits};
|
||||||
use super::{stream::SmartReader, ChunkType};
|
use super::{stream::SmartReader, ChunkType};
|
||||||
use crate::tags::{CompressionMethod, PhotometricInterpretation, Predictor, SampleFormat, Tag};
|
use crate::tags::{
|
||||||
|
CompressionMethod, PhotometricInterpretation, PlanarConfiguration, Predictor, SampleFormat, Tag,
|
||||||
|
};
|
||||||
use crate::{ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError, UsageError};
|
use crate::{ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError, UsageError};
|
||||||
use std::convert::{TryFrom, TryInto};
|
use std::convert::TryFrom;
|
||||||
use std::io::{self, Cursor, Read, Seek};
|
use std::io::{self, Cursor, Read, Seek};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -35,12 +34,11 @@ impl TileAttributes {
|
|||||||
(self.image_height + self.tile_length - 1) / self.tile_length
|
(self.image_height + self.tile_length - 1) / self.tile_length
|
||||||
}
|
}
|
||||||
fn padding_right(&self) -> usize {
|
fn padding_right(&self) -> usize {
|
||||||
self.tile_width - self.image_width % self.tile_width
|
(self.tile_width - self.image_width % self.tile_width) % self.tile_width
|
||||||
}
|
}
|
||||||
fn padding_down(&self) -> usize {
|
fn padding_down(&self) -> usize {
|
||||||
self.tile_length - self.image_height % self.tile_length
|
(self.tile_length - self.image_height % self.tile_length) % self.tile_length
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_padding(&self, tile: usize) -> (usize, usize) {
|
pub fn get_padding(&self, tile: usize) -> (usize, usize) {
|
||||||
let row = tile / self.tiles_across();
|
let row = tile / self.tiles_across();
|
||||||
let column = tile % self.tiles_across();
|
let column = tile % self.tiles_across();
|
||||||
@@ -66,15 +64,16 @@ pub(crate) struct Image {
|
|||||||
pub ifd: Option<Directory>,
|
pub ifd: Option<Directory>,
|
||||||
pub width: u32,
|
pub width: u32,
|
||||||
pub height: u32,
|
pub height: u32,
|
||||||
pub bits_per_sample: Vec<u8>,
|
pub bits_per_sample: u8,
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub samples: u8,
|
pub samples: u16,
|
||||||
pub sample_format: Vec<SampleFormat>,
|
pub sample_format: Vec<SampleFormat>,
|
||||||
pub photometric_interpretation: PhotometricInterpretation,
|
pub photometric_interpretation: PhotometricInterpretation,
|
||||||
pub compression_method: CompressionMethod,
|
pub compression_method: CompressionMethod,
|
||||||
pub predictor: Predictor,
|
pub predictor: Predictor,
|
||||||
pub jpeg_tables: Option<Arc<Vec<u8>>>,
|
pub jpeg_tables: Option<Arc<Vec<u8>>>,
|
||||||
pub chunk_type: ChunkType,
|
pub chunk_type: ChunkType,
|
||||||
|
pub planar_config: PlanarConfiguration,
|
||||||
pub strip_decoder: Option<StripDecodeState>,
|
pub strip_decoder: Option<StripDecodeState>,
|
||||||
pub tile_attributes: Option<TileAttributes>,
|
pub tile_attributes: Option<TileAttributes>,
|
||||||
pub chunk_offsets: Vec<u64>,
|
pub chunk_offsets: Vec<u64>,
|
||||||
@@ -113,15 +112,14 @@ impl Image {
|
|||||||
// Try to parse both the compression method and the number, format, and bits of the included samples.
|
// Try to parse both the compression method and the number, format, and bits of the included samples.
|
||||||
// If they are not explicitly specified, those tags are reset to their default values and not carried from previous images.
|
// If they are not explicitly specified, those tags are reset to their default values and not carried from previous images.
|
||||||
let compression_method = match tag_reader.find_tag(Tag::Compression)? {
|
let compression_method = match tag_reader.find_tag(Tag::Compression)? {
|
||||||
Some(val) => CompressionMethod::from_u16(val.into_u16()?)
|
Some(val) => CompressionMethod::from_u16_exhaustive(val.into_u16()?),
|
||||||
.ok_or(TiffUnsupportedError::UnknownCompressionMethod)?,
|
|
||||||
None => CompressionMethod::None,
|
None => CompressionMethod::None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let jpeg_tables = if compression_method == CompressionMethod::ModernJPEG
|
let jpeg_tables = if compression_method == CompressionMethod::ModernJPEG
|
||||||
&& ifd.contains_key(&Tag::JPEGTables)
|
&& ifd.contains_key(&Tag::JPEGTables)
|
||||||
{
|
{
|
||||||
let mut vec = tag_reader
|
let vec = tag_reader
|
||||||
.find_tag(Tag::JPEGTables)?
|
.find_tag(Tag::JPEGTables)?
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.into_u8_vec()?;
|
.into_u8_vec()?;
|
||||||
@@ -131,22 +129,19 @@ impl Image {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Can we avoid this somehow?
|
|
||||||
if photometric_interpretation == PhotometricInterpretation::RGB {
|
|
||||||
add_app14segment(&mut vec, JpegTagApp14Transform::App14TransformUnknown)
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(Arc::new(vec))
|
Some(Arc::new(vec))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let samples = tag_reader
|
let samples: u16 = tag_reader
|
||||||
.find_tag(Tag::SamplesPerPixel)?
|
.find_tag(Tag::SamplesPerPixel)?
|
||||||
.map(Value::into_u16)
|
.map(Value::into_u16)
|
||||||
.transpose()?
|
.transpose()?
|
||||||
.unwrap_or(1)
|
.unwrap_or(1);
|
||||||
.try_into()?;
|
if samples == 0 {
|
||||||
|
return Err(TiffFormatError::SamplesPerPixelIsZero.into());
|
||||||
|
}
|
||||||
|
|
||||||
let sample_format = match tag_reader.find_tag_uint_vec(Tag::SampleFormat)? {
|
let sample_format = match tag_reader.find_tag_uint_vec(Tag::SampleFormat)? {
|
||||||
Some(vals) => {
|
Some(vals) => {
|
||||||
@@ -165,12 +160,22 @@ impl Image {
|
|||||||
None => vec![SampleFormat::Uint],
|
None => vec![SampleFormat::Uint],
|
||||||
};
|
};
|
||||||
|
|
||||||
let bits_per_sample = match samples {
|
let bits_per_sample: Vec<u8> = tag_reader
|
||||||
1 | 3 | 4 => tag_reader
|
.find_tag_uint_vec(Tag::BitsPerSample)?
|
||||||
.find_tag_uint_vec(Tag::BitsPerSample)?
|
.unwrap_or_else(|| vec![1]);
|
||||||
.unwrap_or_else(|| vec![1]),
|
|
||||||
_ => return Err(TiffUnsupportedError::UnsupportedSampleDepth(samples).into()),
|
// Technically bits_per_sample.len() should be *equal* to samples, but libtiff also allows
|
||||||
};
|
// it to be a single value that applies to all samples.
|
||||||
|
if bits_per_sample.len() != samples.into() && bits_per_sample.len() != 1 {
|
||||||
|
return Err(TiffError::FormatError(
|
||||||
|
TiffFormatError::InconsistentSizesEncountered,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// This library (and libtiff) do not support mixed sample formats.
|
||||||
|
if bits_per_sample.iter().any(|&b| b != bits_per_sample[0]) {
|
||||||
|
return Err(TiffUnsupportedError::InconsistentBitsPerSample(bits_per_sample).into());
|
||||||
|
}
|
||||||
|
|
||||||
let predictor = tag_reader
|
let predictor = tag_reader
|
||||||
.find_tag(Tag::Predictor)?
|
.find_tag(Tag::Predictor)?
|
||||||
@@ -183,6 +188,23 @@ impl Image {
|
|||||||
.transpose()?
|
.transpose()?
|
||||||
.unwrap_or(Predictor::None);
|
.unwrap_or(Predictor::None);
|
||||||
|
|
||||||
|
let planar_config = tag_reader
|
||||||
|
.find_tag(Tag::PlanarConfiguration)?
|
||||||
|
.map(Value::into_u16)
|
||||||
|
.transpose()?
|
||||||
|
.map(|p| {
|
||||||
|
PlanarConfiguration::from_u16(p).ok_or(TiffError::FormatError(
|
||||||
|
TiffFormatError::UnknownPlanarConfiguration(p),
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.transpose()?
|
||||||
|
.unwrap_or(PlanarConfiguration::Chunky);
|
||||||
|
|
||||||
|
let planes = match planar_config {
|
||||||
|
PlanarConfiguration::Chunky => 1,
|
||||||
|
PlanarConfiguration::Planar => samples,
|
||||||
|
};
|
||||||
|
|
||||||
let chunk_type;
|
let chunk_type;
|
||||||
let chunk_offsets;
|
let chunk_offsets;
|
||||||
let chunk_bytes;
|
let chunk_bytes;
|
||||||
@@ -216,7 +238,7 @@ impl Image {
|
|||||||
if chunk_offsets.len() != chunk_bytes.len()
|
if chunk_offsets.len() != chunk_bytes.len()
|
||||||
|| rows_per_strip == 0
|
|| rows_per_strip == 0
|
||||||
|| u32::try_from(chunk_offsets.len())?
|
|| u32::try_from(chunk_offsets.len())?
|
||||||
!= height.saturating_sub(1) / rows_per_strip + 1
|
!= (height.saturating_sub(1) / rows_per_strip + 1) * planes as u32
|
||||||
{
|
{
|
||||||
return Err(TiffError::FormatError(
|
return Err(TiffError::FormatError(
|
||||||
TiffFormatError::InconsistentSizesEncountered,
|
TiffFormatError::InconsistentSizesEncountered,
|
||||||
@@ -255,7 +277,8 @@ impl Image {
|
|||||||
|
|
||||||
let tile = tile_attributes.as_ref().unwrap();
|
let tile = tile_attributes.as_ref().unwrap();
|
||||||
if chunk_offsets.len() != chunk_bytes.len()
|
if chunk_offsets.len() != chunk_bytes.len()
|
||||||
|| chunk_offsets.len() != tile.tiles_down() * tile.tiles_across()
|
|| chunk_offsets.len()
|
||||||
|
!= tile.tiles_down() * tile.tiles_across() * planes as usize
|
||||||
{
|
{
|
||||||
return Err(TiffError::FormatError(
|
return Err(TiffError::FormatError(
|
||||||
TiffFormatError::InconsistentSizesEncountered,
|
TiffFormatError::InconsistentSizesEncountered,
|
||||||
@@ -273,7 +296,7 @@ impl Image {
|
|||||||
ifd: Some(ifd),
|
ifd: Some(ifd),
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
bits_per_sample,
|
bits_per_sample: bits_per_sample[0],
|
||||||
samples,
|
samples,
|
||||||
sample_format,
|
sample_format,
|
||||||
photometric_interpretation,
|
photometric_interpretation,
|
||||||
@@ -281,6 +304,7 @@ impl Image {
|
|||||||
jpeg_tables,
|
jpeg_tables,
|
||||||
predictor,
|
predictor,
|
||||||
chunk_type,
|
chunk_type,
|
||||||
|
planar_config,
|
||||||
strip_decoder,
|
strip_decoder,
|
||||||
tile_attributes,
|
tile_attributes,
|
||||||
chunk_offsets,
|
chunk_offsets,
|
||||||
@@ -290,9 +314,9 @@ impl Image {
|
|||||||
|
|
||||||
pub(crate) fn colortype(&self) -> TiffResult<ColorType> {
|
pub(crate) fn colortype(&self) -> TiffResult<ColorType> {
|
||||||
match self.photometric_interpretation {
|
match self.photometric_interpretation {
|
||||||
PhotometricInterpretation::RGB => match self.bits_per_sample[..] {
|
PhotometricInterpretation::RGB => match self.samples {
|
||||||
[r, g, b] if [r, r] == [g, b] => Ok(ColorType::RGB(r)),
|
3 => Ok(ColorType::RGB(self.bits_per_sample)),
|
||||||
[r, g, b, a] if [r, r, r] == [g, b, a] => Ok(ColorType::RGBA(r)),
|
4 => Ok(ColorType::RGBA(self.bits_per_sample)),
|
||||||
// FIXME: We should _ignore_ other components. In particular:
|
// FIXME: We should _ignore_ other components. In particular:
|
||||||
// > Beware of extra components. Some TIFF files may have more components per pixel
|
// > Beware of extra components. Some TIFF files may have more components per pixel
|
||||||
// than you think. A Baseline TIFF reader must skip over them gracefully,using the
|
// than you think. A Baseline TIFF reader must skip over them gracefully,using the
|
||||||
@@ -301,30 +325,39 @@ impl Image {
|
|||||||
_ => Err(TiffError::UnsupportedError(
|
_ => Err(TiffError::UnsupportedError(
|
||||||
TiffUnsupportedError::InterpretationWithBits(
|
TiffUnsupportedError::InterpretationWithBits(
|
||||||
self.photometric_interpretation,
|
self.photometric_interpretation,
|
||||||
self.bits_per_sample.clone(),
|
vec![self.bits_per_sample; self.samples as usize],
|
||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
PhotometricInterpretation::CMYK => match self.bits_per_sample[..] {
|
PhotometricInterpretation::CMYK => match self.samples {
|
||||||
[c, m, y, k] if [c, c, c] == [m, y, k] => Ok(ColorType::CMYK(c)),
|
4 => Ok(ColorType::CMYK(self.bits_per_sample)),
|
||||||
_ => Err(TiffError::UnsupportedError(
|
_ => Err(TiffError::UnsupportedError(
|
||||||
TiffUnsupportedError::InterpretationWithBits(
|
TiffUnsupportedError::InterpretationWithBits(
|
||||||
self.photometric_interpretation,
|
self.photometric_interpretation,
|
||||||
self.bits_per_sample.clone(),
|
vec![self.bits_per_sample; self.samples as usize],
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
PhotometricInterpretation::YCbCr => match self.samples {
|
||||||
|
3 => Ok(ColorType::YCbCr(self.bits_per_sample)),
|
||||||
|
_ => Err(TiffError::UnsupportedError(
|
||||||
|
TiffUnsupportedError::InterpretationWithBits(
|
||||||
|
self.photometric_interpretation,
|
||||||
|
vec![self.bits_per_sample; self.samples as usize],
|
||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
PhotometricInterpretation::BlackIsZero | PhotometricInterpretation::WhiteIsZero
|
PhotometricInterpretation::BlackIsZero | PhotometricInterpretation::WhiteIsZero
|
||||||
if self.bits_per_sample.len() == 1 =>
|
if self.samples == 1 =>
|
||||||
{
|
{
|
||||||
Ok(ColorType::Gray(self.bits_per_sample[0]))
|
Ok(ColorType::Gray(self.bits_per_sample))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: this is bad we should not fail at this point
|
// TODO: this is bad we should not fail at this point
|
||||||
_ => Err(TiffError::UnsupportedError(
|
_ => Err(TiffError::UnsupportedError(
|
||||||
TiffUnsupportedError::InterpretationWithBits(
|
TiffUnsupportedError::InterpretationWithBits(
|
||||||
self.photometric_interpretation,
|
self.photometric_interpretation,
|
||||||
self.bits_per_sample.clone(),
|
vec![self.bits_per_sample; self.samples as usize],
|
||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
@@ -332,9 +365,10 @@ impl Image {
|
|||||||
|
|
||||||
fn create_reader<'r, R: 'r + Read>(
|
fn create_reader<'r, R: 'r + Read>(
|
||||||
reader: R,
|
reader: R,
|
||||||
|
photometric_interpretation: PhotometricInterpretation,
|
||||||
compression_method: CompressionMethod,
|
compression_method: CompressionMethod,
|
||||||
compressed_length: u64,
|
compressed_length: u64,
|
||||||
jpeg_tables: Option<Arc<Vec<u8>>>,
|
jpeg_tables: Option<&[u8]>,
|
||||||
) -> TiffResult<Box<dyn Read + 'r>> {
|
) -> TiffResult<Box<dyn Read + 'r>> {
|
||||||
Ok(match compression_method {
|
Ok(match compression_method {
|
||||||
CompressionMethod::None => Box::new(reader),
|
CompressionMethod::None => Box::new(reader),
|
||||||
@@ -352,17 +386,61 @@ impl Image {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let jpeg_reader = JpegReader::new(reader, compressed_length, jpeg_tables)?;
|
// Construct new jpeg_reader wrapping a SmartReader.
|
||||||
let mut decoder = jpeg::Decoder::new(jpeg_reader);
|
//
|
||||||
let data = match decoder.decode() {
|
// JPEG compression in TIFF allows saving quantization and/or huffman tables in one
|
||||||
Ok(data) => data,
|
// central location. These `jpeg_tables` are simply prepended to the remaining jpeg image data.
|
||||||
Err(e) => {
|
// Because these `jpeg_tables` start with a `SOI` (HEX: `0xFFD8`) or __start of image__ marker
|
||||||
return Err(TiffError::FormatError(TiffFormatError::Format(
|
// which is also at the beginning of the remaining JPEG image data and would
|
||||||
e.to_string(),
|
// confuse the JPEG renderer, one of these has to be taken off. In this case the first two
|
||||||
)))
|
// bytes of the remaining JPEG data is removed because it follows `jpeg_tables`.
|
||||||
|
// Similary, `jpeg_tables` ends with a `EOI` (HEX: `0xFFD9`) or __end of image__ marker,
|
||||||
|
// this has to be removed as well (last two bytes of `jpeg_tables`).
|
||||||
|
let jpeg_reader = match jpeg_tables {
|
||||||
|
Some(jpeg_tables) => {
|
||||||
|
let mut reader = reader.take(compressed_length);
|
||||||
|
reader.read_exact(&mut [0; 2])?;
|
||||||
|
|
||||||
|
Box::new(
|
||||||
|
Cursor::new(&jpeg_tables[..jpeg_tables.len() - 2])
|
||||||
|
.chain(reader.take(compressed_length)),
|
||||||
|
) as Box<dyn Read>
|
||||||
}
|
}
|
||||||
|
None => Box::new(reader.take(compressed_length)),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut decoder = jpeg::Decoder::new(jpeg_reader);
|
||||||
|
|
||||||
|
match photometric_interpretation {
|
||||||
|
PhotometricInterpretation::RGB => {
|
||||||
|
decoder.set_color_transform(jpeg::ColorTransform::RGB)
|
||||||
|
}
|
||||||
|
PhotometricInterpretation::WhiteIsZero => {
|
||||||
|
decoder.set_color_transform(jpeg::ColorTransform::None)
|
||||||
|
}
|
||||||
|
PhotometricInterpretation::BlackIsZero => {
|
||||||
|
decoder.set_color_transform(jpeg::ColorTransform::None)
|
||||||
|
}
|
||||||
|
PhotometricInterpretation::TransparencyMask => {
|
||||||
|
decoder.set_color_transform(jpeg::ColorTransform::None)
|
||||||
|
}
|
||||||
|
PhotometricInterpretation::CMYK => {
|
||||||
|
decoder.set_color_transform(jpeg::ColorTransform::CMYK)
|
||||||
|
}
|
||||||
|
PhotometricInterpretation::YCbCr => {
|
||||||
|
decoder.set_color_transform(jpeg::ColorTransform::YCbCr)
|
||||||
|
}
|
||||||
|
photometric_interpretation => {
|
||||||
|
return Err(TiffError::UnsupportedError(
|
||||||
|
TiffUnsupportedError::UnsupportedInterpretation(
|
||||||
|
photometric_interpretation,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = decoder.decode()?;
|
||||||
|
|
||||||
Box::new(Cursor::new(data))
|
Box::new(Cursor::new(data))
|
||||||
}
|
}
|
||||||
method => {
|
method => {
|
||||||
@@ -373,6 +451,28 @@ impl Image {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Samples per pixel within chunk.
|
||||||
|
///
|
||||||
|
/// In planar config, samples are stored in separate strips/chunks, also called bands.
|
||||||
|
///
|
||||||
|
/// Example with `bits_per_sample = [8, 8, 8]` and `PhotometricInterpretation::RGB`:
|
||||||
|
/// * `PlanarConfiguration::Chunky` -> 3 (RGBRGBRGB...)
|
||||||
|
/// * `PlanarConfiguration::Planar` -> 1 (RRR...) (GGG...) (BBB...)
|
||||||
|
pub(crate) fn samples_per_pixel(&self) -> usize {
|
||||||
|
match self.planar_config {
|
||||||
|
PlanarConfiguration::Chunky => self.samples.into(),
|
||||||
|
PlanarConfiguration::Planar => 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Number of strips per pixel.
|
||||||
|
pub(crate) fn strips_per_pixel(&self) -> usize {
|
||||||
|
match self.planar_config {
|
||||||
|
PlanarConfiguration::Chunky => 1,
|
||||||
|
PlanarConfiguration::Planar => self.samples.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn chunk_file_range(&self, chunk: u32) -> TiffResult<(u64, u64)> {
|
pub(crate) fn chunk_file_range(&self, chunk: u32) -> TiffResult<(u64, u64)> {
|
||||||
let file_offset = self
|
let file_offset = self
|
||||||
.chunk_offsets
|
.chunk_offsets
|
||||||
@@ -412,7 +512,10 @@ impl Image {
|
|||||||
|
|
||||||
match self.chunk_type {
|
match self.chunk_type {
|
||||||
ChunkType::Strip => {
|
ChunkType::Strip => {
|
||||||
let strip_height_without_padding = chunk_index
|
let strip_attrs = self.strip_decoder.as_ref().unwrap();
|
||||||
|
let strips_per_band =
|
||||||
|
self.height.saturating_sub(1) / strip_attrs.rows_per_strip + 1;
|
||||||
|
let strip_height_without_padding = (chunk_index % strips_per_band)
|
||||||
.checked_mul(dims.1)
|
.checked_mul(dims.1)
|
||||||
.and_then(|x| self.height.checked_sub(x))
|
.and_then(|x| self.height.checked_sub(x))
|
||||||
.ok_or(TiffError::UsageError(UsageError::InvalidChunkIndex(
|
.ok_or(TiffError::UsageError(UsageError::InvalidChunkIndex(
|
||||||
@@ -443,6 +546,7 @@ impl Image {
|
|||||||
output_width: usize,
|
output_width: usize,
|
||||||
byte_order: ByteOrder,
|
byte_order: ByteOrder,
|
||||||
chunk_index: u32,
|
chunk_index: u32,
|
||||||
|
limits: &Limits,
|
||||||
) -> TiffResult<()> {
|
) -> TiffResult<()> {
|
||||||
// Validate that the provided buffer is of the expected type.
|
// Validate that the provided buffer is of the expected type.
|
||||||
let color_type = self.colortype()?;
|
let color_type = self.colortype()?;
|
||||||
@@ -450,6 +554,7 @@ impl Image {
|
|||||||
(ColorType::RGB(n), _)
|
(ColorType::RGB(n), _)
|
||||||
| (ColorType::RGBA(n), _)
|
| (ColorType::RGBA(n), _)
|
||||||
| (ColorType::CMYK(n), _)
|
| (ColorType::CMYK(n), _)
|
||||||
|
| (ColorType::YCbCr(n), _)
|
||||||
| (ColorType::Gray(n), _)
|
| (ColorType::Gray(n), _)
|
||||||
if usize::from(n) == buffer.byte_len() * 8 => {}
|
if usize::from(n) == buffer.byte_len() * 8 => {}
|
||||||
(ColorType::Gray(n), DecodingBuffer::U8(_)) if n < 8 => match self.predictor {
|
(ColorType::Gray(n), DecodingBuffer::U8(_)) if n < 8 => match self.predictor {
|
||||||
@@ -496,21 +601,28 @@ impl Image {
|
|||||||
.ok_or(TiffError::FormatError(
|
.ok_or(TiffError::FormatError(
|
||||||
TiffFormatError::InconsistentSizesEncountered,
|
TiffFormatError::InconsistentSizesEncountered,
|
||||||
))?;
|
))?;
|
||||||
|
if *compressed_bytes > limits.intermediate_buffer_size as u64 {
|
||||||
|
return Err(TiffError::LimitsExceeded);
|
||||||
|
}
|
||||||
|
|
||||||
let byte_len = buffer.byte_len();
|
let byte_len = buffer.byte_len();
|
||||||
let compression_method = self.compression_method;
|
let compression_method = self.compression_method;
|
||||||
let photometric_interpretation = self.photometric_interpretation;
|
let photometric_interpretation = self.photometric_interpretation;
|
||||||
let predictor = self.predictor;
|
let predictor = self.predictor;
|
||||||
let samples = self.bits_per_sample.len();
|
let samples = self.samples_per_pixel();
|
||||||
|
|
||||||
let chunk_dims = self.chunk_dimensions()?;
|
let chunk_dims = self.chunk_dimensions()?;
|
||||||
let data_dims = self.chunk_data_dimensions(chunk_index)?;
|
let data_dims = self.chunk_data_dimensions(chunk_index)?;
|
||||||
|
|
||||||
let padding_right = chunk_dims.0 - data_dims.0;
|
let padding_right = chunk_dims.0 - data_dims.0;
|
||||||
|
|
||||||
let jpeg_tables = self.jpeg_tables.clone();
|
let mut reader = Self::create_reader(
|
||||||
let mut reader =
|
reader,
|
||||||
Self::create_reader(reader, compression_method, *compressed_bytes, jpeg_tables)?;
|
photometric_interpretation,
|
||||||
|
compression_method,
|
||||||
|
*compressed_bytes,
|
||||||
|
self.jpeg_tables.as_deref().map(|a| &**a),
|
||||||
|
)?;
|
||||||
|
|
||||||
if output_width == data_dims.0 as usize && padding_right == 0 {
|
if output_width == data_dims.0 as usize && padding_right == 0 {
|
||||||
let total_samples = data_dims.0 as usize * data_dims.1 as usize * samples;
|
let total_samples = data_dims.0 as usize * data_dims.1 as usize * samples;
|
||||||
@@ -518,8 +630,8 @@ impl Image {
|
|||||||
reader.read_exact(tile)?;
|
reader.read_exact(tile)?;
|
||||||
|
|
||||||
for row in 0..data_dims.1 as usize {
|
for row in 0..data_dims.1 as usize {
|
||||||
let row_start = row as usize * output_width as usize * samples;
|
let row_start = row * output_width * samples;
|
||||||
let row_end = (row + 1) * output_width as usize * samples;
|
let row_end = (row + 1) * output_width * samples;
|
||||||
let row = buffer.subrange(row_start..row_end);
|
let row = buffer.subrange(row_start..row_end);
|
||||||
super::fix_endianness_and_predict(row, samples, byte_order, predictor);
|
super::fix_endianness_and_predict(row, samples, byte_order, predictor);
|
||||||
}
|
}
|
||||||
@@ -532,7 +644,7 @@ impl Image {
|
|||||||
let mut encoded = vec![0u8; chunk_dims.0 as usize * samples * byte_len];
|
let mut encoded = vec![0u8; chunk_dims.0 as usize * samples * byte_len];
|
||||||
|
|
||||||
for row in 0..data_dims.1 as usize {
|
for row in 0..data_dims.1 as usize {
|
||||||
let row_start = row * output_width as usize * samples;
|
let row_start = row * output_width * samples;
|
||||||
let row_end = row_start + data_dims.0 as usize * samples;
|
let row_end = row_start + data_dims.0 as usize * samples;
|
||||||
|
|
||||||
reader.read_exact(&mut encoded)?;
|
reader.read_exact(&mut encoded)?;
|
||||||
@@ -547,7 +659,7 @@ impl Image {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for row in 0..data_dims.1 as usize {
|
for row in 0..data_dims.1 as usize {
|
||||||
let row_start = row * output_width as usize * samples;
|
let row_start = row * output_width * samples;
|
||||||
let row_end = row_start + data_dims.0 as usize * samples;
|
let row_end = row_start + data_dims.0 as usize * samples;
|
||||||
|
|
||||||
let row = &mut buffer.as_bytes_mut()[(row_start * byte_len)..(row_end * byte_len)];
|
let row = &mut buffer.as_bytes_mut()[(row_start * byte_len)..(row_end * byte_len)];
|
||||||
|
|||||||
+27
-21
@@ -10,7 +10,8 @@ use crate::{
|
|||||||
use self::ifd::Directory;
|
use self::ifd::Directory;
|
||||||
use self::image::Image;
|
use self::image::Image;
|
||||||
use crate::tags::{
|
use crate::tags::{
|
||||||
CompressionMethod, PhotometricInterpretation, Predictor, SampleFormat, Tag, Type,
|
CompressionMethod, PhotometricInterpretation, PlanarConfiguration, Predictor, SampleFormat,
|
||||||
|
Tag, Type,
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::stream::{ByteOrder, EndianReader, SmartReader};
|
use self::stream::{ByteOrder, EndianReader, SmartReader};
|
||||||
@@ -220,7 +221,7 @@ impl<'a> DecodingBuffer<'a> {
|
|||||||
|
|
||||||
fn as_bytes_mut(&mut self) -> &mut [u8] {
|
fn as_bytes_mut(&mut self) -> &mut [u8] {
|
||||||
match self {
|
match self {
|
||||||
DecodingBuffer::U8(buf) => &mut *buf,
|
DecodingBuffer::U8(ref mut buf) => buf,
|
||||||
DecodingBuffer::I8(buf) => bytecast::i8_as_ne_mut_bytes(buf),
|
DecodingBuffer::I8(buf) => bytecast::i8_as_ne_mut_bytes(buf),
|
||||||
DecodingBuffer::U16(buf) => bytecast::u16_as_ne_mut_bytes(buf),
|
DecodingBuffer::U16(buf) => bytecast::u16_as_ne_mut_bytes(buf),
|
||||||
DecodingBuffer::I16(buf) => bytecast::i16_as_ne_mut_bytes(buf),
|
DecodingBuffer::I16(buf) => bytecast::i16_as_ne_mut_bytes(buf),
|
||||||
@@ -370,8 +371,8 @@ pub fn fp_predict_f32(input: &mut [u8], output: &mut [f32], samples: usize) {
|
|||||||
for i in 0..output.len() {
|
for i in 0..output.len() {
|
||||||
// TODO: use f32::from_be_bytes() when we can (version 1.40)
|
// TODO: use f32::from_be_bytes() when we can (version 1.40)
|
||||||
output[i] = f32::from_bits(u32::from_be_bytes([
|
output[i] = f32::from_bits(u32::from_be_bytes([
|
||||||
input[input.len() / 4 * 0 + i],
|
input[i],
|
||||||
input[input.len() / 4 * 1 + i],
|
input[input.len() / 4 + i],
|
||||||
input[input.len() / 4 * 2 + i],
|
input[input.len() / 4 * 2 + i],
|
||||||
input[input.len() / 4 * 3 + i],
|
input[input.len() / 4 * 3 + i],
|
||||||
]));
|
]));
|
||||||
@@ -383,8 +384,8 @@ pub fn fp_predict_f64(input: &mut [u8], output: &mut [f64], samples: usize) {
|
|||||||
for i in 0..output.len() {
|
for i in 0..output.len() {
|
||||||
// TODO: use f64::from_be_bytes() when we can (version 1.40)
|
// TODO: use f64::from_be_bytes() when we can (version 1.40)
|
||||||
output[i] = f64::from_bits(u64::from_be_bytes([
|
output[i] = f64::from_bits(u64::from_be_bytes([
|
||||||
input[input.len() / 8 * 0 + i],
|
input[i],
|
||||||
input[input.len() / 8 * 1 + i],
|
input[input.len() / 8 + i],
|
||||||
input[input.len() / 8 * 2 + i],
|
input[input.len() / 8 * 2 + i],
|
||||||
input[input.len() / 8 * 3 + i],
|
input[input.len() / 8 * 3 + i],
|
||||||
input[input.len() / 8 * 4 + i],
|
input[input.len() / 8 * 4 + i],
|
||||||
@@ -571,7 +572,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
ifd: None,
|
ifd: None,
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
bits_per_sample: vec![1],
|
bits_per_sample: 1,
|
||||||
samples: 1,
|
samples: 1,
|
||||||
sample_format: vec![SampleFormat::Uint],
|
sample_format: vec![SampleFormat::Uint],
|
||||||
photometric_interpretation: PhotometricInterpretation::BlackIsZero,
|
photometric_interpretation: PhotometricInterpretation::BlackIsZero,
|
||||||
@@ -579,6 +580,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
jpeg_tables: None,
|
jpeg_tables: None,
|
||||||
predictor: Predictor::None,
|
predictor: Predictor::None,
|
||||||
chunk_type: ChunkType::Strip,
|
chunk_type: ChunkType::Strip,
|
||||||
|
planar_config: PlanarConfiguration::Chunky,
|
||||||
strip_decoder: None,
|
strip_decoder: None,
|
||||||
tile_attributes: None,
|
tile_attributes: None,
|
||||||
chunk_offsets: Vec::new(),
|
chunk_offsets: Vec::new(),
|
||||||
@@ -1016,7 +1018,12 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
None => return Err(TiffError::IntSizeError),
|
None => return Err(TiffError::IntSizeError),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(height / rows_per_strip)
|
let strips = match self.image().planar_config {
|
||||||
|
PlanarConfiguration::Chunky => height / rows_per_strip,
|
||||||
|
PlanarConfiguration::Planar => height / rows_per_strip * self.image().samples as u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(strips)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Number of tiles in image
|
/// Number of tiles in image
|
||||||
@@ -1042,6 +1049,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
output_width,
|
output_width,
|
||||||
byte_order,
|
byte_order,
|
||||||
chunk_index,
|
chunk_index,
|
||||||
|
&self.limits,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -1050,19 +1058,13 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
fn result_buffer(&self, width: usize, height: usize) -> TiffResult<DecodingResult> {
|
fn result_buffer(&self, width: usize, height: usize) -> TiffResult<DecodingResult> {
|
||||||
let buffer_size = match width
|
let buffer_size = match width
|
||||||
.checked_mul(height)
|
.checked_mul(height)
|
||||||
.and_then(|x| x.checked_mul(self.image().bits_per_sample.len()))
|
.and_then(|x| x.checked_mul(self.image().samples_per_pixel()))
|
||||||
{
|
{
|
||||||
Some(s) => s,
|
Some(s) => s,
|
||||||
None => return Err(TiffError::LimitsExceeded),
|
None => return Err(TiffError::LimitsExceeded),
|
||||||
};
|
};
|
||||||
|
|
||||||
let max_sample_bits = self
|
let max_sample_bits = self.image().bits_per_sample;
|
||||||
.image()
|
|
||||||
.bits_per_sample
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.max()
|
|
||||||
.unwrap_or(8);
|
|
||||||
match self
|
match self
|
||||||
.image()
|
.image()
|
||||||
.sample_format
|
.sample_format
|
||||||
@@ -1094,9 +1096,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
TiffUnsupportedError::UnsupportedBitsPerChannel(n),
|
TiffUnsupportedError::UnsupportedBitsPerChannel(n),
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
format => {
|
format => Err(TiffUnsupportedError::UnsupportedSampleFormat(vec![*format]).into()),
|
||||||
Err(TiffUnsupportedError::UnsupportedSampleFormat(vec![format.clone()]).into())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1145,7 +1145,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let samples = self.image().bits_per_sample.len();
|
let samples = self.image().samples_per_pixel();
|
||||||
if samples == 0 {
|
if samples == 0 {
|
||||||
return Err(TiffError::FormatError(
|
return Err(TiffError::FormatError(
|
||||||
TiffFormatError::InconsistentSizesEncountered,
|
TiffFormatError::InconsistentSizesEncountered,
|
||||||
@@ -1155,7 +1155,12 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
let chunks_across = ((width - 1) / chunk_dimensions.0 + 1) as usize;
|
let chunks_across = ((width - 1) / chunk_dimensions.0 + 1) as usize;
|
||||||
let strip_samples = width as usize * chunk_dimensions.1 as usize * samples;
|
let strip_samples = width as usize * chunk_dimensions.1 as usize * samples;
|
||||||
|
|
||||||
for chunk in 0..self.image().chunk_offsets.len() {
|
let image_chunks = self.image().chunk_offsets.len() / self.image().strips_per_pixel();
|
||||||
|
// For multi-band images, only the first band is read.
|
||||||
|
// Possible improvements:
|
||||||
|
// * pass requested band as parameter
|
||||||
|
// * collect bands to a RGB encoding result in case of RGB bands
|
||||||
|
for chunk in 0..image_chunks {
|
||||||
self.goto_offset_u64(self.image().chunk_offsets[chunk])?;
|
self.goto_offset_u64(self.image().chunk_offsets[chunk])?;
|
||||||
|
|
||||||
let x = chunk % chunks_across;
|
let x = chunk % chunks_across;
|
||||||
@@ -1168,6 +1173,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
width as usize,
|
width as usize,
|
||||||
byte_order,
|
byte_order,
|
||||||
chunk as u32,
|
chunk as u32,
|
||||||
|
&self.limits,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+9
-160
@@ -1,8 +1,7 @@
|
|||||||
//! All IO functionality needed for TIFF decoding
|
//! All IO functionality needed for TIFF decoding
|
||||||
|
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::io::{self, BufRead, BufReader, Read, Seek, SeekFrom, Take};
|
use std::io::{self, BufRead, BufReader, Read, Seek, Take};
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
/// Byte order of the TIFF file.
|
/// Byte order of the TIFF file.
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
@@ -183,156 +182,6 @@ impl<R: Read> Read for LZWReader<R> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
|
||||||
/// ## JPEG Reader (for "new-style" JPEG format (TIFF compression tag 7))
|
|
||||||
///
|
|
||||||
|
|
||||||
pub(crate) struct JpegReader {
|
|
||||||
jpeg_tables: Option<Arc<Vec<u8>>>,
|
|
||||||
|
|
||||||
buffer: io::Cursor<Vec<u8>>,
|
|
||||||
|
|
||||||
offset: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl JpegReader {
|
|
||||||
/// Constructs new JpegReader wrapping a SmartReader.
|
|
||||||
/// Because JPEG compression in TIFF allows to save quantization and/or huffman tables in one
|
|
||||||
/// central location, the constructor accepts this data as `jpeg_tables` here containing either
|
|
||||||
/// or both.
|
|
||||||
/// These `jpeg_tables` are simply prepended to the remaining jpeg image data.
|
|
||||||
/// Because these `jpeg_tables` start with a `SOI` (HEX: `0xFFD8`) or __start of image__ marker
|
|
||||||
/// which is also at the beginning of the remaining JPEG image data and would
|
|
||||||
/// confuse the JPEG renderer, one of these has to be taken off. In this case the first two
|
|
||||||
/// bytes of the remaining JPEG data is removed because it follows `jpeg_tables`.
|
|
||||||
/// Similary, `jpeg_tables` ends with a `EOI` (HEX: `0xFFD9`) or __end of image__ marker,
|
|
||||||
/// this has to be removed as well (last two bytes of `jpeg_tables`).
|
|
||||||
pub fn new<R: Read>(
|
|
||||||
mut reader: R,
|
|
||||||
length: u64,
|
|
||||||
jpeg_tables: Option<Arc<Vec<u8>>>,
|
|
||||||
) -> io::Result<JpegReader> {
|
|
||||||
// Read jpeg image data
|
|
||||||
let mut segment = vec![0; length as usize];
|
|
||||||
|
|
||||||
reader.read_exact(&mut segment[..])?;
|
|
||||||
|
|
||||||
match jpeg_tables {
|
|
||||||
Some(jpeg_tables) => {
|
|
||||||
assert!(
|
|
||||||
jpeg_tables.len() >= 2,
|
|
||||||
"jpeg_tables, if given, must be at least 2 bytes long. Got {:?}",
|
|
||||||
jpeg_tables
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
length >= 2,
|
|
||||||
"if jpeg_tables is given, length must be at least 2 bytes long, got {}",
|
|
||||||
length
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut buffer = io::Cursor::new(segment);
|
|
||||||
// Skip the first two bytes (marker bytes)
|
|
||||||
buffer.seek(SeekFrom::Start(2))?;
|
|
||||||
|
|
||||||
Ok(JpegReader {
|
|
||||||
buffer,
|
|
||||||
jpeg_tables: Some(jpeg_tables),
|
|
||||||
offset: 0,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
None => Ok(JpegReader {
|
|
||||||
buffer: io::Cursor::new(segment),
|
|
||||||
jpeg_tables: None,
|
|
||||||
offset: 0,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(u8)]
|
|
||||||
pub(crate) enum JpegTagApp14Transform {
|
|
||||||
// App14TransformYCCK = 2,
|
|
||||||
// App14TransformYCbCr = 1,
|
|
||||||
App14TransformUnknown = 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn add_app14segment(jpeg_tables: &mut Vec<u8>, transform: JpegTagApp14Transform) {
|
|
||||||
// Add JPEG Tag APP14 Adobe segment to jpeg_tables.
|
|
||||||
// This segment stores image encoding information for DCT filters.
|
|
||||||
// When `transform` value is 0 which is defined as Unknown, jpeg-decoder interpret the
|
|
||||||
// color-space of image as RGB(3 channels) or CMYK(4).
|
|
||||||
let mut app14_offset = None;
|
|
||||||
let mut dht_offset = None;
|
|
||||||
for (offset, window) in jpeg_tables.windows(2).enumerate() {
|
|
||||||
if window == [0xff, 0xee] {
|
|
||||||
app14_offset = Some(offset);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if window == [0xff, 0xc4] {
|
|
||||||
dht_offset = Some(offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match (app14_offset, dht_offset) {
|
|
||||||
(Some(i), _) => {
|
|
||||||
jpeg_tables[i + 16] = transform as u8;
|
|
||||||
}
|
|
||||||
(None, Some(sos_offset)) => {
|
|
||||||
let app14segment: [u8; 16] = [
|
|
||||||
0xff,
|
|
||||||
0xee,
|
|
||||||
0x00,
|
|
||||||
0x0e,
|
|
||||||
0x41,
|
|
||||||
0x64,
|
|
||||||
0x6f,
|
|
||||||
0x62,
|
|
||||||
0x65,
|
|
||||||
0x00,
|
|
||||||
0x64,
|
|
||||||
0x00,
|
|
||||||
0x00,
|
|
||||||
0x00,
|
|
||||||
0x00,
|
|
||||||
transform as u8,
|
|
||||||
];
|
|
||||||
jpeg_tables.splice(sos_offset..sos_offset, app14segment.iter().copied());
|
|
||||||
}
|
|
||||||
(None, None) => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Read for JpegReader {
|
|
||||||
// #[inline]
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
||||||
let mut start = 0;
|
|
||||||
|
|
||||||
if let Some(jpeg_tables) = &self.jpeg_tables {
|
|
||||||
if jpeg_tables.len() - 2 > self.offset {
|
|
||||||
// Read (rest of) jpeg_tables to buf (without the last two bytes)
|
|
||||||
let size_remaining = jpeg_tables.len() - self.offset - 2;
|
|
||||||
let to_copy = size_remaining.min(buf.len());
|
|
||||||
|
|
||||||
buf[start..start + to_copy]
|
|
||||||
.copy_from_slice(&jpeg_tables[self.offset..self.offset + to_copy]);
|
|
||||||
|
|
||||||
self.offset += to_copy;
|
|
||||||
|
|
||||||
if to_copy == buf.len() {
|
|
||||||
return Ok(to_copy);
|
|
||||||
}
|
|
||||||
|
|
||||||
start += to_copy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let read = self.buffer.read(&mut buf[start..])?;
|
|
||||||
self.offset += read;
|
|
||||||
|
|
||||||
Ok(read + start)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// ## PackBits Reader
|
/// ## PackBits Reader
|
||||||
///
|
///
|
||||||
@@ -370,7 +219,7 @@ impl<R: Read> Read for PackBitsReader<R> {
|
|||||||
let mut header: [u8; 1] = [0];
|
let mut header: [u8; 1] = [0];
|
||||||
self.reader.read_exact(&mut header)?;
|
self.reader.read_exact(&mut header)?;
|
||||||
let h = header[0] as i8;
|
let h = header[0] as i8;
|
||||||
if h >= -127 && h <= -1 {
|
if (-127..=-1).contains(&h) {
|
||||||
let mut data: [u8; 1] = [0];
|
let mut data: [u8; 1] = [0];
|
||||||
self.reader.read_exact(&mut data)?;
|
self.reader.read_exact(&mut data)?;
|
||||||
self.state = PackBitsReaderState::Repeat { value: data[0] };
|
self.state = PackBitsReaderState::Repeat { value: data[0] };
|
||||||
@@ -384,23 +233,23 @@ impl<R: Read> Read for PackBitsReader<R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let length = buf.len().min(self.count);
|
let length = buf.len().min(self.count);
|
||||||
match self.state {
|
let actual = match self.state {
|
||||||
PackBitsReaderState::Literal => {
|
PackBitsReaderState::Literal => self.reader.read(&mut buf[..length])?,
|
||||||
self.reader.read(&mut buf[..length])?;
|
|
||||||
}
|
|
||||||
PackBitsReaderState::Repeat { value } => {
|
PackBitsReaderState::Repeat { value } => {
|
||||||
for b in &mut buf[..length] {
|
for b in &mut buf[..length] {
|
||||||
*b = value;
|
*b = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
length
|
||||||
}
|
}
|
||||||
PackBitsReaderState::Header => unreachable!(),
|
PackBitsReaderState::Header => unreachable!(),
|
||||||
}
|
};
|
||||||
|
|
||||||
self.count -= length;
|
self.count -= actual;
|
||||||
if self.count == 0 {
|
if self.count == 0 {
|
||||||
self.state = PackBitsReaderState::Header;
|
self.state = PackBitsReaderState::Header;
|
||||||
}
|
}
|
||||||
return Ok(length);
|
Ok(actual)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -235,3 +235,11 @@ impl ColorType for CMYK64Float {
|
|||||||
const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64, 64];
|
const BITS_PER_SAMPLE: &'static [u16] = &[64, 64, 64, 64];
|
||||||
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::IEEEFP; 4];
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::IEEEFP; 4];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct YCbCr8;
|
||||||
|
impl ColorType for YCbCr8 {
|
||||||
|
type Inner = u8;
|
||||||
|
const TIFF_VALUE: PhotometricInterpretation = PhotometricInterpretation::YCbCr;
|
||||||
|
const BITS_PER_SAMPLE: &'static [u16] = &[8, 8, 8];
|
||||||
|
const SAMPLE_FORMAT: &'static [SampleFormat] = &[SampleFormat::Uint; 3];
|
||||||
|
}
|
||||||
@@ -46,7 +46,7 @@ impl Compression for Deflate {
|
|||||||
const COMPRESSION_METHOD: CompressionMethod = CompressionMethod::Deflate;
|
const COMPRESSION_METHOD: CompressionMethod = CompressionMethod::Deflate;
|
||||||
|
|
||||||
fn get_algorithm(&self) -> Compressor {
|
fn get_algorithm(&self) -> Compressor {
|
||||||
Compressor::Deflate(self.clone())
|
Compressor::Deflate(*self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ mod tests {
|
|||||||
|
|
||||||
let mut compressed_data = Vec::<u8>::new();
|
let mut compressed_data = Vec::<u8>::new();
|
||||||
let mut writer = Cursor::new(&mut compressed_data);
|
let mut writer = Cursor::new(&mut compressed_data);
|
||||||
Lzw::default().write_to(&mut writer, TEST_DATA).unwrap();
|
Lzw.write_to(&mut writer, TEST_DATA).unwrap();
|
||||||
assert_eq!(EXPECTED_COMPRESSED_DATA, compressed_data.as_slice());
|
assert_eq!(EXPECTED_COMPRESSED_DATA, compressed_data.as_slice());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -38,7 +38,7 @@ pub enum Compressor {
|
|||||||
impl Default for Compressor {
|
impl Default for Compressor {
|
||||||
/// The default compression strategy does not apply any compression.
|
/// The default compression strategy does not apply any compression.
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Compressor::Uncompressed(Uncompressed::default())
|
Compressor::Uncompressed(Uncompressed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,6 +55,5 @@ impl CompressionAlgorithm for Compressor {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
pub const TEST_DATA: &'static [u8] =
|
pub const TEST_DATA: &[u8] = b"This is a string for checking various compression algorithms.";
|
||||||
b"This is a string for checking various compression algorithms.";
|
|
||||||
}
|
}
|
||||||
@@ -80,33 +80,31 @@ impl CompressionAlgorithm for Packbits {
|
|||||||
run_index = 0;
|
run_index = 0;
|
||||||
in_run = false;
|
in_run = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else if bytes_pending > MAX_BYTES {
|
||||||
if bytes_pending > MAX_BYTES {
|
// We have as much differing data as we can output in one chunk.
|
||||||
// We have as much differing data as we can output in one chunk.
|
// Output MAX_BYTES leaving one byte.
|
||||||
// Output MAX_BYTES leaving one byte.
|
offset.get_or_insert(write_u8(&mut bufwriter, encode_diff(MAX_BYTES))?);
|
||||||
offset.get_or_insert(write_u8(&mut bufwriter, encode_diff(MAX_BYTES))?);
|
bufwriter.write_all(&bytes[pending_index..pending_index + MAX_BYTES as usize])?;
|
||||||
bufwriter.write(&bytes[pending_index..pending_index + MAX_BYTES as usize])?;
|
bytes_written += 1 + MAX_BYTES as u64;
|
||||||
bytes_written += 1 + MAX_BYTES as u64;
|
|
||||||
|
|
||||||
pending_index += MAX_BYTES as usize;
|
pending_index += MAX_BYTES as usize;
|
||||||
bytes_pending -= MAX_BYTES;
|
bytes_pending -= MAX_BYTES;
|
||||||
run_index = bytes_pending - 1; // A run could start here
|
run_index = bytes_pending - 1; // A run could start here
|
||||||
} else if curr_byte == last_byte {
|
} else if curr_byte == last_byte {
|
||||||
if (bytes_pending - run_index >= MIN_REPT) || (run_index == 0) {
|
if (bytes_pending - run_index >= MIN_REPT) || (run_index == 0) {
|
||||||
// This is a worthwhile run
|
// This is a worthwhile run
|
||||||
if run_index != 0 {
|
if run_index != 0 {
|
||||||
// Flush differing data out of input buffer
|
// Flush differing data out of input buffer
|
||||||
offset.get_or_insert(write_u8(&mut bufwriter, encode_diff(run_index))?);
|
offset.get_or_insert(write_u8(&mut bufwriter, encode_diff(run_index))?);
|
||||||
bufwriter
|
bufwriter
|
||||||
.write(&bytes[pending_index..pending_index + run_index as usize])?;
|
.write_all(&bytes[pending_index..pending_index + run_index as usize])?;
|
||||||
bytes_written += 1 + run_index as u64;
|
bytes_written += 1 + run_index as u64;
|
||||||
}
|
|
||||||
bytes_pending -= run_index; // Length of run
|
|
||||||
in_run = true;
|
|
||||||
}
|
}
|
||||||
} else {
|
bytes_pending -= run_index; // Length of run
|
||||||
run_index = bytes_pending - 1; // A run could start here
|
in_run = true;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
run_index = bytes_pending - 1; // A run could start here
|
||||||
}
|
}
|
||||||
last_byte = curr_byte;
|
last_byte = curr_byte;
|
||||||
}
|
}
|
||||||
@@ -119,7 +117,7 @@ impl CompressionAlgorithm for Packbits {
|
|||||||
} else {
|
} else {
|
||||||
bytes_written += 1 + bytes_pending as u64;
|
bytes_written += 1 + bytes_pending as u64;
|
||||||
offset.get_or_insert(write_u8(&mut bufwriter, encode_diff(bytes_pending))?);
|
offset.get_or_insert(write_u8(&mut bufwriter, encode_diff(bytes_pending))?);
|
||||||
bufwriter.write(&bytes[pending_index..pending_index + bytes_pending as usize])?;
|
bufwriter.write_all(&bytes[pending_index..pending_index + bytes_pending as usize])?;
|
||||||
}
|
}
|
||||||
|
|
||||||
bufwriter.flush()?;
|
bufwriter.flush()?;
|
||||||
@@ -141,24 +139,20 @@ mod tests {
|
|||||||
|
|
||||||
let mut compressed_data = Vec::<u8>::new();
|
let mut compressed_data = Vec::<u8>::new();
|
||||||
let mut writer = Cursor::new(&mut compressed_data);
|
let mut writer = Cursor::new(&mut compressed_data);
|
||||||
Packbits::default()
|
Packbits.write_to(&mut writer, &UNCOMPRESSED_DATA).unwrap();
|
||||||
.write_to(&mut writer, &UNCOMPRESSED_DATA)
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(compressed_data, EXPECTED_COMPRESSED_DATA);
|
assert_eq!(compressed_data, EXPECTED_COMPRESSED_DATA);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_packbits_rept() {
|
fn test_packbits_rept() {
|
||||||
// compress buffer with repetitive sequence
|
// compress buffer with repetitive sequence
|
||||||
const UNCOMPRESSED_DATA: &'static [u8] =
|
const UNCOMPRESSED_DATA: &[u8] =
|
||||||
b"This strrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrring hangs.";
|
b"This strrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrring hangs.";
|
||||||
const EXPECTED_COMPRESSED_DATA: &'static [u8] = b"\x06This st\xD1r\x09ing hangs.";
|
const EXPECTED_COMPRESSED_DATA: &[u8] = b"\x06This st\xD1r\x09ing hangs.";
|
||||||
|
|
||||||
let mut compressed_data = Vec::<u8>::new();
|
let mut compressed_data = Vec::<u8>::new();
|
||||||
let mut writer = Cursor::new(&mut compressed_data);
|
let mut writer = Cursor::new(&mut compressed_data);
|
||||||
Packbits::default()
|
Packbits.write_to(&mut writer, UNCOMPRESSED_DATA).unwrap();
|
||||||
.write_to(&mut writer, UNCOMPRESSED_DATA)
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(compressed_data, EXPECTED_COMPRESSED_DATA);
|
assert_eq!(compressed_data, EXPECTED_COMPRESSED_DATA);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,23 +186,19 @@ mod tests {
|
|||||||
|
|
||||||
let mut compressed_data = Vec::<u8>::new();
|
let mut compressed_data = Vec::<u8>::new();
|
||||||
let mut writer = Cursor::new(&mut compressed_data);
|
let mut writer = Cursor::new(&mut compressed_data);
|
||||||
Packbits::default()
|
Packbits.write_to(&mut writer, data.as_slice()).unwrap();
|
||||||
.write_to(&mut writer, data.as_slice())
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(compressed_data, EXPECTED_COMPRESSED_DATA);
|
assert_eq!(compressed_data, EXPECTED_COMPRESSED_DATA);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_packbits() {
|
fn test_packbits() {
|
||||||
// compress teststring
|
// compress teststring
|
||||||
const EXPECTED_COMPRESSED_DATA: &'static [u8] =
|
const EXPECTED_COMPRESSED_DATA: &[u8] =
|
||||||
b"\x3CThis is a string for checking various compression algorithms.";
|
b"\x3CThis is a string for checking various compression algorithms.";
|
||||||
|
|
||||||
let mut compressed_data = Vec::<u8>::new();
|
let mut compressed_data = Vec::<u8>::new();
|
||||||
let mut writer = Cursor::new(&mut compressed_data);
|
let mut writer = Cursor::new(&mut compressed_data);
|
||||||
Packbits::default()
|
Packbits.write_to(&mut writer, TEST_DATA).unwrap();
|
||||||
.write_to(&mut writer, TEST_DATA)
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(compressed_data, EXPECTED_COMPRESSED_DATA);
|
assert_eq!(compressed_data, EXPECTED_COMPRESSED_DATA);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -29,9 +29,7 @@ mod tests {
|
|||||||
fn test_no_compression() {
|
fn test_no_compression() {
|
||||||
let mut compressed_data = Vec::<u8>::new();
|
let mut compressed_data = Vec::<u8>::new();
|
||||||
let mut writer = Cursor::new(&mut compressed_data);
|
let mut writer = Cursor::new(&mut compressed_data);
|
||||||
Uncompressed::default()
|
Uncompressed.write_to(&mut writer, TEST_DATA).unwrap();
|
||||||
.write_to(&mut writer, TEST_DATA)
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(TEST_DATA, compressed_data);
|
assert_eq!(TEST_DATA, compressed_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+10
-10
@@ -219,19 +219,19 @@ impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
|
|||||||
|
|
||||||
let offset = self.writer.offset();
|
let offset = self.writer.offset();
|
||||||
|
|
||||||
K::write_entry_count(&mut self.writer, self.ifd.len())?;
|
K::write_entry_count(self.writer, self.ifd.len())?;
|
||||||
for (
|
for (
|
||||||
tag,
|
tag,
|
||||||
&DirectoryEntry {
|
DirectoryEntry {
|
||||||
data_type: ref field_type,
|
data_type: field_type,
|
||||||
ref count,
|
count,
|
||||||
data: ref offset,
|
data: offset,
|
||||||
},
|
},
|
||||||
) in self.ifd.iter()
|
) in self.ifd.iter()
|
||||||
{
|
{
|
||||||
self.writer.write_u16(*tag)?;
|
self.writer.write_u16(*tag)?;
|
||||||
self.writer.write_u16(*field_type)?;
|
self.writer.write_u16(*field_type)?;
|
||||||
(*count).write(&mut self.writer)?;
|
(*count).write(self.writer)?;
|
||||||
self.writer.write_bytes(offset)?;
|
self.writer.write_bytes(offset)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,7 +243,7 @@ impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
|
|||||||
/// This could be used to write tiff strips.
|
/// This could be used to write tiff strips.
|
||||||
pub fn write_data<T: TiffValue>(&mut self, value: T) -> TiffResult<u64> {
|
pub fn write_data<T: TiffValue>(&mut self, value: T) -> TiffResult<u64> {
|
||||||
let offset = self.writer.offset();
|
let offset = self.writer.offset();
|
||||||
value.write(&mut self.writer)?;
|
value.write(self.writer)?;
|
||||||
Ok(offset)
|
Ok(offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,9 +257,9 @@ impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
|
|||||||
let curr_pos = self.writer.offset();
|
let curr_pos = self.writer.offset();
|
||||||
|
|
||||||
self.writer.goto_offset(self.ifd_pointer_pos)?;
|
self.writer.goto_offset(self.ifd_pointer_pos)?;
|
||||||
K::write_offset(&mut self.writer, ifd_pointer)?;
|
K::write_offset(self.writer, ifd_pointer)?;
|
||||||
self.writer.goto_offset(curr_pos)?;
|
self.writer.goto_offset(curr_pos)?;
|
||||||
K::write_offset(&mut self.writer, 0)?;
|
K::write_offset(self.writer, 0)?;
|
||||||
|
|
||||||
self.dropped = true;
|
self.dropped = true;
|
||||||
|
|
||||||
@@ -400,7 +400,7 @@ impl<'a, W: 'a + Write + Seek, T: ColorType, K: TiffKind, D: Compression>
|
|||||||
strip_offsets: Vec::new(),
|
strip_offsets: Vec::new(),
|
||||||
strip_byte_count: Vec::new(),
|
strip_byte_count: Vec::new(),
|
||||||
dropped: false,
|
dropped: false,
|
||||||
compression: compression,
|
compression,
|
||||||
_phantom: ::std::marker::PhantomData,
|
_phantom: ::std::marker::PhantomData,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ impl<W: Write> TiffWriter<W> {
|
|||||||
impl<W: Seek> TiffWriter<W> {
|
impl<W: Seek> TiffWriter<W> {
|
||||||
pub fn goto_offset(&mut self, offset: u64) -> Result<(), io::Error> {
|
pub fn goto_offset(&mut self, offset: u64) -> Result<(), io::Error> {
|
||||||
self.offset = offset;
|
self.offset = offset;
|
||||||
self.writer.seek(SeekFrom::Start(offset as u64))?;
|
self.writer.seek(SeekFrom::Start(offset))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,12 @@
|
|||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::fmt::Display;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::str;
|
use std::str;
|
||||||
use std::string;
|
use std::string;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use jpeg::UnsupportedFeature;
|
||||||
|
|
||||||
use crate::decoder::{ifd::Value, ChunkType};
|
use crate::decoder::{ifd::Value, ChunkType};
|
||||||
use crate::tags::{
|
use crate::tags::{
|
||||||
@@ -62,6 +66,7 @@ pub enum TiffFormatError {
|
|||||||
InvalidTagValueType(Tag),
|
InvalidTagValueType(Tag),
|
||||||
RequiredTagNotFound(Tag),
|
RequiredTagNotFound(Tag),
|
||||||
UnknownPredictor(u16),
|
UnknownPredictor(u16),
|
||||||
|
UnknownPlanarConfiguration(u16),
|
||||||
ByteExpected(Value),
|
ByteExpected(Value),
|
||||||
UnsignedIntegerExpected(Value),
|
UnsignedIntegerExpected(Value),
|
||||||
SignedIntegerExpected(Value),
|
SignedIntegerExpected(Value),
|
||||||
@@ -69,6 +74,8 @@ pub enum TiffFormatError {
|
|||||||
RequiredTagEmpty(Tag),
|
RequiredTagEmpty(Tag),
|
||||||
StripTileTagConflict,
|
StripTileTagConflict,
|
||||||
CycleInOffsets,
|
CycleInOffsets,
|
||||||
|
JpegDecoder(JpegDecoderError),
|
||||||
|
SamplesPerPixelIsZero,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for TiffFormatError {
|
impl fmt::Display for TiffFormatError {
|
||||||
@@ -108,6 +115,9 @@ impl fmt::Display for TiffFormatError {
|
|||||||
UnknownPredictor(ref predictor) => {
|
UnknownPredictor(ref predictor) => {
|
||||||
write!(fmt, "Unknown predictor “{}” encountered", predictor)
|
write!(fmt, "Unknown predictor “{}” encountered", predictor)
|
||||||
}
|
}
|
||||||
|
UnknownPlanarConfiguration(ref planar_config) => {
|
||||||
|
write!(fmt, "Unknown planar configuration “{}” encountered", planar_config)
|
||||||
|
}
|
||||||
ByteExpected(ref val) => write!(fmt, "Expected byte, {:?} found.", val),
|
ByteExpected(ref val) => write!(fmt, "Expected byte, {:?} found.", val),
|
||||||
UnsignedIntegerExpected(ref val) => {
|
UnsignedIntegerExpected(ref val) => {
|
||||||
write!(fmt, "Expected unsigned integer, {:?} found.", val)
|
write!(fmt, "Expected unsigned integer, {:?} found.", val)
|
||||||
@@ -119,6 +129,8 @@ impl fmt::Display for TiffFormatError {
|
|||||||
RequiredTagEmpty(ref val) => write!(fmt, "Required tag {:?} was empty.", val),
|
RequiredTagEmpty(ref val) => write!(fmt, "Required tag {:?} was empty.", val),
|
||||||
StripTileTagConflict => write!(fmt, "File should contain either (StripByteCounts and StripOffsets) or (TileByteCounts and TileOffsets), other combination was found."),
|
StripTileTagConflict => write!(fmt, "File should contain either (StripByteCounts and StripOffsets) or (TileByteCounts and TileOffsets), other combination was found."),
|
||||||
CycleInOffsets => write!(fmt, "File contained a cycle in the list of IFDs"),
|
CycleInOffsets => write!(fmt, "File contained a cycle in the list of IFDs"),
|
||||||
|
JpegDecoder(ref error) => write!(fmt, "{}", error),
|
||||||
|
SamplesPerPixelIsZero => write!(fmt, "Samples per pixel is zero"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,6 +148,7 @@ impl fmt::Display for TiffFormatError {
|
|||||||
pub enum TiffUnsupportedError {
|
pub enum TiffUnsupportedError {
|
||||||
FloatingPointPredictor(ColorType),
|
FloatingPointPredictor(ColorType),
|
||||||
HorizontalPredictor(ColorType),
|
HorizontalPredictor(ColorType),
|
||||||
|
InconsistentBitsPerSample(Vec<u8>),
|
||||||
InterpretationWithBits(PhotometricInterpretation, Vec<u8>),
|
InterpretationWithBits(PhotometricInterpretation, Vec<u8>),
|
||||||
UnknownInterpretation,
|
UnknownInterpretation,
|
||||||
UnknownCompressionMethod,
|
UnknownCompressionMethod,
|
||||||
@@ -146,6 +159,8 @@ pub enum TiffUnsupportedError {
|
|||||||
UnsupportedBitsPerChannel(u8),
|
UnsupportedBitsPerChannel(u8),
|
||||||
UnsupportedPlanarConfig(Option<PlanarConfiguration>),
|
UnsupportedPlanarConfig(Option<PlanarConfiguration>),
|
||||||
UnsupportedDataType,
|
UnsupportedDataType,
|
||||||
|
UnsupportedInterpretation(PhotometricInterpretation),
|
||||||
|
UnsupportedJpegFeature(UnsupportedFeature),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for TiffUnsupportedError {
|
impl fmt::Display for TiffUnsupportedError {
|
||||||
@@ -162,6 +177,9 @@ impl fmt::Display for TiffUnsupportedError {
|
|||||||
"Horizontal predictor for {:?} is unsupported.",
|
"Horizontal predictor for {:?} is unsupported.",
|
||||||
color_type
|
color_type
|
||||||
),
|
),
|
||||||
|
InconsistentBitsPerSample(ref bits_per_sample) => {
|
||||||
|
write!(fmt, "Inconsistent bits per sample: {:?}.", bits_per_sample)
|
||||||
|
}
|
||||||
InterpretationWithBits(ref photometric_interpretation, ref bits_per_sample) => write!(
|
InterpretationWithBits(ref photometric_interpretation, ref bits_per_sample) => write!(
|
||||||
fmt,
|
fmt,
|
||||||
"{:?} with {:?} bits per sample is unsupported",
|
"{:?} with {:?} bits per sample is unsupported",
|
||||||
@@ -191,6 +209,16 @@ impl fmt::Display for TiffUnsupportedError {
|
|||||||
write!(fmt, "Unsupported planar configuration “{:?}”.", config)
|
write!(fmt, "Unsupported planar configuration “{:?}”.", config)
|
||||||
}
|
}
|
||||||
UnsupportedDataType => write!(fmt, "Unsupported data type."),
|
UnsupportedDataType => write!(fmt, "Unsupported data type."),
|
||||||
|
UnsupportedInterpretation(interpretation) => {
|
||||||
|
write!(
|
||||||
|
fmt,
|
||||||
|
"Unsupported photometric interpretation \"{:?}\".",
|
||||||
|
interpretation
|
||||||
|
)
|
||||||
|
}
|
||||||
|
UnsupportedJpegFeature(ref unsupported_feature) => {
|
||||||
|
write!(fmt, "Unsupported JPEG feature {:?}", unsupported_feature)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -310,5 +338,42 @@ impl From<LzwError> for TiffError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct JpegDecoderError {
|
||||||
|
inner: Arc<jpeg::Error>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JpegDecoderError {
|
||||||
|
fn new(error: jpeg::Error) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: Arc::new(error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for JpegDecoderError {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
Arc::ptr_eq(&self.inner, &other.inner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for JpegDecoderError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
self.inner.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<JpegDecoderError> for TiffError {
|
||||||
|
fn from(error: JpegDecoderError) -> Self {
|
||||||
|
TiffError::FormatError(TiffFormatError::JpegDecoder(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<jpeg::Error> for TiffError {
|
||||||
|
fn from(error: jpeg::Error) -> Self {
|
||||||
|
JpegDecoderError::new(error).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Result of an image decoding/encoding process
|
/// Result of an image decoding/encoding process
|
||||||
pub type TiffResult<T> = Result<T, TiffError>;
|
pub type TiffResult<T> = Result<T, TiffError>;
|
||||||
@@ -37,4 +37,7 @@ pub enum ColorType {
|
|||||||
|
|
||||||
/// Pixel is CMYK
|
/// Pixel is CMYK
|
||||||
CMYK(u8),
|
CMYK(u8),
|
||||||
|
|
||||||
|
/// Pixel is YCbCr
|
||||||
|
YCbCr(u8),
|
||||||
}
|
}
|
||||||
+1
-1
@@ -172,7 +172,7 @@ pub enum Type(u16) {
|
|||||||
tags! {
|
tags! {
|
||||||
/// See [TIFF compression tags](https://www.awaresystems.be/imaging/tiff/tifftags/compression.html)
|
/// See [TIFF compression tags](https://www.awaresystems.be/imaging/tiff/tifftags/compression.html)
|
||||||
/// for reference.
|
/// for reference.
|
||||||
pub enum CompressionMethod(u16) {
|
pub enum CompressionMethod(u16) unknown("A custom compression method") {
|
||||||
None = 1,
|
None = 1,
|
||||||
Huffman = 2,
|
Huffman = 2,
|
||||||
Fax3 = 3,
|
Fax3 = 3,
|
||||||
|
|||||||
+45
-12
@@ -285,6 +285,48 @@ fn test_tiled_incremental() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_planar_rgb_u8() {
|
||||||
|
// gdal_translate tiled-rgb-u8.tif planar-rgb-u8.tif -co INTERLEAVE=BAND -co COMPRESS=LZW -co PROFILE=BASELINE
|
||||||
|
let file = "planar-rgb-u8.tif";
|
||||||
|
let expected_type = ColorType::RGB(8);
|
||||||
|
|
||||||
|
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 chunks = decoder.strip_count().unwrap();
|
||||||
|
assert_eq!(chunks as usize, 72);
|
||||||
|
|
||||||
|
// convert -quiet planar-rgb-u8.tif[0] -crop 1x1+0+0 txt:
|
||||||
|
// 0,0: (73,51,30) #49331E srgb(73,51,30)
|
||||||
|
|
||||||
|
// 1st band (red)
|
||||||
|
match decoder.read_chunk(0).unwrap() {
|
||||||
|
DecodingResult::U8(chunk) => {
|
||||||
|
assert_eq!(chunk[0], 73);
|
||||||
|
}
|
||||||
|
_ => panic!("Wrong bit depth"),
|
||||||
|
}
|
||||||
|
// 2nd band (green)
|
||||||
|
match decoder.read_chunk(chunks / 3).unwrap() {
|
||||||
|
DecodingResult::U8(chunk) => {
|
||||||
|
assert_eq!(chunk[0], 51);
|
||||||
|
}
|
||||||
|
_ => panic!("Wrong bit depth"),
|
||||||
|
}
|
||||||
|
// 3rd band (blue)
|
||||||
|
match decoder.read_chunk(chunks / 3 * 2).unwrap() {
|
||||||
|
DecodingResult::U8(chunk) => {
|
||||||
|
assert_eq!(chunk[0], 30);
|
||||||
|
}
|
||||||
|
_ => panic!("Wrong bit depth"),
|
||||||
|
}
|
||||||
|
|
||||||
|
test_image_sum_u8(file, ColorType::RGB(8), 15417630);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_div_zero() {
|
fn test_div_zero() {
|
||||||
use tiff::{TiffError, TiffFormatError};
|
use tiff::{TiffError, TiffFormatError};
|
||||||
@@ -333,9 +375,7 @@ fn fuzzer_testcase5() {
|
|||||||
178, 178, 178,
|
178, 178, 178,
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut decoder = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap();
|
let _ = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap_err();
|
||||||
|
|
||||||
let _ = decoder.read_image().unwrap_err();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -352,7 +392,7 @@ fn fuzzer_testcase1() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_stripped_image_overflow() {
|
fn fuzzer_testcase6() {
|
||||||
let image = [
|
let image = [
|
||||||
73, 73, 42, 0, 8, 0, 0, 0, 8, 0, 0, 1, 4, 0, 1, 0, 0, 0, 100, 0, 0, 148, 1, 1, 4, 0, 1, 0,
|
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, 158, 0, 0, 251, 3, 1, 3, 255, 254, 255, 255, 0, 1, 0, 0, 0, 6, 1, 3, 0, 1, 0, 0, 0,
|
||||||
@@ -361,14 +401,7 @@ fn test_stripped_image_overflow() {
|
|||||||
178, 178,
|
178, 178,
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut decoder = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap();
|
let _ = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap_err();
|
||||||
|
|
||||||
let err = decoder.read_image().unwrap_err();
|
|
||||||
|
|
||||||
match err {
|
|
||||||
tiff::TiffError::LimitsExceeded => {}
|
|
||||||
unexpected => panic!("Unexpected error {}", unexpected),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -264,6 +264,11 @@ fn test_gray_f64_roundtrip() {
|
|||||||
test_f64_roundtrip::<colortype::Gray64Float>("gradient-1c-64b-float.tiff", ColorType::Gray(64));
|
test_f64_roundtrip::<colortype::Gray64Float>("gradient-1c-64b-float.tiff", ColorType::Gray(64));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ycbcr_u8_roundtrip() {
|
||||||
|
test_u8_roundtrip::<colortype::YCbCr8>("tiled-jpeg-ycbcr.tif", ColorType::YCbCr(8));
|
||||||
|
}
|
||||||
|
|
||||||
trait AssertDecode {
|
trait AssertDecode {
|
||||||
fn assert_tag_u32(&mut self, tag: u16) -> u32;
|
fn assert_tag_u32(&mut self, tag: u16) -> u32;
|
||||||
fn assert_tag_u32_vec(&mut self, tag: u16) -> Vec<u32>;
|
fn assert_tag_u32_vec(&mut self, tag: u16) -> Vec<u32>;
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Reference in new issue
Block a user