Prepare for next release
This commit is contained in:
parent
485a127cf6
commit
d86bc80f43
26 changed files with 593 additions and 352 deletions
|
|
@ -1,3 +1,10 @@
|
|||
### Version 2.2.0
|
||||
- Various internal improvements ([#154](https://github.com/shssoichiro/oxipng/pull/154)) ([#158](https://github.com/shssoichiro/oxipng/pull/158)) ([#160](https://github.com/shssoichiro/oxipng/pull/160)) ([#161](https://github.com/shssoichiro/oxipng/pull/161)) ([#162](https://github.com/shssoichiro/oxipng/pull/162)) ([#163](https://github.com/shssoichiro/oxipng/pull/163))
|
||||
- Update `image` crate to 0.21.0
|
||||
- Update `itertools` crate to 0.8.0
|
||||
- Update `zopfli` crate to 0.4.0
|
||||
- Use Rust edition 2018
|
||||
|
||||
### Version 2.1.8
|
||||
- Fix non-standard sBIT headers in other code locations ([#153](https://github.com/shssoichiro/oxipng/issues/153))
|
||||
|
||||
|
|
|
|||
115
Cargo.lock
generated
115
Cargo.lock
generated
|
|
@ -1,8 +1,3 @@
|
|||
[[package]]
|
||||
name = "adler32"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "adler32"
|
||||
version = "1.0.3"
|
||||
|
|
@ -18,7 +13,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.4.8"
|
||||
version = "0.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
@ -29,7 +24,7 @@ name = "atty"
|
|||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
|
@ -51,17 +46,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.2.7"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.25"
|
||||
version = "1.0.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
|
|
@ -96,7 +86,7 @@ name = "cloudflare-zlib-sys"
|
|||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -121,7 +111,7 @@ name = "crossbeam-epoch"
|
|||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"arrayvec 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
@ -144,7 +134,7 @@ version = "0.7.19"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -159,20 +149,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.20.1"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-rational 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"png 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"png 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"safe-transmute 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inflate"
|
||||
version = "0.4.3"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
@ -180,7 +171,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.7.11"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
@ -193,7 +184,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.44"
|
||||
version = "0.2.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
|
|
@ -208,7 +199,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
@ -252,10 +243,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.8.0"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -263,28 +254,28 @@ name = "oxipng"
|
|||
version = "2.1.8"
|
||||
dependencies = [
|
||||
"bit-vec 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cloudflare-zlib 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"image 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"miniz_oxide 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"image 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"miniz_oxide 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rgb 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rgb 0.8.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wild 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"zopfli 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"zopfli 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.12.0"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"deflate 0.7.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"inflate 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"inflate 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
|
@ -305,13 +296,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
dependencies = [
|
||||
"crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.43"
|
||||
version = "0.1.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
|
|
@ -319,12 +310,17 @@ name = "redox_termios"
|
|||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"redox_syscall 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rgb"
|
||||
version = "0.8.11"
|
||||
version = "0.8.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "safe-transmute"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
|
|
@ -342,8 +338,8 @@ name = "termion"
|
|||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_syscall 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
|
@ -399,27 +395,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "zopfli"
|
||||
version = "0.3.7"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"adler32 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"typed-arena 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[metadata]
|
||||
"checksum adler32 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e928aa58f6dbd754bda26eca562a242549cb606e27a2240fc305fc75a7f12af9"
|
||||
"checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c"
|
||||
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
"checksum arrayvec 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "f405cc4c21cd8b784f6c8fc2adf9bc00f59558f0049b5ec21517f875963040cc"
|
||||
"checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71"
|
||||
"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
|
||||
"checksum bit-vec 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4440d5cb623bb7390ae27fec0bb6c61111969860f8e3ae198bfa0663645e67cf"
|
||||
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
|
||||
"checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39"
|
||||
"checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855"
|
||||
"checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d"
|
||||
"checksum cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "f159dfd43363c4d08055a07703eb7a3406b0dac4d0584d96965a3262db3c9d16"
|
||||
"checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb"
|
||||
"checksum cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4a8b715cb4597106ea87c7c84b2f1d452c7492033765df7f32651e66fcf749"
|
||||
"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
|
||||
"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e"
|
||||
"checksum cloudflare-zlib 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cd97a72b7777ef134de513cef589e48c393dc2ea7180ff6dca87dcd3ee078dac"
|
||||
|
|
@ -431,26 +425,27 @@ dependencies = [
|
|||
"checksum deflate 0.7.19 (registry+https://github.com/rust-lang/crates.io-index)" = "8a6abb26e16e8d419b5c78662aa9f82857c2386a073da266840e474d5055ec86"
|
||||
"checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0"
|
||||
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
|
||||
"checksum image 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44665b4395d1844c96e7dc8ed5754782a1cdfd9ef458a80bbe45702681450504"
|
||||
"checksum inflate 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6f53b811ee8e2057ccf9643ca6b4277de90efaf5e61e55fd5254576926bb4245"
|
||||
"checksum itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0d47946d458e94a1b7bcabbf6521ea7c037062c81f534615abcad76e84d4970d"
|
||||
"checksum image 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)" = "52fb0666a1273dac46f9725aa4859bcd5595fc3554cf3495051b4de8db745e7d"
|
||||
"checksum inflate 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "84c683bde2d8413b8f1be3e459c30e4817672b6e7a31d9212b0323154e76eba7"
|
||||
"checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358"
|
||||
"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
|
||||
"checksum libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)" = "10923947f84a519a45c8fefb7dd1b3e8c08747993381adee176d7a82b4195311"
|
||||
"checksum libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)" = "e962c7641008ac010fa60a7dfdc1712449f29c44ef2d4702394aea943ee75047"
|
||||
"checksum lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084"
|
||||
"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3"
|
||||
"checksum miniz_oxide 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5ad30a47319c16cde58d0314f5d98202a80c9083b5f61178457403dfb14e509c"
|
||||
"checksum miniz_oxide 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c468f2369f07d651a5d0bb2c9079f8488a66d5466efe42d0c5c6466edcb7f71e"
|
||||
"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
|
||||
"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea"
|
||||
"checksum num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124"
|
||||
"checksum num-rational 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e96f040177bb3da242b5b1ecf3f54b5d5af3efbbfb18608977a5d2767b22f10"
|
||||
"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"
|
||||
"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30"
|
||||
"checksum png 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f54b9600d584d3b8a739e1662a595fab051329eff43f20e7d8cc22872962145b"
|
||||
"checksum num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5a69d464bdc213aaaff628444e99578ede64e9c854025aa43b9796530afa9238"
|
||||
"checksum png 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9adebf7fb91ccf5eac9da1a8e00e83cb8ae882c3e8d8e4ad59da73cb8c82a2c9"
|
||||
"checksum rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "373814f27745b2686b350dd261bfd24576a6fb0e2c5919b3a2b6005f820b0473"
|
||||
"checksum rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b055d1e92aba6877574d8fe604a63c8b5df60f60e5982bf7ccbb1338ea527356"
|
||||
"checksum redox_syscall 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "679da7508e9a6390aeaf7fbd02a800fdc64b73fe2204dd2c8ae66d22d9d5ad5d"
|
||||
"checksum redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)" = "423e376fffca3dfa06c9e9790a9ccd282fafb3cc6e6397d01dbf64f9bacc6b85"
|
||||
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
|
||||
"checksum rgb 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)" = "002bebda58b24482d6911a59512e8a17fa1defecf5a2162521113b7cc5422dd1"
|
||||
"checksum rgb 0.8.12 (registry+https://github.com/rust-lang/crates.io-index)" = "6909f703ab5260012430324d5e8a5c74b26080597cd465d0bd1d2338d6b24ab7"
|
||||
"checksum safe-transmute 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9604873ffe1980bc1f179103704a65c8aca141c248d9e52b7af95ff10578166e"
|
||||
"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27"
|
||||
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
|
||||
"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
|
||||
|
|
@ -462,4 +457,4 @@ dependencies = [
|
|||
"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
|
||||
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
"checksum zopfli 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9fd2fb70c78a87410032abe2a9eb2bec4984a2d9bc26adb1f8f768e02673e84e"
|
||||
"checksum zopfli 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4079b79464426ade2a1b0177fb0ce8396ba6b4084267407e333573c666073964"
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ categories = [
|
|||
]
|
||||
description = "A lossless PNG compression optimizer"
|
||||
documentation = "https://docs.rs/oxipng"
|
||||
edition = "2018"
|
||||
exclude = [
|
||||
"tests/*",
|
||||
"bench/*",
|
||||
|
|
@ -30,9 +31,9 @@ required-features = ["binary"]
|
|||
bit-vec = "^0.5.0"
|
||||
byteorder = "^1.0.0"
|
||||
crc = "^1.2.0"
|
||||
itertools = "^0.7.7"
|
||||
itertools = "^0.8.0"
|
||||
num_cpus = "^1.0.0"
|
||||
zopfli = "^0.3.4"
|
||||
zopfli = "^0.4.0"
|
||||
miniz_oxide = "0.2.0"
|
||||
rgb = "0.8.11"
|
||||
|
||||
|
|
@ -54,7 +55,7 @@ cloudflare-zlib = "^0.2.2"
|
|||
[dependencies.image]
|
||||
default-features = false
|
||||
features = ["png_codec"]
|
||||
version = "^0.20.0"
|
||||
version = "^0.21.0"
|
||||
|
||||
[features]
|
||||
binary = [
|
||||
|
|
|
|||
|
|
@ -3,10 +3,12 @@
|
|||
extern crate oxipng;
|
||||
extern crate test;
|
||||
|
||||
use oxipng::internal_tests::*;
|
||||
use std::path::PathBuf;
|
||||
use test::Bencher;
|
||||
|
||||
use oxipng::internal_tests::*;
|
||||
use oxipng::Deadline;
|
||||
|
||||
#[bench]
|
||||
fn deflate_16_bits_strategy_0(b: &mut Bencher) {
|
||||
let input = test::black_box(PathBuf::from("tests/files/rgb_16_should_be_rgb_16.png"));
|
||||
|
|
@ -14,7 +16,14 @@ fn deflate_16_bits_strategy_0(b: &mut Bencher) {
|
|||
|
||||
b.iter(|| {
|
||||
let min = AtomicMin::new(None);
|
||||
deflate(png.raw.data.as_ref(), 9, 0, 15, &min)
|
||||
deflate(
|
||||
png.raw.data.as_ref(),
|
||||
9,
|
||||
0,
|
||||
15,
|
||||
&min,
|
||||
&Deadline::new(None, false),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -25,7 +34,14 @@ fn deflate_8_bits_strategy_0(b: &mut Bencher) {
|
|||
|
||||
b.iter(|| {
|
||||
let min = AtomicMin::new(None);
|
||||
deflate(png.raw.data.as_ref(), 9, 0, 15, &min)
|
||||
deflate(
|
||||
png.raw.data.as_ref(),
|
||||
9,
|
||||
0,
|
||||
15,
|
||||
&min,
|
||||
&Deadline::new(None, false),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -38,7 +54,14 @@ fn deflate_4_bits_strategy_0(b: &mut Bencher) {
|
|||
|
||||
b.iter(|| {
|
||||
let min = AtomicMin::new(None);
|
||||
deflate(png.raw.data.as_ref(), 9, 0, 15, &min)
|
||||
deflate(
|
||||
png.raw.data.as_ref(),
|
||||
9,
|
||||
0,
|
||||
15,
|
||||
&min,
|
||||
&Deadline::new(None, false),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -51,7 +74,14 @@ fn deflate_2_bits_strategy_0(b: &mut Bencher) {
|
|||
|
||||
b.iter(|| {
|
||||
let min = AtomicMin::new(None);
|
||||
deflate(png.raw.data.as_ref(), 9, 0, 15, &min)
|
||||
deflate(
|
||||
png.raw.data.as_ref(),
|
||||
9,
|
||||
0,
|
||||
15,
|
||||
&min,
|
||||
&Deadline::new(None, false),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -64,7 +94,14 @@ fn deflate_1_bits_strategy_0(b: &mut Bencher) {
|
|||
|
||||
b.iter(|| {
|
||||
let min = AtomicMin::new(None);
|
||||
deflate(png.raw.data.as_ref(), 9, 0, 15, &min)
|
||||
deflate(
|
||||
png.raw.data.as_ref(),
|
||||
9,
|
||||
0,
|
||||
15,
|
||||
&min,
|
||||
&Deadline::new(None, false),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -75,7 +112,14 @@ fn deflate_16_bits_strategy_1(b: &mut Bencher) {
|
|||
|
||||
b.iter(|| {
|
||||
let min = AtomicMin::new(None);
|
||||
deflate(png.raw.data.as_ref(), 9, 1, 15, &min)
|
||||
deflate(
|
||||
png.raw.data.as_ref(),
|
||||
9,
|
||||
1,
|
||||
15,
|
||||
&min,
|
||||
&Deadline::new(None, false),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -86,7 +130,14 @@ fn deflate_8_bits_strategy_1(b: &mut Bencher) {
|
|||
|
||||
b.iter(|| {
|
||||
let min = AtomicMin::new(None);
|
||||
deflate(png.raw.data.as_ref(), 9, 1, 15, &min)
|
||||
deflate(
|
||||
png.raw.data.as_ref(),
|
||||
9,
|
||||
1,
|
||||
15,
|
||||
&min,
|
||||
&Deadline::new(None, false),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -99,7 +150,14 @@ fn deflate_4_bits_strategy_1(b: &mut Bencher) {
|
|||
|
||||
b.iter(|| {
|
||||
let min = AtomicMin::new(None);
|
||||
deflate(png.raw.data.as_ref(), 9, 1, 15, &min)
|
||||
deflate(
|
||||
png.raw.data.as_ref(),
|
||||
9,
|
||||
1,
|
||||
15,
|
||||
&min,
|
||||
&Deadline::new(None, false),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -112,7 +170,14 @@ fn deflate_2_bits_strategy_1(b: &mut Bencher) {
|
|||
|
||||
b.iter(|| {
|
||||
let min = AtomicMin::new(None);
|
||||
deflate(png.raw.data.as_ref(), 9, 1, 15, &min)
|
||||
deflate(
|
||||
png.raw.data.as_ref(),
|
||||
9,
|
||||
1,
|
||||
15,
|
||||
&min,
|
||||
&Deadline::new(None, false),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -125,7 +190,14 @@ fn deflate_1_bits_strategy_1(b: &mut Bencher) {
|
|||
|
||||
b.iter(|| {
|
||||
let min = AtomicMin::new(None);
|
||||
deflate(png.raw.data.as_ref(), 9, 1, 15, &min)
|
||||
deflate(
|
||||
png.raw.data.as_ref(),
|
||||
9,
|
||||
1,
|
||||
15,
|
||||
&min,
|
||||
&Deadline::new(None, false),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -136,7 +208,14 @@ fn deflate_16_bits_strategy_2(b: &mut Bencher) {
|
|||
|
||||
b.iter(|| {
|
||||
let min = AtomicMin::new(None);
|
||||
deflate(png.raw.data.as_ref(), 9, 2, 15, &min)
|
||||
deflate(
|
||||
png.raw.data.as_ref(),
|
||||
9,
|
||||
2,
|
||||
15,
|
||||
&min,
|
||||
&Deadline::new(None, false),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -147,7 +226,14 @@ fn deflate_8_bits_strategy_2(b: &mut Bencher) {
|
|||
|
||||
b.iter(|| {
|
||||
let min = AtomicMin::new(None);
|
||||
deflate(png.raw.data.as_ref(), 9, 2, 15, &min)
|
||||
deflate(
|
||||
png.raw.data.as_ref(),
|
||||
9,
|
||||
2,
|
||||
15,
|
||||
&min,
|
||||
&Deadline::new(None, false),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -160,7 +246,14 @@ fn deflate_4_bits_strategy_2(b: &mut Bencher) {
|
|||
|
||||
b.iter(|| {
|
||||
let min = AtomicMin::new(None);
|
||||
deflate(png.raw.data.as_ref(), 9, 2, 15, &min)
|
||||
deflate(
|
||||
png.raw.data.as_ref(),
|
||||
9,
|
||||
2,
|
||||
15,
|
||||
&min,
|
||||
&Deadline::new(None, false),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -173,7 +266,14 @@ fn deflate_2_bits_strategy_2(b: &mut Bencher) {
|
|||
|
||||
b.iter(|| {
|
||||
let min = AtomicMin::new(None);
|
||||
deflate(png.raw.data.as_ref(), 9, 2, 15, &min)
|
||||
deflate(
|
||||
png.raw.data.as_ref(),
|
||||
9,
|
||||
2,
|
||||
15,
|
||||
&min,
|
||||
&Deadline::new(None, false),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -186,7 +286,14 @@ fn deflate_1_bits_strategy_2(b: &mut Bencher) {
|
|||
|
||||
b.iter(|| {
|
||||
let min = AtomicMin::new(None);
|
||||
deflate(png.raw.data.as_ref(), 9, 2, 15, &min)
|
||||
deflate(
|
||||
png.raw.data.as_ref(),
|
||||
9,
|
||||
2,
|
||||
15,
|
||||
&min,
|
||||
&Deadline::new(None, false),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -197,7 +304,14 @@ fn deflate_16_bits_strategy_3(b: &mut Bencher) {
|
|||
|
||||
b.iter(|| {
|
||||
let min = AtomicMin::new(None);
|
||||
deflate(png.raw.data.as_ref(), 9, 3, 15, &min)
|
||||
deflate(
|
||||
png.raw.data.as_ref(),
|
||||
9,
|
||||
3,
|
||||
15,
|
||||
&min,
|
||||
&Deadline::new(None, false),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -208,7 +322,14 @@ fn deflate_8_bits_strategy_3(b: &mut Bencher) {
|
|||
|
||||
b.iter(|| {
|
||||
let min = AtomicMin::new(None);
|
||||
deflate(png.raw.data.as_ref(), 9, 3, 15, &min)
|
||||
deflate(
|
||||
png.raw.data.as_ref(),
|
||||
9,
|
||||
3,
|
||||
15,
|
||||
&min,
|
||||
&Deadline::new(None, false),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -221,7 +342,14 @@ fn deflate_4_bits_strategy_3(b: &mut Bencher) {
|
|||
|
||||
b.iter(|| {
|
||||
let min = AtomicMin::new(None);
|
||||
deflate(png.raw.data.as_ref(), 9, 3, 15, &min)
|
||||
deflate(
|
||||
png.raw.data.as_ref(),
|
||||
9,
|
||||
3,
|
||||
15,
|
||||
&min,
|
||||
&Deadline::new(None, false),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -234,7 +362,14 @@ fn deflate_2_bits_strategy_3(b: &mut Bencher) {
|
|||
|
||||
b.iter(|| {
|
||||
let min = AtomicMin::new(None);
|
||||
deflate(png.raw.data.as_ref(), 9, 3, 15, &min)
|
||||
deflate(
|
||||
png.raw.data.as_ref(),
|
||||
9,
|
||||
3,
|
||||
15,
|
||||
&min,
|
||||
&Deadline::new(None, false),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -247,7 +382,14 @@ fn deflate_1_bits_strategy_3(b: &mut Bencher) {
|
|||
|
||||
b.iter(|| {
|
||||
let min = AtomicMin::new(None);
|
||||
deflate(png.raw.data.as_ref(), 9, 3, 15, &min)
|
||||
deflate(
|
||||
png.raw.data.as_ref(),
|
||||
9,
|
||||
3,
|
||||
15,
|
||||
&min,
|
||||
&Deadline::new(None, false),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ pub enum ColorType {
|
|||
|
||||
impl fmt::Display for ColorType {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
|
|
@ -73,7 +73,7 @@ pub enum BitDepth {
|
|||
|
||||
impl fmt::Display for BitDepth {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
|
|
@ -127,7 +127,7 @@ pub enum AlphaOptim {
|
|||
}
|
||||
|
||||
impl fmt::Display for AlphaOptim {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use Deadline;
|
||||
use atomicmin::AtomicMin;
|
||||
use crate::atomicmin::AtomicMin;
|
||||
use crate::Deadline;
|
||||
use crate::PngError;
|
||||
use crate::PngResult;
|
||||
pub use cloudflare_zlib::is_supported;
|
||||
use cloudflare_zlib::*;
|
||||
use PngError;
|
||||
use PngResult;
|
||||
|
||||
impl From<ZError> for PngError {
|
||||
fn from(err: ZError) -> Self {
|
||||
|
|
@ -28,7 +28,7 @@ pub(crate) fn cfzlib_deflate(
|
|||
// max size is generally checked after each split,
|
||||
// so splitting the buffer into pieces gives more checks
|
||||
// = better chance of hitting it sooner.
|
||||
let chunk_size = (data.len()/4).max(1<<15).min(1<<18); // 32-256KB
|
||||
let chunk_size = (data.len() / 4).max(1 << 15).min(1 << 18); // 32-256KB
|
||||
for chunk in data.chunks(chunk_size) {
|
||||
stream.compress_with_limit(chunk, max_size)?;
|
||||
if deadline.passed() {
|
||||
|
|
@ -47,7 +47,8 @@ fn compress_test() {
|
|||
15,
|
||||
&AtomicMin::new(None),
|
||||
&Deadline::new(None, false),
|
||||
).unwrap();
|
||||
let res = ::deflate::inflate(&vec).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
let res = crate::deflate::inflate(&vec).unwrap();
|
||||
assert_eq!(&res, b"azxcvbnm");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use atomicmin::AtomicMin;
|
||||
use error::PngError;
|
||||
use crate::atomicmin::AtomicMin;
|
||||
use crate::error::PngError;
|
||||
use crate::PngResult;
|
||||
use miniz_oxide::deflate::core::*;
|
||||
use PngResult;
|
||||
|
||||
pub fn compress_to_vec_oxipng(
|
||||
input: &[u8],
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
use Deadline;
|
||||
use atomicmin::AtomicMin;
|
||||
use error::PngError;
|
||||
use crate::atomicmin::AtomicMin;
|
||||
use crate::error::PngError;
|
||||
use crate::Deadline;
|
||||
use crate::PngResult;
|
||||
use miniz_oxide;
|
||||
use std::cmp::max;
|
||||
use zopfli;
|
||||
use PngResult;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub mod miniz_stream;
|
||||
|
|
@ -26,7 +26,14 @@ pub fn inflate(data: &[u8]) -> PngResult<Vec<u8>> {
|
|||
}
|
||||
|
||||
/// Compress a data stream using the DEFLATE algorithm
|
||||
pub(crate) fn deflate(data: &[u8], zc: u8, zs: u8, zw: u8, max_size: &AtomicMin, deadline: &Deadline) -> PngResult<Vec<u8>> {
|
||||
pub(crate) fn deflate(
|
||||
data: &[u8],
|
||||
zc: u8,
|
||||
zs: u8,
|
||||
zw: u8,
|
||||
max_size: &AtomicMin,
|
||||
deadline: &Deadline,
|
||||
) -> PngResult<Vec<u8>> {
|
||||
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
|
||||
{
|
||||
if cfzlib::is_supported() {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ impl Error for PngError {
|
|||
|
||||
impl fmt::Display for PngError {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
PngError::DeflatedDataTooLong(_) => f.write_str("deflated data too long"),
|
||||
PngError::TimedOut => f.write_str("timed out"),
|
||||
|
|
|
|||
|
|
@ -1,22 +1,28 @@
|
|||
//! Check if a reduction makes file smaller, and keep best reductions.
|
||||
//! Works asynchronously when possible
|
||||
|
||||
use atomicmin::AtomicMin;
|
||||
use crate::atomicmin::AtomicMin;
|
||||
use crate::deflate;
|
||||
use crate::png::PngData;
|
||||
use crate::png::PngImage;
|
||||
use crate::png::STD_COMPRESSION;
|
||||
use crate::png::STD_FILTERS;
|
||||
use crate::png::STD_STRATEGY;
|
||||
use crate::png::STD_WINDOW;
|
||||
#[cfg(not(feature = "parallel"))]
|
||||
use crate::rayon;
|
||||
#[cfg(not(feature = "parallel"))]
|
||||
use crate::rayon::prelude::*;
|
||||
use crate::Deadline;
|
||||
#[cfg(feature = "parallel")]
|
||||
use rayon;
|
||||
#[cfg(feature = "parallel")]
|
||||
use rayon::prelude::*;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::atomic::Ordering::SeqCst;
|
||||
use deflate;
|
||||
use Deadline;
|
||||
use png::PngData;
|
||||
use png::PngImage;
|
||||
use png::STD_COMPRESSION;
|
||||
use png::STD_FILTERS;
|
||||
use png::STD_STRATEGY;
|
||||
use png::STD_WINDOW;
|
||||
use rayon::prelude::*;
|
||||
use std::sync::mpsc::*;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use rayon;
|
||||
|
||||
struct Candidate {
|
||||
image: PngData,
|
||||
|
|
@ -100,16 +106,20 @@ impl Evaluator {
|
|||
) {
|
||||
best_candidate_size.set_min(idat_data.len());
|
||||
// the rest is shipped to the evavluation/collection thread
|
||||
eval_send.as_ref().expect("not finished yet").send(Candidate {
|
||||
image: PngData {
|
||||
idat_data,
|
||||
raw: Arc::clone(&image),
|
||||
},
|
||||
bias,
|
||||
filter,
|
||||
is_reduction,
|
||||
nth,
|
||||
}).expect("send");
|
||||
eval_send
|
||||
.as_ref()
|
||||
.expect("not finished yet")
|
||||
.send(Candidate {
|
||||
image: PngData {
|
||||
idat_data,
|
||||
raw: Arc::clone(&image),
|
||||
},
|
||||
bias,
|
||||
filter,
|
||||
is_reduction,
|
||||
nth,
|
||||
})
|
||||
.expect("send");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -124,11 +134,33 @@ impl Evaluator {
|
|||
let is_best = if let Some(ref old) = best_result {
|
||||
// ordering is important - later file gets to use bias over earlier, but not the other way
|
||||
// (this way bias=0 replaces, but doesn't forbid later optimizations)
|
||||
let new_len = (new.image.idat_data.len() as f64 * if new.nth > old.nth {new.bias as f64} else {1.0}) as usize;
|
||||
let old_len = (old.image.idat_data.len() as f64 * if new.nth < old.nth {old.bias as f64} else {1.0}) as usize;
|
||||
let new_len = (new.image.idat_data.len() as f64
|
||||
* if new.nth > old.nth {
|
||||
f64::from(new.bias)
|
||||
} else {
|
||||
1.0
|
||||
}) as usize;
|
||||
let old_len = (old.image.idat_data.len() as f64
|
||||
* if new.nth < old.nth {
|
||||
f64::from(old.bias)
|
||||
} else {
|
||||
1.0
|
||||
}) as usize;
|
||||
// choose smallest compressed, or if compresses the same, smallest uncompressed, or cheaper filter
|
||||
let new = (new_len, new.image.raw.data.len(), new.image.raw.ihdr.bit_depth, new.filter, new.nth);
|
||||
let old = (old_len, old.image.raw.data.len(), old.image.raw.ihdr.bit_depth, old.filter, old.nth);
|
||||
let new = (
|
||||
new_len,
|
||||
new.image.raw.data.len(),
|
||||
new.image.raw.ihdr.bit_depth,
|
||||
new.filter,
|
||||
new.nth,
|
||||
);
|
||||
let old = (
|
||||
old_len,
|
||||
old.image.raw.data.len(),
|
||||
old.image.raw.ihdr.bit_depth,
|
||||
old.filter,
|
||||
old.nth,
|
||||
);
|
||||
// <= instead of < is important, because best_candidate_size has been set already,
|
||||
// so the current result may be comparing its size with itself
|
||||
new <= old
|
||||
|
|
@ -136,14 +168,9 @@ impl Evaluator {
|
|||
true
|
||||
};
|
||||
if is_best {
|
||||
best_result = if new.is_reduction {
|
||||
Some(new)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
best_result = if new.is_reduction { Some(new) } else { None };
|
||||
}
|
||||
}
|
||||
best_result.map(|res| res.image)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
141
src/filters.rs
141
src/filters.rs
|
|
@ -24,35 +24,40 @@ pub fn filter_line(filter: u8, bpp: usize, data: &[u8], last_line: &[u8]) -> Vec
|
|||
);
|
||||
};
|
||||
}
|
||||
3 => for (i, byte) in data.iter().enumerate() {
|
||||
if last_line.is_empty() {
|
||||
filtered.push(match i.checked_sub(bpp) {
|
||||
Some(x) => byte.wrapping_sub(data[x] >> 1),
|
||||
None => *byte,
|
||||
});
|
||||
} else {
|
||||
filtered.push(match i.checked_sub(bpp) {
|
||||
Some(x) => byte
|
||||
.wrapping_sub(((u16::from(data[x]) + u16::from(last_line[i])) >> 1) as u8),
|
||||
None => byte.wrapping_sub(last_line[i] >> 1),
|
||||
});
|
||||
};
|
||||
},
|
||||
4 => for (i, byte) in data.iter().enumerate() {
|
||||
if last_line.is_empty() {
|
||||
filtered.push(match i.checked_sub(bpp) {
|
||||
Some(x) => byte.wrapping_sub(data[x]),
|
||||
None => *byte,
|
||||
});
|
||||
} else {
|
||||
filtered.push(match i.checked_sub(bpp) {
|
||||
Some(x) => {
|
||||
byte.wrapping_sub(paeth_predictor(data[x], last_line[i], last_line[x]))
|
||||
}
|
||||
None => byte.wrapping_sub(last_line[i]),
|
||||
});
|
||||
};
|
||||
},
|
||||
3 => {
|
||||
for (i, byte) in data.iter().enumerate() {
|
||||
if last_line.is_empty() {
|
||||
filtered.push(match i.checked_sub(bpp) {
|
||||
Some(x) => byte.wrapping_sub(data[x] >> 1),
|
||||
None => *byte,
|
||||
});
|
||||
} else {
|
||||
filtered.push(match i.checked_sub(bpp) {
|
||||
Some(x) => byte.wrapping_sub(
|
||||
((u16::from(data[x]) + u16::from(last_line[i])) >> 1) as u8,
|
||||
),
|
||||
None => byte.wrapping_sub(last_line[i] >> 1),
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
4 => {
|
||||
for (i, byte) in data.iter().enumerate() {
|
||||
if last_line.is_empty() {
|
||||
filtered.push(match i.checked_sub(bpp) {
|
||||
Some(x) => byte.wrapping_sub(data[x]),
|
||||
None => *byte,
|
||||
});
|
||||
} else {
|
||||
filtered.push(match i.checked_sub(bpp) {
|
||||
Some(x) => {
|
||||
byte.wrapping_sub(paeth_predictor(data[x], last_line[i], last_line[x]))
|
||||
}
|
||||
None => byte.wrapping_sub(last_line[i]),
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
filtered
|
||||
|
|
@ -64,17 +69,19 @@ pub fn unfilter_line(filter: u8, bpp: usize, data: &[u8], last_line: &[u8]) -> V
|
|||
0 => {
|
||||
unfiltered.extend_from_slice(data);
|
||||
}
|
||||
1 => for (i, byte) in data.iter().enumerate() {
|
||||
match i.checked_sub(bpp) {
|
||||
Some(x) => {
|
||||
let b = unfiltered[x];
|
||||
unfiltered.push(byte.wrapping_add(b));
|
||||
}
|
||||
None => {
|
||||
unfiltered.push(*byte);
|
||||
}
|
||||
};
|
||||
},
|
||||
1 => {
|
||||
for (i, byte) in data.iter().enumerate() {
|
||||
match i.checked_sub(bpp) {
|
||||
Some(x) => {
|
||||
let b = unfiltered[x];
|
||||
unfiltered.push(byte.wrapping_add(b));
|
||||
}
|
||||
None => {
|
||||
unfiltered.push(*byte);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
if last_line.is_empty() {
|
||||
unfiltered.extend_from_slice(data);
|
||||
|
|
@ -113,33 +120,35 @@ pub fn unfilter_line(filter: u8, bpp: usize, data: &[u8], last_line: &[u8]) -> V
|
|||
};
|
||||
}
|
||||
}
|
||||
4 => for (i, byte) in data.iter().enumerate() {
|
||||
if last_line.is_empty() {
|
||||
match i.checked_sub(bpp) {
|
||||
Some(x) => {
|
||||
let b = unfiltered[x];
|
||||
unfiltered.push(byte.wrapping_add(b));
|
||||
}
|
||||
None => {
|
||||
unfiltered.push(*byte);
|
||||
}
|
||||
4 => {
|
||||
for (i, byte) in data.iter().enumerate() {
|
||||
if last_line.is_empty() {
|
||||
match i.checked_sub(bpp) {
|
||||
Some(x) => {
|
||||
let b = unfiltered[x];
|
||||
unfiltered.push(byte.wrapping_add(b));
|
||||
}
|
||||
None => {
|
||||
unfiltered.push(*byte);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
match i.checked_sub(bpp) {
|
||||
Some(x) => {
|
||||
let b = unfiltered[x];
|
||||
unfiltered.push(byte.wrapping_add(paeth_predictor(
|
||||
b,
|
||||
last_line[i],
|
||||
last_line[x],
|
||||
)));
|
||||
}
|
||||
None => {
|
||||
unfiltered.push(byte.wrapping_add(last_line[i]));
|
||||
}
|
||||
};
|
||||
};
|
||||
} else {
|
||||
match i.checked_sub(bpp) {
|
||||
Some(x) => {
|
||||
let b = unfiltered[x];
|
||||
unfiltered.push(byte.wrapping_add(paeth_predictor(
|
||||
b,
|
||||
last_line[i],
|
||||
last_line[x],
|
||||
)));
|
||||
}
|
||||
None => {
|
||||
unfiltered.push(byte.wrapping_add(last_line[i]));
|
||||
}
|
||||
};
|
||||
};
|
||||
},
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
unfiltered
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
use crate::colors::{BitDepth, ColorType};
|
||||
use crate::error::PngError;
|
||||
use crate::PngResult;
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
use colors::{BitDepth, ColorType};
|
||||
use crc::crc32;
|
||||
use error::PngError;
|
||||
use std::collections::HashSet;
|
||||
use std::io::Cursor;
|
||||
use PngResult;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
/// Headers from the IHDR chunk of the image
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use headers::IhdrData;
|
||||
use crate::headers::IhdrData;
|
||||
use crate::png::PngImage;
|
||||
use bit_vec::BitVec;
|
||||
use png::PngImage;
|
||||
|
||||
#[must_use]
|
||||
pub fn interlace_image(png: &PngImage) -> PngImage {
|
||||
|
|
|
|||
85
src/lib.rs
85
src/lib.rs
|
|
@ -1,28 +1,18 @@
|
|||
extern crate bit_vec;
|
||||
extern crate byteorder;
|
||||
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
|
||||
extern crate cloudflare_zlib;
|
||||
extern crate crc;
|
||||
extern crate image;
|
||||
extern crate itertools;
|
||||
extern crate miniz_oxide;
|
||||
extern crate num_cpus;
|
||||
use num_cpus;
|
||||
#[cfg(feature = "parallel")]
|
||||
extern crate rayon;
|
||||
#[cfg(not(feature = "parallel"))]
|
||||
mod rayon;
|
||||
extern crate rgb;
|
||||
extern crate zopfli;
|
||||
|
||||
use reduction::*;
|
||||
use atomicmin::AtomicMin;
|
||||
use crate::atomicmin::AtomicMin;
|
||||
use crate::colors::BitDepth;
|
||||
use crate::deflate::inflate;
|
||||
use crate::evaluate::Evaluator;
|
||||
use crate::png::PngData;
|
||||
use crate::png::PngImage;
|
||||
use crate::reduction::*;
|
||||
use crc::crc32;
|
||||
use deflate::inflate;
|
||||
use evaluate::Evaluator;
|
||||
use image::{DynamicImage, GenericImageView, ImageFormat, Pixel};
|
||||
use png::PngImage;
|
||||
use png::PngData;
|
||||
use colors::BitDepth;
|
||||
use rayon::prelude::*;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fmt;
|
||||
|
|
@ -30,13 +20,13 @@ use std::fs::{copy, File};
|
|||
use std::io::{stdin, stdout, BufWriter, Read, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::time::{Duration, Instant};
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
pub use colors::AlphaOptim;
|
||||
pub use deflate::Deflaters;
|
||||
pub use error::PngError;
|
||||
pub use headers::Headers;
|
||||
pub use crate::colors::AlphaOptim;
|
||||
pub use crate::deflate::Deflaters;
|
||||
pub use crate::error::PngError;
|
||||
pub use crate::headers::Headers;
|
||||
|
||||
mod atomicmin;
|
||||
mod colors;
|
||||
|
|
@ -52,11 +42,11 @@ mod reduction;
|
|||
/// Private to oxipng; don't use outside tests and benches
|
||||
#[doc(hidden)]
|
||||
pub mod internal_tests {
|
||||
pub use atomicmin::*;
|
||||
pub use colors::*;
|
||||
pub use deflate::*;
|
||||
pub use headers::*;
|
||||
pub use png::*;
|
||||
pub use crate::atomicmin::*;
|
||||
pub use crate::colors::*;
|
||||
pub use crate::deflate::*;
|
||||
pub use crate::headers::*;
|
||||
pub use crate::png::*;
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
@ -92,7 +82,7 @@ impl InFile {
|
|||
}
|
||||
|
||||
impl fmt::Display for InFile {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
InFile::Path(ref p) => write!(f, "{}", p.display()),
|
||||
InFile::StdIn => f.write_str("stdin"),
|
||||
|
|
@ -465,7 +455,12 @@ struct TrialOptions {
|
|||
}
|
||||
|
||||
/// Perform optimization on the input PNG object using the options provided
|
||||
fn optimize_png(png: &mut PngData, original_data: &[u8], opts: &Options, deadline: Arc<Deadline>) -> PngResult<Vec<u8>> {
|
||||
fn optimize_png(
|
||||
png: &mut PngData,
|
||||
original_data: &[u8],
|
||||
opts: &Options,
|
||||
deadline: Arc<Deadline>,
|
||||
) -> PngResult<Vec<u8>> {
|
||||
type TrialWithData = (TrialOptions, Vec<u8>);
|
||||
|
||||
let original_png = png.clone();
|
||||
|
|
@ -582,7 +577,8 @@ fn optimize_png(png: &mut PngData, original_data: &[u8], opts: &Options, deadlin
|
|||
.map(|f| {
|
||||
let png = png.clone();
|
||||
(*f, png.raw.filter_image(*f))
|
||||
}).collect();
|
||||
})
|
||||
.collect();
|
||||
|
||||
let original_len = original_png.idat_data.len();
|
||||
let added_interlacing = opts.interlace == Some(1) && original_png.raw.ihdr.interlaced == 0;
|
||||
|
|
@ -639,9 +635,8 @@ fn optimize_png(png: &mut PngData, original_data: &[u8], opts: &Options, deadlin
|
|||
None
|
||||
}
|
||||
});
|
||||
let best: Option<TrialWithData> = best.reduce_with(|i, j| {
|
||||
if i.1.len() <= j.1.len() { i } else { j }
|
||||
});
|
||||
let best: Option<TrialWithData> =
|
||||
best.reduce_with(|i, j| if i.1.len() <= j.1.len() { i } else { j });
|
||||
|
||||
if let Some(better) = best {
|
||||
png.idat_data = better.1;
|
||||
|
|
@ -698,7 +693,7 @@ fn optimize_png(png: &mut PngData, original_data: &[u8], opts: &Options, deadlin
|
|||
|
||||
let (old_png, new_png) = rayon::join(
|
||||
|| image::load_from_memory_with_format(original_data, ImageFormat::PNG),
|
||||
|| image::load_from_memory_with_format(&output, ImageFormat::PNG)
|
||||
|| image::load_from_memory_with_format(&output, ImageFormat::PNG),
|
||||
);
|
||||
|
||||
if let Ok(new_png) = new_png {
|
||||
|
|
@ -720,8 +715,12 @@ fn optimize_png(png: &mut PngData, original_data: &[u8], opts: &Options, deadlin
|
|||
Err(PngError::new("The resulting image is corrupted"))
|
||||
}
|
||||
|
||||
fn perform_reductions(mut png: Arc<PngImage>, opts: &Options, deadline: &Deadline, eval: &Evaluator) {
|
||||
|
||||
fn perform_reductions(
|
||||
mut png: Arc<PngImage>,
|
||||
opts: &Options,
|
||||
deadline: &Deadline,
|
||||
eval: &Evaluator,
|
||||
) {
|
||||
// must be done first to evaluate rest with the correct interlacing
|
||||
if let Some(interlacing) = opts.interlace {
|
||||
if let Some(reduced) = png.change_interlacing(interlacing) {
|
||||
|
|
@ -752,7 +751,9 @@ fn perform_reductions(mut png: Arc<PngImage>, opts: &Options, deadline: &Deadlin
|
|||
let bits = reduced.ihdr.bit_depth;
|
||||
png = Arc::new(reduced);
|
||||
eval.try_image(png.clone(), 1.0);
|
||||
if (bits == BitDepth::One || bits == BitDepth::Two) && previous.ihdr.bit_depth != BitDepth::Four {
|
||||
if (bits == BitDepth::One || bits == BitDepth::Two)
|
||||
&& previous.ihdr.bit_depth != BitDepth::Four
|
||||
{
|
||||
// Also try 16-color mode for all lower bits images, since that may compress better
|
||||
if let Some(reduced) = reduce_bit_depth(&previous, 4) {
|
||||
eval.try_image(Arc::new(reduced), 0.98);
|
||||
|
|
@ -848,9 +849,11 @@ fn perform_strip(png: &mut PngData, opts: &Options) {
|
|||
.map_or(false, |name| hdrs.contains(name))
|
||||
});
|
||||
}
|
||||
Headers::Strip(ref hdrs) => for hdr in hdrs {
|
||||
raw.aux_headers.remove(hdr.as_bytes());
|
||||
},
|
||||
Headers::Strip(ref hdrs) => {
|
||||
for hdr in hdrs {
|
||||
raw.aux_headers.remove(hdr.as_bytes());
|
||||
}
|
||||
}
|
||||
Headers::Safe => {
|
||||
const PRESERVED_HEADERS: [[u8; 4]; 9] = [
|
||||
*b"cHRM", *b"gAMA", *b"iCCP", *b"sBIT", *b"sRGB", *b"bKGD", *b"hIST", *b"pHYs",
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||
#![warn(trivial_casts, trivial_numeric_casts, unused_import_braces)]
|
||||
#![deny(missing_debug_implementations, missing_copy_implementations)]
|
||||
|
||||
extern crate clap;
|
||||
extern crate oxipng;
|
||||
extern crate wild;
|
||||
|
||||
use clap::{App, AppSettings, Arg, ArgMatches};
|
||||
use oxipng::AlphaOptim;
|
||||
use oxipng::Deflaters;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use crate::colors::ColorType;
|
||||
use crate::deflate;
|
||||
use crate::error::PngError;
|
||||
use crate::filters::*;
|
||||
use crate::headers::*;
|
||||
use crate::interlace::{deinterlace_image, interlace_image};
|
||||
use byteorder::{BigEndian, WriteBytesExt};
|
||||
use colors::ColorType;
|
||||
use crc::crc32;
|
||||
use deflate;
|
||||
use error::PngError;
|
||||
use filters::*;
|
||||
use headers::*;
|
||||
use interlace::{deinterlace_image, interlace_image};
|
||||
use rgb::ComponentSlice;
|
||||
use rgb::RGBA8;
|
||||
use std::collections::HashMap;
|
||||
|
|
@ -180,7 +180,8 @@ impl PngData {
|
|||
let _ = ihdr_data.write_u8(self.raw.ihdr.interlaced);
|
||||
write_png_block(b"IHDR", &ihdr_data, &mut output);
|
||||
// Ancillary headers
|
||||
for (key, header) in self.raw
|
||||
for (key, header) in self
|
||||
.raw
|
||||
.aux_headers
|
||||
.iter()
|
||||
.filter(|&(key, _)| !(key == b"bKGD" || key == b"hIST" || key == b"tRNS"))
|
||||
|
|
@ -196,16 +197,20 @@ impl PngData {
|
|||
}
|
||||
write_png_block(b"PLTE", &palette_data, &mut output);
|
||||
let num_transparent =
|
||||
palette.iter().take(max_palette_size).enumerate().fold(
|
||||
0,
|
||||
|prev, (index, px)| {
|
||||
if px.a != 255 {
|
||||
index + 1
|
||||
} else {
|
||||
prev
|
||||
}
|
||||
},
|
||||
);
|
||||
palette
|
||||
.iter()
|
||||
.take(max_palette_size)
|
||||
.enumerate()
|
||||
.fold(
|
||||
0,
|
||||
|prev, (index, px)| {
|
||||
if px.a != 255 {
|
||||
index + 1
|
||||
} else {
|
||||
prev
|
||||
}
|
||||
},
|
||||
);
|
||||
if num_transparent > 0 {
|
||||
let trns_data: Vec<_> = palette[0..num_transparent].iter().map(|px| px.a).collect();
|
||||
write_png_block(b"tRNS", &trns_data, &mut output);
|
||||
|
|
@ -215,7 +220,8 @@ impl PngData {
|
|||
write_png_block(b"tRNS", transparency_pixel, &mut output);
|
||||
}
|
||||
// Special ancillary headers that need to come after PLTE but before IDAT
|
||||
for (key, header) in self.raw
|
||||
for (key, header) in self
|
||||
.raw
|
||||
.aux_headers
|
||||
.iter()
|
||||
.filter(|&(key, _)| key == b"bKGD" || key == b"hIST" || key == b"tRNS")
|
||||
|
|
@ -232,7 +238,6 @@ impl PngData {
|
|||
}
|
||||
|
||||
impl PngImage {
|
||||
|
||||
/// Convert the image to the specified interlacing type
|
||||
/// Returns true if the interlacing was changed, false otherwise
|
||||
/// The `interlace` parameter specifies the *new* interlacing mode
|
||||
|
|
@ -261,13 +266,13 @@ impl PngImage {
|
|||
|
||||
/// Return an iterator over the scanlines of the image
|
||||
#[inline]
|
||||
pub fn scan_lines(&self) -> ScanLines {
|
||||
pub fn scan_lines(&self) -> ScanLines<'_> {
|
||||
ScanLines::new(self)
|
||||
}
|
||||
|
||||
/// Return an iterator over the scanlines of the image
|
||||
#[inline]
|
||||
pub fn scan_lines_mut(&mut self) -> ScanLinesMut {
|
||||
pub fn scan_lines_mut(&mut self) -> ScanLinesMut<'_> {
|
||||
ScanLinesMut::new(self)
|
||||
}
|
||||
|
||||
|
|
@ -331,7 +336,8 @@ impl PngImage {
|
|||
let signed = x as i8;
|
||||
acc + i16::from(signed).abs() as u64
|
||||
})
|
||||
}).unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
filtered.push(*best_filter);
|
||||
filtered.extend_from_slice(best_line);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use png::PngImage;
|
||||
use crate::png::PngImage;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// An iterator over the scan lines of a PNG image
|
||||
|
|
|
|||
26
src/rayon.rs
26
src/rayon.rs
|
|
@ -3,8 +3,13 @@ pub mod prelude {
|
|||
}
|
||||
|
||||
pub trait ParallelIterator: Iterator + Sized {
|
||||
fn with_max_len(self, _l: usize) -> Self {self}
|
||||
fn reduce_with<OP>(mut self, op: OP) -> Option<Self::Item> where OP: Fn(Self::Item, Self::Item) -> Self::Item + Sync {
|
||||
fn with_max_len(self, _l: usize) -> Self {
|
||||
self
|
||||
}
|
||||
fn reduce_with<OP>(mut self, op: OP) -> Option<Self::Item>
|
||||
where
|
||||
OP: Fn(Self::Item, Self::Item) -> Self::Item + Sync,
|
||||
{
|
||||
if let Some(a) = self.next() {
|
||||
Some(self.fold(a, op))
|
||||
} else {
|
||||
|
|
@ -14,18 +19,21 @@ pub trait ParallelIterator: Iterator + Sized {
|
|||
}
|
||||
|
||||
pub trait IntoParallelIterator {
|
||||
type Iter: Iterator<Item=Self::Item>;
|
||||
type Iter: Iterator<Item = Self::Item>;
|
||||
type Item: Send;
|
||||
fn into_par_iter(self) -> Self::Iter;
|
||||
}
|
||||
|
||||
pub trait IntoParallelRefIterator<'data> {
|
||||
type Iter: Iterator<Item=Self::Item>;
|
||||
type Iter: Iterator<Item = Self::Item>;
|
||||
type Item: Send + 'data;
|
||||
fn par_iter(&'data self) -> Self::Iter;
|
||||
}
|
||||
|
||||
impl<I: IntoIterator> IntoParallelIterator for I where I::Item: Send {
|
||||
impl<I: IntoIterator> IntoParallelIterator for I
|
||||
where
|
||||
I::Item: Send,
|
||||
{
|
||||
type Iter = I::IntoIter;
|
||||
type Item = I::Item;
|
||||
|
||||
|
|
@ -35,7 +43,8 @@ impl<I: IntoIterator> IntoParallelIterator for I where I::Item: Send {
|
|||
}
|
||||
|
||||
impl<'data, I: 'data + ?Sized> IntoParallelRefIterator<'data> for I
|
||||
where &'data I: IntoParallelIterator
|
||||
where
|
||||
&'data I: IntoParallelIterator,
|
||||
{
|
||||
type Iter = <&'data I as IntoParallelIterator>::Iter;
|
||||
type Item = <&'data I as IntoParallelIterator>::Item;
|
||||
|
|
@ -45,13 +54,12 @@ impl<'data, I: 'data + ?Sized> IntoParallelRefIterator<'data> for I
|
|||
}
|
||||
}
|
||||
|
||||
impl<I: Iterator> ParallelIterator for I {
|
||||
}
|
||||
impl<I: Iterator> ParallelIterator for I {}
|
||||
|
||||
pub fn join<A, B>(a: impl FnOnce() -> A, b: impl FnOnce() -> B) -> (A, B) {
|
||||
(a(), b())
|
||||
}
|
||||
|
||||
pub fn spawn(a: impl FnOnce() -> A) -> A {
|
||||
pub fn spawn<A>(a: impl FnOnce() -> A) -> A {
|
||||
a()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,21 @@
|
|||
use evaluate::Evaluator;
|
||||
use itertools::flatten;
|
||||
use png::scan_lines::ScanLine;
|
||||
use crate::colors::AlphaOptim;
|
||||
use crate::colors::ColorType;
|
||||
use crate::evaluate::Evaluator;
|
||||
use crate::headers::IhdrData;
|
||||
use crate::png::scan_lines::ScanLine;
|
||||
use crate::png::PngImage;
|
||||
#[cfg(not(feature = "parallel"))]
|
||||
use crate::rayon::prelude::*;
|
||||
#[cfg(feature = "parallel")]
|
||||
use rayon::prelude::*;
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
use colors::AlphaOptim;
|
||||
use headers::IhdrData;
|
||||
use png::PngImage;
|
||||
use colors::ColorType;
|
||||
use rayon::prelude::*;
|
||||
|
||||
pub(crate) fn try_alpha_reductions(png: Arc<PngImage>, alphas: &HashSet<AlphaOptim>, eval: &Evaluator) {
|
||||
pub(crate) fn try_alpha_reductions(
|
||||
png: Arc<PngImage>,
|
||||
alphas: &HashSet<AlphaOptim>,
|
||||
eval: &Evaluator,
|
||||
) {
|
||||
assert!(!alphas.is_empty());
|
||||
let alphas = alphas.iter().collect::<Vec<_>>();
|
||||
let alphas_iter = alphas.par_iter().with_max_len(1);
|
||||
|
|
@ -88,7 +94,7 @@ fn reduced_alpha_to_white(png: &PngImage, bpc: usize, bpp: usize) -> Vec<u8> {
|
|||
|
||||
fn reduced_alpha_to_up(png: &PngImage, bpc: usize, bpp: usize) -> Vec<u8> {
|
||||
let mut lines = Vec::new();
|
||||
let mut scan_lines = png.scan_lines().collect::<Vec<ScanLine>>();
|
||||
let mut scan_lines = png.scan_lines().collect::<Vec<ScanLine<'_>>>();
|
||||
scan_lines.reverse();
|
||||
let mut last_line = Vec::new();
|
||||
let mut current_line = Vec::with_capacity(last_line.len());
|
||||
|
|
@ -111,7 +117,7 @@ fn reduced_alpha_to_up(png: &PngImage, bpc: usize, bpp: usize) -> Vec<u8> {
|
|||
lines.push(current_line.clone());
|
||||
current_line.clear();
|
||||
}
|
||||
flatten(lines.into_iter().rev()).collect()
|
||||
lines.into_iter().rev().flatten().collect()
|
||||
}
|
||||
|
||||
fn reduced_alpha_to_down(png: &PngImage, bpc: usize, bpp: usize) -> Vec<u8> {
|
||||
|
|
@ -154,7 +160,7 @@ fn reduced_alpha_to_left(png: &PngImage, bpc: usize, bpp: usize) -> Vec<u8> {
|
|||
last_pixel = pixel.to_owned();
|
||||
}
|
||||
reduced.push(line.filter);
|
||||
reduced.extend(flatten(line_bytes.chunks(bpp).rev()));
|
||||
reduced.extend(line_bytes.chunks(bpp).rev().flatten());
|
||||
}
|
||||
reduced
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use headers::IhdrData;
|
||||
use crate::colors::{BitDepth, ColorType};
|
||||
use crate::headers::IhdrData;
|
||||
use crate::png::PngImage;
|
||||
use bit_vec::BitVec;
|
||||
use colors::{BitDepth, ColorType};
|
||||
use png::PngImage;
|
||||
|
||||
const ONE_BIT_PERMUTATIONS: [u8; 2] = [0b0000_0000, 0b1111_1111];
|
||||
const TWO_BIT_PERMUTATIONS: [u8; 5] = [
|
||||
|
|
@ -30,8 +30,7 @@ const FOUR_BIT_PERMUTATIONS: [u8; 11] = [
|
|||
#[must_use]
|
||||
pub fn reduce_bit_depth(png: &PngImage, minimum_bits: usize) -> Option<PngImage> {
|
||||
if png.ihdr.bit_depth != BitDepth::Sixteen {
|
||||
if png.ihdr.color_type == ColorType::Indexed
|
||||
|| png.ihdr.color_type == ColorType::Grayscale
|
||||
if png.ihdr.color_type == ColorType::Indexed || png.ihdr.color_type == ColorType::Grayscale
|
||||
{
|
||||
return reduce_bit_depth_8_or_less(png, minimum_bits);
|
||||
}
|
||||
|
|
@ -40,8 +39,8 @@ pub fn reduce_bit_depth(png: &PngImage, minimum_bits: usize) -> Option<PngImage>
|
|||
|
||||
// Reduce from 16 to 8 bits per channel per pixel
|
||||
let mut reduced = Vec::with_capacity(
|
||||
(png.ihdr.width * png.ihdr.height * u32::from(png.channels_per_pixel())
|
||||
+ png.ihdr.height) as usize,
|
||||
(png.ihdr.width * png.ihdr.height * u32::from(png.channels_per_pixel()) + png.ihdr.height)
|
||||
as usize,
|
||||
);
|
||||
let mut high_byte = 0;
|
||||
|
||||
|
|
@ -84,17 +83,25 @@ pub fn reduce_bit_depth_8_or_less(png: &PngImage, mut minimum_bits: usize) -> Op
|
|||
}
|
||||
for line in png.scan_lines() {
|
||||
if png.ihdr.color_type == ColorType::Indexed {
|
||||
let line_max = line.data.iter().map(|&byte| match png.ihdr.bit_depth {
|
||||
BitDepth::Two => (byte & 0x3).max((byte >> 2) & 0x3).max((byte >> 4) & 0x3).max(byte >> 6),
|
||||
let line_max = line
|
||||
.data
|
||||
.iter()
|
||||
.map(|&byte| match png.ihdr.bit_depth {
|
||||
BitDepth::Two => (byte & 0x3)
|
||||
.max((byte >> 2) & 0x3)
|
||||
.max((byte >> 4) & 0x3)
|
||||
.max(byte >> 6),
|
||||
BitDepth::Four => (byte & 0xF).max(byte >> 4),
|
||||
_ => byte,
|
||||
}).max().unwrap_or(0);
|
||||
_ => byte,
|
||||
})
|
||||
.max()
|
||||
.unwrap_or(0);
|
||||
let required_bits = match line_max {
|
||||
x if x > 0x0F => 8,
|
||||
x if x > 0x03 => 4,
|
||||
x if x > 0x01 => 2,
|
||||
_ => 1,
|
||||
};
|
||||
};
|
||||
if required_bits > minimum_bits {
|
||||
minimum_bits = required_bits;
|
||||
if minimum_bits >= bit_depth {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use headers::IhdrData;
|
||||
use colors::{BitDepth, ColorType};
|
||||
use crate::colors::{BitDepth, ColorType};
|
||||
use crate::headers::IhdrData;
|
||||
use crate::png::PngImage;
|
||||
use itertools::Itertools;
|
||||
use png::PngImage;
|
||||
use rgb::{FromSlice, RGB8, RGBA8};
|
||||
use std::collections::HashMap;
|
||||
use std::hash::Hash;
|
||||
|
|
@ -145,7 +145,8 @@ pub fn reduced_color_to_palette(png: &PngImage) -> Option<PngImage> {
|
|||
} else {
|
||||
None
|
||||
}
|
||||
}).max();
|
||||
})
|
||||
.max();
|
||||
let trns_size = num_transparent.map(|n| n + 8).unwrap_or(0);
|
||||
|
||||
let headers_size = palette.len() * 3 + 8 + trns_size;
|
||||
|
|
@ -212,9 +213,9 @@ pub fn reduce_rgb_to_grayscale(png: &PngImage) -> Option<PngImage> {
|
|||
} else {
|
||||
let pixel_bytes = cur_pixel
|
||||
.iter()
|
||||
.step(2)
|
||||
.step_by(2)
|
||||
.cloned()
|
||||
.zip(cur_pixel.iter().skip(1).step(2).cloned())
|
||||
.zip(cur_pixel.iter().skip(1).step_by(2).cloned())
|
||||
.unique()
|
||||
.collect::<Vec<(u8, u8)>>();
|
||||
if pixel_bytes.len() > 1 {
|
||||
|
|
@ -254,7 +255,7 @@ pub fn reduce_rgb_to_grayscale(png: &PngImage) -> Option<PngImage> {
|
|||
data: reduced,
|
||||
ihdr: IhdrData {
|
||||
color_type: ColorType::Grayscale,
|
||||
.. png.ihdr
|
||||
..png.ihdr
|
||||
},
|
||||
aux_headers,
|
||||
palette: None,
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
use headers::IhdrData;
|
||||
use std::collections::HashMap;
|
||||
use colors::{BitDepth, ColorType};
|
||||
use std::collections::hash_map::Entry::*;
|
||||
use png::PngImage;
|
||||
use crate::colors::{BitDepth, ColorType};
|
||||
use crate::headers::IhdrData;
|
||||
use crate::png::PngImage;
|
||||
use rgb::RGBA8;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::hash_map::Entry::*;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub mod alpha;
|
||||
use alpha::*;
|
||||
use crate::alpha::*;
|
||||
pub mod bit_depth;
|
||||
use bit_depth::*;
|
||||
use crate::bit_depth::*;
|
||||
pub mod color;
|
||||
use color::*;
|
||||
use crate::color::*;
|
||||
|
||||
pub(crate) use bit_depth::reduce_bit_depth;
|
||||
pub(crate) use alpha::try_alpha_reductions;
|
||||
pub(crate) use crate::alpha::try_alpha_reductions;
|
||||
pub(crate) use crate::bit_depth::reduce_bit_depth;
|
||||
|
||||
/// Attempt to reduce the number of colors in the palette
|
||||
/// Returns `None` if palette hasn't changed
|
||||
|
|
@ -38,33 +38,41 @@ pub fn reduced_palette(png: &PngImage) -> Option<PngImage> {
|
|||
// Find palette entries that are never used
|
||||
for line in png.scan_lines() {
|
||||
match png.ihdr.bit_depth {
|
||||
BitDepth::Eight => for &byte in line.data {
|
||||
used[byte as usize] = true;
|
||||
},
|
||||
BitDepth::Four => for &byte in line.data {
|
||||
used[(byte & 0x0F) as usize] = true;
|
||||
used[(byte >> 4) as usize] = true;
|
||||
},
|
||||
BitDepth::Two => for &byte in line.data {
|
||||
used[(byte & 0x03) as usize] = true;
|
||||
used[((byte >> 2) & 0x03) as usize] = true;
|
||||
used[((byte >> 4) & 0x03) as usize] = true;
|
||||
used[(byte >> 6) as usize] = true;
|
||||
},
|
||||
BitDepth::Eight => {
|
||||
for &byte in line.data {
|
||||
used[byte as usize] = true;
|
||||
}
|
||||
}
|
||||
BitDepth::Four => {
|
||||
for &byte in line.data {
|
||||
used[(byte & 0x0F) as usize] = true;
|
||||
used[(byte >> 4) as usize] = true;
|
||||
}
|
||||
}
|
||||
BitDepth::Two => {
|
||||
for &byte in line.data {
|
||||
used[(byte & 0x03) as usize] = true;
|
||||
used[((byte >> 2) & 0x03) as usize] = true;
|
||||
used[((byte >> 4) & 0x03) as usize] = true;
|
||||
used[(byte >> 6) as usize] = true;
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
let mut next_index = 0u16;
|
||||
let mut seen = HashMap::with_capacity(palette.len());
|
||||
for (i, (used, palette_map)) in
|
||||
used.iter().cloned().zip(palette_map.iter_mut()).enumerate()
|
||||
for (i, (used, palette_map)) in used.iter().cloned().zip(palette_map.iter_mut()).enumerate()
|
||||
{
|
||||
if !used {
|
||||
continue;
|
||||
}
|
||||
// There are invalid files that use pixel indices beyond palette size
|
||||
let color = palette.get(i).cloned().unwrap_or(RGBA8::new(0, 0, 0, 255));
|
||||
let color = palette
|
||||
.get(i)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| RGBA8::new(0, 0, 0, 255));
|
||||
match seen.entry(color) {
|
||||
Vacant(new) => {
|
||||
*palette_map = Some(next_index as u8);
|
||||
|
|
@ -96,7 +104,10 @@ fn do_palette_reduction(png: &PngImage, palette_map: &[Option<u8>; 256]) -> Opti
|
|||
|
||||
let mut aux_headers = png.aux_headers.clone();
|
||||
if let Some(bkgd_header) = png.aux_headers.get(b"bKGD") {
|
||||
if let Some(Some(map_to)) = bkgd_header.get(0).and_then(|&idx| palette_map.get(idx as usize)) {
|
||||
if let Some(Some(map_to)) = bkgd_header
|
||||
.get(0)
|
||||
.and_then(|&idx| palette_map.get(idx as usize))
|
||||
{
|
||||
aux_headers.insert(*b"bKGD", vec![*map_to]);
|
||||
}
|
||||
}
|
||||
|
|
@ -146,11 +157,13 @@ fn palette_map_to_byte_map(png: &PngImage, palette_map: &[Option<u8>; 256]) -> O
|
|||
_ => {}
|
||||
}
|
||||
|
||||
return Some(byte_map)
|
||||
Some(byte_map)
|
||||
}
|
||||
|
||||
fn reordered_palette(palette: &[RGBA8], palette_map: &[Option<u8>; 256]) -> Vec<RGBA8> {
|
||||
let max_index = palette_map.iter().cloned()
|
||||
let max_index = palette_map
|
||||
.iter()
|
||||
.cloned()
|
||||
.filter_map(|x| x)
|
||||
.max()
|
||||
.unwrap_or(0) as usize;
|
||||
|
|
@ -163,7 +176,6 @@ fn reordered_palette(palette: &[RGBA8], palette_map: &[Option<u8>; 256]) -> Vec<
|
|||
new_palette
|
||||
}
|
||||
|
||||
|
||||
/// Attempt to reduce the color type of the image
|
||||
/// Returns true if the color type was reduced, false otherwise
|
||||
pub fn reduce_color_type(png: &PngImage) -> Option<PngImage> {
|
||||
|
|
@ -173,7 +185,9 @@ pub fn reduce_color_type(png: &PngImage) -> Option<PngImage> {
|
|||
// Go down one step at a time
|
||||
// Maybe not the most efficient, but it's safe
|
||||
if reduced.ihdr.color_type == ColorType::RGBA {
|
||||
if let Some(r) = reduce_rgba_to_grayscale_alpha(&reduced).or_else(|| reduced_alpha_channel(&reduced)) {
|
||||
if let Some(r) =
|
||||
reduce_rgba_to_grayscale_alpha(&reduced).or_else(|| reduced_alpha_channel(&reduced))
|
||||
{
|
||||
reduced = Cow::Owned(r);
|
||||
} else if let Some(r) = reduced_color_to_palette(&reduced) {
|
||||
reduced = Cow::Owned(r);
|
||||
|
|
@ -189,7 +203,9 @@ pub fn reduce_color_type(png: &PngImage) -> Option<PngImage> {
|
|||
}
|
||||
|
||||
if reduced.ihdr.color_type == ColorType::RGB {
|
||||
if let Some(r) = reduce_rgb_to_grayscale(&reduced).or_else(|| reduced_color_to_palette(&reduced)) {
|
||||
if let Some(r) =
|
||||
reduce_rgb_to_grayscale(&reduced).or_else(|| reduced_color_to_palette(&reduced))
|
||||
{
|
||||
reduced = Cow::Owned(r);
|
||||
should_reduce_bit_depth = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
extern crate oxipng;
|
||||
use oxipng;
|
||||
|
||||
use oxipng::internal_tests::*;
|
||||
use oxipng::{InFile, OutFile};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
extern crate oxipng;
|
||||
use oxipng;
|
||||
|
||||
use oxipng::Headers;
|
||||
use oxipng::OutFile;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
extern crate oxipng;
|
||||
use oxipng;
|
||||
|
||||
use oxipng::internal_tests::*;
|
||||
use oxipng::{InFile, OutFile};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
extern crate oxipng;
|
||||
use oxipng;
|
||||
|
||||
use oxipng::internal_tests::*;
|
||||
use oxipng::{InFile, OutFile};
|
||||
|
|
@ -33,7 +33,10 @@ fn test_it_converts(
|
|||
let (output, opts) = custom.unwrap_or_else(|| get_opts(&input));
|
||||
let png = PngData::new(&input, opts.fix_errors).unwrap();
|
||||
|
||||
assert_eq!(png.raw.ihdr.color_type, color_type_in, "test file is broken");
|
||||
assert_eq!(
|
||||
png.raw.ihdr.color_type, color_type_in,
|
||||
"test file is broken"
|
||||
);
|
||||
assert_eq!(png.raw.ihdr.bit_depth, bit_depth_in, "test file is broken");
|
||||
|
||||
match oxipng::optimize(&InFile::Path(input), &output, &opts) {
|
||||
|
|
@ -51,8 +54,14 @@ fn test_it_converts(
|
|||
}
|
||||
};
|
||||
|
||||
assert_eq!(png.raw.ihdr.color_type, color_type_out, "optimized to wrong color type");
|
||||
assert_eq!(png.raw.ihdr.bit_depth, bit_depth_out, "optimized to wrong bit depth");
|
||||
assert_eq!(
|
||||
png.raw.ihdr.color_type, color_type_out,
|
||||
"optimized to wrong color type"
|
||||
);
|
||||
assert_eq!(
|
||||
png.raw.ihdr.bit_depth, bit_depth_out,
|
||||
"optimized to wrong bit depth"
|
||||
);
|
||||
if let Some(palette) = png.raw.palette.as_ref() {
|
||||
assert!(palette.len() <= 1 << (png.raw.ihdr.bit_depth.as_u8() as usize));
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Reference in a new issue