diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/png | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/png')
22 files changed, 4914 insertions, 0 deletions
diff --git a/third_party/rust/png/.cargo-checksum.json b/third_party/rust/png/.cargo-checksum.json new file mode 100644 index 0000000000..7ab63698df --- /dev/null +++ b/third_party/rust/png/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"CHANGES.md":"8e911a50bdb41ddae0e40f629a1e1e6be7a50e7351ed5c2879c7cf69f49e2ecf","Cargo.lock":"816e27b455f10525f6c17db52aa3ab5c149bd73d8b70f68f884e689b8be5469f","Cargo.toml":"eb9223a451850fd57aa564fcf5f8a42729fe25f55ca3ce78f4578b7361ae1555","Dockerfile":"c11639c29932f0ce19bd51d48ddbbe95cfe2baf978e1432d83a02e8c00273b62","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"eaf40297c75da471f7cda1f3458e8d91b4b2ec866e609527a13acfa93b638652","README.md":"86b59909aec85ba466fb3821bf0a4beee741486f1199401d12b6f9039951ccd7","benches/README.md":"0c60c3d497abdf6c032863aa47da41bc6bb4f5ff696d45dec0e6eb33459b14b0","benches/decoder.rs":"81a13f5c731f674cc21a6aa472bcf4cc6c3581a41b1e002fa3996fc9a6e0dfe5","examples/pngcheck.rs":"afc0b028c08302d6c041e90201d217482237d09a4495adc360a97c385b311cdb","examples/show.rs":"ccd00c9d1cb7b6b1c2e181994dec6ebdee32c7d5ac9c5bc5592de4496256d696","fuzzit.sh":"b9e93f9bd504b0277dbdecc5148099b7c02080a14d694a71f31cba7a8fa06a11","src/chunk.rs":"f8aa01e68683b5cd41ac2580ac11f2d6a6190c52007e3f115dc5b7c3b0354e63","src/common.rs":"a0bc64f16cfdf511925a2869d9fda70ed07272a8278c1d2db142821babd4c931","src/decoder/mod.rs":"5cdb1cdbc7b52846053437b9730f150284929db1dc174280f3485cb25f9528c9","src/decoder/stream.rs":"656a694867c66cac107a1be981baf9429a232a5f077dcd120be47f5c1afcde91","src/encoder.rs":"a50bdece254e79ed4f633488ea49a5bf2d657bbcf0af552249305ca855b086cf","src/filter.rs":"d21d6f3d87044a6dd71d17e3fcdf23bf41fe23486123dc683c5b63b4e9b33c68","src/lib.rs":"533ca87e8771468637d4cdc0a2def4737b1e74e98ff69a8c11296d905e8ad34e","src/traits.rs":"0a5ae6012e567acdf46aff9d09e89c713d3fa7c399e32a3580b07b86b5f87ef5","src/utils.rs":"ff368caa4ecf41401b51f9d1f693f014079ae2fa369b668505bbc1b1b065d4f6"},"package":"ef859a23054bbfee7811284275ae522f0434a3c8e7f4b74bd4a35ae7e1c4a283"}
\ No newline at end of file diff --git a/third_party/rust/png/CHANGES.md b/third_party/rust/png/CHANGES.md new file mode 100644 index 0000000000..f7b038013c --- /dev/null +++ b/third_party/rust/png/CHANGES.md @@ -0,0 +1,24 @@ +## 0.15.3 + +* Fix panic while trying to encode empty images. Such images are no longer + accepted and error when calling `write_header` before any data has been + written. The specification does not permit empty images. + +## 0.15.2 + +* Fix `EXPAND` transformation to leave bit depths above 8 unchanged + +## 0.15.1 + +* Fix encoding writing invalid chunks. Images written can be corrected: see + https://github.com/image-rs/image/issues/1074 for a recovery. +* Fix a panic in bit unpacking with checked arithmetic (e.g. in debug builds) +* Added better fuzzer integration +* Update `term`, `rand` dev-dependency +* Note: The `show` example program requires a newer compiler than 1.34.2 on + some targets due to depending on `glium`. This is not considered a breaking + bug. + +## 0.15 + +Begin of changelog diff --git a/third_party/rust/png/Cargo.lock b/third_party/rust/png/Cargo.lock new file mode 100644 index 0000000000..8d48d1f87d --- /dev/null +++ b/third_party/rust/png/Cargo.lock @@ -0,0 +1,1142 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "adler32" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "andrew" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "line_drawing 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rusttype 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)", + "xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "android_glue" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "approx" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "arrayref" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "arrayvec" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "arrayvec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "autocfg" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "backtrace" +version = "0.3.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace-sys" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "base64" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "blake2b_simd" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byteorder" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "c2-chacha" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cc" +version = "1.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cgl" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gleam 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cocoa" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "block 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "objc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "constant_time_eq" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "core-foundation" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "core-foundation-sys" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "core-graphics" +version = "0.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crc32fast" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-utils" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "deflate" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "dirs" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "dirs-sys" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_users 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "dlib" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "downcast-rs" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "failure" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "failure_derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fnv" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "getopts" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "getrandom" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gl_generator" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "khronos_api 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gl_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "khronos_api 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gleam" +version = "0.6.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gl_generator 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "glium" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "gl_generator 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glutin 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "glutin" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "android_glue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cgl 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cocoa 0.18.5 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", + "glutin_egl_sys 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "glutin_gles2_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "glutin_glx_sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "glutin_wgl_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "objc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "shared_library 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-client 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winit 0.19.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "glutin_egl_sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gl_generator 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "glutin_gles2_sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gl_generator 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "objc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "glutin_glx_sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gl_generator 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "x11-dl 2.18.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "glutin_wgl_sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gl_generator 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "inflate" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.65" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libloading" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "line_drawing" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lock_api" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "log" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "memmap" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "nix" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "num-traits" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "malloc_buf 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ordered-float" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "osmesa-sys" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "shared_library 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot_core" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "pkg-config" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "png" +version = "0.15.3" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "deflate 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", + "glium 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "inflate 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "term 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "proc-macro2" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro2" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_chacha" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_os" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "raw-window-handle" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "redox_users" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-argon2 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rust-argon2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rusttype" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rusttype 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rusttype" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "stb_truetype 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "same-file" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "scopeguard" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "shared_library" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "smallvec" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "smithay-client-toolkit" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "andrew 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dlib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-client 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-commons 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-protocols 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "stb_truetype" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syn" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "synstructure" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "term" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-width" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "walkdir" +version = "2.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasi" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "wayland-client" +version = "0.21.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "downcast-rs 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-commons 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-scanner 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-sys 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wayland-commons" +version = "0.21.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-sys 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wayland-protocols" +version = "0.21.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-client 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-commons 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-scanner 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-sys 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wayland-scanner" +version = "0.21.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wayland-sys" +version = "0.21.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dlib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winit" +version = "0.19.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "android_glue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cocoa 0.18.5 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "objc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "raw-window-handle 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "smithay-client-toolkit 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-client 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "x11-dl 2.18.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "x11-dl" +version = "2.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "xdg" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "xml-rs" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" +"checksum andrew 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9b7f09f89872c2b6b29e319377b1fbe91c6f5947df19a25596e121cf19a7b35e" +"checksum android_glue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "000444226fcff248f2bc4c7625be32c63caccfecc2723a2b9f78a7487a49c407" +"checksum approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3" +"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" +"checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" +"checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" +"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" +"checksum backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)" = "924c76597f0d9ca25d762c25a4d369d51267536465dc5064bdf0eb073ed477ea" +"checksum backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491" +"checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" +"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +"checksum blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "5850aeee1552f495dd0250014cf64b82b7c8879a89d83b33bbdace2cc4f63182" +"checksum block 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" +"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" +"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" +"checksum cc 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)" = "0213d356d3c4ea2c18c40b037c3be23cd639825c18f25ee670ac7813beeef99c" +"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +"checksum cgl 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "55e7ec0b74fe5897894cbc207092c577e87c52f8a59e8ca8d97ef37551f60a49" +"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +"checksum cocoa 0.18.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1706996401131526e36b3b49f0c4d912639ce110996f3ca144d78946727bce54" +"checksum constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "995a44c877f9212528ccc74b21a232f66ad69001e40ede5bcee2ac9ef2657120" +"checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" +"checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" +"checksum core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)" = "56790968ab1c8a1202a102e6de05fc6e1ec87da99e4e93e9a7d13efbfc1e95a9" +"checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" +"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" +"checksum deflate 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)" = "707b6a7b384888a70c8d2e8650b3e60170dfc6a67bb4aa67b6dfca57af4bedb4" +"checksum dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" +"checksum dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" +"checksum dlib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "77e51249a9d823a4cb79e3eca6dcd756153e8ed0157b6c04775d04bf1b13b76a" +"checksum downcast-rs 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5fe414cc2fd4447b7da94b27ddfb6831a8a06f35f6d077ab5613ec703866c49a" +"checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" +"checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" +"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" +"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +"checksum getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)" = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +"checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407" +"checksum gl_generator 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "39a23d5e872a275135d66895d954269cf5e8661d234eb1c2480f4ce0d586acbd" +"checksum gl_generator 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ca98bbde17256e02d17336a6bdb5a50f7d0ccacee502e191d3e3d0ec2f96f84a" +"checksum gleam 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "cae10d7c99d0e77b4766e850a60898a17c1abaf01075531f1066f03dc7dc5fc5" +"checksum glium 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)" = "213290166315d0ff3cf48dd76a31f3ad57c934182d3e87aeec890f2747226a21" +"checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +"checksum glutin 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6e5cc17ac6cb35704f434faaa5bb5e4254a66f50853976c7e6f00aee09ff1bf2" +"checksum glutin_egl_sys 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "772edef3b28b8ad41e4ea202748e65eefe8e5ffd1f4535f1219793dbb20b3d4c" +"checksum glutin_gles2_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "89996c30857ae1b4de4b5189abf1ea822a20a9fe9e1c93e5e7b862ff0bdd5cdf" +"checksum glutin_glx_sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1290a5ca5e46fcfa7f66f949cc9d9194b2cb6f2ed61892c8c2b82343631dba57" +"checksum glutin_wgl_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f801bbc91efc22dd1c4818a47814fc72bf74d024510451b119381579bfa39021" +"checksum inflate 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff" +"checksum khronos_api 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" +"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +"checksum libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)" = "1a31a0627fdf1f6a39ec0dd577e101440b7db22672c0901fe00a9a6fbb5c24e8" +"checksum libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" +"checksum line_drawing 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5cc7ad3d82c845bdb5dde34ffdcc7a5fb4d2996e1e1ee0f19c33bc80e15196b9" +"checksum lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8912e782533a93a167888781b836336a6ca5da6175c05944c86cf28c31104dc" +"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +"checksum malloc_buf 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +"checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" +"checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +"checksum nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce" +"checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +"checksum num-traits 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "443c53b3c3531dfcbfa499d8893944db78474ad7a1d87fa2d94d1a2231693ac6" +"checksum objc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +"checksum ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "18869315e81473c951eb56ad5558bbc56978562d3ecfb87abb7a1e944cea4518" +"checksum osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "88cfece6e95d2e717e0872a7f53a8684712ad13822a7979bc760b9c77ec0013b" +"checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" +"checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" +"checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +"checksum pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "72d5370d90f49f70bd033c3d75e87fc529fbfff9d6f7cccef07d6170079d91ea" +"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" +"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +"checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" +"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412" +"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" +"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +"checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" +"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +"checksum raw-window-handle 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9db80d08d3ed847ce4fb3def46de0af4bfb6155bd09bd6eaf28b5ac72541c1f1" +"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +"checksum redox_users 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ecedbca3bf205f8d8f5c2b44d83cd0690e39ee84b951ed649e9f1841132b66d" +"checksum rust-argon2 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ca4eaef519b494d1f2848fc602d18816fed808a981aedf4f1f00ceb7c9d32cf" +"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" +"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +"checksum rusttype 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)" = "310942406a39981bed7e12b09182a221a29e0990f3e7e0c971f131922ed135d5" +"checksum rusttype 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6fa38506b5cbf2fb67f915e2725cb5012f1b9a785b0ab55c4733acda5f6554ef" +"checksum same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421" +"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" +"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +"checksum shared_library 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11" +"checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" +"checksum smithay-client-toolkit 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2ccb8c57049b2a34d2cc2b203fa785020ba0129d31920ef0d317430adaf748fa" +"checksum stb_truetype 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "824210d6fb52cbc3ad2545270ead6860c7311aa5450642b078da4515937b6f7a" +"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" +"checksum synstructure 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f085a5855930c0441ca1288cf044ea4aecf4f43a91668abdb870b4ba546a203" +"checksum term 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c0863a3345e70f61d613eab32ee046ccd1bcc5f9105fe402c61fcd0c13eeb8b5" +"checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20" +"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +"checksum walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9658c94fa8b940eab2250bd5a457f9c48b748420d71293b165c8cdbe2f55f71e" +"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" +"checksum wayland-client 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)" = "49963e5f9eeaf637bfcd1b9f0701c99fd5cd05225eb51035550d4272806f2713" +"checksum wayland-commons 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)" = "40c08896768b667e1df195d88a62a53a2d1351a1ed96188be79c196b35bb32ec" +"checksum wayland-protocols 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)" = "4afde2ea2a428eee6d7d2c8584fdbe8b82eee8b6c353e129a434cd6e07f42145" +"checksum wayland-scanner 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3828c568714507315ee425a9529edc4a4aa9901409e373e9e0027e7622b79e" +"checksum wayland-sys 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)" = "520ab0fd578017a0ee2206623ba9ef4afe5e8f23ca7b42f6acfba2f4e66b1628" +"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +"checksum winit 0.19.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1e96eb4bb472fa43e718e8fa4aef82f86cd9deac9483a1e1529230babdb394a8" +"checksum x11-dl 2.18.4 (registry+https://github.com/rust-lang/crates.io-index)" = "be65e1342a3baae65439cd03306778831a3d133b0d20243a7fb83fd5cf403c58" +"checksum xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" +"checksum xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "541b12c998c5b56aa2b4e6f18f03664eef9a4fd0a246a55594efae6cc2d964b5" diff --git a/third_party/rust/png/Cargo.toml b/third_party/rust/png/Cargo.toml new file mode 100644 index 0000000000..2e57fab7fe --- /dev/null +++ b/third_party/rust/png/Cargo.toml @@ -0,0 +1,56 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies +# +# If you believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) + +[package] +edition = "2018" +name = "png" +version = "0.15.3" +authors = ["nwin <nwin@users.noreply.github.com>"] +exclude = ["tests/*"] +description = "PNG decoding and encoding library in pure Rust" +categories = ["multimedia::images"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/image-rs/image-png.git" +[dependencies.bitflags] +version = "1.0" + +[dependencies.crc32fast] +version = "1.2.0" + +[dependencies.deflate] +version = "0.7.12" +optional = true + +[dependencies.inflate] +version = "0.4.2" +[dev-dependencies.getopts] +version = "0.2.14" + +[dev-dependencies.glium] +version = "0.24" +features = ["glutin"] +default-features = false + +[dev-dependencies.glob] +version = "0.3" + +[dev-dependencies.rand] +version = "0.7.0" + +[dev-dependencies.term] +version = "0.6.1" + +[features] +benchmarks = [] +default = ["png-encoding"] +png-encoding = ["deflate"] +unstable = [] diff --git a/third_party/rust/png/Dockerfile b/third_party/rust/png/Dockerfile new file mode 100644 index 0000000000..780e28c09b --- /dev/null +++ b/third_party/rust/png/Dockerfile @@ -0,0 +1,5 @@ +FROM rustlang/rust:nightly +WORKDIR /image-png +COPY . . +RUN cargo install cargo-fuzz +RUN cargo fuzz run decode -- -runs=0 diff --git a/third_party/rust/png/LICENSE-APACHE b/third_party/rust/png/LICENSE-APACHE new file mode 100644 index 0000000000..16fe87b06e --- /dev/null +++ b/third_party/rust/png/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/third_party/rust/png/LICENSE-MIT b/third_party/rust/png/LICENSE-MIT new file mode 100644 index 0000000000..ea471f02b7 --- /dev/null +++ b/third_party/rust/png/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2015 nwin + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/third_party/rust/png/README.md b/third_party/rust/png/README.md new file mode 100644 index 0000000000..abcceb6a32 --- /dev/null +++ b/third_party/rust/png/README.md @@ -0,0 +1,39 @@ +# PNG Decoder/Encoder +[![Build Status](https://travis-ci.org/image-rs/image-png.svg?branch=master)](https://travis-ci.org/image-rs/image-png) +[![Documentation](https://docs.rs/png/badge.svg)](https://docs.rs/png) +[![Crates.io](https://img.shields.io/crates/v/png.svg)](https://crates.io/crates/png) +![Lines of Code](https://tokei.rs/b1/github/image-rs/image-png) +[![License](https://img.shields.io/crates/l/png.svg)](https://github.com/image-rs/image-png) +[![fuzzit](https://app.fuzzit.dev/badge?org_id=image-rs)](https://app.fuzzit.dev/orgs/image-rs/dashboard) + +PNG decoder/encoder in pure Rust. + +It contains all features required to handle the entirety of [the PngSuite by +Willem van Schack][PngSuite]. + +[PngSuite]: http://www.schaik.com/pngsuite2011/pngsuite.html + +## pngcheck + +The `pngcheck` utility is a small demonstration binary that checks and prints +metadata on every `.png` image provided via parameter. You can run it (for +example on the test directories) with + +```bash +cargo run --release --example pngcheck ./tests/pngsuite/* +``` + +## License + +Licensed under either of + + * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any +additional terms or conditions. diff --git a/third_party/rust/png/benches/README.md b/third_party/rust/png/benches/README.md new file mode 100644 index 0000000000..bf13a5544a --- /dev/null +++ b/third_party/rust/png/benches/README.md @@ -0,0 +1,6 @@ +# Getting started with benchmarking + +To run the benchmarks you need a nightly rust toolchain. +Then you launch it with + + rustup run nightly cargo bench --features=benchmarks diff --git a/third_party/rust/png/benches/decoder.rs b/third_party/rust/png/benches/decoder.rs new file mode 100644 index 0000000000..7d9cce933c --- /dev/null +++ b/third_party/rust/png/benches/decoder.rs @@ -0,0 +1,25 @@ +#![cfg(feature = "benchmarks")]
+#![feature(test)]
+
+extern crate png;
+extern crate test;
+
+use std::fs::File;
+use std::io::Read;
+
+use png::Decoder;
+
+#[bench]
+fn bench_big(b: &mut test::Bencher) {
+ let mut data = Vec::new();
+ File::open("tests/pngsuite/PngSuite.png").unwrap().read_to_end(&mut data).unwrap();
+ let decoder = Decoder::new(&*data);
+ let (info, _) = decoder.read_info().unwrap();
+ let mut image = vec![0; info.buffer_size()];
+ b.iter(|| {
+ let decoder = Decoder::new(&*data);
+ let (_, mut decoder) = decoder.read_info().unwrap();
+ test::black_box(decoder.next_frame(&mut image)).unwrap();
+ });
+ b.bytes = info.buffer_size() as u64
+}
diff --git a/third_party/rust/png/examples/pngcheck.rs b/third_party/rust/png/examples/pngcheck.rs new file mode 100644 index 0000000000..6cd0b5b7e2 --- /dev/null +++ b/third_party/rust/png/examples/pngcheck.rs @@ -0,0 +1,360 @@ +#![allow(non_upper_case_globals)] + +extern crate getopts; +extern crate glob; +extern crate png; + +use std::io; +use std::io::prelude::*; +use std::path::Path; +use std::fs::File; +use std::env; + +use getopts::{Matches, Options, ParsingStyle}; +use term::{color, Attr}; + +fn parse_args() -> Matches { + let args: Vec<String> = env::args().collect(); + let mut opts = Options::new(); + opts.optflag("c", "", "colorize output (for ANSI terminals)") + .optflag("q", "", "test quietly (output only errors)") + //.optflag("t", "", "print contents of tEXt chunks (can be used with -q)"); + .optflag("v", "", "test verbosely (print most chunk data)") + .parsing_style(ParsingStyle::StopAtFirstFree); + if args.len() > 1 { + match opts.parse(&args[1..]) { + Ok(matches) => return matches, + Err(err) => println!("{}", err) + } + } + println!("{}", opts.usage(&format!("Usage: pngcheck [-cpt] [file ...]"))); + std::process::exit(0); +} + +#[derive(Clone, Copy)] +struct Config { + quiet: bool, + verbose: bool, + color: bool +} + +fn display_interlaced(i: bool) -> &'static str { + if i { + "interlaced" + } else { + "non-interlaced" + } +} + +fn display_image_type(bits: u8, color: png::ColorType) -> String { + use png::ColorType::*; + format!( + "{}-bit {}", + bits, + match color { + Grayscale => "grayscale", + RGB => "RGB", + Indexed => "palette", + GrayscaleAlpha => "grayscale+alpha", + RGBA => "RGB+alpha" + } + ) +} +// channels after expansion of tRNS +fn final_channels(c: png::ColorType, trns: bool) -> u8 { + use png::ColorType::*; + match c { + Grayscale => 1 + if trns { 1 } else { 0 }, + RGB => 3, + Indexed => 3 + if trns { 1 } else { 0 }, + GrayscaleAlpha => 2, + RGBA => 4 + } +} +fn check_image<P: AsRef<Path>>(c: Config, fname: P) -> io::Result<()> { + // TODO improve performance by resusing allocations from decoder + use png::Decoded::*; + let mut t = term::stdout().ok_or(io::Error::new( + io::ErrorKind::Other, + "could not open terminal" + ))?; + let mut data = vec![0; 10*1024]; + let data_p = data.as_mut_ptr(); + let mut reader = io::BufReader::new(File::open(&fname)?); + let fname = fname.as_ref().to_string_lossy(); + let n = reader.read(&mut data)?; + let mut buf = &data[..n]; + let mut pos = 0; + let mut decoder = png::StreamingDecoder::new(); + // Image data + let mut width = 0; + let mut height = 0; + let mut color = png::ColorType::Grayscale; + let mut bits = 0; + let mut trns = false; + let mut interlaced = false; + let mut compressed_size = 0; + let mut n_chunks = 0; + let mut have_idat = false; + macro_rules! c_ratio( + // TODO add palette entries to compressed_size + () => ({ + compressed_size as f32/( + height as u64 * + (width as u64 * final_channels(color, trns) as u64 * bits as u64 + 7)>>3 + ) as f32 + }); + ); + let display_error = |err| -> Result<_, io::Error> { + let mut t = term::stdout().ok_or(io::Error::new( + io::ErrorKind::Other, + "could not open terminal" + ))?; + if c.verbose { + if c.color { + print!(": "); + t.fg(color::RED)?; + writeln!(t, "{}", err)?; + t.attr(Attr::Bold)?; + write!(t, "ERRORS DETECTED")?; + t.reset()?; + } else { + println!(": {}", err); + print!("ERRORS DETECTED") + } + println!(" in {}", fname); + } else { + if !c.quiet { if c.color { + t.fg(color::RED)?; + t.attr(Attr::Bold)?; + write!(t, "ERROR")?; + t.reset()?; + write!(t, ": ")?; + t.fg(color::YELLOW)?; + writeln!(t, "{}", fname)?; + t.reset()?; + } else { + println!("ERROR: {}", fname) + }} + print!("{}: ", fname); + if c.color { + t.fg(color::RED)?; + writeln!(t, "{}", err)?; + t.reset()?; + } else { + println!("{}", err); + } + + } + Ok(()) + }; + + if c.verbose { + print!("File: "); + if c.color { + t.attr(Attr::Bold)?; + write!(t, "{}", fname)?; + t.reset()?; + } else { + print!("{}", fname); + } + print!(" ({}) bytes", data.len()) + + } + loop { + if buf.len() == 0 { + // circumvent borrow checker + assert!(!data.is_empty()); + let n = reader.read(unsafe { + ::std::slice::from_raw_parts_mut(data_p, data.len()) + })?; + + // EOF + if n == 0 { + println!("ERROR: premature end of file {}", fname); + break; + } + buf = &data[..n]; + } + match decoder.update(buf, &mut Vec::new()) { + Ok((_, ImageEnd)) => { + if !have_idat { + display_error(png::DecodingError::Format("IDAT chunk missing".into()))?; + break; + } + if !c.verbose && !c.quiet { + if c.color { + t.fg(color::GREEN)?; + t.attr(Attr::Bold)?; + write!(t, "OK")?; + t.reset()?; + write!(t, ": ")?; + t.fg(color::YELLOW)?; + write!(t, "{}", fname)?; + t.reset()?; + } else { + print!("OK: {}", fname) + } + println!( + " ({}x{}, {}{}, {}, {:.1}%)", + width, + height, + display_image_type(bits, color), + (if trns { "+trns" } else { "" }), + display_interlaced(interlaced), + 100.0*(1.0-c_ratio!()) + ) + } else if !c.quiet { + println!(""); + if c.color { + t.fg(color::GREEN)?; + t.attr(Attr::Bold)?; + write!(t, "No errors detected ")?; + t.reset()?; + } else { + print!("No errors detected "); + } + println!( + "in {} ({} chunks, {:.1}% compression)", + fname, + n_chunks, + 100.0*(1.0-c_ratio!()) + ) + } + break + }, + Ok((n, res)) => { + buf = &buf[n..]; + pos += n; + match res { + Header(w, h, b, c, i) => { + width = w; + height = h; + bits = b as u8; + color = c; + interlaced = i; + } + ChunkBegin(len, type_str) => { + use png::chunk; + n_chunks += 1; + if c.verbose { + let chunk = String::from_utf8_lossy(&type_str); + println!(""); + print!(" chunk "); + if c.color { + t.fg(color::YELLOW)?; + write!(t, "{}", chunk)?; + t.reset()?; + } else { + print!("{}", chunk) + } + print!( + " at offset {:#07x}, length {}", + pos - 4, // substract chunk name length + len + ) + } + match type_str { + chunk::IDAT => { + have_idat = true; + compressed_size += len + }, + chunk::tRNS => { + trns = true; + }, + _ => () + } + } + ImageData => { + //println!("got {} bytes of image data", data.len()) + } + ChunkComplete(_, type_str) if c.verbose => { + use png::chunk::*; + match type_str { + IHDR => { + println!(""); + print!( + " {} x {} image, {}{}, {}", + width, + height, + display_image_type(bits, color), + (if trns { "+trns" } else { "" }), + display_interlaced(interlaced), + ); + } + _ => () + } + } + AnimationControl(actl) => { + println!(""); + print!( + " {} frames, {} plays", + actl.num_frames, + actl.num_plays, + ); + } + FrameControl(fctl) => { + println!(""); + println!( + " sequence #{}, {} x {} pixels @ ({}, {})", + fctl.sequence_number, + fctl.width, + fctl.height, + fctl.x_offset, + fctl.y_offset, + /*fctl.delay_num, + fctl.delay_den, + fctl.dispose_op, + fctl.blend_op,*/ + ); + print!( + " {}/{} s delay, dispose: {}, blend: {}", + fctl.delay_num, + if fctl.delay_den == 0 { 100 } else {fctl.delay_den}, + fctl.dispose_op, + fctl.blend_op, + ); + } + _ => () + } + //println!("{} {:?}", n, res) + }, + Err(err) => { + let _ = display_error(err); + break + } + } + } + Ok(()) +} + +fn main() { + let m = parse_args(); + + let config = Config { + quiet: m.opt_present("q"), + verbose: m.opt_present("v"), + color: m.opt_present("c") + }; + + for file in m.free { + let result = if file.contains("*") { + glob::glob(&file).map_err(|err| { + io::Error::new(io::ErrorKind::Other, err) + }).and_then(|mut glob| glob.try_for_each(|entry| { + entry.map_err(|err| { + io::Error::new(io::ErrorKind::Other, err) + }).and_then(|file| { + check_image(config, file) + }) + })) + } else { + check_image(config, &file) + }; + + result.unwrap_or_else(|err| { + println!("{}: {}", file, err); + std::process::exit(1) + }); + } +} diff --git a/third_party/rust/png/examples/show.rs b/third_party/rust/png/examples/show.rs new file mode 100644 index 0000000000..9185b8f77e --- /dev/null +++ b/third_party/rust/png/examples/show.rs @@ -0,0 +1,199 @@ +extern crate glium; +extern crate glob; +extern crate png; + +use std::env; +use std::io; +use std::fs::File; +use std::borrow::Cow; +use std::path; +use std::error::Error; + +use glium::{Surface, Rect, BlitTarget}; +use glium::texture::{RawImage2d, ClientFormat}; +use glium::glutin::{self, Event, VirtualKeyCode, dpi}; +use glium::backend::glutin::Display; + +/// Load the image using `png` +fn load_image(path: &path::PathBuf) -> io::Result<RawImage2d<'static, u8>> { + use png::ColorType::*; + let decoder = png::Decoder::new(File::open(path)?); + let (info, mut reader) = decoder.read_info()?; + let mut img_data = vec![0; info.buffer_size()]; + reader.next_frame(&mut img_data)?; + + let (data, format) = match info.color_type { + RGB => (img_data, ClientFormat::U8U8U8), + RGBA => (img_data, ClientFormat::U8U8U8U8), + Grayscale => ( + { + let mut vec = Vec::with_capacity(img_data.len()*3); + for g in img_data { + vec.extend([g, g, g].iter().cloned()) + } + vec + }, + ClientFormat::U8U8U8 + ), + GrayscaleAlpha => ( + { + let mut vec = Vec::with_capacity(img_data.len()*3); + for ga in img_data.chunks(2) { + let g = ga[0]; let a = ga[1]; + vec.extend([g, g, g, a].iter().cloned()) + } + vec + }, + ClientFormat::U8U8U8U8 + ), + _ => unreachable!("uncovered color type") + }; + + Ok(RawImage2d { + data: Cow::Owned(data), + width: info.width, + height: info.height, + format: format + }) +} + +fn main_loop(files: Vec<path::PathBuf>) -> io::Result<()> { + use glium::glutin::{KeyboardInput, WindowEvent}; + + let mut files = files.iter(); + let image = load_image(files.next().unwrap())?; + + let mut events_loop = glutin::EventsLoop::new(); + let window = glutin::WindowBuilder::new(); + let context = glutin::ContextBuilder::new() + .with_vsync(true); + + let display = Display::new(window, context, &events_loop) + .map_err(|err| io::Error::new( + io::ErrorKind::Other, + err.description() + ))?; + // building the display, ie. the main object + resize_window(&display, &image); + let mut opengl_texture = glium::Texture2d::new(&display, image).unwrap(); + + let mut stop = false; + let mut res = Ok(()); + 'main: loop { + let frame = display.draw(); + fill_v_flipped(&opengl_texture.as_surface(), &frame, glium::uniforms::MagnifySamplerFilter::Linear); + frame.finish().unwrap(); + + // polling and handling the events received by the window + events_loop.poll_events(|event| { + if stop {return;} + match event { + Event::WindowEvent {event: WindowEvent::CloseRequested, ..} => { + stop = true; + return; + } + Event::WindowEvent { + event: WindowEvent::KeyboardInput { + input: KeyboardInput { + state: glutin::ElementState::Pressed, + virtual_keycode: code, + .. + }, + .. + }, + .. + } => match code { + Some(VirtualKeyCode::Escape) => { + stop = true; + return; + } + Some(VirtualKeyCode::Right) => { + match files.next() { + Some(path) => { + let image = match load_image(path) { + Ok(image) => image, + Err(err) => { + stop = true; + res = Err(err); + return; + } + }; + resize_window(&display, &image); + opengl_texture = glium::Texture2d::new(&display, image).unwrap(); + }, + None => { + stop = true; + return; + } + } + }, + _ => () + }, + _ => () + } + }); + + if stop {break 'main;} + } + res +} + +fn fill_v_flipped<S1, S2>(src: &S1, target: &S2, filter: glium::uniforms::MagnifySamplerFilter) +where S1: Surface, S2: Surface { + let src_dim = src.get_dimensions(); + let src_rect = Rect { left: 0, bottom: 0, width: src_dim.0 as u32, height: src_dim.1 as u32 }; + let target_dim = target.get_dimensions(); + let target_rect = BlitTarget { left: 0, bottom: target_dim.1, width: target_dim.0 as i32, height: -(target_dim.1 as i32) }; + src.blit_color(&src_rect, target, &target_rect, filter); +} + +fn resize_window(display: &Display, image: &RawImage2d<'static, u8>) { + let mut width = image.width; + let mut height = image.height; + if width < 50 && height < 50 { + width *= 10; + height *= 10; + } else if width < 5 && height < 5 { + width *= 10; + height *= 10; + } + display.gl_window().window().set_inner_size(dpi::LogicalSize::new(f64::from(width), f64::from(height))); +} + +fn main() { + let args: Vec<String> = env::args().collect(); + if args.len() < 2 { + println!("Usage: show files [...]"); + } else { + let mut files = vec![]; + for file in args.iter().skip(1) { + match if file.contains("*") { + (|| -> io::Result<_> { + for entry in glob::glob(&file).map_err(|err| { + io::Error::new(io::ErrorKind::Other, err.msg) + })? { + files.push(entry.map_err(|_| { + io::Error::new(io::ErrorKind::Other, "glob error") + })?) + } + Ok(()) + })() + } else { + files.push(path::PathBuf::from(file)); + Ok(()) + } { + Ok(_) => (), + Err(err) => { + println!("{}: {}", file, err); + break + } + } + + } + // "tests/pngsuite/pngsuite.png" + match main_loop(files) { + Ok(_) => (), + Err(err) => println!("Error: {}", err) + } + } +} diff --git a/third_party/rust/png/fuzzit.sh b/third_party/rust/png/fuzzit.sh new file mode 100755 index 0000000000..465730ffb7 --- /dev/null +++ b/third_party/rust/png/fuzzit.sh @@ -0,0 +1,16 @@ +# Arguments: fuzz target-id, fuzzer name +fuzzer_target="$1" +fuzzer_binary="$2" + +ORG_NAME="image-rs" +FUZZIT_VERSION=2.4.46 + +# Get cargo-fuzz +cargo install cargo-fuzz +# Build the fuzzer binary +cargo fuzz run "$fuzzer_binary" -- -runs=0 + +wget -O fuzzit "https://github.com/fuzzitdev/fuzzit/releases/download/v$FUZZIT_VERSION/fuzzit_Linux_x86_64" +chmod a+x fuzzit +./fuzzit --version +./fuzzit create job --type local-regression "$ORG_NAME/$fuzzer_target" "./fuzz/target/x86_64-unknown-linux-gnu/debug/$fuzzer_binary" diff --git a/third_party/rust/png/src/chunk.rs b/third_party/rust/png/src/chunk.rs new file mode 100644 index 0000000000..9e24e6e9a0 --- /dev/null +++ b/third_party/rust/png/src/chunk.rs @@ -0,0 +1,59 @@ +//! Chunk types and functions +#![allow(dead_code)] +#![allow(non_upper_case_globals)] + +pub type ChunkType = [u8; 4]; + +// -- Critical chunks -- + +/// Image header +pub const IHDR: ChunkType = [b'I', b'H', b'D', b'R']; +/// Palette +pub const PLTE: ChunkType = [b'P', b'L', b'T', b'E']; +/// Image data +pub const IDAT: ChunkType = [b'I', b'D', b'A', b'T']; +/// Image trailer +pub const IEND: ChunkType = [b'I', b'E', b'N', b'D']; + +// -- Ancillary chunks -- + +/// Transparency +pub const tRNS: ChunkType = [b't', b'R', b'N', b'S']; +/// Background colour +pub const bKGD: ChunkType = [b'b', b'K', b'G', b'D']; +/// Image last-modification time +pub const tIME: ChunkType = [b't', b'I', b'M', b'E']; +/// Physical pixel dimensions +pub const pHYs: ChunkType = [b'p', b'H', b'Y', b's']; + +// -- Extension chunks -- + +/// Animation control +pub const acTL: ChunkType = [b'a', b'c', b'T', b'L']; +/// Frame control +pub const fcTL: ChunkType = [b'f', b'c', b'T', b'L']; +/// Frame data +pub const fdAT: ChunkType = [b'f', b'd', b'A', b'T']; + +// -- Chunk type determination -- + +/// Returns true if the chunk is critical. +pub fn is_critical(type_: ChunkType) -> bool { + type_[0] & 32 == 0 +} + +/// Returns true if the chunk is private. +pub fn is_private(type_: ChunkType) -> bool { + type_[1] & 32 != 0 +} + +/// Checks whether the reserved bit of the chunk name is set. +/// If it is set the chunk name is invalid. +pub fn reserved_set(type_: ChunkType) -> bool { + type_[2] & 32 != 0 +} + +/// Returns true if the chunk is safe to copy if unknown. +pub fn safe_to_copy(type_: ChunkType) -> bool { + type_[3] & 32 != 0 +}
\ No newline at end of file diff --git a/third_party/rust/png/src/common.rs b/third_party/rust/png/src/common.rs new file mode 100644 index 0000000000..014efb751e --- /dev/null +++ b/third_party/rust/png/src/common.rs @@ -0,0 +1,404 @@ +//! Common types shared between the encoder and decoder +use crate::filter; + +use std::fmt; + +/// Describes the layout of samples in a pixel +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +pub enum ColorType { + Grayscale = 0, + RGB = 2, + Indexed = 3, + GrayscaleAlpha = 4, + RGBA = 6 +} + +impl ColorType { + /// Returns the number of samples used per pixel of `ColorType` + pub fn samples(&self) -> usize { + use self::ColorType::*; + match *self { + Grayscale | Indexed => 1, + RGB => 3, + GrayscaleAlpha => 2, + RGBA => 4 + } + } + + /// u8 -> Self. Temporary solution until Rust provides a canonical one. + pub fn from_u8(n: u8) -> Option<ColorType> { + match n { + 0 => Some(ColorType::Grayscale), + 2 => Some(ColorType::RGB), + 3 => Some(ColorType::Indexed), + 4 => Some(ColorType::GrayscaleAlpha), + 6 => Some(ColorType::RGBA), + _ => None + } + } +} + +/// Bit depth of the png file +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +pub enum BitDepth { + One = 1, + Two = 2, + Four = 4, + Eight = 8, + Sixteen = 16, +} + +impl BitDepth { + /// u8 -> Self. Temporary solution until Rust provides a canonical one. + pub fn from_u8(n: u8) -> Option<BitDepth> { + match n { + 1 => Some(BitDepth::One), + 2 => Some(BitDepth::Two), + 4 => Some(BitDepth::Four), + 8 => Some(BitDepth::Eight), + 16 => Some(BitDepth::Sixteen), + _ => None + } + } +} + +/// Pixel dimensions information +#[derive(Clone, Copy, Debug)] +pub struct PixelDimensions { + /// Pixels per unit, X axis + pub xppu: u32, + /// Pixels per unit, Y axis + pub yppu: u32, + /// Either *Meter* or *Unspecified* + pub unit: Unit, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +/// Physical unit of the pixel dimensions +pub enum Unit { + Unspecified = 0, + Meter = 1, +} + +impl Unit { + /// u8 -> Self. Temporary solution until Rust provides a canonical one. + pub fn from_u8(n: u8) -> Option<Unit> { + match n { + 0 => Some(Unit::Unspecified), + 1 => Some(Unit::Meter), + _ => None + } + } +} + +/// How to reset buffer of an animated png (APNG) at the end of a frame. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +pub enum DisposeOp { + /// Leave the buffer unchanged. + None = 0, + /// Clear buffer with the background color. + Background = 1, + /// Reset the buffer to the state before the current frame. + Previous = 2, +} + +impl DisposeOp { + /// u8 -> Self. Using enum_primitive or transmute is probably the right thing but this will do for now. + pub fn from_u8(n: u8) -> Option<DisposeOp> { + match n { + 0 => Some(DisposeOp::None), + 1 => Some(DisposeOp::Background), + 2 => Some(DisposeOp::Previous), + _ => None + } + } +} + +impl fmt::Display for DisposeOp { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let name = match *self { + DisposeOp::None => "DISPOSE_OP_NONE", + DisposeOp::Background => "DISPOSE_OP_BACKGROUND", + DisposeOp::Previous => "DISPOSE_OP_PREVIOUS", + }; + write!(f, "{}", name) + } +} + +/// How pixels are written into the buffer. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +pub enum BlendOp { + /// Pixels overwrite the value at their position. + Source = 0, + /// The new pixels are blended into the current state based on alpha. + Over = 1, +} + +impl BlendOp { + /// u8 -> Self. Using enum_primitive or transmute is probably the right thing but this will do for now. + pub fn from_u8(n: u8) -> Option<BlendOp> { + match n { + 0 => Some(BlendOp::Source), + 1 => Some(BlendOp::Over), + _ => None + } + } +} + +impl fmt::Display for BlendOp { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let name = match *self { + BlendOp::Source => "BLEND_OP_SOURCE", + BlendOp::Over => "BLEND_OP_OVER", + }; + write!(f, "{}", name) + } +} + +/// Frame control information +#[derive(Clone, Copy, Debug)] +pub struct FrameControl { + /// Sequence number of the animation chunk, starting from 0 + pub sequence_number: u32, + /// Width of the following frame + pub width: u32, + /// Height of the following frame + pub height: u32, + /// X position at which to render the following frame + pub x_offset: u32, + /// Y position at which to render the following frame + pub y_offset: u32, + /// Frame delay fraction numerator + pub delay_num: u16, + /// Frame delay fraction denominator + pub delay_den: u16, + /// Type of frame area disposal to be done after rendering this frame + pub dispose_op: DisposeOp, + /// Type of frame area rendering for this frame + pub blend_op: BlendOp, +} + +impl Default for FrameControl { + fn default() -> FrameControl { + FrameControl { + sequence_number: 0, + width: 0, + height: 0, + x_offset: 0, + y_offset: 0, + delay_num: 1, + delay_den: 30, + dispose_op: DisposeOp::None, + blend_op: BlendOp::Source, + } + } +} + +impl FrameControl { + pub fn set_seq_num(&mut self, s: u32) { + self.sequence_number = s; + } + + pub fn inc_seq_num(&mut self, i: u32) { + self.sequence_number += i; + } + +} + +/// Animation control information +#[derive(Clone, Copy, Debug)] +pub struct AnimationControl { + /// Number of frames + pub num_frames: u32, + /// Number of times to loop this APNG. 0 indicates infinite looping. + pub num_plays: u32, +} + +/// The type and strength of applied compression. +#[derive(Debug, Clone)] +pub enum Compression { + /// Default level + Default, + /// Fast minimal compression + Fast, + /// Higher compression level + /// + /// Best in this context isn't actually the highest possible level + /// the encoder can do, but is meant to emulate the `Best` setting in the `Flate2` + /// library. + Best, + Huffman, + Rle, +} + +/// PNG info struct +#[derive(Debug)] +pub struct Info { + pub width: u32, + pub height: u32, + pub bit_depth: BitDepth, + pub color_type: ColorType, + pub interlaced: bool, + pub trns: Option<Vec<u8>>, + pub pixel_dims: Option<PixelDimensions>, + pub palette: Option<Vec<u8>>, + pub frame_control: Option<FrameControl>, + pub animation_control: Option<AnimationControl>, + pub compression: Compression, + pub filter: filter::FilterType, +} + +impl Default for Info { + fn default() -> Info { + Info { + width: 0, + height: 0, + bit_depth: BitDepth::Eight, + color_type: ColorType::Grayscale, + interlaced: false, + palette: None, + trns: None, + pixel_dims: None, + frame_control: None, + animation_control: None, + // Default to `deflate::Compresion::Fast` and `filter::FilterType::Sub` + // to maintain backward compatible output. + compression: Compression::Fast, + filter: filter::FilterType::Sub, + } + } +} + +impl Info { + /// Size of the image + pub fn size(&self) -> (u32, u32) { + (self.width, self.height) + } + + /// Returns true if the image is an APNG image. + pub fn is_animated(&self) -> bool { + self.frame_control.is_some() && self.animation_control.is_some() + } + + /// Returns the frame control information of the image + pub fn animation_control(&self) -> Option<&AnimationControl> { + self.animation_control.as_ref() + } + + /// Returns the frame control information of the current frame + pub fn frame_control(&self) -> Option<&FrameControl> { + self.frame_control.as_ref() + } + + /// Returns the bits per pixel + pub fn bits_per_pixel(&self) -> usize { + self.color_type.samples() * self.bit_depth as usize + } + + /// Returns the bytes per pixel + pub fn bytes_per_pixel(&self) -> usize { + self.color_type.samples() * ((self.bit_depth as usize + 7) >> 3) + } + + /// Returns the number of bytes needed for one deinterlaced image + pub fn raw_bytes(&self) -> usize { + self.height as usize * self.raw_row_length() + } + + /// Returns the number of bytes needed for one deinterlaced row + pub fn raw_row_length(&self) -> usize { + let bits = self.width as usize * self.color_type.samples() * self.bit_depth as usize; + let extra = bits % 8; + bits/8 + + match extra { 0 => 0, _ => 1 } + + 1 // filter method + } + + /// Returns the number of bytes needed for one deinterlaced row of width `width` + pub fn raw_row_length_from_width(&self, width: u32) -> usize { + let bits = width as usize * self.color_type.samples() * self.bit_depth as usize; + let extra = bits % 8; + bits/8 + + match extra { 0 => 0, _ => 1 } + + 1 // filter method + } +} + + +bitflags! { + /// # Output transformations + /// + /// Only `IDENTITY` and `TRANSFORM_EXPAND | TRANSFORM_STRIP_ALPHA` can be used at the moment. + pub struct Transformations: u32 { + /// No transformation + const IDENTITY = 0x0000; // read and write */ + /// Strip 16-bit samples to 8 bits + const STRIP_16 = 0x0001; // read only */ + /// Discard the alpha channel + const STRIP_ALPHA = 0x0002; // read only */ + /// Expand 1; 2 and 4-bit samples to bytes + const PACKING = 0x0004; // read and write */ + /// Change order of packed pixels to LSB first + const PACKSWAP = 0x0008; // read and write */ + /// Expand paletted images to RGB; expand grayscale images of + /// less than 8-bit depth to 8-bit depth; and expand tRNS chunks + /// to alpha channels. + const EXPAND = 0x0010; // read only */ + /// Invert monochrome images + const INVERT_MONO = 0x0020; // read and write */ + /// Normalize pixels to the sBIT depth + const SHIFT = 0x0040; // read and write */ + /// Flip RGB to BGR; RGBA to BGRA + const BGR = 0x0080; // read and write */ + /// Flip RGBA to ARGB or GA to AG + const SWAP_ALPHA = 0x0100; // read and write */ + /// Byte-swap 16-bit samples + const SWAP_ENDIAN = 0x0200; // read and write */ + /// Change alpha from opacity to transparency + const INVERT_ALPHA = 0x0400; // read and write */ + const STRIP_FILLER = 0x0800; // write only */ + const STRIP_FILLER_BEFORE = 0x0800; // write only + const STRIP_FILLER_AFTER = 0x1000; // write only */ + const GRAY_TO_RGB = 0x2000; // read only */ + const EXPAND_16 = 0x4000; // read only */ + const SCALE_16 = 0x8000; // read only */ + } +} + +/// Mod to encapsulate the converters depending on the `deflate` crate. +/// +/// Since this only contains trait impls, there is no need to make this public, they are simply +/// available when the mod is compiled as well. +#[cfg(feature = "png-encoding")] +mod deflate_convert { + extern crate deflate; + use super::Compression; + + impl From<deflate::Compression> for Compression { + fn from(c: deflate::Compression) -> Self { + match c { + deflate::Compression::Default => Compression::Default, + deflate::Compression::Fast => Compression::Fast, + deflate::Compression::Best => Compression::Best, + } + } + } + + impl From<Compression> for deflate::CompressionOptions { + fn from(c: Compression) -> Self { + match c { + Compression::Default => deflate::CompressionOptions::default(), + Compression::Fast => deflate::CompressionOptions::fast(), + Compression::Best => deflate::CompressionOptions::high(), + Compression::Huffman => deflate::CompressionOptions::huffman_only(), + Compression::Rle => deflate::CompressionOptions::rle(), + } + } + } +} + diff --git a/third_party/rust/png/src/decoder/mod.rs b/third_party/rust/png/src/decoder/mod.rs new file mode 100644 index 0000000000..7665983206 --- /dev/null +++ b/third_party/rust/png/src/decoder/mod.rs @@ -0,0 +1,543 @@ +mod stream; + +pub use self::stream::{StreamingDecoder, Decoded, DecodingError}; +use self::stream::{CHUNCK_BUFFER_SIZE, get_info}; + +use std::mem; +use std::borrow; +use std::io::{Read, Write, BufReader, BufRead}; + +use crate::common::{ColorType, BitDepth, Info, Transformations}; +use crate::filter::{unfilter, FilterType}; +use crate::chunk::IDAT; +use crate::utils; + +/* +pub enum InterlaceHandling { + /// Outputs the raw rows + RawRows, + /// Fill missing the pixels from the existing ones + Rectangle, + /// Only fill the needed pixels + Sparkle +} +*/ + +/// Output info +#[derive(Debug)] +pub struct OutputInfo { + pub width: u32, + pub height: u32, + pub color_type: ColorType, + pub bit_depth: BitDepth, + pub line_size: usize, +} + +impl OutputInfo { + /// Returns the size needed to hold a decoded frame + pub fn buffer_size(&self) -> usize { + self.line_size * self.height as usize + } +} + +#[derive(Clone, Copy, Debug)] +/// Limits on the resources the `Decoder` is allowed too use +pub struct Limits { + /// maximum number of bytes the decoder is allowed to allocate, default is 64Mib + pub bytes: usize, +} + +impl Default for Limits { + fn default() -> Limits { + Limits { + bytes: 1024*1024*64, + } + } +} + +/// PNG Decoder +pub struct Decoder<R: Read> { + /// Reader + r: R, + /// Output transformations + transform: Transformations, + /// Limits on resources the Decoder is allowed to use + limits: Limits, +} + +impl<R: Read> Decoder<R> { + pub fn new(r: R) -> Decoder<R> { + Decoder::new_with_limits(r, Limits::default()) + } + + pub fn new_with_limits(r: R, limits: Limits) -> Decoder<R> { + Decoder { + r, + transform: crate::Transformations::EXPAND | crate::Transformations::SCALE_16 | crate::Transformations::STRIP_16, + limits, + } + } + + /// Limit resource usage + /// + /// ``` + /// use std::fs::File; + /// use png::{Decoder, Limits}; + /// // This image is 32x32 pixels, so the deocder will allocate more than four bytes + /// let mut limits = Limits::default(); + /// limits.bytes = 4; + /// let mut decoder = Decoder::new_with_limits(File::open("tests/pngsuite/basi0g01.png").unwrap(), limits); + /// assert!(decoder.read_info().is_err()); + /// // This image is 32x32 pixels, so the decoder will allocate less than 10Kib + /// let mut limits = Limits::default(); + /// limits.bytes = 10*1024; + /// let mut decoder = Decoder::new_with_limits(File::open("tests/pngsuite/basi0g01.png").unwrap(), limits); + /// assert!(decoder.read_info().is_ok()); + /// ``` + pub fn set_limits(&mut self, limits: Limits) { + self.limits = limits; + } + + /// Reads all meta data until the first IDAT chunk + pub fn read_info(self) -> Result<(OutputInfo, Reader<R>), DecodingError> { + let mut r = Reader::new(self.r, StreamingDecoder::new(), self.transform, self.limits); + r.init()?; + let (ct, bits) = r.output_color_type(); + let info = { + let info = r.info(); + OutputInfo { + width: info.width, + height: info.height, + color_type: ct, + bit_depth: bits, + line_size: r.output_line_size(info.width), + } + }; + Ok((info, r)) + } + + /// Set the allowed and performed transformations. + /// + /// A transformation is a pre-processing on the raw image data modifying content or encoding. + /// Many options have an impact on memory or CPU usage during decoding. + pub fn set_transformations(&mut self, transform: Transformations) { + self.transform = transform; + } +} + +struct ReadDecoder<R: Read> { + reader: BufReader<R>, + decoder: StreamingDecoder, + at_eof: bool +} + +impl<R: Read> ReadDecoder<R> { + /// Returns the next decoded chunk. If the chunk is an ImageData chunk, its contents are written + /// into image_data. + fn decode_next(&mut self, image_data: &mut Vec<u8>) -> Result<Option<Decoded>, DecodingError> { + while !self.at_eof { + let (consumed, result) = { + let buf = self.reader.fill_buf()?; + if buf.is_empty() { + return Err(DecodingError::Format( + "unexpected EOF".into() + )) + } + self.decoder.update(buf, image_data)? + }; + self.reader.consume(consumed); + match result { + Decoded::Nothing => (), + Decoded::ImageEnd => self.at_eof = true, + result => return Ok(Some(result)) + } + } + Ok(None) + } + + fn info(&self) -> Option<&Info> { + get_info(&self.decoder) + } +} + +/// PNG reader (mostly high-level interface) +/// +/// Provides a high level that iterates over lines or whole images. +pub struct Reader<R: Read> { + decoder: ReadDecoder<R>, + bpp: usize, + rowlen: usize, + adam7: Option<utils::Adam7Iterator>, + /// Previous raw line + prev: Vec<u8>, + /// Current raw line + current: Vec<u8>, + /// Output transformations + transform: Transformations, + /// Processed line + processed: Vec<u8>, + limits: Limits, +} + +macro_rules! get_info( + ($this:expr) => { + $this.decoder.info().unwrap() + } +); + +impl<R: Read> Reader<R> { + /// Creates a new PNG reader + fn new(r: R, d: StreamingDecoder, t: Transformations, limits: Limits) -> Reader<R> { + Reader { + decoder: ReadDecoder { + reader: BufReader::with_capacity(CHUNCK_BUFFER_SIZE, r), + decoder: d, + at_eof: false + }, + bpp: 0, + rowlen: 0, + adam7: None, + prev: Vec::new(), + current: Vec::new(), + transform: t, + processed: Vec::new(), + limits, + } + } + + /// Reads all meta data until the first IDAT chunk + fn init(&mut self) -> Result<(), DecodingError> { + use crate::Decoded::*; + if self.decoder.info().is_some() { + Ok(()) + } else { + loop { + match self.decoder.decode_next(&mut Vec::new())? { + Some(ChunkBegin(_, IDAT)) => break, + None => return Err(DecodingError::Format( + "IDAT chunk missing".into() + )), + _ => (), + } + } + { + let info = match self.decoder.info() { + Some(info) => info, + None => return Err(DecodingError::Format( + "IHDR chunk missing".into() + )) + }; + self.bpp = info.bytes_per_pixel(); + self.rowlen = info.raw_row_length(); + if info.interlaced { + self.adam7 = Some(utils::Adam7Iterator::new(info.width, info.height)) + } + } + self.allocate_out_buf()?; + self.prev = vec![0; self.rowlen]; + Ok(()) + } + } + + pub fn info(&self) -> &Info { + get_info!(self) + } + + /// Decodes the next frame into `buf` + pub fn next_frame(&mut self, buf: &mut [u8]) -> Result<(), DecodingError> { + // TODO 16 bit + let (color_type, bit_depth) = self.output_color_type(); + let width = get_info!(self).width; + if buf.len() < self.output_buffer_size() { + return Err(DecodingError::Other( + "supplied buffer is too small to hold the image".into() + )) + } + if get_info!(self).interlaced { + while let Some((row, adam7)) = self.next_interlaced_row()? { + let (pass, line, _) = adam7.unwrap(); + let samples = color_type.samples() as u8; + utils::expand_pass(buf, width, row, pass, line, samples * (bit_depth as u8)); + } + } else { + let mut len = 0; + while let Some(row) = self.next_row()? { + len += (&mut buf[len..]).write(row)?; + } + } + Ok(()) + } + + /// Returns the next processed row of the image + pub fn next_row(&mut self) -> Result<Option<&[u8]>, DecodingError> { + self.next_interlaced_row().map(|v| v.map(|v| v.0)) + } + + /// Returns the next processed row of the image + pub fn next_interlaced_row(&mut self) -> Result<Option<(&[u8], Option<(u8, u32, u32)>)>, DecodingError> { + use crate::common::ColorType::*; + let transform = self.transform; + if transform == crate::Transformations::IDENTITY { + self.next_raw_interlaced_row() + } else { + // swap buffer to circumvent borrow issues + let mut buffer = mem::replace(&mut self.processed, Vec::new()); + let (got_next, adam7) = if let Some((row, adam7)) = self.next_raw_interlaced_row()? { + (&mut buffer[..]).write_all(row)?; + (true, adam7) + } else { + (false, None) + }; + // swap back + let _ = mem::replace(&mut self.processed, buffer); + if got_next { + let (color_type, bit_depth, trns) = { + let info = get_info!(self); + (info.color_type, info.bit_depth as u8, info.trns.is_some()) + }; + let output_buffer = if let Some((_, _, width)) = adam7 { + let width = self.line_size(width); + &mut self.processed[..width] + } else { + &mut *self.processed + }; + let mut len = output_buffer.len(); + if transform.contains(crate::Transformations::EXPAND) { + match color_type { + Indexed => { + expand_paletted(output_buffer, get_info!(self))? + } + Grayscale | GrayscaleAlpha if bit_depth < 8 => expand_gray_u8( + output_buffer, get_info!(self) + ), + Grayscale | RGB if trns => { + let channels = color_type.samples(); + let trns = get_info!(self).trns.as_ref().unwrap(); + if bit_depth == 8 { + utils::expand_trns_line(output_buffer, &*trns, channels); + } else { + utils::expand_trns_line16(output_buffer, &*trns, channels); + } + }, + _ => () + } + } + if bit_depth == 16 && transform.intersects(crate::Transformations::SCALE_16 | crate::Transformations::STRIP_16) { + len /= 2; + for i in 0..len { + output_buffer[i] = output_buffer[2 * i]; + } + } + Ok(Some(( + &output_buffer[..len], + adam7 + ))) + } else { + Ok(None) + } + } + } + + /// Returns the color type and the number of bits per sample + /// of the data returned by `Reader::next_row` and Reader::frames`. + pub fn output_color_type(&mut self) -> (ColorType, BitDepth) { + use crate::common::ColorType::*; + let t = self.transform; + let info = get_info!(self); + if t == crate::Transformations::IDENTITY { + (info.color_type, info.bit_depth) + } else { + let bits = match info.bit_depth as u8 { + 16 if t.intersects( + crate::Transformations::SCALE_16 | crate::Transformations::STRIP_16 + ) => 8, + n if n < 8 && t.contains(crate::Transformations::EXPAND) => 8, + n => n + }; + let color_type = if t.contains(crate::Transformations::EXPAND) { + let has_trns = info.trns.is_some(); + match info.color_type { + Grayscale if has_trns => GrayscaleAlpha, + RGB if has_trns => RGBA, + Indexed if has_trns => RGBA, + Indexed => RGB, + ct => ct + } + } else { + info.color_type + }; + (color_type, BitDepth::from_u8(bits).unwrap()) + } + } + + /// Returns the number of bytes required to hold a deinterlaced image frame + /// that is decoded using the given input transformations. + pub fn output_buffer_size(&self) -> usize { + let (width, height) = get_info!(self).size(); + let size = self.output_line_size(width); + size * height as usize + } + + /// Returns the number of bytes required to hold a deinterlaced row. + pub fn output_line_size(&self, width: u32) -> usize { + let size = self.line_size(width); + if get_info!(self).bit_depth as u8 == 16 && self.transform.intersects( + crate::Transformations::SCALE_16 | crate::Transformations::STRIP_16 + ) { + size / 2 + } else { + size + } + } + + /// Returns the number of bytes required to decode a deinterlaced row. + fn line_size(&self, width: u32) -> usize { + use crate::common::ColorType::*; + let t = self.transform; + let info = get_info!(self); + let trns = info.trns.is_some(); + // TODO 16 bit + let bits = match info.color_type { + Indexed if trns && t.contains(crate::Transformations::EXPAND) => 4 * 8, + Indexed if t.contains(crate::Transformations::EXPAND) => 3 * 8, + RGB if trns && t.contains(crate::Transformations::EXPAND) => 4 * 8, + Grayscale if trns && t.contains(crate::Transformations::EXPAND) => 2 * 8, + Grayscale if t.contains(crate::Transformations::EXPAND) => 1 * 8, + GrayscaleAlpha if t.contains(crate::Transformations::EXPAND) => 2 * 8, + // divide by 2 as it will get mutiplied by two later + _ if info.bit_depth as u8 == 16 => info.bits_per_pixel() / 2, + _ => info.bits_per_pixel() + } + * width as usize + * if info.bit_depth as u8 == 16 { 2 } else { 1 }; + let len = bits / 8; + let extra = bits % 8; + len + match extra { 0 => 0, _ => 1 } + } + + fn allocate_out_buf(&mut self) -> Result<(), DecodingError> { + let width = get_info!(self).width; + let bytes = self.limits.bytes; + if bytes < self.line_size(width) { + return Err(DecodingError::LimitsExceeded); + } + self.processed = vec![0; self.line_size(width)]; + Ok(()) + } + + /// Returns the next raw row of the image + fn next_raw_interlaced_row(&mut self) -> Result<Option<(&[u8], Option<(u8, u32, u32)>)>, DecodingError> { + let _ = get_info!(self); + let bpp = self.bpp; + let (rowlen, passdata) = if let Some(ref mut adam7) = self.adam7 { + let last_pass = adam7.current_pass(); + if let Some((pass, line, len)) = adam7.next() { + let rowlen = get_info!(self).raw_row_length_from_width(len); + if last_pass != pass { + self.prev.clear(); + for _ in 0..rowlen { + self.prev.push(0); + } + } + (rowlen, Some((pass, line, len))) + } else { + return Ok(None) + } + } else { + (self.rowlen, None) + }; + loop { + if self.current.len() >= rowlen { + if let Some(filter) = FilterType::from_u8(self.current[0]) { + if let Err(message) = unfilter(filter, bpp, &self.prev[1..rowlen], &mut self.current[1..rowlen]) { + return Err(DecodingError::Format( + borrow::Cow::Borrowed(message) + )) + } + self.prev[..rowlen].copy_from_slice(&self.current[..rowlen]); + self.current.drain(0..rowlen); + return Ok( + Some(( + &self.prev[1..rowlen], + passdata + )) + ) + } else { + return Err(DecodingError::Format( + format!("invalid filter method ({})", self.current[0]).into() + )) + } + } else { + let val = self.decoder.decode_next(&mut self.current)?; + match val { + Some(Decoded::ImageData) => {} + None => { + if !self.current.is_empty() { + return Err(DecodingError::Format( + "file truncated".into() + )) + } else { + return Ok(None) + } + } + _ => () + } + } + } + } +} + +fn expand_paletted(buffer: &mut [u8], info: &Info) -> Result<(), DecodingError> { + if let Some(palette) = info.palette.as_ref() { + if let BitDepth::Sixteen = info.bit_depth { + Err(DecodingError::Format("Bit depth '16' is not valid for paletted images".into())) + } else { + let black = [0, 0, 0]; + if let Some(ref trns) = info.trns { + utils::unpack_bits(buffer, 4, info.bit_depth as u8, |i, chunk| { + let (rgb, a) = ( + palette.get(3*i as usize..3*i as usize+3).unwrap_or(&black), + *trns.get(i as usize).unwrap_or(&0xFF) + ); + chunk[0] = rgb[0]; + chunk[1] = rgb[1]; + chunk[2] = rgb[2]; + chunk[3] = a; + }); + } else { + utils::unpack_bits(buffer, 3, info.bit_depth as u8, |i, chunk| { + let rgb = palette.get(3*i as usize..3*i as usize+3).unwrap_or(&black); + chunk[0] = rgb[0]; + chunk[1] = rgb[1]; + chunk[2] = rgb[2]; + }) + } + Ok(()) + } + } else { + Err(DecodingError::Format("missing palette".into())) + } +} + +fn expand_gray_u8(buffer: &mut [u8], info: &Info) { + let rescale = true; + let scaling_factor = if rescale { + (255)/((1u16 << info.bit_depth as u8) - 1) as u8 + } else { + 1 + }; + if let Some(ref trns) = info.trns { + utils::unpack_bits(buffer, 2, info.bit_depth as u8, |pixel, chunk| { + if pixel == trns[0] { + chunk[1] = 0 + } else { + chunk[1] = 0xFF + } + chunk[0] = pixel * scaling_factor + }) + } else { + utils::unpack_bits(buffer, 1, info.bit_depth as u8, |val, chunk| { + chunk[0] = val * scaling_factor + }) + } +} diff --git a/third_party/rust/png/src/decoder/stream.rs b/third_party/rust/png/src/decoder/stream.rs new file mode 100644 index 0000000000..648a0275e5 --- /dev/null +++ b/third_party/rust/png/src/decoder/stream.rs @@ -0,0 +1,665 @@ +extern crate crc32fast; +extern crate inflate; + +use std::borrow::Cow; +use std::default::Default; +use std::error; +use std::fmt; +use std::io; +use std::cmp::min; +use std::convert::From; + +use crc32fast::Hasher as Crc32; + +use self::inflate::InflateStream; +use crate::traits::ReadBytesExt; +use crate::common::{BitDepth, BlendOp, ColorType, DisposeOp, Info, Unit, PixelDimensions, AnimationControl, FrameControl}; +use crate::chunk::{self, ChunkType, IHDR, IDAT, IEND}; + +/// TODO check if these size are reasonable +pub const CHUNCK_BUFFER_SIZE: usize = 32*1024; + +/// Determines if checksum checks should be disabled globally. +/// +/// This is used only in fuzzing. `afl` automatically adds `--cfg fuzzing` to RUSTFLAGS which can +/// be used to detect that build. +const CHECKSUM_DISABLED: bool = cfg!(fuzzing); + +fn zlib_stream() -> InflateStream { + if CHECKSUM_DISABLED { + InflateStream::from_zlib_no_checksum() + } else { + InflateStream::from_zlib() + } +} + +#[derive(Debug)] +enum U32Value { + // CHUNKS + Length, + Type(u32), + Crc(ChunkType) +} + +#[derive(Debug)] +enum State { + Signature(u8, [u8; 7]), + U32Byte3(U32Value, u32), + U32Byte2(U32Value, u32), + U32Byte1(U32Value, u32), + U32(U32Value), + ReadChunk(ChunkType, bool), + PartialChunk(ChunkType), + DecodeData(ChunkType, usize), +} + +#[derive(Debug)] +/// Result of the decoding process +pub enum Decoded { + /// Nothing decoded yet + Nothing, + Header(u32, u32, BitDepth, ColorType, bool), + ChunkBegin(u32, ChunkType), + ChunkComplete(u32, ChunkType), + PixelDimensions(PixelDimensions), + AnimationControl(AnimationControl), + FrameControl(FrameControl), + /// Decoded raw image data. + ImageData, + PartialChunk(ChunkType), + ImageEnd, +} + +#[derive(Debug)] +pub enum DecodingError { + IoError(io::Error), + Format(Cow<'static, str>), + InvalidSignature, + CrcMismatch { + /// bytes to skip to try to recover from this error + recover: usize, + /// Stored CRC32 value + crc_val: u32, + /// Calculated CRC32 sum + crc_sum: u32, + chunk: ChunkType + }, + Other(Cow<'static, str>), + CorruptFlateStream, + LimitsExceeded, +} + +impl error::Error for DecodingError { + fn description(&self) -> &str { + use self::DecodingError::*; + match *self { + IoError(ref err) => err.description(), + Format(ref desc) | Other(ref desc) => &desc, + InvalidSignature => "invalid signature", + CrcMismatch { .. } => "CRC error", + CorruptFlateStream => "compressed data stream corrupted", + LimitsExceeded => "limits are exceeded" + } + } +} + +impl fmt::Display for DecodingError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(fmt, "{}", (self as &dyn error::Error).description()) + } +} + +impl From<io::Error> for DecodingError { + fn from(err: io::Error) -> DecodingError { + DecodingError::IoError(err) + } +} + +impl From<String> for DecodingError { + fn from(err: String) -> DecodingError { + DecodingError::Other(err.into()) + } +} + +impl From<DecodingError> for io::Error { + fn from(err: DecodingError) -> io::Error { + use std::error::Error; + match err { + DecodingError::IoError(err) => err, + err => io::Error::new( + io::ErrorKind::Other, + err.description() + ) + } + } +} + +/// PNG StreamingDecoder (low-level interface) +pub struct StreamingDecoder { + state: Option<State>, + current_chunk: ChunkState, + inflater: InflateStream, + info: Option<Info>, + current_seq_no: Option<u32>, + have_idat: bool, +} + +struct ChunkState { + /// Partial crc until now. + crc: Crc32, + + /// Remanining bytes to be read. + remaining: u32, + + /// Non-decoded bytes in the chunk. + raw_bytes: Vec<u8>, +} + +impl StreamingDecoder { + /// Creates a new StreamingDecoder + /// + /// Allocates the internal buffers. + pub fn new() -> StreamingDecoder { + StreamingDecoder { + state: Some(State::Signature(0, [0; 7])), + current_chunk: ChunkState::default(), + inflater: zlib_stream(), + info: None, + current_seq_no: None, + have_idat: false + } + } + + /// Resets the StreamingDecoder + pub fn reset(&mut self) { + self.state = Some(State::Signature(0, [0; 7])); + self.current_chunk.crc = Crc32::new(); + self.current_chunk.remaining = 0; + self.current_chunk.raw_bytes.clear(); + self.inflater = zlib_stream(); + self.info = None; + self.current_seq_no = None; + self.have_idat = false; + } + + /// Low level StreamingDecoder interface. + /// + /// Allows to stream partial data to the encoder. Returns a tuple containing the bytes that have + /// been consumed from the input buffer and the current decoding result. If the decoded chunk + /// was an image data chunk, it also appends the read data to `image_data`. + pub fn update(&mut self, mut buf: &[u8], image_data: &mut Vec<u8>) + -> Result<(usize, Decoded), DecodingError> { + let len = buf.len(); + while !buf.is_empty() && self.state.is_some() { + match self.next_state(buf, image_data) { + Ok((bytes, Decoded::Nothing)) => { + buf = &buf[bytes..] + } + Ok((bytes, result)) => { + buf = &buf[bytes..]; + return Ok((len-buf.len(), result)); + } + Err(err) => return Err(err) + } + } + Ok((len-buf.len(), Decoded::Nothing)) + } + + fn next_state<'a>(&'a mut self, buf: &[u8], image_data: &mut Vec<u8>) + -> Result<(usize, Decoded), DecodingError> { + use self::State::*; + + macro_rules! goto ( + ($n:expr, $state:expr) => ({ + self.state = Some($state); + Ok(($n, Decoded::Nothing)) + }); + ($state:expr) => ({ + self.state = Some($state); + Ok((1, Decoded::Nothing)) + }); + ($n:expr, $state:expr, emit $res:expr) => ({ + self.state = Some($state); + Ok(($n, $res)) + }); + ($state:expr, emit $res:expr) => ({ + self.state = Some($state); + Ok((1, $res)) + }) + ); + + let current_byte = buf[0]; + + // Driver should ensure that state is never None + let state = self.state.take().unwrap(); + //println!("state: {:?}", state); + + match state { + Signature(i, mut signature) if i < 7 => { + signature[i as usize] = current_byte; + goto!(Signature(i+1, signature)) + } + Signature(_, signature) if signature == [137, 80, 78, 71, 13, 10, 26] && current_byte == 10 => { + goto!(U32(U32Value::Length)) + } + Signature(..) => Err(DecodingError::InvalidSignature), + U32Byte3(type_, mut val) => { + use self::U32Value::*; + val |= u32::from(current_byte); + match type_ { + Length => goto!(U32(Type(val))), + Type(length) => { + let type_str = [ + (val >> 24) as u8, + (val >> 16) as u8, + (val >> 8) as u8, + val as u8 + ]; + self.current_chunk.crc.reset(); + self.current_chunk.crc.update(&type_str); + self.current_chunk.remaining = length; + goto!( + ReadChunk(type_str, true), + emit Decoded::ChunkBegin(length, type_str) + ) + }, + Crc(type_str) => { + let sum = self.current_chunk.crc.clone().finalize(); + if CHECKSUM_DISABLED || val == sum { + goto!( + State::U32(U32Value::Length), + emit if type_str == IEND { + Decoded::ImageEnd + } else { + Decoded::ChunkComplete(val, type_str) + } + ) + } else { + Err(DecodingError::CrcMismatch { + recover: 1, + crc_val: val, + crc_sum: sum, + chunk: type_str + }) + } + }, + } + }, + U32Byte2(type_, val) => { + goto!(U32Byte3(type_, val | u32::from(current_byte) << 8)) + }, + U32Byte1(type_, val) => { + goto!(U32Byte2(type_, val | u32::from(current_byte) << 16)) + }, + U32(type_) => { + goto!(U32Byte1(type_, u32::from(current_byte) << 24)) + }, + PartialChunk(type_str) => { + match type_str { + IDAT => { + self.have_idat = true; + goto!( + 0, + DecodeData(type_str, 0), + emit Decoded::PartialChunk(type_str) + ) + }, + chunk::fdAT => { + if let Some(seq_no) = self.current_seq_no { + let mut buf = &self.current_chunk.raw_bytes[..]; + let next_seq_no = buf.read_be()?; + if next_seq_no != seq_no + 1 { + return Err(DecodingError::Format(format!( + "Sequence is not in order, expected #{} got #{}.", + seq_no + 1, + next_seq_no + ).into())) + } + self.current_seq_no = Some(next_seq_no); + } else { + return Err(DecodingError::Format("fcTL chunk missing before fdAT chunk.".into())) + } + goto!( + 0, + DecodeData(type_str, 4), + emit Decoded::PartialChunk(type_str) + ) + }, + // Handle other chunks + _ => { + if self.current_chunk.remaining == 0 { // complete chunk + Ok((0, self.parse_chunk(type_str)?)) + } else { + goto!( + 0, ReadChunk(type_str, true), + emit Decoded::PartialChunk(type_str) + ) + } + } + } + + }, + ReadChunk(type_str, clear) => { + if clear { + self.current_chunk.raw_bytes.clear(); + } + if self.current_chunk.remaining > 0 { + let ChunkState { crc, remaining, raw_bytes } = &mut self.current_chunk; + let buf_avail = raw_bytes.capacity() - raw_bytes.len(); + let bytes_avail = min(buf.len(), buf_avail); + let n = min(*remaining, bytes_avail as u32); + if buf_avail == 0 { + goto!(0, PartialChunk(type_str)) + } else { + let buf = &buf[..n as usize]; + crc.update(buf); + raw_bytes.extend_from_slice(buf); + *remaining -= n; + if *remaining == 0 { + goto!(n as usize, PartialChunk(type_str + )) + } else { + goto!(n as usize, ReadChunk(type_str, false)) + } + + } + } else { + goto!(0, U32(U32Value::Crc(type_str))) + } + } + DecodeData(type_str, mut n) => { + let chunk_len = self.current_chunk.raw_bytes.len(); + let chunk_data = &self.current_chunk.raw_bytes[n..]; + let (c, data) = self.inflater.update(chunk_data)?; + image_data.extend_from_slice(data); + n += c; + if n == chunk_len && data.is_empty() && c == 0 { + goto!( + 0, + ReadChunk(type_str, true), + emit Decoded::ImageData + ) + } else { + goto!( + 0, + DecodeData(type_str, n), + emit Decoded::ImageData + ) + } + } + } + } + + fn parse_chunk(&mut self, type_str: [u8; 4]) + -> Result<Decoded, DecodingError> { + self.state = Some(State::U32(U32Value::Crc(type_str))); + if self.info.is_none() && type_str != IHDR { + return Err(DecodingError::Format(format!( + "{} chunk appeared before IHDR chunk", String::from_utf8_lossy(&type_str) + ).into())) + } + match match type_str { + IHDR => { + self.parse_ihdr() + } + chunk::PLTE => { + self.parse_plte() + } + chunk::tRNS => { + self.parse_trns() + } + chunk::pHYs => { + self.parse_phys() + } + chunk::acTL => { + self.parse_actl() + } + chunk::fcTL => { + self.parse_fctl() + } + _ => Ok(Decoded::PartialChunk(type_str)) + } { + Err(err) =>{ + // Borrow of self ends here, because Decoding error does not borrow self. + self.state = None; + Err(err) + }, + ok => ok + } + } + + fn get_info_or_err(&self) -> Result<&Info, DecodingError> { + self.info.as_ref().ok_or_else(|| DecodingError::Format( + "IHDR chunk missing".into() + )) + } + + fn parse_fctl(&mut self) + -> Result<Decoded, DecodingError> { + let mut buf = &self.current_chunk.raw_bytes[..]; + let next_seq_no = buf.read_be()?; + + // Asuming that fcTL is required before *every* fdAT-sequence + self.current_seq_no = Some(if let Some(seq_no) = self.current_seq_no { + if next_seq_no != seq_no + 1 { + return Err(DecodingError::Format(format!( + "Sequence is not in order, expected #{} got #{}.", + seq_no + 1, + next_seq_no + ).into())) + } + next_seq_no + } else { + if next_seq_no != 0 { + return Err(DecodingError::Format(format!( + "Sequence is not in order, expected #{} got #{}.", + 0, + next_seq_no + ).into())) + } + 0 + }); + self.inflater = zlib_stream(); + let fc = FrameControl { + sequence_number: next_seq_no, + width: buf.read_be()?, + height: buf.read_be()?, + x_offset: buf.read_be()?, + y_offset: buf.read_be()?, + delay_num: buf.read_be()?, + delay_den: buf.read_be()?, + dispose_op: match DisposeOp::from_u8(buf.read_be()?) { + Some(dispose_op) => dispose_op, + None => return Err(DecodingError::Format("invalid dispose operation".into())) + }, + blend_op : match BlendOp::from_u8(buf.read_be()?) { + Some(blend_op) => blend_op, + None => return Err(DecodingError::Format("invalid blend operation".into())) + }, + }; + self.info.as_mut().unwrap().frame_control = Some(fc); + Ok(Decoded::FrameControl(fc)) + } + + fn parse_actl(&mut self) + -> Result<Decoded, DecodingError> { + if self.have_idat { + Err(DecodingError::Format( + "acTL chunk appeared after first IDAT chunk".into() + )) + } else { + let mut buf = &self.current_chunk.raw_bytes[..]; + let actl = AnimationControl { + num_frames: buf.read_be()?, + num_plays: buf.read_be()? + }; + self.info.as_mut().unwrap().animation_control = Some(actl); + Ok(Decoded::AnimationControl(actl)) + } + } + + fn parse_plte(&mut self) + -> Result<Decoded, DecodingError> { + if let Some(info) = self.info.as_mut() { + info.palette = Some(self.current_chunk.raw_bytes.clone()) + } + Ok(Decoded::Nothing) + } + + fn parse_trns(&mut self) + -> Result<Decoded, DecodingError> { + use crate::common::ColorType::*; + let (color_type, bit_depth) = { + let info = self.get_info_or_err()?; + (info.color_type, info.bit_depth as u8) + }; + let vec = self.current_chunk.raw_bytes.clone(); + let len = vec.len(); + let info = match self.info { + Some(ref mut info) => info, + None => return Err(DecodingError::Format( + "tRNS chunk occured before IHDR chunk".into() + )) + }; + info.trns = Some(vec); + let vec = info.trns.as_mut().unwrap(); + match color_type { + Grayscale => { + if len < 2 { + return Err(DecodingError::Format( + "not enough palette entries".into() + )) + } + if bit_depth < 16 { + vec[0] = vec[1]; + vec.truncate(1); + } + Ok(Decoded::Nothing) + }, + RGB => { + if len < 6 { + return Err(DecodingError::Format( + "not enough palette entries".into() + )) + } + if bit_depth < 16 { + vec[0] = vec[1]; + vec[1] = vec[3]; + vec[2] = vec[5]; + vec.truncate(3); + } + Ok(Decoded::Nothing) + }, + Indexed => { + let _ = info.palette.as_ref().ok_or_else(|| DecodingError::Format( + "tRNS chunk occured before PLTE chunk".into() + )); + Ok(Decoded::Nothing) + }, + c => Err(DecodingError::Format( + format!("tRNS chunk found for color type ({})", c as u8).into() + )) + } + + } + + fn parse_phys(&mut self) + -> Result<Decoded, DecodingError> { + if self.have_idat { + Err(DecodingError::Format( + "pHYs chunk appeared after first IDAT chunk".into() + )) + } else { + let mut buf = &self.current_chunk.raw_bytes[..]; + let xppu = buf.read_be()?; + let yppu = buf.read_be()?; + let unit = buf.read_be()?; + let unit = match Unit::from_u8(unit) { + Some(unit) => unit, + None => return Err(DecodingError::Format( + format!("invalid unit ({})", unit).into() + )) + }; + let pixel_dims = PixelDimensions { + xppu, + yppu, + unit, + }; + self.info.as_mut().unwrap().pixel_dims = Some(pixel_dims); + Ok(Decoded::PixelDimensions(pixel_dims)) + } + } + + fn parse_ihdr(&mut self) + -> Result<Decoded, DecodingError> { + // TODO: check if color/bit depths combination is valid + let mut buf = &self.current_chunk.raw_bytes[..]; + let width = buf.read_be()?; + let height = buf.read_be()?; + let bit_depth = buf.read_be()?; + let bit_depth = match BitDepth::from_u8(bit_depth) { + Some(bits) => bits, + None => return Err(DecodingError::Format( + format!("invalid bit depth ({})", bit_depth).into() + )) + }; + let color_type = buf.read_be()?; + let color_type = match ColorType::from_u8(color_type) { + Some(color_type) => color_type, + None => return Err(DecodingError::Format( + format!("invalid color type ({})", color_type).into() + )) + }; + match buf.read_be()? { // compression method + 0u8 => (), + n => return Err(DecodingError::Format( + format!("unknown compression method ({})", n).into() + )) + } + match buf.read_be()? { // filter method + 0u8 => (), + n => return Err(DecodingError::Format( + format!("unknown filter method ({})", n).into() + )) + } + let interlaced = match buf.read_be()? { + 0u8 => false, + 1 => { + true + }, + n => return Err(DecodingError::Format( + format!("unknown interlace method ({})", n).into() + )) + }; + let mut info = Info::default(); + + info.width = width; + info.height = height; + info.bit_depth = bit_depth; + info.color_type = color_type; + info.interlaced = interlaced; + self.info = Some(info); + Ok(Decoded::Header( + width, + height, + bit_depth, + color_type, + interlaced + )) + } +} + +impl Default for ChunkState { + fn default() -> Self { + ChunkState { + crc:Crc32::new(), + remaining: 0, + raw_bytes: Vec::with_capacity(CHUNCK_BUFFER_SIZE), + } + } +} + +#[inline(always)] +pub fn get_info(d: &StreamingDecoder) -> Option<&Info> { + d.info.as_ref() +} diff --git a/third_party/rust/png/src/encoder.rs b/third_party/rust/png/src/encoder.rs new file mode 100644 index 0000000000..ffa6629ec5 --- /dev/null +++ b/third_party/rust/png/src/encoder.rs @@ -0,0 +1,477 @@ +extern crate crc32fast; +extern crate deflate; + +use std::borrow::Cow; +use std::error; +use std::fmt; +use std::io::{self, Read, Write}; +use std::mem; +use std::result; + +use crc32fast::Hasher as Crc32; + +use crate::chunk; +use crate::common::{Info, ColorType, BitDepth, Compression}; +use crate::filter::{FilterType, filter}; +use crate::traits::WriteBytesExt; + +pub type Result<T> = result::Result<T, EncodingError>; + +#[derive(Debug)] +pub enum EncodingError { + IoError(io::Error), + Format(Cow<'static, str>), +} + +impl error::Error for EncodingError { + fn description(&self) -> &str { + use self::EncodingError::*; + match *self { + IoError(ref err) => err.description(), + Format(ref desc) => &desc, + } + } +} + +impl fmt::Display for EncodingError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { + write!(fmt, "{}", (self as &dyn error::Error).description()) + } +} + +impl From<io::Error> for EncodingError { + fn from(err: io::Error) -> EncodingError { + EncodingError::IoError(err) + } +} +impl From<EncodingError> for io::Error { + fn from(err: EncodingError) -> io::Error { + io::Error::new(io::ErrorKind::Other, (&err as &dyn error::Error).description()) + } +} + +/// PNG Encoder +pub struct Encoder<W: Write> { + w: W, + info: Info, +} + +impl<W: Write> Encoder<W> { + pub fn new(w: W, width: u32, height: u32) -> Encoder<W> { + let mut info = Info::default(); + info.width = width; + info.height = height; + Encoder { w, info } + } + + pub fn write_header(self) -> Result<Writer<W>> { + Writer::new(self.w, self.info).init() + } + + /// Set the color of the encoded image. + /// + /// These correspond to the color types in the png IHDR data that will be written. The length + /// of the image data that is later supplied must match the color type, otherwise an error will + /// be emitted. + pub fn set_color(&mut self, color: ColorType) { + self.info.color_type = color; + } + + /// Set the indicated depth of the image data. + pub fn set_depth(&mut self, depth: BitDepth) { + self.info.bit_depth = depth; + } + + /// Set compression parameters. + /// + /// Accepts a `Compression` or any type that can transform into a `Compression`. Notably `deflate::Compression` and + /// `deflate::CompressionOptions` which "just work". + pub fn set_compression<C: Into<Compression>>(&mut self, compression: C) { + self.info.compression = compression.into(); + } + + /// Set the used filter type. + /// + /// The default filter is [`FilterType::Sub`] which provides a basic prediction algorithm for + /// sample values based on the previous. For a potentially better compression ratio, at the + /// cost of more complex processing, try out [`FilterType::Paeth`]. + /// + /// [`FilterType::Sub`]: enum.FilterType.html#variant.Sub + /// [`FilterType::Paeth`]: enum.FilterType.html#variant.Paeth + pub fn set_filter(&mut self, filter: FilterType) { + self.info.filter = filter; + } +} + +/// PNG writer +pub struct Writer<W: Write> { + w: W, + info: Info, +} + +impl<W: Write> Writer<W> { + fn new(w: W, info: Info) -> Writer<W> { + Writer { w, info } + } + + fn init(mut self) -> Result<Self> { + if self.info.width == 0 { + return Err(EncodingError::Format("Zero width not allowed".into())); + } + + if self.info.height == 0 { + return Err(EncodingError::Format("Zero height not allowed".into())); + } + + self.w.write_all(&[137, 80, 78, 71, 13, 10, 26, 10])?; + let mut data = [0; 13]; + (&mut data[..]).write_be(self.info.width)?; + (&mut data[4..]).write_be(self.info.height)?; + data[8] = self.info.bit_depth as u8; + data[9] = self.info.color_type as u8; + data[12] = if self.info.interlaced { 1 } else { 0 }; + self.write_chunk(chunk::IHDR, &data)?; + Ok(self) + } + + pub fn write_chunk(&mut self, name: [u8; 4], data: &[u8]) -> Result<()> { + self.w.write_be(data.len() as u32)?; + self.w.write_all(&name)?; + self.w.write_all(data)?; + let mut crc = Crc32::new(); + crc.update(&name); + crc.update(data); + self.w.write_be(crc.finalize())?; + Ok(()) + } + + /// Writes the image data. + pub fn write_image_data(&mut self, data: &[u8]) -> Result<()> { + const MAX_CHUNK_LEN: u32 = (1u32 << 31) - 1; + let bpp = self.info.bytes_per_pixel(); + let in_len = self.info.raw_row_length() - 1; + let mut prev = vec![0; in_len]; + let mut current = vec![0; in_len]; + let data_size = in_len * self.info.height as usize; + if data_size != data.len() { + let message = format!("wrong data size, expected {} got {}", data_size, data.len()); + return Err(EncodingError::Format(message.into())); + } + let mut zlib = deflate::write::ZlibEncoder::new(Vec::new(), self.info.compression.clone()); + let filter_method = self.info.filter; + for line in data.chunks(in_len) { + current.copy_from_slice(&line); + zlib.write_all(&[filter_method as u8])?; + filter(filter_method, bpp, &prev, &mut current); + zlib.write_all(¤t)?; + mem::swap(&mut prev, &mut current); + } + let zlib_encoded = zlib.finish()?; + for chunk in zlib_encoded.chunks(MAX_CHUNK_LEN as usize) { + self.write_chunk(chunk::IDAT, &chunk)?; + } + Ok(()) + } + + /// Create an stream writer. + /// + /// This allows you create images that do not fit + /// in memory. The default chunk size is 4K, use + /// `stream_writer_with_size` to set another chuck + /// size. + pub fn stream_writer(&mut self) -> StreamWriter<W> { + self.stream_writer_with_size(4 * 1024) + } + + /// Create a stream writer with custom buffer size. + /// + /// See `stream_writer` + pub fn stream_writer_with_size(&mut self, size: usize) -> StreamWriter<W> { + StreamWriter::new(self, size) + } +} + +impl<W: Write> Drop for Writer<W> { + fn drop(&mut self) { + let _ = self.write_chunk(chunk::IEND, &[]); + } +} + +struct ChunkWriter<'a, W: Write> { + writer: &'a mut Writer<W>, + buffer: Vec<u8>, + index: usize, +} + +impl<'a, W: Write> ChunkWriter<'a, W> { + fn new(writer: &'a mut Writer<W>, buf_len: usize) -> ChunkWriter<'a, W> { + ChunkWriter { + writer, + buffer: vec![0; buf_len], + index: 0, + } + } +} + +impl<'a, W: Write> Write for ChunkWriter<'a, W> { + fn write(&mut self, mut buf: &[u8]) -> io::Result<usize> { + let written = buf.read(&mut self.buffer[self.index..])?; + self.index += written; + + if self.index + 1 >= self.buffer.len() { + self.writer.write_chunk(chunk::IDAT, &self.buffer)?; + self.index = 0; + } + + Ok(written) + } + + fn flush(&mut self) -> io::Result<()> { + if self.index > 0 { + self.writer.write_chunk(chunk::IDAT, &self.buffer[..=self.index])?; + } + self.index = 0; + Ok(()) + } +} + +impl<'a, W: Write> Drop for ChunkWriter<'a, W> { + fn drop(&mut self) { + let _ = self.flush(); + } +} + + +/// Streaming png writer +/// +/// This may silently fail in the destructor, so it is a good idea to call +/// [`finish`](#method.finish) or [`flush`](https://doc.rust-lang.org/stable/std/io/trait.Write.html#tymethod.flush) before dropping. +pub struct StreamWriter<'a, W: Write> { + writer: deflate::write::ZlibEncoder<ChunkWriter<'a, W>>, + prev_buf: Vec<u8>, + curr_buf: Vec<u8>, + index: usize, + bpp: usize, + filter: FilterType, +} + +impl<'a, W: Write> StreamWriter<'a, W> { + fn new(writer: &'a mut Writer<W>, buf_len: usize) -> StreamWriter<'a, W> { + let bpp = writer.info.bytes_per_pixel(); + let in_len = writer.info.raw_row_length() - 1; + let filter = writer.info.filter; + let prev_buf = vec![0; in_len]; + let curr_buf = vec![0; in_len]; + + let compression = writer.info.compression.clone(); + let chunk_writer = ChunkWriter::new(writer, buf_len); + let zlib = deflate::write::ZlibEncoder::new(chunk_writer, compression); + + StreamWriter { + writer: zlib, + index: 0, + prev_buf, + curr_buf, + bpp, + filter, + } + } + + pub fn finish(mut self) -> Result<()> { + // TODO: call `writer.finish` somehow? + self.flush()?; + Ok(()) + } +} + +impl<'a, W: Write> Write for StreamWriter<'a, W> { + fn write(&mut self, mut buf: &[u8]) -> io::Result<usize> { + let written = buf.read(&mut self.curr_buf[self.index..])?; + self.index += written; + + if self.index >= self.curr_buf.len() { + self.writer.write_all(&[self.filter as u8])?; + filter(self.filter, self.bpp, &self.prev_buf, &mut self.curr_buf); + self.writer.write_all(&self.curr_buf)?; + mem::swap(&mut self.prev_buf, &mut self.curr_buf); + self.index = 0; + } + + Ok(written) + } + + fn flush(&mut self) -> io::Result<()> { + self.writer.flush()?; + if self.index > 0 { + let message = format!("wrong data size, got {} bytes too many", self.index); + return Err(EncodingError::Format(message.into()).into()); + } + Ok(()) + } +} + +impl<'a, W: Write> Drop for StreamWriter<'a, W> { + fn drop(&mut self) { + let _ = self.flush(); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + extern crate glob; + + use rand::{thread_rng, Rng}; + use std::{io, cmp}; + use std::io::Write; + use std::fs::File; + + #[test] + fn roundtrip() { + // More loops = more random testing, but also more test wait time + for _ in 0..10 { + for path in glob::glob("tests/pngsuite/*.png").unwrap().map(|r| r.unwrap()) { + if path.file_name().unwrap().to_str().unwrap().starts_with("x") { + // x* files are expected to fail to decode + continue; + } + // Decode image + let decoder = crate::Decoder::new(File::open(path).unwrap()); + let (info, mut reader) = decoder.read_info().unwrap(); + if info.line_size != 32 { + // TODO encoding only works with line size 32? + continue; + } + let mut buf = vec![0; info.buffer_size()]; + reader.next_frame(&mut buf).unwrap(); + // Encode decoded image + let mut out = Vec::new(); + { + let mut wrapper = RandomChunkWriter { + rng: thread_rng(), + w: &mut out + }; + + let mut encoder = Encoder::new(&mut wrapper, info.width, info.height).write_header().unwrap(); + encoder.write_image_data(&buf).unwrap(); + } + // Decode encoded decoded image + let decoder = crate::Decoder::new(&*out); + let (info, mut reader) = decoder.read_info().unwrap(); + let mut buf2 = vec![0; info.buffer_size()]; + reader.next_frame(&mut buf2).unwrap(); + // check if the encoded image is ok: + assert_eq!(buf, buf2); + } + } + } + + #[test] + fn roundtrip_stream() { + // More loops = more random testing, but also more test wait time + for _ in 0..10 { + for path in glob::glob("tests/pngsuite/*.png").unwrap().map(|r| r.unwrap()) { + if path.file_name().unwrap().to_str().unwrap().starts_with("x") { + // x* files are expected to fail to decode + continue; + } + // Decode image + let decoder = crate::Decoder::new(File::open(path).unwrap()); + let (info, mut reader) = decoder.read_info().unwrap(); + if info.line_size != 32 { + // TODO encoding only works with line size 32? + continue; + } + let mut buf = vec![0; info.buffer_size()]; + reader.next_frame(&mut buf).unwrap(); + // Encode decoded image + let mut out = Vec::new(); + { + let mut wrapper = RandomChunkWriter { + rng: thread_rng(), + w: &mut out + }; + + let mut encoder = Encoder::new(&mut wrapper, info.width, info.height).write_header().unwrap(); + let mut stream_writer = encoder.stream_writer(); + + let mut outer_wrapper = RandomChunkWriter { + rng: thread_rng(), + w: &mut stream_writer + }; + + outer_wrapper.write_all(&buf).unwrap(); + } + // Decode encoded decoded image + let decoder = crate::Decoder::new(&*out); + let (info, mut reader) = decoder.read_info().unwrap(); + let mut buf2 = vec![0; info.buffer_size()]; + reader.next_frame(&mut buf2).unwrap(); + // check if the encoded image is ok: + assert_eq!(buf, buf2); + } + } + } + + #[test] + fn expect_error_on_wrong_image_len() -> Result<()> { + use std::io::Cursor; + + let width = 10; + let height = 10; + + let output = vec![0u8; 1024]; + let writer = Cursor::new(output); + let mut encoder = Encoder::new(writer, width as u32, height as u32); + encoder.set_depth(BitDepth::Eight); + encoder.set_color(ColorType::RGB); + let mut png_writer = encoder.write_header()?; + + let correct_image_size = width * height * 3; + let image = vec![0u8; correct_image_size + 1]; + let result = png_writer.write_image_data(image.as_ref()); + assert!(result.is_err()); + + Ok(()) + } + + #[test] + fn expect_error_on_empty_image() -> Result<()> { + use std::io::Cursor; + + let output = vec![0u8; 1024]; + let mut writer = Cursor::new(output); + + let encoder = Encoder::new(&mut writer, 0, 0); + assert!(encoder.write_header().is_err()); + + let encoder = Encoder::new(&mut writer, 100, 0); + assert!(encoder.write_header().is_err()); + + let encoder = Encoder::new(&mut writer, 0, 100); + assert!(encoder.write_header().is_err()); + + Ok(()) + } + + /// A Writer that only writes a few bytes at a time + struct RandomChunkWriter<'a, R: Rng, W: Write + 'a> { + rng: R, + w: &'a mut W + } + + impl<'a, R: Rng, W: Write + 'a> Write for RandomChunkWriter<'a, R, W> { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + // choose a random length to write + let len = cmp::min(self.rng.gen_range(1, 50), buf.len()); + + self.w.write(&buf[0..len]) + } + + fn flush(&mut self) -> io::Result<()> { + self.w.flush() + } + } + +} diff --git a/third_party/rust/png/src/filter.rs b/third_party/rust/png/src/filter.rs new file mode 100644 index 0000000000..4f81d8e9f3 --- /dev/null +++ b/third_party/rust/png/src/filter.rs @@ -0,0 +1,158 @@ +use std; + +/// The byte level filter applied to scanlines to prepare them for compression. +/// +/// Compression in general benefits from repetitive data. The filter is a content-aware method of +/// compressing the range of occurring byte values to help the compression algorithm. Note that +/// this does not operate on pixels but on raw bytes of a scanline. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +pub enum FilterType { + NoFilter = 0, + Sub = 1, + Up = 2, + Avg = 3, + Paeth = 4 +} + + impl FilterType { + /// u8 -> Self. Temporary solution until Rust provides a canonical one. + pub fn from_u8(n: u8) -> Option<FilterType> { + match n { + 0 => Some(FilterType::NoFilter), + 1 => Some(FilterType::Sub), + 2 => Some(FilterType::Up), + 3 => Some(FilterType::Avg), + 4 => Some(FilterType::Paeth), + _ => None + } + } +} + +fn filter_paeth(a: u8, b: u8, c: u8) -> u8 { + let ia = i16::from(a); + let ib = i16::from(b); + let ic = i16::from(c); + + let p = ia + ib - ic; + + let pa = (p - ia).abs(); + let pb = (p - ib).abs(); + let pc = (p - ic).abs(); + + if pa <= pb && pa <= pc { + a + } else if pb <= pc { + b + } else { + c + } +} + +pub fn unfilter(filter: FilterType, bpp: usize, previous: &[u8], current: &mut [u8]) -> std::result::Result<(), &'static str> { + use self::FilterType::*; + assert!(bpp > 0); + let len = current.len(); + + match filter { + NoFilter => Ok(()), + Sub => { + for i in bpp..len { + current[i] = current[i].wrapping_add( + current[i - bpp] + ); + } + Ok(()) + } + Up => { + if previous.len() < len { + Err("Filtering failed: not enough data in previous row") + } else { + for i in 0..len { + current[i] = current[i].wrapping_add( + previous[i] + ); + } + Ok(()) + } + } + Avg => { + if previous.len() < len { + Err("Filtering failed: not enough data in previous row") + } else if bpp > len { + Err("Filtering failed: bytes per pixel is greater than length of row") + } else { + for i in 0..bpp { + current[i] = current[i].wrapping_add( + previous[i] / 2 + ); + } + + for i in bpp..len { + current[i] = current[i].wrapping_add( + ((i16::from(current[i - bpp]) + i16::from(previous[i])) / 2) as u8 + ); + } + Ok(()) + } + } + Paeth => { + if previous.len() < len { + Err("Filtering failed: not enough data in previous row") + } else if bpp > len { + Err("Filtering failed: bytes per pixel is greater than length of row") + } else { + for i in 0..bpp { + current[i] = current[i].wrapping_add( + filter_paeth(0, previous[i], 0) + ); + } + + for i in bpp..len { + current[i] = current[i].wrapping_add( + filter_paeth(current[i - bpp], previous[i], previous[i - bpp]) + ); + } + Ok(()) + } + } + } +} + +pub fn filter(method: FilterType, bpp: usize, previous: &[u8], current: &mut [u8]) { + use self::FilterType::*; + assert!(bpp > 0); + let len = current.len(); + + match method { + NoFilter => (), + Sub => { + for i in (bpp..len).rev() { + current[i] = current[i].wrapping_sub(current[i - bpp]); + } + } + Up => { + for i in 0..len { + current[i] = current[i].wrapping_sub(previous[i]); + } + } + Avg => { + for i in (bpp..len).rev() { + current[i] = current[i].wrapping_sub(current[i - bpp].wrapping_add(previous[i]) / 2); + } + + for i in 0..bpp { + current[i] = current[i].wrapping_sub(previous[i] / 2); + } + } + Paeth => { + for i in (bpp..len).rev() { + current[i] = current[i].wrapping_sub(filter_paeth(current[i - bpp], previous[i], previous[i - bpp])); + } + + for i in 0..bpp { + current[i] = current[i].wrapping_sub(filter_paeth(0, previous[i], 0)); + } + } + } +} diff --git a/third_party/rust/png/src/lib.rs b/third_party/rust/png/src/lib.rs new file mode 100644 index 0000000000..15e9ec3b15 --- /dev/null +++ b/third_party/rust/png/src/lib.rs @@ -0,0 +1,61 @@ +//! # PNG encoder and decoder +//! This crate contains a PNG encoder and decoder. It supports reading of single lines or whole frames. +//! ## The decoder +//! The most important types for decoding purposes are [`Decoder`](struct.Decoder.html) and +//! [`Reader`](struct.Reader.html). They both wrap a `std::io::Read`. +//! `Decoder` serves as a builder for `Reader`. Calling `Decoder::read_info` reads from the `Read` until the +//! image data is reached. +//! ### Using the decoder +//! use std::fs::File; +//! +//! // The decoder is a build for reader and can be used to set various decoding options +//! // via `Transformations`. The default output transformation is `Transformations::EXPAND +//! // | Transformations::STRIP_ALPHA`. +//! let decoder = png::Decoder::new(File::open("tests/pngsuite/basi0g01.png").unwrap()); +//! let (info, mut reader) = decoder.read_info().unwrap(); +//! // Allocate the output buffer. +//! let mut buf = vec![0; info.buffer_size()]; +//! // Read the next frame. Currently this function should only called once. +//! // The default options +//! reader.next_frame(&mut buf).unwrap(); +//! ## Encoder +//! ### Using the encoder +//! ```no_run +//! # #[cfg(feature = "png-encoding")] { +//! // For reading and opening files +//! use std::path::Path; +//! use std::fs::File; +//! use std::io::BufWriter; +//! +//! let path = Path::new(r"/path/to/image.png"); +//! let file = File::create(path).unwrap(); +//! let ref mut w = BufWriter::new(file); +//! +//! let mut encoder = png::Encoder::new(w, 2, 1); // Width is 2 pixels and height is 1. +//! encoder.set_color(png::ColorType::RGBA); +//! encoder.set_depth(png::BitDepth::Eight); +//! let mut writer = encoder.write_header().unwrap(); +//! +//! let data = [255, 0, 0, 255, 0, 0, 0, 255]; // An array containing a RGBA sequence. First pixel is red and second pixel is black. +//! writer.write_image_data(&data).unwrap(); // Save +//! # } +//! ``` +//! +//#![cfg_attr(test, feature(test))] + +#[macro_use] extern crate bitflags; + +pub mod chunk; +mod decoder; +#[cfg(feature = "png-encoding")] +mod encoder; +mod filter; +mod traits; +mod common; +mod utils; + +pub use crate::common::*; +pub use crate::decoder::{Decoder, Reader, OutputInfo, StreamingDecoder, Decoded, DecodingError, Limits}; +#[cfg(feature = "png-encoding")] +pub use crate::encoder::{Encoder, Writer, StreamWriter, EncodingError}; +pub use crate::filter::FilterType; diff --git a/third_party/rust/png/src/traits.rs b/third_party/rust/png/src/traits.rs new file mode 100644 index 0000000000..fac36f3699 --- /dev/null +++ b/third_party/rust/png/src/traits.rs @@ -0,0 +1,72 @@ +use std::io; + +// Will be replaced by stdlib solution +fn read_all<R: io::Read + ?Sized>(this: &mut R, buf: &mut [u8]) -> io::Result<()> { + let mut total = 0; + while total < buf.len() { + match this.read(&mut buf[total..]) { + Ok(0) => return Err(io::Error::new(io::ErrorKind::Other, + "failed to read the whole buffer")), + Ok(n) => total += n, + Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } + Ok(()) +} + +/// Read extension to read big endian data +pub trait ReadBytesExt<T>: io::Read { + /// Read `T` from a bytes stream. Most significant byte first. + fn read_be(&mut self) -> io::Result<T>; + +} + +/// Write extension to write big endian data +pub trait WriteBytesExt<T>: io::Write { + /// Writes `T` to a bytes stream. Most significant byte first. + fn write_be(&mut self, _: T) -> io::Result<()>; + +} + +impl<W: io::Read + ?Sized> ReadBytesExt<u8> for W { + #[inline] + fn read_be(&mut self) -> io::Result<u8> { + let mut byte = [0]; + read_all(self, &mut byte)?; + Ok(byte[0]) + } +} +impl<W: io::Read + ?Sized> ReadBytesExt<u16> for W { + #[inline] + fn read_be(&mut self) -> io::Result<u16> { + let mut bytes = [0, 0]; + read_all(self, &mut bytes)?; + Ok((u16::from(bytes[0])) << 8 | u16::from(bytes[1])) + } +} + +impl<W: io::Read + ?Sized> ReadBytesExt<u32> for W { + #[inline] + fn read_be(&mut self) -> io::Result<u32> { + let mut bytes = [0, 0, 0, 0]; + read_all(self, &mut bytes)?; + Ok( (u32::from(bytes[0])) << 24 + | (u32::from(bytes[1])) << 16 + | (u32::from(bytes[2])) << 8 + | u32::from(bytes[3]) + ) + } +} + +impl<W: io::Write + ?Sized> WriteBytesExt<u32> for W { + #[inline] + fn write_be(&mut self, n: u32) -> io::Result<()> { + self.write_all(&[ + (n >> 24) as u8, + (n >> 16) as u8, + (n >> 8) as u8, + n as u8 + ]) + } +} diff --git a/third_party/rust/png/src/utils.rs b/third_party/rust/png/src/utils.rs new file mode 100644 index 0000000000..1e91629dcf --- /dev/null +++ b/third_party/rust/png/src/utils.rs @@ -0,0 +1,376 @@ +//! Utility functions +use std::iter::{repeat, StepBy}; +use std::ops::Range; + +#[inline(always)] +pub fn unpack_bits<F>(buf: &mut [u8], channels: usize, bit_depth: u8, func: F) +where F: Fn(u8, &mut[u8]) { + // Return early if empty. This enables to subtract `channels` later without overflow. + if buf.len() < channels { + return; + } + + let bits = buf.len()/channels*bit_depth as usize; + let extra_bits = bits % 8; + let entries = bits / 8 + match extra_bits { + 0 => 0, + _ => 1 + }; + let skip = match extra_bits { + 0 => 0, + n => (8-n) / bit_depth as usize + }; + let mask = ((1u16 << bit_depth) - 1) as u8; + let i = + (0..entries) + .rev() // reverse iterator + .flat_map(|idx| + // this has to be reversed too + (0..8).step_by(bit_depth.into()) + .zip(repeat(idx)) + ) + .skip(skip); + let j = (0..=buf.len() - channels).rev().step_by(channels); + for ((shift, i), j) in i.zip(j) { + let pixel = (buf[i] & (mask << shift)) >> shift; + func(pixel, &mut buf[j..(j + channels)]) + } +} + +pub fn expand_trns_line(buf: &mut[u8], trns: &[u8], channels: usize) { + // Return early if empty. This enables to subtract `channels` later without overflow. + if buf.len() < (channels+1) { + return; + } + + let i = (0..=buf.len() / (channels+1) * channels - channels).rev().step_by(channels); + let j = (0..=buf.len() - (channels+1)).rev().step_by(channels+1); + for (i, j) in i.zip(j) { + let i_pixel = i; + let j_chunk = j; + if &buf[i_pixel..i_pixel+channels] == trns { + buf[j_chunk+channels] = 0 + } else { + buf[j_chunk+channels] = 0xFF + } + for k in (0..channels).rev() { + buf[j_chunk+k] = buf[i_pixel+k]; + } + } +} + +pub fn expand_trns_line16(buf: &mut[u8], trns: &[u8], channels: usize) { + let c2 = 2 * channels; + // Return early if empty. This enables to subtract `channels` later without overflow. + if buf.len() < (c2+2) { + return; + } + + let i = (0..=buf.len() / (c2+2) * c2 - c2).rev().step_by(c2); + let j = (0..=buf.len() - (c2+2)).rev().step_by(c2+2); + for (i, j) in i.zip(j) { + let i_pixel = i; + let j_chunk = j; + if &buf[i_pixel..i_pixel+c2] == trns { + buf[j_chunk+c2] = 0; + buf[j_chunk+c2 + 1] = 0 + } else { + buf[j_chunk+c2] = 0xFF; + buf[j_chunk+c2 + 1] = 0xFF + } + for k in (0..c2).rev() { + buf[j_chunk+k] = buf[i_pixel+k]; + } + } +} + + +/// This iterator iterates over the different passes of an image Adam7 encoded +/// PNG image +/// The pattern is: +/// 16462646 +/// 77777777 +/// 56565656 +/// 77777777 +/// 36463646 +/// 77777777 +/// 56565656 +/// 77777777 +/// +#[derive(Clone)] +pub struct Adam7Iterator { + line: u32, + lines: u32, + line_width: u32, + current_pass: u8, + width: u32, + height: u32, +} + +impl Adam7Iterator { + pub fn new(width: u32, height: u32) -> Adam7Iterator { + let mut this = Adam7Iterator { + line: 0, + lines: 0, + line_width: 0, + current_pass: 1, + width, + height, + }; + this.init_pass(); + this + } + + /// Calculates the bounds of the current pass + fn init_pass(&mut self) { + let w = f64::from(self.width); + let h = f64::from(self.height); + let (line_width, lines) = match self.current_pass { + 1 => (w/8.0, h/8.0), + 2 => ((w-4.0)/8.0, h/8.0), + 3 => (w/4.0, (h-4.0)/8.0), + 4 => ((w-2.0)/4.0, h/4.0), + 5 => (w/2.0, (h-2.0)/4.0), + 6 => ((w-1.0)/2.0, h/2.0), + 7 => (w, (h-1.0)/2.0), + _ => unreachable!() + }; + self.line_width = line_width.ceil() as u32; + self.lines = lines.ceil() as u32; + self.line = 0; + } + + /// The current pass#. + pub fn current_pass(&self) -> u8 { + self.current_pass + } +} + +/// Iterates over the (passes, lines, widths) +impl Iterator for Adam7Iterator { + type Item = (u8, u32, u32); + fn next(&mut self) -> Option<(u8, u32, u32)> { + if self.line < self.lines && self.line_width > 0 { + let this_line = self.line; + self.line += 1; + Some((self.current_pass, this_line, self.line_width)) + } else if self.current_pass < 7 { + self.current_pass += 1; + self.init_pass(); + self.next() + } else { + None + } + } +} + +fn subbyte_pixels<'a>(scanline: &'a [u8], bits_pp: usize) -> impl Iterator<Item=u8> + 'a { + (0..scanline.len() * 8).step_by(bits_pp).map(move |bit_idx| { + let byte_idx = bit_idx / 8; + + // sub-byte samples start in the high-order bits + let rem = 8 - bit_idx % 8 - bits_pp; + + match bits_pp { + // evenly divides bytes + 1 => (scanline[byte_idx] >> rem) & 1, + 2 => (scanline[byte_idx] >> rem) & 3, + 4 => (scanline[byte_idx] >> rem) & 15, + _ => unreachable!(), + } + }) +} + +/// Given pass, image width, and line number, produce an iterator of bit positions of pixels to copy +/// from the input scanline to the image buffer. +fn expand_adam7_bits(pass: u8, width: usize, line_no: usize, bits_pp: usize) -> StepBy<Range<usize>> { + let (line_mul, line_off, samp_mul, samp_off) = match pass { + 1 => (8, 0, 8, 0), + 2 => (8, 0, 8, 4), + 3 => (8, 4, 4, 0), + 4 => (4, 0, 4, 2), + 5 => (4, 2, 2, 0), + 6 => (2, 0, 2, 1), + 7 => (2, 1, 1, 0), + _ => panic!("Adam7 pass out of range: {}", pass) + }; + + // the equivalent line number in progressive scan + let prog_line = line_mul * line_no + line_off; + // line width is rounded up to the next byte + let line_width = (width * bits_pp + 7) & !7; + let line_start = prog_line * line_width; + let start = line_start + (samp_off * bits_pp); + let stop = line_start + (width * bits_pp); + + (start .. stop).step_by(bits_pp * samp_mul) +} + +/// Expands an Adam 7 pass +pub fn expand_pass( + img: &mut [u8], width: u32, scanline: &[u8], + pass: u8, line_no: u32, bits_pp: u8) { + + let width = width as usize; + let line_no = line_no as usize; + let bits_pp = bits_pp as usize; + + // pass is out of range but don't blow up + if pass == 0 || pass > 7 { return; } + + let bit_indices = expand_adam7_bits(pass, width, line_no, bits_pp); + + if bits_pp < 8 { + for (pos, px) in bit_indices.zip(subbyte_pixels(scanline, bits_pp)) { + let rem = 8 - pos % 8 - bits_pp; + img[pos / 8] |= px << rem as u8; + } + } else { + let bytes_pp = bits_pp / 8; + + for (bitpos, px) in bit_indices.zip(scanline.chunks(bytes_pp)) { + for (offset, val) in px.iter().enumerate() { + img[bitpos / 8 + offset] = *val; + } + } + } +} + +#[test] +fn test_adam7() { + /* + 1646 + 7777 + 5656 + 7777 + */ + let it = Adam7Iterator::new(4, 4); + let passes: Vec<_> = it.collect(); + assert_eq!(&*passes, &[(1, 0, 1), (4, 0, 1), (5, 0, 2), (6, 0, 2), (6, 1, 2), (7, 0, 4), (7, 1, 4)]); +} + +#[test] +fn test_subbyte_pixels() { + let scanline = &[0b10101010, 0b10101010]; + + + let pixels = subbyte_pixels(scanline, 1).collect::<Vec<_>>(); + assert_eq!(pixels.len(), 16); + assert_eq!(pixels, [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]); +} + +#[test] +fn test_expand_adam7_bits() { + let width = 32; + let bits_pp = 1; + + let expected = |offset: usize, step: usize, count: usize| (0 .. count).map(move |i| step * i + offset).collect::<Vec<_>>(); + + for line_no in 0..8 { + let start = 8 * line_no * width; + + assert_eq!( + expand_adam7_bits(1, width, line_no, bits_pp).collect::<Vec<_>>(), + expected(start, 8, 4) + ); + + let start = start + 4; + + assert_eq!( + expand_adam7_bits(2, width, line_no, bits_pp).collect::<Vec<_>>(), + expected(start, 8, 4) + ); + + let start = (8 * line_no + 4) as usize * width as usize; + + assert_eq!( + expand_adam7_bits(3, width, line_no, bits_pp).collect::<Vec<_>>(), + expected(start, 4, 8) + ); + } + + for line_no in 0 .. 16 { + let start = 4 * line_no * width + 2; + + assert_eq!( + expand_adam7_bits(4, width, line_no, bits_pp).collect::<Vec<_>>(), + expected(start, 4, 8) + ); + + let start = (4 * line_no + 2) * width; + + assert_eq!( + expand_adam7_bits(5, width, line_no, bits_pp).collect::<Vec<_>>(), + expected(start, 2, 16) + ) + } + + for line_no in 0 .. 32 { + let start = 2 * line_no * width + 1; + + assert_eq!( + expand_adam7_bits(6, width, line_no, bits_pp).collect::<Vec<_>>(), + expected(start, 2, 16), + "line_no: {}", line_no + ); + + let start = (2 * line_no + 1) * width; + + assert_eq!( + expand_adam7_bits(7, width, line_no, bits_pp).collect::<Vec<_>>(), + expected(start, 1, 32) + ); + } +} + +#[test] +fn test_expand_pass_subbyte() { + let mut img = [0u8; 8]; + let width = 8; + let bits_pp = 1; + + expand_pass(&mut img, width, &[0b10000000], 1, 0, bits_pp); + assert_eq!(img, [0b10000000u8, 0, 0, 0, 0, 0, 0, 0]); + + expand_pass(&mut img, width, &[0b10000000], 2, 0, bits_pp); + assert_eq!(img, [0b10001000u8, 0, 0, 0, 0, 0, 0, 0]); + + expand_pass(&mut img, width, &[0b11000000], 3, 0, bits_pp); + assert_eq!(img, [0b10001000u8, 0, 0, 0, 0b10001000, 0, 0, 0]); + + expand_pass(&mut img, width, &[0b11000000], 4, 0, bits_pp); + assert_eq!(img, [0b10101010u8, 0, 0, 0, 0b10001000, 0, 0, 0]); + + expand_pass(&mut img, width, &[0b11000000], 4, 1, bits_pp); + assert_eq!(img, [0b10101010u8, 0, 0, 0, 0b10101010, 0, 0, 0]); + + expand_pass(&mut img, width, &[0b11110000], 5, 0, bits_pp); + assert_eq!(img, [0b10101010u8, 0, 0b10101010, 0, 0b10101010, 0, 0, 0]); + + expand_pass(&mut img, width, &[0b11110000], 5, 1, bits_pp); + assert_eq!(img, [0b10101010u8, 0, 0b10101010, 0, 0b10101010, 0, 0b10101010, 0]); + + expand_pass(&mut img, width, &[0b11110000], 6, 0, bits_pp); + assert_eq!(img, [0b11111111u8, 0, 0b10101010, 0, 0b10101010, 0, 0b10101010, 0]); + + expand_pass(&mut img, width, &[0b11110000], 6, 1, bits_pp); + assert_eq!(img, [0b11111111u8, 0, 0b11111111, 0, 0b10101010, 0, 0b10101010, 0]); + + expand_pass(&mut img, width, &[0b11110000], 6, 2, bits_pp); + assert_eq!(img, [0b11111111u8, 0, 0b11111111, 0, 0b11111111, 0, 0b10101010, 0]); + + expand_pass(&mut img, width, &[0b11110000], 6, 3, bits_pp); + assert_eq!([0b11111111u8, 0, 0b11111111, 0, 0b11111111, 0, 0b11111111, 0], img); + + expand_pass(&mut img, width, &[0b11111111], 7, 0, bits_pp); + assert_eq!([0b11111111u8, 0b11111111, 0b11111111, 0, 0b11111111, 0, 0b11111111, 0], img); + + expand_pass(&mut img, width, &[0b11111111], 7, 1, bits_pp); + assert_eq!([0b11111111u8, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0, 0b11111111, 0], img); + + expand_pass(&mut img, width, &[0b11111111], 7, 2, bits_pp); + assert_eq!([0b11111111u8, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0], img); + + expand_pass(&mut img, width, &[0b11111111], 7, 3, bits_pp); + assert_eq!([0b11111111u8, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111], img); +} |