Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0ba4a6788 | ||
|
|
4a2e76858b | ||
|
|
a67f50568c | ||
|
|
d6f6b8a01e | ||
|
|
8ec91b64f6 | ||
|
|
60f866c119 | ||
|
|
daa0e3eabf | ||
|
|
24d203c7c3 | ||
|
|
473868a7bb | ||
|
|
aefea7786f | ||
|
|
d1ad9b2664 | ||
|
|
bd1d085189 | ||
|
|
aa10d27a28 | ||
|
|
8fa175c371 | ||
|
|
2a15aa6b7e | ||
|
|
42feeba8da |
No files matched your search
+38
-17
@@ -9,32 +9,53 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
rust: ["1.61", stable, beta, nightly]
|
||||
command: [build, test]
|
||||
rust: ["1.61.0", stable, beta, nightly]
|
||||
steps:
|
||||
- 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
|
||||
run: >
|
||||
cargo build --verbose --no-default-features --features "$FEATURES"
|
||||
run: cargo build -v
|
||||
- name: test
|
||||
run: >
|
||||
cargo test --tests --benches --no-default-features --features "$FEATURES"
|
||||
if: ${{ matrix.rust != '1.61' }}
|
||||
env:
|
||||
FEATURES: ${{ matrix.features }}
|
||||
if: ${{ matrix.rust != '1.61.0' }}
|
||||
run: cargo test -v && cargo doc -v
|
||||
|
||||
rustfmt:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: stable
|
||||
override: true
|
||||
components: rustfmt
|
||||
- name: Run rustfmt check
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: -- --check
|
||||
run: cargo fmt -- --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
|
||||
@@ -1,3 +1,12 @@
|
||||
# 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:
|
||||
|
||||
+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]
|
||||
name = "tiff"
|
||||
version = "0.9.0"
|
||||
edition = "2018"
|
||||
version = "0.9.1"
|
||||
edition = "2021"
|
||||
resolver = "2"
|
||||
|
||||
# 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])))
|
||||
}),
|
||||
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| {
|
||||
Ok(UnsignedBig(u64::from(reader.read_u16()?)))
|
||||
|
||||
+125
-42
@@ -1,11 +1,13 @@
|
||||
use super::ifd::{Directory, Value};
|
||||
use super::stream::{ByteOrder, DeflateReader, JpegReader, LZWReader, PackBitsReader};
|
||||
use super::stream::{ByteOrder, DeflateReader, LZWReader, PackBitsReader};
|
||||
use super::tag_reader::TagReader;
|
||||
use super::{fp_predict_f32, fp_predict_f64, DecodingBuffer, Limits};
|
||||
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 std::convert::{TryFrom, TryInto};
|
||||
use std::convert::TryFrom;
|
||||
use std::io::{self, Cursor, Read, Seek};
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -62,15 +64,16 @@ pub(crate) struct Image {
|
||||
pub ifd: Option<Directory>,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub bits_per_sample: Vec<u8>,
|
||||
pub bits_per_sample: u8,
|
||||
#[allow(unused)]
|
||||
pub samples: u8,
|
||||
pub samples: u16,
|
||||
pub sample_format: Vec<SampleFormat>,
|
||||
pub photometric_interpretation: PhotometricInterpretation,
|
||||
pub compression_method: CompressionMethod,
|
||||
pub predictor: Predictor,
|
||||
pub jpeg_tables: Option<Arc<Vec<u8>>>,
|
||||
pub chunk_type: ChunkType,
|
||||
pub planar_config: PlanarConfiguration,
|
||||
pub strip_decoder: Option<StripDecodeState>,
|
||||
pub tile_attributes: Option<TileAttributes>,
|
||||
pub chunk_offsets: Vec<u64>,
|
||||
@@ -109,8 +112,7 @@ impl Image {
|
||||
// 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.
|
||||
let compression_method = match tag_reader.find_tag(Tag::Compression)? {
|
||||
Some(val) => CompressionMethod::from_u16(val.into_u16()?)
|
||||
.ok_or(TiffUnsupportedError::UnknownCompressionMethod)?,
|
||||
Some(val) => CompressionMethod::from_u16_exhaustive(val.into_u16()?),
|
||||
None => CompressionMethod::None,
|
||||
};
|
||||
|
||||
@@ -132,12 +134,14 @@ impl Image {
|
||||
None
|
||||
};
|
||||
|
||||
let samples = tag_reader
|
||||
let samples: u16 = tag_reader
|
||||
.find_tag(Tag::SamplesPerPixel)?
|
||||
.map(Value::into_u16)
|
||||
.transpose()?
|
||||
.unwrap_or(1)
|
||||
.try_into()?;
|
||||
.unwrap_or(1);
|
||||
if samples == 0 {
|
||||
return Err(TiffFormatError::SamplesPerPixelIsZero.into());
|
||||
}
|
||||
|
||||
let sample_format = match tag_reader.find_tag_uint_vec(Tag::SampleFormat)? {
|
||||
Some(vals) => {
|
||||
@@ -156,12 +160,22 @@ impl Image {
|
||||
None => vec![SampleFormat::Uint],
|
||||
};
|
||||
|
||||
let bits_per_sample = match samples {
|
||||
1 | 3 | 4 => tag_reader
|
||||
.find_tag_uint_vec(Tag::BitsPerSample)?
|
||||
.unwrap_or_else(|| vec![1]),
|
||||
_ => return Err(TiffUnsupportedError::UnsupportedSampleDepth(samples).into()),
|
||||
};
|
||||
let bits_per_sample: Vec<u8> = tag_reader
|
||||
.find_tag_uint_vec(Tag::BitsPerSample)?
|
||||
.unwrap_or_else(|| vec![1]);
|
||||
|
||||
// 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
|
||||
.find_tag(Tag::Predictor)?
|
||||
@@ -174,6 +188,23 @@ impl Image {
|
||||
.transpose()?
|
||||
.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_offsets;
|
||||
let chunk_bytes;
|
||||
@@ -207,7 +238,7 @@ impl Image {
|
||||
if chunk_offsets.len() != chunk_bytes.len()
|
||||
|| rows_per_strip == 0
|
||||
|| u32::try_from(chunk_offsets.len())?
|
||||
!= height.saturating_sub(1) / rows_per_strip + 1
|
||||
!= (height.saturating_sub(1) / rows_per_strip + 1) * planes as u32
|
||||
{
|
||||
return Err(TiffError::FormatError(
|
||||
TiffFormatError::InconsistentSizesEncountered,
|
||||
@@ -246,7 +277,8 @@ impl Image {
|
||||
|
||||
let tile = tile_attributes.as_ref().unwrap();
|
||||
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(
|
||||
TiffFormatError::InconsistentSizesEncountered,
|
||||
@@ -264,7 +296,7 @@ impl Image {
|
||||
ifd: Some(ifd),
|
||||
width,
|
||||
height,
|
||||
bits_per_sample,
|
||||
bits_per_sample: bits_per_sample[0],
|
||||
samples,
|
||||
sample_format,
|
||||
photometric_interpretation,
|
||||
@@ -272,6 +304,7 @@ impl Image {
|
||||
jpeg_tables,
|
||||
predictor,
|
||||
chunk_type,
|
||||
planar_config,
|
||||
strip_decoder,
|
||||
tile_attributes,
|
||||
chunk_offsets,
|
||||
@@ -281,9 +314,9 @@ impl Image {
|
||||
|
||||
pub(crate) fn colortype(&self) -> TiffResult<ColorType> {
|
||||
match self.photometric_interpretation {
|
||||
PhotometricInterpretation::RGB => match self.bits_per_sample[..] {
|
||||
[r, g, b] if [r, r] == [g, b] => Ok(ColorType::RGB(r)),
|
||||
[r, g, b, a] if [r, r, r] == [g, b, a] => Ok(ColorType::RGBA(r)),
|
||||
PhotometricInterpretation::RGB => match self.samples {
|
||||
3 => Ok(ColorType::RGB(self.bits_per_sample)),
|
||||
4 => Ok(ColorType::RGBA(self.bits_per_sample)),
|
||||
// FIXME: We should _ignore_ other components. In particular:
|
||||
// > Beware of extra components. Some TIFF files may have more components per pixel
|
||||
// than you think. A Baseline TIFF reader must skip over them gracefully,using the
|
||||
@@ -292,39 +325,39 @@ impl Image {
|
||||
_ => Err(TiffError::UnsupportedError(
|
||||
TiffUnsupportedError::InterpretationWithBits(
|
||||
self.photometric_interpretation,
|
||||
self.bits_per_sample.clone(),
|
||||
vec![self.bits_per_sample; self.samples as usize],
|
||||
),
|
||||
)),
|
||||
},
|
||||
PhotometricInterpretation::CMYK => match self.bits_per_sample[..] {
|
||||
[c, m, y, k] if [c, c, c] == [m, y, k] => Ok(ColorType::CMYK(c)),
|
||||
PhotometricInterpretation::CMYK => match self.samples {
|
||||
4 => Ok(ColorType::CMYK(self.bits_per_sample)),
|
||||
_ => Err(TiffError::UnsupportedError(
|
||||
TiffUnsupportedError::InterpretationWithBits(
|
||||
self.photometric_interpretation,
|
||||
self.bits_per_sample.clone(),
|
||||
vec![self.bits_per_sample; self.samples as usize],
|
||||
),
|
||||
)),
|
||||
},
|
||||
PhotometricInterpretation::YCbCr => match self.bits_per_sample[..] {
|
||||
[y, cb, cr] if [y, y] == [cb, cr] => Ok(ColorType::YCbCr(y)),
|
||||
PhotometricInterpretation::YCbCr => match self.samples {
|
||||
3 => Ok(ColorType::YCbCr(self.bits_per_sample)),
|
||||
_ => Err(TiffError::UnsupportedError(
|
||||
TiffUnsupportedError::InterpretationWithBits(
|
||||
self.photometric_interpretation,
|
||||
self.bits_per_sample.clone(),
|
||||
vec![self.bits_per_sample; self.samples as usize],
|
||||
),
|
||||
)),
|
||||
},
|
||||
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
|
||||
_ => Err(TiffError::UnsupportedError(
|
||||
TiffUnsupportedError::InterpretationWithBits(
|
||||
self.photometric_interpretation,
|
||||
self.bits_per_sample.clone(),
|
||||
vec![self.bits_per_sample; self.samples as usize],
|
||||
),
|
||||
)),
|
||||
}
|
||||
@@ -335,7 +368,7 @@ impl Image {
|
||||
photometric_interpretation: PhotometricInterpretation,
|
||||
compression_method: CompressionMethod,
|
||||
compressed_length: u64,
|
||||
jpeg_tables: Option<Arc<Vec<u8>>>,
|
||||
jpeg_tables: Option<&[u8]>,
|
||||
) -> TiffResult<Box<dyn Read + 'r>> {
|
||||
Ok(match compression_method {
|
||||
CompressionMethod::None => Box::new(reader),
|
||||
@@ -353,7 +386,29 @@ impl Image {
|
||||
));
|
||||
}
|
||||
|
||||
let jpeg_reader = JpegReader::new(reader, compressed_length, jpeg_tables)?;
|
||||
// Construct new jpeg_reader wrapping a SmartReader.
|
||||
//
|
||||
// JPEG compression in TIFF allows saving quantization and/or huffman tables in one
|
||||
// central location. 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`).
|
||||
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 {
|
||||
@@ -396,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)> {
|
||||
let file_offset = self
|
||||
.chunk_offsets
|
||||
@@ -435,7 +512,10 @@ impl Image {
|
||||
|
||||
match self.chunk_type {
|
||||
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)
|
||||
.and_then(|x| self.height.checked_sub(x))
|
||||
.ok_or(TiffError::UsageError(UsageError::InvalidChunkIndex(
|
||||
@@ -466,6 +546,7 @@ impl Image {
|
||||
output_width: usize,
|
||||
byte_order: ByteOrder,
|
||||
chunk_index: u32,
|
||||
limits: &Limits,
|
||||
) -> TiffResult<()> {
|
||||
// Validate that the provided buffer is of the expected type.
|
||||
let color_type = self.colortype()?;
|
||||
@@ -520,25 +601,27 @@ impl Image {
|
||||
.ok_or(TiffError::FormatError(
|
||||
TiffFormatError::InconsistentSizesEncountered,
|
||||
))?;
|
||||
if *compressed_bytes > limits.intermediate_buffer_size as u64 {
|
||||
return Err(TiffError::LimitsExceeded);
|
||||
}
|
||||
|
||||
let byte_len = buffer.byte_len();
|
||||
let compression_method = self.compression_method;
|
||||
let photometric_interpretation = self.photometric_interpretation;
|
||||
let predictor = self.predictor;
|
||||
let samples = self.bits_per_sample.len();
|
||||
let samples = self.samples_per_pixel();
|
||||
|
||||
let chunk_dims = self.chunk_dimensions()?;
|
||||
let data_dims = self.chunk_data_dimensions(chunk_index)?;
|
||||
|
||||
let padding_right = chunk_dims.0 - data_dims.0;
|
||||
|
||||
let jpeg_tables = self.jpeg_tables.clone();
|
||||
let mut reader = Self::create_reader(
|
||||
reader,
|
||||
photometric_interpretation,
|
||||
compression_method,
|
||||
*compressed_bytes,
|
||||
jpeg_tables,
|
||||
self.jpeg_tables.as_deref().map(|a| &**a),
|
||||
)?;
|
||||
|
||||
if output_width == data_dims.0 as usize && padding_right == 0 {
|
||||
@@ -547,8 +630,8 @@ impl Image {
|
||||
reader.read_exact(tile)?;
|
||||
|
||||
for row in 0..data_dims.1 as usize {
|
||||
let row_start = row as usize * output_width as usize * samples;
|
||||
let row_end = (row + 1) * output_width as usize * samples;
|
||||
let row_start = row * output_width * samples;
|
||||
let row_end = (row + 1) * output_width * samples;
|
||||
let row = buffer.subrange(row_start..row_end);
|
||||
super::fix_endianness_and_predict(row, samples, byte_order, predictor);
|
||||
}
|
||||
@@ -561,7 +644,7 @@ impl Image {
|
||||
let mut encoded = vec![0u8; chunk_dims.0 as usize * samples * byte_len];
|
||||
|
||||
for row in 0..data_dims.1 as usize {
|
||||
let row_start = row * output_width as usize * samples;
|
||||
let row_start = row * output_width * samples;
|
||||
let row_end = row_start + data_dims.0 as usize * samples;
|
||||
|
||||
reader.read_exact(&mut encoded)?;
|
||||
@@ -576,7 +659,7 @@ impl Image {
|
||||
}
|
||||
} else {
|
||||
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 = &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::image::Image;
|
||||
use crate::tags::{
|
||||
CompressionMethod, PhotometricInterpretation, Predictor, SampleFormat, Tag, Type,
|
||||
CompressionMethod, PhotometricInterpretation, PlanarConfiguration, Predictor, SampleFormat,
|
||||
Tag, Type,
|
||||
};
|
||||
|
||||
use self::stream::{ByteOrder, EndianReader, SmartReader};
|
||||
@@ -220,7 +221,7 @@ impl<'a> DecodingBuffer<'a> {
|
||||
|
||||
fn as_bytes_mut(&mut self) -> &mut [u8] {
|
||||
match self {
|
||||
DecodingBuffer::U8(buf) => &mut *buf,
|
||||
DecodingBuffer::U8(ref mut buf) => buf,
|
||||
DecodingBuffer::I8(buf) => bytecast::i8_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),
|
||||
@@ -370,8 +371,8 @@ pub fn fp_predict_f32(input: &mut [u8], output: &mut [f32], samples: usize) {
|
||||
for i in 0..output.len() {
|
||||
// TODO: use f32::from_be_bytes() when we can (version 1.40)
|
||||
output[i] = f32::from_bits(u32::from_be_bytes([
|
||||
input[input.len() / 4 * 0 + i],
|
||||
input[input.len() / 4 * 1 + i],
|
||||
input[i],
|
||||
input[input.len() / 4 + i],
|
||||
input[input.len() / 4 * 2 + 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() {
|
||||
// TODO: use f64::from_be_bytes() when we can (version 1.40)
|
||||
output[i] = f64::from_bits(u64::from_be_bytes([
|
||||
input[input.len() / 8 * 0 + i],
|
||||
input[input.len() / 8 * 1 + i],
|
||||
input[i],
|
||||
input[input.len() / 8 + i],
|
||||
input[input.len() / 8 * 2 + i],
|
||||
input[input.len() / 8 * 3 + i],
|
||||
input[input.len() / 8 * 4 + i],
|
||||
@@ -571,7 +572,7 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
ifd: None,
|
||||
width: 0,
|
||||
height: 0,
|
||||
bits_per_sample: vec![1],
|
||||
bits_per_sample: 1,
|
||||
samples: 1,
|
||||
sample_format: vec![SampleFormat::Uint],
|
||||
photometric_interpretation: PhotometricInterpretation::BlackIsZero,
|
||||
@@ -579,6 +580,7 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
jpeg_tables: None,
|
||||
predictor: Predictor::None,
|
||||
chunk_type: ChunkType::Strip,
|
||||
planar_config: PlanarConfiguration::Chunky,
|
||||
strip_decoder: None,
|
||||
tile_attributes: None,
|
||||
chunk_offsets: Vec::new(),
|
||||
@@ -1016,7 +1018,12 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
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
|
||||
@@ -1042,6 +1049,7 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
output_width,
|
||||
byte_order,
|
||||
chunk_index,
|
||||
&self.limits,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
@@ -1050,19 +1058,13 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
fn result_buffer(&self, width: usize, height: usize) -> TiffResult<DecodingResult> {
|
||||
let buffer_size = match width
|
||||
.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,
|
||||
None => return Err(TiffError::LimitsExceeded),
|
||||
};
|
||||
|
||||
let max_sample_bits = self
|
||||
.image()
|
||||
.bits_per_sample
|
||||
.iter()
|
||||
.cloned()
|
||||
.max()
|
||||
.unwrap_or(8);
|
||||
let max_sample_bits = self.image().bits_per_sample;
|
||||
match self
|
||||
.image()
|
||||
.sample_format
|
||||
@@ -1094,9 +1096,7 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
TiffUnsupportedError::UnsupportedBitsPerChannel(n),
|
||||
)),
|
||||
},
|
||||
format => {
|
||||
Err(TiffUnsupportedError::UnsupportedSampleFormat(vec![format.clone()]).into())
|
||||
}
|
||||
format => Err(TiffUnsupportedError::UnsupportedSampleFormat(vec![*format]).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 {
|
||||
return Err(TiffError::FormatError(
|
||||
TiffFormatError::InconsistentSizesEncountered,
|
||||
@@ -1155,7 +1155,12 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
let chunks_across = ((width - 1) / chunk_dimensions.0 + 1) as usize;
|
||||
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])?;
|
||||
|
||||
let x = chunk % chunks_across;
|
||||
@@ -1168,6 +1173,7 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
width as usize,
|
||||
byte_order,
|
||||
chunk as u32,
|
||||
&self.limits,
|
||||
)?;
|
||||
}
|
||||
|
||||
|
||||
+3
-102
@@ -1,8 +1,7 @@
|
||||
//! All IO functionality needed for TIFF decoding
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::io::{self, BufRead, BufReader, Read, Seek, SeekFrom, Take};
|
||||
use std::sync::Arc;
|
||||
use std::io::{self, BufRead, BufReader, Read, Seek, Take};
|
||||
|
||||
/// Byte order of the TIFF file.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
@@ -183,104 +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,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
///
|
||||
@@ -318,7 +219,7 @@ impl<R: Read> Read for PackBitsReader<R> {
|
||||
let mut header: [u8; 1] = [0];
|
||||
self.reader.read_exact(&mut header)?;
|
||||
let h = header[0] as i8;
|
||||
if h >= -127 && h <= -1 {
|
||||
if (-127..=-1).contains(&h) {
|
||||
let mut data: [u8; 1] = [0];
|
||||
self.reader.read_exact(&mut data)?;
|
||||
self.state = PackBitsReaderState::Repeat { value: data[0] };
|
||||
@@ -348,7 +249,7 @@ impl<R: Read> Read for PackBitsReader<R> {
|
||||
if self.count == 0 {
|
||||
self.state = PackBitsReaderState::Header;
|
||||
}
|
||||
return Ok(actual);
|
||||
Ok(actual)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ impl Compression for Deflate {
|
||||
const COMPRESSION_METHOD: CompressionMethod = CompressionMethod::Deflate;
|
||||
|
||||
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 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());
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,7 @@ pub enum Compressor {
|
||||
impl Default for Compressor {
|
||||
/// The default compression strategy does not apply any compression.
|
||||
fn default() -> Self {
|
||||
Compressor::Uncompressed(Uncompressed::default())
|
||||
Compressor::Uncompressed(Uncompressed)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,5 @@ impl CompressionAlgorithm for Compressor {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
pub const TEST_DATA: &'static [u8] =
|
||||
b"This is a string for checking various compression algorithms.";
|
||||
pub const TEST_DATA: &[u8] = b"This is a string for checking various compression algorithms.";
|
||||
}
|
||||
@@ -80,33 +80,31 @@ impl CompressionAlgorithm for Packbits {
|
||||
run_index = 0;
|
||||
in_run = false;
|
||||
}
|
||||
} else {
|
||||
if bytes_pending > MAX_BYTES {
|
||||
// We have as much differing data as we can output in one chunk.
|
||||
// Output MAX_BYTES leaving one byte.
|
||||
offset.get_or_insert(write_u8(&mut bufwriter, encode_diff(MAX_BYTES))?);
|
||||
bufwriter.write(&bytes[pending_index..pending_index + MAX_BYTES as usize])?;
|
||||
bytes_written += 1 + MAX_BYTES as u64;
|
||||
} else if bytes_pending > MAX_BYTES {
|
||||
// We have as much differing data as we can output in one chunk.
|
||||
// Output MAX_BYTES leaving one byte.
|
||||
offset.get_or_insert(write_u8(&mut bufwriter, encode_diff(MAX_BYTES))?);
|
||||
bufwriter.write_all(&bytes[pending_index..pending_index + MAX_BYTES as usize])?;
|
||||
bytes_written += 1 + MAX_BYTES as u64;
|
||||
|
||||
pending_index += MAX_BYTES as usize;
|
||||
bytes_pending -= MAX_BYTES;
|
||||
run_index = bytes_pending - 1; // A run could start here
|
||||
} else if curr_byte == last_byte {
|
||||
if (bytes_pending - run_index >= MIN_REPT) || (run_index == 0) {
|
||||
// This is a worthwhile run
|
||||
if run_index != 0 {
|
||||
// Flush differing data out of input buffer
|
||||
offset.get_or_insert(write_u8(&mut bufwriter, encode_diff(run_index))?);
|
||||
bufwriter
|
||||
.write(&bytes[pending_index..pending_index + run_index as usize])?;
|
||||
bytes_written += 1 + run_index as u64;
|
||||
}
|
||||
bytes_pending -= run_index; // Length of run
|
||||
in_run = true;
|
||||
pending_index += MAX_BYTES as usize;
|
||||
bytes_pending -= MAX_BYTES;
|
||||
run_index = bytes_pending - 1; // A run could start here
|
||||
} else if curr_byte == last_byte {
|
||||
if (bytes_pending - run_index >= MIN_REPT) || (run_index == 0) {
|
||||
// This is a worthwhile run
|
||||
if run_index != 0 {
|
||||
// Flush differing data out of input buffer
|
||||
offset.get_or_insert(write_u8(&mut bufwriter, encode_diff(run_index))?);
|
||||
bufwriter
|
||||
.write_all(&bytes[pending_index..pending_index + run_index as usize])?;
|
||||
bytes_written += 1 + run_index as u64;
|
||||
}
|
||||
} else {
|
||||
run_index = bytes_pending - 1; // A run could start here
|
||||
bytes_pending -= run_index; // Length of run
|
||||
in_run = true;
|
||||
}
|
||||
} else {
|
||||
run_index = bytes_pending - 1; // A run could start here
|
||||
}
|
||||
last_byte = curr_byte;
|
||||
}
|
||||
@@ -119,7 +117,7 @@ impl CompressionAlgorithm for Packbits {
|
||||
} else {
|
||||
bytes_written += 1 + bytes_pending as u64;
|
||||
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()?;
|
||||
@@ -141,24 +139,20 @@ mod tests {
|
||||
|
||||
let mut compressed_data = Vec::<u8>::new();
|
||||
let mut writer = Cursor::new(&mut compressed_data);
|
||||
Packbits::default()
|
||||
.write_to(&mut writer, &UNCOMPRESSED_DATA)
|
||||
.unwrap();
|
||||
Packbits.write_to(&mut writer, &UNCOMPRESSED_DATA).unwrap();
|
||||
assert_eq!(compressed_data, EXPECTED_COMPRESSED_DATA);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_packbits_rept() {
|
||||
// compress buffer with repetitive sequence
|
||||
const UNCOMPRESSED_DATA: &'static [u8] =
|
||||
const UNCOMPRESSED_DATA: &[u8] =
|
||||
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 writer = Cursor::new(&mut compressed_data);
|
||||
Packbits::default()
|
||||
.write_to(&mut writer, UNCOMPRESSED_DATA)
|
||||
.unwrap();
|
||||
Packbits.write_to(&mut writer, UNCOMPRESSED_DATA).unwrap();
|
||||
assert_eq!(compressed_data, EXPECTED_COMPRESSED_DATA);
|
||||
}
|
||||
|
||||
@@ -192,23 +186,19 @@ mod tests {
|
||||
|
||||
let mut compressed_data = Vec::<u8>::new();
|
||||
let mut writer = Cursor::new(&mut compressed_data);
|
||||
Packbits::default()
|
||||
.write_to(&mut writer, data.as_slice())
|
||||
.unwrap();
|
||||
Packbits.write_to(&mut writer, data.as_slice()).unwrap();
|
||||
assert_eq!(compressed_data, EXPECTED_COMPRESSED_DATA);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_packbits() {
|
||||
// compress teststring
|
||||
const EXPECTED_COMPRESSED_DATA: &'static [u8] =
|
||||
const EXPECTED_COMPRESSED_DATA: &[u8] =
|
||||
b"\x3CThis is a string for checking various compression algorithms.";
|
||||
|
||||
let mut compressed_data = Vec::<u8>::new();
|
||||
let mut writer = Cursor::new(&mut compressed_data);
|
||||
Packbits::default()
|
||||
.write_to(&mut writer, TEST_DATA)
|
||||
.unwrap();
|
||||
Packbits.write_to(&mut writer, TEST_DATA).unwrap();
|
||||
assert_eq!(compressed_data, EXPECTED_COMPRESSED_DATA);
|
||||
}
|
||||
}
|
||||
@@ -29,9 +29,7 @@ mod tests {
|
||||
fn test_no_compression() {
|
||||
let mut compressed_data = Vec::<u8>::new();
|
||||
let mut writer = Cursor::new(&mut compressed_data);
|
||||
Uncompressed::default()
|
||||
.write_to(&mut writer, TEST_DATA)
|
||||
.unwrap();
|
||||
Uncompressed.write_to(&mut writer, TEST_DATA).unwrap();
|
||||
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();
|
||||
|
||||
K::write_entry_count(&mut self.writer, self.ifd.len())?;
|
||||
K::write_entry_count(self.writer, self.ifd.len())?;
|
||||
for (
|
||||
tag,
|
||||
&DirectoryEntry {
|
||||
data_type: ref field_type,
|
||||
ref count,
|
||||
data: ref offset,
|
||||
DirectoryEntry {
|
||||
data_type: field_type,
|
||||
count,
|
||||
data: offset,
|
||||
},
|
||||
) in self.ifd.iter()
|
||||
{
|
||||
self.writer.write_u16(*tag)?;
|
||||
self.writer.write_u16(*field_type)?;
|
||||
(*count).write(&mut self.writer)?;
|
||||
(*count).write(self.writer)?;
|
||||
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.
|
||||
pub fn write_data<T: TiffValue>(&mut self, value: T) -> TiffResult<u64> {
|
||||
let offset = self.writer.offset();
|
||||
value.write(&mut self.writer)?;
|
||||
value.write(self.writer)?;
|
||||
Ok(offset)
|
||||
}
|
||||
|
||||
@@ -257,9 +257,9 @@ impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
|
||||
let curr_pos = self.writer.offset();
|
||||
|
||||
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)?;
|
||||
K::write_offset(&mut self.writer, 0)?;
|
||||
K::write_offset(self.writer, 0)?;
|
||||
|
||||
self.dropped = true;
|
||||
|
||||
@@ -400,7 +400,7 @@ impl<'a, W: 'a + Write + Seek, T: ColorType, K: TiffKind, D: Compression>
|
||||
strip_offsets: Vec::new(),
|
||||
strip_byte_count: Vec::new(),
|
||||
dropped: false,
|
||||
compression: compression,
|
||||
compression,
|
||||
_phantom: ::std::marker::PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ impl<W: Write> TiffWriter<W> {
|
||||
impl<W: Seek> TiffWriter<W> {
|
||||
pub fn goto_offset(&mut self, offset: u64) -> Result<(), io::Error> {
|
||||
self.offset = offset;
|
||||
self.writer.seek(SeekFrom::Start(offset as u64))?;
|
||||
self.writer.seek(SeekFrom::Start(offset))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -66,6 +66,7 @@ pub enum TiffFormatError {
|
||||
InvalidTagValueType(Tag),
|
||||
RequiredTagNotFound(Tag),
|
||||
UnknownPredictor(u16),
|
||||
UnknownPlanarConfiguration(u16),
|
||||
ByteExpected(Value),
|
||||
UnsignedIntegerExpected(Value),
|
||||
SignedIntegerExpected(Value),
|
||||
@@ -74,6 +75,7 @@ pub enum TiffFormatError {
|
||||
StripTileTagConflict,
|
||||
CycleInOffsets,
|
||||
JpegDecoder(JpegDecoderError),
|
||||
SamplesPerPixelIsZero,
|
||||
}
|
||||
|
||||
impl fmt::Display for TiffFormatError {
|
||||
@@ -113,6 +115,9 @@ impl fmt::Display for TiffFormatError {
|
||||
UnknownPredictor(ref 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),
|
||||
UnsignedIntegerExpected(ref val) => {
|
||||
write!(fmt, "Expected unsigned integer, {:?} found.", val)
|
||||
@@ -125,6 +130,7 @@ impl fmt::Display for TiffFormatError {
|
||||
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"),
|
||||
JpegDecoder(ref error) => write!(fmt, "{}", error),
|
||||
SamplesPerPixelIsZero => write!(fmt, "Samples per pixel is zero"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -142,6 +148,7 @@ impl fmt::Display for TiffFormatError {
|
||||
pub enum TiffUnsupportedError {
|
||||
FloatingPointPredictor(ColorType),
|
||||
HorizontalPredictor(ColorType),
|
||||
InconsistentBitsPerSample(Vec<u8>),
|
||||
InterpretationWithBits(PhotometricInterpretation, Vec<u8>),
|
||||
UnknownInterpretation,
|
||||
UnknownCompressionMethod,
|
||||
@@ -170,6 +177,9 @@ impl fmt::Display for TiffUnsupportedError {
|
||||
"Horizontal predictor for {:?} is unsupported.",
|
||||
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!(
|
||||
fmt,
|
||||
"{:?} with {:?} bits per sample is unsupported",
|
||||
|
||||
+1
-1
@@ -172,7 +172,7 @@ pub enum Type(u16) {
|
||||
tags! {
|
||||
/// See [TIFF compression tags](https://www.awaresystems.be/imaging/tiff/tifftags/compression.html)
|
||||
/// for reference.
|
||||
pub enum CompressionMethod(u16) {
|
||||
pub enum CompressionMethod(u16) unknown("A custom compression method") {
|
||||
None = 1,
|
||||
Huffman = 2,
|
||||
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]
|
||||
fn test_div_zero() {
|
||||
use tiff::{TiffError, TiffFormatError};
|
||||
@@ -333,9 +375,7 @@ fn fuzzer_testcase5() {
|
||||
178, 178, 178,
|
||||
];
|
||||
|
||||
let mut decoder = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap();
|
||||
|
||||
let _ = decoder.read_image().unwrap_err();
|
||||
let _ = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -352,7 +392,7 @@ fn fuzzer_testcase1() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stripped_image_overflow() {
|
||||
fn fuzzer_testcase6() {
|
||||
let image = [
|
||||
73, 73, 42, 0, 8, 0, 0, 0, 8, 0, 0, 1, 4, 0, 1, 0, 0, 0, 100, 0, 0, 148, 1, 1, 4, 0, 1, 0,
|
||||
0, 0, 158, 0, 0, 251, 3, 1, 3, 255, 254, 255, 255, 0, 1, 0, 0, 0, 6, 1, 3, 0, 1, 0, 0, 0,
|
||||
@@ -361,14 +401,7 @@ fn test_stripped_image_overflow() {
|
||||
178, 178,
|
||||
];
|
||||
|
||||
let mut decoder = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap();
|
||||
|
||||
let err = decoder.read_image().unwrap_err();
|
||||
|
||||
match err {
|
||||
tiff::TiffError::LimitsExceeded => {}
|
||||
unexpected => panic!("Unexpected error {}", unexpected),
|
||||
}
|
||||
let _ = tiff::decoder::Decoder::new(std::io::Cursor::new(&image)).unwrap_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Binary file not shown.
Reference in new issue
Block a user