Initial vendor packages

Signed-off-by: Valentin Popov <valentin@popov.link>
This commit is contained in:
2024-01-08 01:21:28 +04:00
parent 5ecd8cf2cb
commit 1b6a04ca55
7309 changed files with 2160054 additions and 0 deletions

1
vendor/gif/.cargo-checksum.json vendored Normal file
View File

@ -0,0 +1 @@
{"files":{"Cargo.lock":"20a658232e7d744bf594e89cdfe4ac46327c6b8bca2f90abdf9a257b411c3dd3","Cargo.toml":"7fd8ea5c4bea9a774294dd6f340a887c8144ea8beec978da3fda1395df0ee984","Changes.md":"2fe27380488b71f47c0219f4a99b73cbd0f170cbb3237626a44f2a8567774d38","LICENSE-APACHE":"769f80b5bcb42ed0af4e4d2fd74e1ac9bf843cb80c5a29219d1ef3544428a6bb","LICENSE-MIT":"77257f3d2181236b1aee78920238062ae64efe13c5d858b2db126e79c9e1b14f","README.md":"2fdb933b17e84901abbc50ff9fae053a6fb363b56b7093a26695c18863214c95","benches/decode.rs":"a2720a39e2b45022ad9d06a611a1e25d0c8b54738eb9889829b83e5ce320b8a4","benches/rgb_frame.rs":"5ccb6a8ddc14517390d90c2337948751c22e02779069483695ae9d69015d37d2","benches/samples/test.gif":"178c8642416d3f8210a206fb8fef92d307549bd48a552d50464833fa11aad4da","benches/samples/test.png":"dd61356a2c917d3c8e8b982177288768746383af9ae893720ac6465843116168","examples/check.rs":"ddd0870eea6820d3e8d3d71d44879cd0dd2bcac6ec2e91179bb5df892594bc50","examples/explode.rs":"735ae5980aa9106cedbb42cc3a7e0af5dfa3d77c35ae142ab49f499e76cb07a2","src/common.rs":"20ec5382f79999c7ad8774f506cd90987c0d422f32bdbb8abe1434092b7973b4","src/encoder.rs":"f5d5a1aaf362b528b1345503326b2a86c9c90efb1cf9507b62c1d07f5e74d43b","src/lib.rs":"a0035a39aa3649daf3d1ba3439c36e89bb2f6c1eea1aca8ada55e96ede4a3bc8","src/reader/decoder.rs":"2251591967cf56883166b49d034c14bab39b75d586f7eea1f9e81b5a84a2c8d7","src/reader/mod.rs":"d82c4b555ed78bfe860855c38181c74ce7365ea7e1cceb94560a8d2918a4a0af","src/traits.rs":"0c39abb20949ca7615cb7a2340edd2aa5e6cec5686b013352d3736018dee4a94","tests/check_testimages.rs":"dc392491ab67cda8d83a2df2cbdef6578bb88dd5ac5d036e90131c91b83029f6","tests/crashtest.rs":"e22fbda43133ff1f0b2905a6e01dc5ddd854b817b0b28dbf798ade3eb84c8315","tests/decode.rs":"9985342b5fe581e099207e25ba67031fbb46cd9158b84799ab5e9bde23ead31a","tests/results.txt":"0861feaaf22d0473d522353830e15293ba6e87bfab4848d23b8152f4f8b2a03b","tests/roundtrip.rs":"3a77998058c5ea60b092a22bd866e4587d9ad4d660a1c6ddb018efb3e44075d4","tests/stall.rs":"99a7bfd19211b3820e529a4a4dd174fffd9b8d142f3c6ab345e3faa83ff77c2e","tests/stall/issue-101-infinite-empty-loop.gif":"0a32d7e7b6e1373fd11f23b1e061e11f99cfea93c21937868fb642479bf02c0d"},"package":"80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045"}

668
vendor/gif/Cargo.lock generated vendored Normal file
View File

@ -0,0 +1,668 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bstr"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
dependencies = [
"lazy_static",
"memchr",
"regex-automata",
"serde",
]
[[package]]
name = "bumpalo"
version = "3.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
[[package]]
name = "cast"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "2.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [
"bitflags",
"textwrap",
"unicode-width",
]
[[package]]
name = "color_quant"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "crc32fast"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
dependencies = [
"cfg-if",
]
[[package]]
name = "criterion"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f"
dependencies = [
"atty",
"cast",
"clap",
"criterion-plot",
"csv",
"itertools",
"lazy_static",
"num-traits",
"oorandom",
"plotters",
"rayon",
"regex",
"serde",
"serde_cbor",
"serde_derive",
"serde_json",
"tinytemplate",
"walkdir",
]
[[package]]
name = "criterion-plot"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876"
dependencies = [
"cast",
"itertools",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
dependencies = [
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac"
dependencies = [
"cfg-if",
]
[[package]]
name = "csv"
version = "1.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1"
dependencies = [
"bstr",
"csv-core",
"itoa 0.4.8",
"ryu",
"serde",
]
[[package]]
name = "csv-core"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90"
dependencies = [
"memchr",
]
[[package]]
name = "either"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
[[package]]
name = "flate2"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6"
dependencies = [
"crc32fast",
"miniz_oxide 0.5.4",
]
[[package]]
name = "gif"
version = "0.12.0"
dependencies = [
"color_quant",
"criterion",
"glob",
"png",
"weezl",
]
[[package]]
name = "glob"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]]
name = "half"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
[[package]]
name = "itoa"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
[[package]]
name = "js-sys"
version = "0.3.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.137"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
[[package]]
name = "log"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if",
]
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "memoffset"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
dependencies = [
"autocfg",
]
[[package]]
name = "miniz_oxide"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34"
dependencies = [
"adler",
]
[[package]]
name = "miniz_oxide"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
dependencies = [
"adler",
]
[[package]]
name = "num-traits"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "once_cell"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
[[package]]
name = "oorandom"
version = "11.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "plotters"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97"
dependencies = [
"num-traits",
"plotters-backend",
"plotters-svg",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "plotters-backend"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142"
[[package]]
name = "plotters-svg"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f"
dependencies = [
"plotters-backend",
]
[[package]]
name = "png"
version = "0.17.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d708eaf860a19b19ce538740d2b4bdeeb8337fa53f7738455e706623ad5c638"
dependencies = [
"bitflags",
"crc32fast",
"flate2",
"miniz_oxide 0.6.2",
]
[[package]]
name = "proc-macro2"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rayon"
version = "1.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d"
dependencies = [
"autocfg",
"crossbeam-deque",
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-utils",
"num_cpus",
]
[[package]]
name = "regex"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
dependencies = [
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
[[package]]
name = "regex-syntax"
version = "0.6.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
[[package]]
name = "ryu"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "serde"
version = "1.0.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965"
[[package]]
name = "serde_cbor"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5"
dependencies = [
"half",
"serde",
]
[[package]]
name = "serde_derive"
version = "1.0.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45"
dependencies = [
"itoa 1.0.4",
"ryu",
"serde",
]
[[package]]
name = "syn"
version = "1.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]]
name = "tinytemplate"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
dependencies = [
"serde",
"serde_json",
]
[[package]]
name = "unicode-ident"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
[[package]]
name = "unicode-width"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
name = "walkdir"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
dependencies = [
"same-file",
"winapi",
"winapi-util",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
[[package]]
name = "web-sys"
version = "0.3.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "weezl"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

83
vendor/gif/Cargo.toml vendored Normal file
View File

@ -0,0 +1,83 @@
# 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 are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2018"
name = "gif"
version = "0.12.0"
authors = ["The image-rs Developers"]
exclude = [
"tests/crashtest/*",
"tests/samples/*",
"benches/*.gif",
"gif-afl/*",
]
description = "GIF de- and encoder"
homepage = "https://github.com/image-rs/image-gif"
documentation = "https://docs.rs/gif"
readme = "README.md"
license = "MIT/Apache-2.0"
repository = "https://github.com/image-rs/image-gif"
[[test]]
name = "check_testimages"
required-features = ["std"]
[[test]]
name = "crashtest"
required-features = ["std"]
[[test]]
name = "decode"
required-features = ["std"]
[[test]]
name = "stall"
required-features = ["std"]
[[test]]
name = "roundtrip"
required-features = ["std"]
[[bench]]
name = "decode"
harness = false
required-features = ["std"]
[[bench]]
name = "rgb_frame"
harness = false
required-features = ["std, color_quant"]
[dependencies.color_quant]
version = "1.0"
optional = true
[dependencies.weezl]
version = "0.1.5"
[dev-dependencies.criterion]
version = "0.3.1"
[dev-dependencies.glob]
version = "0.3"
[dev-dependencies.png]
version = "0.17.2"
[features]
default = [
"raii_no_panic",
"std",
"color_quant",
]
raii_no_panic = []
std = []

60
vendor/gif/Changes.md vendored Normal file
View File

@ -0,0 +1,60 @@
# v0.12.0
Features:
- Add compression of pre-compressed frame data, via `Encoder::write_lzw_pre_encoded_frame`.
- The `color_quant` dependency is now optional. Turning it off disables some
interfaces that would internally build quantization tables. The generic
implementation of creating such tables can be prohibitively costly compared
to specialized algorithms in some use cases.
Optimization:
- Avoid some allocations in by replacing `flat_map` argument with arrays
# v0.11.4
Bufixes:
- Fix decoding confusing superfluous image data from previous frames with
current frame data.
- Bump minimum required version of `weezl`.
Features:
- Add `Encoder::{get_ref, get_mut, into_inner}` to access underlying stream.
# v0.11.3
Bugfixes:
- Fix panic while decoding some images, has no precise cause in the file.
- Warn about `set_extensions` being unimplemented...
Features:
- Added `StreamingDecoder::version` to query the precise version of the
standard used for encoding the file. This is merely a hint.
- Added `DecodeOptions::allow_unknown_blocks` to skip over unknown or
unspecified block kinds.
Optimization:
- `Frame::from_rgba` now recognizes when less than 256 colors are being used,
dynamically skipping the quantization phase.
- Encoding image chunks is faster and simpler
# v0.11.2
- Fix panic when LZW code size is invalid
- Added option to omit check for lzw end code
# v0.11.1
- Frames out-of-bounds of the screen descriptor are again accepted by default.
- Added `DecodeOptions::check_frame_consistency` to turn this validation on.
# v0.11
- Rename `Reader` to `Decoder`.
- Reworked `Decoder` into `DecodeOptions`.
- The decoding error is now opaque and no longer allocates a string. Adding
more information or more error conditions is forward compatible.
- Replace the lzw decoder with `weezl`, up to +350% throughput.
- The dysfunctional C-API has been (temporarily?) removed
- It may get reintroduced as a separate crate at some point
- Added a `std` feature. It must be active for now.

201
vendor/gif/LICENSE-APACHE vendored Normal file
View File

@ -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.

21
vendor/gif/LICENSE-MIT vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
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.

83
vendor/gif/README.md vendored Normal file
View File

@ -0,0 +1,83 @@
# GIF en- and decoding library [![Build Status](https://github.com/image-rs/image-gif/workflows/Rust%20CI/badge.svg)](https://github.com/image-rs/image-gif/actions)
GIF en- and decoder written in Rust ([API Documentation](https://docs.rs/gif/)).
# GIF encoding and decoding library
This library provides all functions necessary to de- and encode GIF files.
## High level interface
The high level interface consists of the two types
[`Encoder`](https://docs.rs/gif/*/gif/struct.Encoder.html) and [`Decoder`](https://docs.rs/gif/*/gif/struct.Decoder.html).
### Decoding GIF files
```rust
// Open the file
use std::fs::File;
let input = File::open("tests/samples/sample_1.gif").unwrap();
// Configure the decoder such that it will expand the image to RGBA.
let mut options = gif::DecodeOptions::new();
options.set_color_output(gif::ColorOutput::RGBA);
// Read the file header
let mut decoder = options.read_info(input).unwrap();
while let Some(frame) = decoder.read_next_frame().unwrap() {
// Process every frame
}
```
### Encoding GIF files
The encoder can be used to save simple computer generated images:
```rust
use gif::{Frame, Encoder, Repeat};
use std::fs::File;
use std::borrow::Cow;
let color_map = &[0xFF, 0xFF, 0xFF, 0, 0, 0];
let (width, height) = (6, 6);
let beacon_states = [[
0, 0, 0, 0, 0, 0,
0, 1, 1, 0, 0, 0,
0, 1, 1, 0, 0, 0,
0, 0, 0, 1, 1, 0,
0, 0, 0, 1, 1, 0,
0, 0, 0, 0, 0, 0,
], [
0, 0, 0, 0, 0, 0,
0, 1, 1, 0, 0, 0,
0, 1, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0,
0, 0, 0, 1, 1, 0,
0, 0, 0, 0, 0, 0,
]];
let mut image = File::create("target/beacon.gif").unwrap();
let mut encoder = Encoder::new(&mut image, width, height, color_map).unwrap();
encoder.set_repeat(Repeat::Infinite).unwrap();
for state in &beacon_states {
let mut frame = Frame::default();
frame.width = width;
frame.height = height;
frame.buffer = Cow::Borrowed(&*state);
encoder.write_frame(&frame).unwrap();
}
```
[`Frame::from_*`](https://docs.rs/gif/*/gif/struct.Frame.html) can be used to convert a true color image to a paletted
image with a maximum of 256 colors:
```rust
use std::fs::File;
// Get pixel data from some source
let mut pixels: Vec<u8> = vec![0; 30_000];
// Create frame from data
let frame = gif::Frame::from_rgb(100, 100, &mut *pixels);
// Create encoder
let mut image = File::create("target/indexed_color.gif").unwrap();
let mut encoder = gif::Encoder::new(&mut image, frame.width, frame.height, &[]).unwrap();
// Write frame to file
encoder.write_frame(&frame).unwrap();
```

81
vendor/gif/benches/decode.rs vendored Normal file
View File

@ -0,0 +1,81 @@
use criterion::{black_box, BenchmarkId, BenchmarkGroup, Criterion, Throughput, measurement::Measurement};
use gif::Decoder;
fn read_image(image: &[u8]) -> Option<Vec<u8>> {
let decoder = Decoder::new(black_box(image));
//decoder.set_param(gif::ColorOutput::RGBA);
let mut reader = decoder.unwrap();
while let Some(_) = reader.next_frame_info().unwrap() {
let mut v = vec![0; reader.buffer_size()];
reader.fill_buffer(&mut v).unwrap();
return Some(v);
}
None
}
fn read_metadata(image: &[u8]) {
let decoder = Decoder::new(black_box(image));
decoder.unwrap();
}
fn main() {
struct BenchDef {
data: &'static [u8],
id: &'static str,
sample_size: usize,
}
fn run_bench_def<M: Measurement>(group: &mut BenchmarkGroup<M>, def: BenchDef) {
group
.sample_size(def.sample_size)
.throughput(Throughput::Bytes(def.data.len() as u64))
.bench_with_input(
BenchmarkId::new(def.id, def.data.len()),
def.data,
|b, input| {
b.iter(|| read_image(input))
}
);
}
let mut c = Criterion::default().configure_from_args();
let mut group = c.benchmark_group("gif");
run_bench_def(&mut group, BenchDef {
data: include_bytes!("note.gif"),
id: "note.gif",
sample_size: 100,
});
run_bench_def(&mut group, BenchDef {
data: include_bytes!("photo.gif"),
id: "photo.gif",
sample_size: 20,
});
run_bench_def(&mut group, BenchDef {
data: include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/samples/sample_1.gif")),
id: "sample_1.gif",
sample_size: 100,
});
run_bench_def(&mut group, BenchDef {
data: include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/samples/sample_big.gif")),
id: "sample_big.gif",
sample_size: 20,
});
group
.bench_with_input(
"extract-metadata-note",
include_bytes!("note.gif"),
|b, input| {
b.iter(|| read_metadata(input))
}
);
group.finish();
c.final_summary();
}

73
vendor/gif/benches/rgb_frame.rs vendored Normal file
View File

@ -0,0 +1,73 @@
use std::fs;
use criterion::{Criterion, Throughput};
use gif::{Encoder, Frame, Repeat};
use png;
const DIR: &str = "benches/samples";
fn main()
{
let mut c = Criterion::default().configure_from_args();
let mut group = c.benchmark_group("rgb_frame");
let dir = fs::read_dir(DIR).expect("Cant'r read dir:\n{}");
for path in dir {
let path = path.expect("Can't read path:\n{}").path();
if path.extension().unwrap() != "png" {
continue;
}
let mut reader = {
let input = fs::File::open(&path).unwrap();
let decoder = png::Decoder::new(input);
decoder.read_info().unwrap()
};
let mut buf = vec![0; reader.output_buffer_size()];
let info = reader.next_frame(&mut buf).unwrap();
let (w, h, size) = {
// could use try_into().unwrap() but probably no need
(info.width as u16, info.height as u16, info.buffer_size())
};
//size might have to be adjusted for large images
group
.sample_size(50)
.throughput(Throughput::Bytes(size as u64))
.bench_function(path.file_name().unwrap().to_str().unwrap(),
|b| {
match info.color_type {
png::ColorType::Rgb => b.iter(|| {
Frame::from_rgb_speed(w, h, &mut buf[..size], 30)
}),
png::ColorType::Rgba => b.iter(|| {
Frame::from_rgba_speed(w, h, &mut buf[..size], 30)
}),
c => {
println!("Image has wrong color type: {:?}", c);
}
}
});
// actually write the image as a singe frame gif... while MSE can be used
// for quality check, it might not be as good as visual inspection
let mut encoder = {
let output = fs::File::create(path.with_extension("gif")).unwrap();
Encoder::new(output, w, h, &[]).unwrap()
};
encoder.set_repeat(Repeat::Finite(0)).unwrap();
let frame = match info.color_type {
png::ColorType::Rgb => Frame::from_rgb(w, h, &mut buf[..size]),
png::ColorType::Rgba => Frame::from_rgba(w, h, &mut buf[..size]),
_ => continue,
};
encoder.write_frame(&frame).unwrap();
}
group.finish();
c.final_summary();
}

BIN
vendor/gif/benches/samples/test.gif vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

BIN
vendor/gif/benches/samples/test.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 419 KiB

41
vendor/gif/examples/check.rs vendored Normal file
View File

@ -0,0 +1,41 @@
use std::{env, fs, process};
fn main() {
let file = env::args().nth(1)
.unwrap_or_else(|| explain_usage());
let file = fs::File::open(&file)
.expect("failed to open input file");
let mut reader = {
let mut options = gif::DecodeOptions::new();
options.allow_unknown_blocks(true);
options.read_info(file).unwrap()
};
loop {
let frame = match reader.read_next_frame() {
Ok(Some(frame)) => frame,
Ok(None) => break,
Err(error) => {
println!("Error: {:?}", error);
break;
}
};
println!(
" Frame:\n \
delay: {:?}\n \
canvas: {}x{}+{}+{}\n \
dispose: {:?}\n \
needs_input: {:?}",
frame.delay,
frame.width, frame.height, frame.left, frame.top,
frame.dispose,
frame.needs_user_input
);
}
}
fn explain_usage() -> ! {
println!("Print information on the frames of a gif.\n\nUsage: check <file>");
process::exit(1)
}

44
vendor/gif/examples/explode.rs vendored Normal file
View File

@ -0,0 +1,44 @@
//! Exports each GIF frame as a separate image.
use std::env;
use std::fs::File;
use std::path::PathBuf;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let input_path = PathBuf::from(
env::args_os()
.nth(1)
.ok_or("Specify a GIF path as the first argument")?,
);
let input = File::open(&input_path)?;
let mut options = gif::DecodeOptions::new();
options.set_color_output(gif::ColorOutput::Indexed);
let mut decoder = options.read_info(input)?;
let screen_width = decoder.width();
let screen_height = decoder.height();
let global_pal = decoder.global_palette().unwrap_or_default().to_vec();
let output_file_stem = input_path.file_stem().unwrap().to_str().unwrap();
let mut frame_number = 1;
while let Some(frame) = decoder.read_next_frame()? {
let output_path = format!("{}.{:03}.gif", output_file_stem, frame_number);
let mut output = File::create(&output_path)?;
let mut encoder = gif::Encoder::new(&mut output, screen_width, screen_height, &global_pal)?;
encoder.write_frame(&frame)?;
frame_number += 1;
use gif::DisposalMethod::*;
let disposal = match frame.dispose {
Any => "any",
Keep => "keep",
Background => "background",
Previous => "previous",
};
eprintln!(
"Written {} ({}x{}@{}x{} delay={} {})",
output_path, frame.width, frame.height, frame.top, frame.left, frame.delay, disposal
);
}
Ok(())
}

346
vendor/gif/src/common.rs vendored Normal file
View File

@ -0,0 +1,346 @@
use std::borrow::Cow;
use std::collections::HashMap;
use std::collections::HashSet;
/// Disposal method
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(u8)]
pub enum DisposalMethod {
/// StreamingDecoder is not required to take any action.
Any = 0,
/// Do not dispose.
Keep = 1,
/// Restore to background color.
Background = 2,
/// Restore to previous.
Previous = 3,
}
impl DisposalMethod {
/// Converts `u8` to `Option<Self>`
pub fn from_u8(n: u8) -> Option<DisposalMethod> {
match n {
0 => Some(DisposalMethod::Any),
1 => Some(DisposalMethod::Keep),
2 => Some(DisposalMethod::Background),
3 => Some(DisposalMethod::Previous),
_ => None
}
}
}
/// Known GIF block labels.
///
/// Note that the block uniquely specifies the layout of bytes that follow and how they are
/// framed. For example, the header always has a fixed length but is followed by a variable amount
/// of additional data. An image descriptor may be followed by a local color table depending on
/// information read in it. Therefore, it doesn't make sense to continue parsing after encountering
/// an unknown block as the semantics of following bytes are unclear.
///
/// The extension block provides a common framing for an arbitrary amount of application specific
/// data which may be ignored.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(u8)]
pub enum Block {
/// Image block.
Image = 0x2C,
/// Extension block.
Extension = 0x21,
/// Image trailer.
Trailer = 0x3B,
}
impl Block {
/// Converts `u8` to `Option<Self>`
pub fn from_u8(n: u8) -> Option<Block> {
match n {
0x2C => Some(Block::Image),
0x21 => Some(Block::Extension),
0x3B => Some(Block::Trailer),
_ => None
}
}
}
/// A newtype wrapper around an arbitrary extension ID.
///
/// An extension is some amount of byte data organized in sub-blocks so that one can skip over it
/// without knowing the semantics. Though technically you likely want to use a `Application`
/// extension, the library tries to stay flexible here.
///
/// This allows us to customize the set of impls compared to a raw `u8`. It also clarifies the
/// intent and gives some inherent methods for interoperability with known extension types.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct AnyExtension(pub u8);
/// Known GIF extension labels.
///
/// These are extensions which may be interpreted by the library and to which a specification with
/// the internal data layout is known.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(u8)]
pub enum Extension {
/// Plain Text extension.
///
/// This instructs the decoder to render a text as characters in a grid of cells, in a
/// mono-spaced font of its choosing. This is seldom actually implemented and ignored by
/// ImageMagick. The color is always taken from the global table which further complicates any
/// use. No real information on the frame sequencing of this block is available in the
/// standard.
Text = 0x01,
/// Control extension.
Control = 0xF9,
/// Comment extension.
Comment = 0xFE,
/// Application extension.
///
/// See [ImageMagick] for an idea of commonly recognized extensions.
///
/// [ImageMagick]: https://github.com/ImageMagick/ImageMagick/blob/b0b58c6303195928060f55f9c3ca8233ab7f7733/coders/gif.c#L1128
Application = 0xFF,
}
impl AnyExtension {
/// Decode the label as a known extension.
pub fn into_known(self) -> Option<Extension> {
Extension::from_u8(self.0)
}
}
impl From<Extension> for AnyExtension {
fn from(ext: Extension) -> Self {
AnyExtension(ext as u8)
}
}
impl Extension {
/// Converts `u8` to a `Extension` if it is known.
pub fn from_u8(n: u8) -> Option<Extension> {
match n {
0x01 => Some(Extension::Text),
0xF9 => Some(Extension::Control),
0xFE => Some(Extension::Comment),
0xFF => Some(Extension::Application),
_ => None
}
}
}
/// A GIF frame
#[derive(Debug, Clone)]
pub struct Frame<'a> {
/// Frame delay in units of 10 ms.
pub delay: u16,
/// Disposal method.
pub dispose: DisposalMethod,
/// Transparent index (if available).
pub transparent: Option<u8>,
/// True if the frame needs user input to be displayed.
pub needs_user_input: bool,
/// Offset from the top border of the canvas.
pub top: u16,
/// Offset from the left border of the canvas.
pub left: u16,
/// Width of the frame.
pub width: u16,
/// Height of the frame.
pub height: u16,
/// True if the image is interlaced.
pub interlaced: bool,
/// Frame local color palette if available.
pub palette: Option<Vec<u8>>,
/// Buffer containing the image data.
/// Only indices unless configured differently.
pub buffer: Cow<'a, [u8]>
}
impl<'a> Default for Frame<'a> {
fn default() -> Frame<'a> {
Frame {
delay: 0,
dispose: DisposalMethod::Keep,
transparent: None,
needs_user_input: false,
top: 0,
left: 0,
width: 0,
height: 0,
interlaced: false,
palette: None,
buffer: Cow::Borrowed(&[])
}
}
}
impl Frame<'static> {
/// Creates a frame from pixels in RGBA format.
///
/// This is a lossy method. The `gif` format does not support arbitrary alpha but only a 1-bit
/// transparency mask per pixel. Any non-zero alpha value will be interpreted as a fully opaque
/// pixel. Additionally, only 256 colors can appear in a single frame. The palette will be
/// reduced by the NeuQuant algorithm if necessary. Different frames have independent palettes.
///
/// *Note: This method is not optimized for speed.*
///
/// # Panics:
/// * If the length of pixels does not equal `width * height * 4`.
#[cfg(feature = "color_quant")]
pub fn from_rgba(width: u16, height: u16, pixels: &mut [u8]) -> Frame<'static> {
Frame::from_rgba_speed(width, height, pixels, 1)
}
/// Creates a frame from pixels in RGBA format.
///
/// `speed` is a value in the range [1, 30].
/// The higher the value the faster it runs at the cost of image quality.
/// A `speed` of 10 is a good compromise between speed and quality.
///
/// This is a lossy method. The `gif` format does not support arbitrary alpha but only a 1-bit
/// transparency mask per pixel. Any non-zero alpha value will be interpreted as a fully opaque
/// pixel. Additionally, only 256 colors can appear in a single frame. The palette will be
/// reduced by the NeuQuant algorithm if necessary. Different frames have independent palettes.
///
/// # Panics:
/// * If the length of pixels does not equal `width * height * 4`.
/// * If `speed < 1` or `speed > 30`
#[cfg(feature = "color_quant")]
pub fn from_rgba_speed(width: u16, height: u16, pixels: &mut [u8], speed: i32) -> Frame<'static> {
assert_eq!(width as usize * height as usize * 4, pixels.len(), "Too much or too little pixel data for the given width and height to create a GIF Frame");
assert!(speed >= 1 && speed <= 30, "speed needs to be in the range [1, 30]");
let mut transparent = None;
for pix in pixels.chunks_exact_mut(4) {
if pix[3] != 0 {
pix[3] = 0xFF;
} else {
transparent = Some([pix[0], pix[1], pix[2], pix[3]])
}
}
// Attempt to build a palette of all colors. If we go over 256 colors,
// switch to the NeuQuant algorithm.
let mut colors: HashSet<(u8, u8, u8, u8)> = HashSet::new();
for pixel in pixels.chunks_exact(4) {
if colors.insert((pixel[0], pixel[1], pixel[2], pixel[3])) && colors.len() > 256 {
// > 256 colours, let's use NeuQuant.
let nq = color_quant::NeuQuant::new(speed, 256, pixels);
return Frame {
width,
height,
buffer: Cow::Owned(pixels.chunks_exact(4).map(|pix| nq.index_of(pix) as u8).collect()),
palette: Some(nq.color_map_rgb()),
transparent: transparent.map(|t| nq.index_of(&t) as u8),
..Frame::default()
};
}
}
// Palette size <= 256 elements, we can build an exact palette.
let mut colors_vec: Vec<(u8, u8, u8, u8)> = colors.into_iter().collect();
colors_vec.sort();
let palette = colors_vec.iter().flat_map(|&(r, g, b, _a)| [r, g, b]).collect();
let colors_lookup: HashMap<(u8, u8, u8, u8), u8> = colors_vec.into_iter().zip(0..=255).collect();
let index_of = | pixel: &[u8] |
*colors_lookup.get(&(pixel[0], pixel[1], pixel[2], pixel[3])).unwrap();
return Frame {
width,
height,
buffer: Cow::Owned(pixels.chunks_exact(4).map(|pix| index_of(pix)).collect()),
palette: Some(palette),
transparent: transparent.map(|t| index_of(&t)),
..Frame::default()
}
}
/// Creates a frame from a palette and indexed pixels.
///
/// # Panics:
/// * If the length of pixels does not equal `width * height`.
/// * If the length of palette > `256 * 3`.
pub fn from_palette_pixels(width: u16, height: u16, pixels: &[u8], palette: &[u8], transparent: Option<u8>) -> Frame<'static> {
assert_eq!(width as usize * height as usize, pixels.len(), "Too many or too little pixels for the given width and height to create a GIF Frame");
assert!(palette.len() <= 256*3, "Too many palette values to create a GIF Frame");
Frame {
width,
height,
buffer: Cow::Owned(pixels.to_vec()),
palette: Some(palette.to_vec()),
transparent,
..Frame::default()
}
}
/// Creates a frame from indexed pixels in the global palette.
///
/// # Panics:
/// * If the length of pixels does not equal `width * height`.
pub fn from_indexed_pixels(width: u16, height: u16, pixels: &[u8], transparent: Option<u8>) -> Frame<'static> {
assert_eq!(width as usize * height as usize, pixels.len(), "Too many or too little pixels for the given width and height to create a GIF Frame");
Frame {
width,
height,
buffer: Cow::Owned(pixels.to_vec()),
palette: None,
transparent,
..Frame::default()
}
}
/// Creates a frame from pixels in RGB format.
///
/// This is a lossy method. In the `gif` format only 256 colors can appear in a single frame.
/// The palette will be reduced by the NeuQuant algorithm if necessary. Different frames have
/// independent palettes.
///
/// *Note: This method is not optimized for speed.*
///
/// # Panics:
/// * If the length of pixels does not equal `width * height * 3`.
#[cfg(feature = "color_quant")]
pub fn from_rgb(width: u16, height: u16, pixels: &[u8]) -> Frame<'static> {
Frame::from_rgb_speed(width, height, pixels, 1)
}
/// Creates a frame from pixels in RGB format.
///
/// `speed` is a value in the range [1, 30].
///
/// This is a lossy method. In the `gif` format only 256 colors can appear in a single frame.
/// The palette will be reduced by the NeuQuant algorithm if necessary. Different frames have
/// independent palettes.
///
/// The higher the value the faster it runs at the cost of image quality.
/// A `speed` of 10 is a good compromise between speed and quality.
///
/// # Panics:
/// * If the length of pixels does not equal `width * height * 3`.
/// * If `speed < 1` or `speed > 30`
#[cfg(feature = "color_quant")]
pub fn from_rgb_speed(width: u16, height: u16, pixels: &[u8], speed: i32) -> Frame<'static> {
assert_eq!(width as usize * height as usize * 3, pixels.len(), "Too much or too little pixel data for the given width and height to create a GIF Frame");
let mut vec: Vec<u8> = Vec::with_capacity(pixels.len() + width as usize * height as usize);
for v in pixels.chunks_exact(3) {
vec.extend_from_slice(&[v[0], v[1], v[2], 0xFF])
}
Frame::from_rgba_speed(width, height, &mut vec, speed)
}
pub(crate) fn required_bytes(&self) -> usize {
usize::from(self.width) * usize::from(self.height)
}
}
#[test]
#[cfg(feature = "color_quant")]
// Creating the `colors_lookup` hashmap in Frame::from_rgba_speed panics due to
// overflow while bypassing NeuQuant and zipping a RangeFrom with 256 colors.
// Changing .zip(0_u8..) to .zip(0_u8..=255) fixes this issue.
fn rgba_speed_avoid_panic_256_colors() {
let side = 16;
let pixel_data: Vec<u8> = (0..=255).map(|a| vec![a, a, a]).flatten().collect();
Frame::from_rgb(side, side, &pixel_data);
}

434
vendor/gif/src/encoder.rs vendored Normal file
View File

@ -0,0 +1,434 @@
//! # Minimal gif encoder
use std::io;
use std::io::prelude::*;
use std::fmt;
use std::error;
use std::borrow::Cow;
use weezl::{BitOrder, encode::Encoder as LzwEncoder};
use crate::traits::{WriteBytesExt};
use crate::common::{AnyExtension, Block, DisposalMethod, Extension, Frame};
#[derive(Debug)]
enum FormatErrorKind {
/// The image has too many colors.
TooManyColors,
/// The image has no color palette which is required.
MissingColorPalette,
}
/// The image has incorrect properties, making it impossible to encode as a gif.
#[derive(Debug)]
pub struct EncodingFormatError {
kind: FormatErrorKind
}
impl error::Error for EncodingFormatError {}
impl fmt::Display for EncodingFormatError {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.kind {
FormatErrorKind::TooManyColors => write!(fmt, "the image has too many colors"),
FormatErrorKind::MissingColorPalette => write!(fmt, "the GIF format requires a color palette but none was given")
}
}
}
impl From<FormatErrorKind> for EncodingFormatError {
fn from(kind: FormatErrorKind) -> Self {
EncodingFormatError { kind }
}
}
#[derive(Debug)]
/// Encoding error.
pub enum EncodingError {
/// Returned if the to image is not encodable as a gif.
Format(EncodingFormatError),
/// Wraps `std::io::Error`.
Io(io::Error),
}
impl fmt::Display for EncodingError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match self {
EncodingError::Io(err) => err.fmt(fmt),
EncodingError::Format(err) => err.fmt(fmt),
}
}
}
impl error::Error for EncodingError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
EncodingError::Io(err) => Some(err),
EncodingError::Format(err) => Some(err),
}
}
}
impl From<io::Error> for EncodingError {
fn from(err: io::Error) -> Self {
EncodingError::Io(err)
}
}
impl From<EncodingFormatError> for EncodingError {
fn from(err: EncodingFormatError) -> Self {
EncodingError::Format(err)
}
}
impl From<FormatErrorKind> for EncodingError {
fn from(kind: FormatErrorKind) -> Self {
EncodingError::Format(kind.into())
}
}
/// Number of repetitions
#[derive(Copy, Clone, Debug)]
pub enum Repeat {
/// Finite number of repetitions
Finite(u16),
/// Infinite number of repetitions
Infinite
}
/// Extension data.
pub enum ExtensionData {
/// Control extension. Use `ExtensionData::new_control_ext` to construct.
Control {
/// Flags.
flags: u8,
/// Frame delay.
delay: u16,
/// Transparent index.
trns: u8
},
/// Sets the number of repetitions
Repetitions(Repeat)
}
impl ExtensionData {
/// Constructor for control extension data.
///
/// `delay` is given in units of 10 ms.
pub fn new_control_ext(delay: u16, dispose: DisposalMethod,
needs_user_input: bool, trns: Option<u8>) -> ExtensionData {
let mut flags = 0;
let trns = match trns {
Some(trns) => {
flags |= 1;
trns as u8
},
None => 0
};
flags |= (needs_user_input as u8) << 1;
flags |= (dispose as u8) << 2;
ExtensionData::Control {
flags: flags,
delay: delay,
trns: trns
}
}
}
impl<W: Write> Encoder<W> {
/// Creates a new encoder.
///
/// `global_palette` gives the global color palette in the format `[r, g, b, ...]`,
/// if no global palette shall be used an empty slice may be supplied.
pub fn new(w: W, width: u16, height: u16, global_palette: &[u8]) -> Result<Self, EncodingError> {
let buffer_size = (width as usize) * (height as usize);
Encoder {
w: Some(w),
global_palette: false,
width: width,
height: height,
buffer: Vec::with_capacity(buffer_size)
}.write_global_palette(global_palette)
}
/// Write an extension block that signals a repeat behaviour.
pub fn set_repeat(&mut self, repeat: Repeat) -> Result<(), EncodingError> {
self.write_extension(ExtensionData::Repetitions(repeat))
}
/// Writes the global color palette.
pub fn write_global_palette(mut self, palette: &[u8]) -> Result<Self, EncodingError> {
self.global_palette = true;
let mut flags = 0;
flags |= 0b1000_0000;
let num_colors = palette.len() / 3;
if num_colors > 256 {
return Err(EncodingError::from(FormatErrorKind::TooManyColors));
}
// Size of global color table.
flags |= flag_size(num_colors);
// Color resolution .. FIXME. This is mostly ignored (by ImageMagick at least) but hey, we
// should use some sensible value here or even allow configuring it?
flags |= flag_size(num_colors) << 4; // wtf flag
self.write_screen_desc(flags)?;
self.write_color_table(palette)?;
Ok(self)
}
/// Writes a frame to the image.
///
/// Note: This function also writes a control extension if necessary.
pub fn write_frame(&mut self, frame: &Frame) -> Result<(), EncodingError> {
self.write_frame_header(frame)?;
self.write_image_block(&frame.buffer)
}
fn write_frame_header(&mut self, frame: &Frame) -> Result<(), EncodingError> {
// TODO commented off to pass test in lib.rs
//if frame.delay > 0 || frame.transparent.is_some() {
self.write_extension(ExtensionData::new_control_ext(
frame.delay,
frame.dispose,
frame.needs_user_input,
frame.transparent
))?;
//}
let writer = self.w.as_mut().unwrap();
writer.write_le(Block::Image as u8)?;
writer.write_le(frame.left)?;
writer.write_le(frame.top)?;
writer.write_le(frame.width)?;
writer.write_le(frame.height)?;
let mut flags = 0;
if frame.interlaced {
flags |= 0b0100_0000;
}
match frame.palette {
Some(ref palette) => {
flags |= 0b1000_0000;
let num_colors = palette.len() / 3;
if num_colors > 256 {
return Err(EncodingError::from(FormatErrorKind::TooManyColors));
}
flags |= flag_size(num_colors);
writer.write_le(flags)?;
self.write_color_table(palette)
},
None => if !self.global_palette {
Err(EncodingError::from(FormatErrorKind::MissingColorPalette))
} else {
writer.write_le(flags).map_err(Into::into)
}
}
}
fn write_image_block(&mut self, data: &[u8]) -> Result<(), EncodingError> {
self.buffer.clear();
lzw_encode(data, &mut self.buffer);
let writer = self.w.as_mut().unwrap();
Self::write_encoded_image_block(writer, &self.buffer)
}
fn write_encoded_image_block(writer: &mut W, data_with_min_code_size: &[u8]) -> Result<(), EncodingError> {
let (&min_code_size, data) = data_with_min_code_size.split_first().unwrap_or((&2, &[]));
writer.write_le(min_code_size)?;
// Write blocks. `chunks_exact` seems to be slightly faster
// than `chunks` according to both Rust docs and benchmark results.
let mut iter = data.chunks_exact(0xFF);
while let Some(full_block) = iter.next() {
writer.write_le(0xFFu8)?;
writer.write_all(full_block)?;
}
let last_block = iter.remainder();
if !last_block.is_empty() {
writer.write_le(last_block.len() as u8)?;
writer.write_all(last_block)?;
}
writer.write_le(0u8).map_err(Into::into)
}
fn write_color_table(&mut self, table: &[u8]) -> Result<(), EncodingError> {
let writer = self.w.as_mut().unwrap();
let num_colors = table.len() / 3;
if num_colors > 256 {
return Err(EncodingError::from(FormatErrorKind::TooManyColors));
}
let size = flag_size(num_colors);
writer.write_all(&table[..num_colors * 3])?;
// Waste some space as of gif spec
for _ in 0..((2 << size) - num_colors) {
writer.write_all(&[0, 0, 0])?
}
Ok(())
}
/// Writes an extension to the image.
///
/// It is normally not necessary to call this method manually.
pub fn write_extension(&mut self, extension: ExtensionData) -> Result<(), EncodingError> {
use self::ExtensionData::*;
// 0 finite repetitions can only be achieved
// if the corresponting extension is not written
if let Repetitions(Repeat::Finite(0)) = extension {
return Ok(())
}
let writer = self.w.as_mut().unwrap();
writer.write_le(Block::Extension as u8)?;
match extension {
Control { flags, delay, trns } => {
writer.write_le(Extension::Control as u8)?;
writer.write_le(4u8)?;
writer.write_le(flags)?;
writer.write_le(delay)?;
writer.write_le(trns)?;
}
Repetitions(repeat) => {
writer.write_le(Extension::Application as u8)?;
writer.write_le(11u8)?;
writer.write_all(b"NETSCAPE2.0")?;
writer.write_le(3u8)?;
writer.write_le(1u8)?;
match repeat {
Repeat::Finite(no) => writer.write_le(no)?,
Repeat::Infinite => writer.write_le(0u16)?,
}
}
}
writer.write_le(0u8).map_err(Into::into)
}
/// Writes a raw extension to the image.
///
/// This method can be used to write an unsupported extension to the file. `func` is the extension
/// identifier (e.g. `Extension::Application as u8`). `data` are the extension payload blocks. If any
/// contained slice has a lenght > 255 it is automatically divided into sub-blocks.
pub fn write_raw_extension(&mut self, func: AnyExtension, data: &[&[u8]]) -> io::Result<()> {
let writer = self.w.as_mut().unwrap();
writer.write_le(Block::Extension as u8)?;
writer.write_le(func.0)?;
for block in data {
for chunk in block.chunks(0xFF) {
writer.write_le(chunk.len() as u8)?;
writer.write_all(chunk)?;
}
}
writer.write_le(0u8)
}
/// Writes a frame to the image, but expects `Frame.buffer` to contain LZW-encoded data
/// from [`Frame::make_lzw_pre_encoded`].
///
/// Note: This function also writes a control extension if necessary.
pub fn write_lzw_pre_encoded_frame(&mut self, frame: &Frame) -> Result<(), EncodingError> {
self.write_frame_header(frame)?;
let writer = self.w.as_mut().unwrap();
Self::write_encoded_image_block(writer, &frame.buffer)
}
/// Writes the logical screen desriptor
fn write_screen_desc(&mut self, flags: u8) -> io::Result<()> {
let writer = self.w.as_mut().unwrap();
writer.write_all(b"GIF89a")?;
writer.write_le(self.width)?;
writer.write_le(self.height)?;
writer.write_le(flags)?; // packed field
writer.write_le(0u8)?; // bg index
writer.write_le(0u8) // aspect ratio
}
/// Gets a reference to the writer instance used by this encoder.
pub fn get_ref(&self) -> &W {
self.w.as_ref().unwrap()
}
/// Gets a mutable reference to the writer instance used by this encoder.
///
/// It is inadvisable to directly write to the underlying writer.
pub fn get_mut(&mut self) -> &mut W {
self.w.as_mut().unwrap()
}
/// Returns writer instance used by this encoder
pub fn into_inner(mut self) -> io::Result<W> {
self.write_trailer()?;
Ok(self.w.take().unwrap())
}
/// Write the final tailer.
fn write_trailer(&mut self) -> io::Result<()> {
self.w.as_mut().unwrap().write_le(Block::Trailer as u8)
}
}
/// Encodes the data into the provided buffer.
///
/// The first byte is the minimum code size, followed by LZW data.
fn lzw_encode(data: &[u8], buffer: &mut Vec<u8>) {
let min_code_size = match flag_size(1 + data.iter().copied().max().unwrap_or(0) as usize) + 1 {
1 => 2, // As per gif spec: The minimal code size has to be >= 2
n => n
};
buffer.push(min_code_size);
let mut enc = LzwEncoder::new(BitOrder::Lsb, min_code_size);
let len = enc.into_vec(buffer).encode_all(data).consumed_out;
buffer.truncate(len+1);
}
impl Frame<'_> {
/// Replace frame's buffer with a LZW-compressed one for use with [`Encoder::write_lzw_pre_encoded_frame`].
///
/// Frames can be compressed in any order, separately from the `Encoder`, which can be used to compress frames in parallel.
pub fn make_lzw_pre_encoded(&mut self) {
let mut buffer = Vec::with_capacity(self.buffer.len() / 2);
lzw_encode(&self.buffer, &mut buffer);
self.buffer = Cow::Owned(buffer);
}
}
/// GIF encoder.
pub struct Encoder<W: Write> {
w: Option<W>,
global_palette: bool,
width: u16,
height: u16,
buffer: Vec<u8>
}
impl<W: Write> Drop for Encoder<W> {
#[cfg(feature = "raii_no_panic")]
fn drop(&mut self) {
if self.w.is_some() {
let _ = self.write_trailer();
}
}
#[cfg(not(feature = "raii_no_panic"))]
fn drop(&mut self) {
if self.w.is_some() {
self.write_trailer().unwrap();
}
}
}
// Color table size converted to flag bits
fn flag_size(size: usize) -> u8 {
match size {
0 ..=2 => 0,
3 ..=4 => 1,
5 ..=8 => 2,
9 ..=16 => 3,
17 ..=32 => 4,
33 ..=64 => 5,
65 ..=128 => 6,
129..=256 => 7,
_ => 7
}
}
#[test]
fn error_cast() {
let _ : Box<dyn error::Error> = EncodingError::from(FormatErrorKind::MissingColorPalette).into();
}

154
vendor/gif/src/lib.rs vendored Normal file
View File

@ -0,0 +1,154 @@
//! # GIF en- and decoding library [![Build Status](https://github.com/image-rs/image-gif/workflows/Rust%20CI/badge.svg)](https://github.com/image-rs/image-gif/actions)
//!
//! GIF en- and decoder written in Rust ([API Documentation](https://docs.rs/gif)).
//!
//! # GIF encoding and decoding library
//!
//! This library provides all functions necessary to de- and encode GIF files.
//!
//! ## High level interface
//!
//! The high level interface consists of the two types
//! [`Encoder`](struct.Encoder.html) and [`Decoder`](struct.Decoder.html).
//!
//! ### Decoding GIF files
//!
//! ```rust
//! // Open the file
//! use std::fs::File;
//! let mut decoder = gif::DecodeOptions::new();
//! // Configure the decoder such that it will expand the image to RGBA.
//! decoder.set_color_output(gif::ColorOutput::RGBA);
//! // Read the file header
//! let file = File::open("tests/samples/sample_1.gif").unwrap();
//! let mut decoder = decoder.read_info(file).unwrap();
//! while let Some(frame) = decoder.read_next_frame().unwrap() {
//! // Process every frame
//! }
//! ```
//!
//!
//!
//! ### Encoding GIF files
//!
//! The encoder can be used so save simple computer generated images:
//!
//! ```rust
//! use gif::{Frame, Encoder, Repeat};
//! use std::fs::File;
//! use std::borrow::Cow;
//!
//! let color_map = &[0xFF, 0xFF, 0xFF, 0, 0, 0];
//! let (width, height) = (6, 6);
//! let mut beacon_states = [[
//! 0, 0, 0, 0, 0, 0,
//! 0, 1, 1, 0, 0, 0,
//! 0, 1, 1, 0, 0, 0,
//! 0, 0, 0, 1, 1, 0,
//! 0, 0, 0, 1, 1, 0,
//! 0, 0, 0, 0, 0, 0,
//! ], [
//! 0, 0, 0, 0, 0, 0,
//! 0, 1, 1, 0, 0, 0,
//! 0, 1, 0, 0, 0, 0,
//! 0, 0, 0, 0, 1, 0,
//! 0, 0, 0, 1, 1, 0,
//! 0, 0, 0, 0, 0, 0,
//! ]];
//! let mut image = File::create("tests/samples/beacon.gif").unwrap();;
//! let mut encoder = Encoder::new(&mut image, width, height, color_map).unwrap();
//! encoder.set_repeat(Repeat::Infinite).unwrap();
//! for state in &beacon_states {
//! let mut frame = Frame::default();
//! frame.width = width;
//! frame.height = height;
//! frame.buffer = Cow::Borrowed(&*state);
//! encoder.write_frame(&frame).unwrap();
//! }
//! ```
//!
//! [`Frame::from_*`](struct.Frame.html) can be used to convert a true color image to a paletted
//! image with a maximum of 256 colors:
//!
//! ```rust
//! # #[cfg(feature = "color_quant")] {
//! use std::fs::File;
//!
//! // Get pixel data from some source
//! let mut pixels: Vec<u8> = vec![0; 30_000];
//! // Create frame from data
//! let frame = gif::Frame::from_rgb(100, 100, &mut *pixels);
//! // Create encoder
//! let mut image = File::create("target/indexed_color.gif").unwrap();
//! let mut encoder = gif::Encoder::new(&mut image, frame.width, frame.height, &[]).unwrap();
//! // Write frame to file
//! encoder.write_frame(&frame).unwrap();
//! # }
//! ```
// TODO: make this compile
// ```rust
// use gif::{Frame, Encoder};
// use std::fs::File;
// let color_map = &[0, 0, 0, 0xFF, 0xFF, 0xFF];
// let mut frame = Frame::default();
// // Generate checkerboard lattice
// for (i, j) in (0..10).zip(0..10) {
// frame.buffer.push(if (i * j) % 2 == 0 {
// 1
// } else {
// 0
// })
// }
// # (|| {
// {
// let mut file = File::create("test.gif")?;
// let mut encoder = Encoder::new(&mut file, 100, 100);
// encoder.write_global_palette(color_map)?.write_frame(&frame)
// }
// # })().unwrap();
// ```
#![deny(missing_docs)]
#![cfg(feature = "std")]
mod traits;
mod common;
mod reader;
mod encoder;
pub use crate::common::{AnyExtension, Block, Extension, DisposalMethod, Frame};
pub use crate::reader::{StreamingDecoder, Decoded, DecodingError, DecodingFormatError};
/// StreamingDecoder configuration parameters
pub use crate::reader::{ColorOutput, MemoryLimit, Extensions};
pub use crate::reader::{DecodeOptions, Decoder, Version};
pub use crate::encoder::{Encoder, ExtensionData, Repeat, EncodingError};
#[cfg(test)]
#[test]
fn round_trip() {
use std::io::prelude::*;
use std::fs::File;
let mut data = vec![];
File::open("tests/samples/sample_1.gif").unwrap().read_to_end(&mut data).unwrap();
let mut decoder = Decoder::new(&*data).unwrap();
let palette: Vec<u8> = decoder.palette().unwrap().into();
let frame = decoder.read_next_frame().unwrap().unwrap();
let mut data2 = vec![];
{
let mut encoder = Encoder::new(&mut data2, frame.width, frame.height, &palette).unwrap();
encoder.write_frame(frame).unwrap();
}
assert_eq!(&data[..], &data2[..])
}
macro_rules! insert_as_doc {
{ $content:expr } => {
#[doc = $content] extern { }
}
}
// Provides the README.md as doc, to ensure the example works!
#[cfg(feature = "color_quant")]
insert_as_doc!(include_str!("../README.md"));

724
vendor/gif/src/reader/decoder.rs vendored Normal file
View File

@ -0,0 +1,724 @@
use std::cmp;
use std::error;
use std::fmt;
use std::io;
use std::mem;
use std::default::Default;
use crate::common::{AnyExtension, Block, DisposalMethod, Extension, Frame};
use crate::reader::DecodeOptions;
use weezl::{BitOrder, decode::Decoder as LzwDecoder, LzwStatus};
/// GIF palettes are RGB
pub const PLTE_CHANNELS: usize = 3;
/// An error returned in the case of the image not being formatted properly.
#[derive(Debug)]
pub struct DecodingFormatError {
underlying: Box<dyn error::Error + Send + Sync + 'static>
}
impl fmt::Display for DecodingFormatError {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&*self.underlying, fmt)
}
}
impl error::Error for DecodingFormatError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
Some(&*self.underlying as _)
}
}
impl DecodingFormatError {
fn new(
err: impl Into<Box<dyn error::Error + Send + Sync>>,
) -> Self {
DecodingFormatError {
underlying: err.into(),
}
}
}
#[derive(Debug)]
/// Decoding error.
pub enum DecodingError {
/// Returned if the image is found to be malformed.
Format(DecodingFormatError),
/// Wraps `std::io::Error`.
Io(io::Error),
}
impl DecodingError {
#[inline]
pub(crate) fn format(
err: impl Into<Box<dyn error::Error + Send + Sync>>,
) -> Self {
DecodingError::Format(DecodingFormatError::new(err))
}
}
impl fmt::Display for DecodingError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
DecodingError::Format(ref d) => d.fmt(fmt),
DecodingError::Io(ref err) => err.fmt(fmt),
}
}
}
impl error::Error for DecodingError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match *self {
DecodingError::Format(ref err) => Some(err),
DecodingError::Io(ref err) => Some(err),
}
}
}
impl From<io::Error> for DecodingError {
fn from(err: io::Error) -> Self {
DecodingError::Io(err)
}
}
impl From<DecodingFormatError> for DecodingError {
fn from(err: DecodingFormatError) -> Self {
DecodingError::Format(err)
}
}
/// Configures how extensions should be handled
#[derive(PartialEq, Debug)]
pub enum Extensions {
/// Saves all extention data
Save,
/// Skips the data of unknown extensions
/// and extracts the data from known ones
Skip
}
/// Indicates whether a certain object has been decoded
#[derive(Debug)]
pub enum Decoded<'a> {
/// Decoded nothing.
Nothing,
/// Global palette.
GlobalPalette(Vec<u8>),
/// Index of the background color in the global palette.
BackgroundColor(u8),
/// Decoded the image trailer.
Trailer,
/// The start of a block.
BlockStart(Block),
/// Decoded a sub-block. More sub-block are available.
///
/// Indicates the label of the extension which might be unknown. A label of `0` is used when
/// the sub block does not belong to an extension.
SubBlockFinished(AnyExtension, &'a [u8]),
/// Decoded the last (or only) sub-block of a block.
///
/// Indicates the label of the extension which might be unknown. A label of `0` is used when
/// the sub block does not belong to an extension.
BlockFinished(AnyExtension, &'a [u8]),
/// Decoded all information of the next frame.
///
/// The returned frame does **not** contain any owned image data.
Frame(&'a Frame<'static>),
/// Decoded some data of the current frame.
Data(&'a [u8]),
/// No more data available the current frame.
DataEnd,
}
/// Internal state of the GIF decoder
#[derive(Debug)]
enum State {
Magic(usize, [u8; 6]),
U16Byte1(U16Value, u8),
U16(U16Value),
Byte(ByteValue),
GlobalPalette(usize),
BlockStart(Option<Block>),
/// Block end, with remaining expected data. NonZero for invalid EOF.
BlockEnd(u8),
ExtensionBlock(AnyExtension),
SkipBlock(usize),
LocalPalette(usize),
LzwInit(u8),
DecodeSubBlock(usize),
FrameDecoded,
Trailer
}
use self::State::*;
/// U16 values that may occur in a GIF image
#[derive(Debug)]
enum U16Value {
/// Logical screen descriptor width
ScreenWidth,
/// Logical screen descriptor height
ScreenHeight,
/// Delay time
Delay,
/// Left frame offset
ImageLeft,
/// Top frame offset
ImageTop,
/// Frame width
ImageWidth,
/// Frame height
ImageHeight,
}
/// Single byte screen descriptor values
#[derive(Debug)]
enum ByteValue {
GlobalFlags,
Background { table_size: usize },
AspectRatio { table_size: usize },
ControlFlags,
ImageFlags,
TransparentIdx,
CodeSize,
}
/// GIF decoder which supports streaming
pub struct StreamingDecoder {
state: Option<State>,
lzw_reader: Option<LzwDecoder>,
decode_buffer: Vec<u8>,
skip_extensions: bool,
check_frame_consistency: bool,
check_for_end_code: bool,
allow_unknown_blocks: bool,
version: Version,
width: u16,
height: u16,
global_color_table: Vec<u8>,
background_color: [u8; 4],
/// ext buffer
ext: ExtensionData,
/// Frame data
current: Option<Frame<'static>>,
}
/// One version number of the GIF standard.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Version {
/// Version 87a, from May 1987.
V87a,
/// Version 89a, from July 1989.
V89a,
}
struct ExtensionData {
id: AnyExtension,
data: Vec<u8>,
is_block_end: bool,
}
impl StreamingDecoder {
/// Creates a new streaming decoder
pub fn new() -> StreamingDecoder {
let options = DecodeOptions::new();
Self::with_options(&options)
}
pub(crate) fn with_options(options: &DecodeOptions) -> Self {
StreamingDecoder {
state: Some(Magic(0, [0; 6])),
lzw_reader: None,
decode_buffer: vec![],
skip_extensions: true,
check_frame_consistency: options.check_frame_consistency,
check_for_end_code: options.check_for_end_code,
allow_unknown_blocks: options.allow_unknown_blocks,
version: Version::V87a,
width: 0,
height: 0,
global_color_table: Vec::new(),
background_color: [0, 0, 0, 0xFF],
ext: ExtensionData {
id: AnyExtension(0),
data: Vec::with_capacity(256), // 0xFF + 1 byte length
is_block_end: true,
},
current: None
}
}
/// Updates the internal state of the decoder.
///
/// Returns the number of bytes consumed from the input buffer
/// and the last decoding result.
pub fn update<'a>(&'a mut self, mut buf: &[u8])
-> Result<(usize, Decoded<'a>), DecodingError> {
// NOTE: Do not change the function signature without double-checking the
// unsafe block!
let len = buf.len();
while buf.len() > 0 && self.state.is_some() {
match self.next_state(buf) {
Ok((bytes, Decoded::Nothing)) => {
buf = &buf[bytes..]
}
Ok((bytes, Decoded::Trailer)) => {
buf = &buf[bytes..];
break
}
Ok((bytes, result)) => {
buf = &buf[bytes..];
return Ok(
(len-buf.len(),
// This transmute just casts the lifetime away. Since Rust only
// has SESE regions, this early return cannot be worked out and
// such that the borrow region of self includes the whole block.
// The explixit lifetimes in the function signature ensure that
// this is safe.
// ### NOTE
// To check that everything is sound, return the result without
// the match (e.g. `return Ok(self.next_state(buf)?)`). If
// it compiles the returned lifetime is correct.
unsafe {
mem::transmute::<Decoded, Decoded>(result)
}
))
}
Err(err) => return Err(err)
}
}
Ok((len-buf.len(), Decoded::Nothing))
}
/// Returns the data of the last extension that has been decoded.
pub fn last_ext(&self) -> (AnyExtension, &[u8], bool) {
(self.ext.id, &self.ext.data, self.ext.is_block_end)
}
#[inline(always)]
/// Current frame info as a mutable ref.
pub fn current_frame_mut<'a>(&'a mut self) -> &'a mut Frame<'static> {
self.current.as_mut().unwrap()
}
#[inline(always)]
/// Current frame info as a ref.
pub fn current_frame<'a>(&'a self) -> &'a Frame<'static> {
self.current.as_ref().unwrap()
}
/// Width of the image
pub fn width(&self) -> u16 {
self.width
}
/// Height of the image
pub fn height(&self) -> u16 {
self.height
}
/// The version number of the GIF standard used in this image.
///
/// We suppose a minimum of `V87a` compatibility. This value will be reported until we have
/// read the version information in the magic header bytes.
pub fn version(&self) -> Version {
self.version
}
/// Configure whether extensions are saved or skipped.
#[deprecated = "Does not work as intended. In fact, doesn't do anything. This may disappear soon."]
pub fn set_extensions(&mut self, extensions: Extensions) {
self.skip_extensions = match extensions {
Extensions::Skip => true,
Extensions::Save => false,
}
}
fn next_state<'a>(&'a mut self, buf: &[u8]) -> Result<(usize, Decoded<'a>), DecodingError> {
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 b = buf[0];
// Driver should ensure that state is never None
let state = self.state.take().unwrap();
//println!("{:?}", state);
match state {
Magic(i, mut version) => if i < 6 {
version[i] = b;
goto!(Magic(i+1, version))
} else if &version[..3] == b"GIF" {
self.version = match &version[3..] {
b"87a" => Version::V87a,
b"89a" => Version::V89a,
_ => return Err(DecodingError::format("unsupported GIF version"))
};
goto!(U16Byte1(U16Value::ScreenWidth, b))
} else {
Err(DecodingError::format("malformed GIF header"))
},
U16(next) => goto!(U16Byte1(next, b)),
U16Byte1(next, value) => {
use self::U16Value::*;
let value = ((b as u16) << 8) | value as u16;
match (next, value) {
(ScreenWidth, width) => {
self.width = width;
goto!(U16(U16Value::ScreenHeight))
},
(ScreenHeight, height) => {
self.height = height;
goto!(Byte(ByteValue::GlobalFlags))
},
(Delay, delay) => {
self.ext.data.push(value as u8);
self.ext.data.push(b);
self.current_frame_mut().delay = delay;
goto!(Byte(ByteValue::TransparentIdx))
},
(ImageLeft, left) => {
self.current_frame_mut().left = left;
goto!(U16(U16Value::ImageTop))
},
(ImageTop, top) => {
self.current_frame_mut().top = top;
goto!(U16(U16Value::ImageWidth))
},
(ImageWidth, width) => {
self.current_frame_mut().width = width;
goto!(U16(U16Value::ImageHeight))
},
(ImageHeight, height) => {
self.current_frame_mut().height = height;
goto!(Byte(ByteValue::ImageFlags))
}
}
}
Byte(value) => {
use self::ByteValue::*;
match value {
GlobalFlags => {
let global_table = b & 0x80 != 0;
let entries = if global_table {
let entries = PLTE_CHANNELS*(1 << ((b & 0b111) + 1) as usize);
self.global_color_table.reserve_exact(entries);
entries
} else {
0usize
};
goto!(Byte(Background { table_size: entries }))
},
Background { table_size } => {
goto!(
Byte(AspectRatio { table_size: table_size }),
emit Decoded::BackgroundColor(b)
)
},
AspectRatio { table_size } => {
goto!(GlobalPalette(table_size))
},
ControlFlags => {
self.ext.data.push(b);
let control_flags = b;
if control_flags & 1 != 0 {
// Set to Some(...), gets overwritten later
self.current_frame_mut().transparent = Some(0)
}
self.current_frame_mut().needs_user_input =
control_flags & 0b10 != 0;
self.current_frame_mut().dispose = match DisposalMethod::from_u8(
(control_flags & 0b11100) >> 2
) {
Some(method) => method,
None => DisposalMethod::Any
};
goto!(U16(U16Value::Delay))
}
TransparentIdx => {
self.ext.data.push(b);
if let Some(ref mut idx) = self.current_frame_mut().transparent {
*idx = b
}
goto!(SkipBlock(0))
//goto!(AwaitBlockEnd)
}
ImageFlags => {
let local_table = (b & 0b1000_0000) != 0;
let interlaced = (b & 0b0100_0000) != 0;
let table_size = b & 0b0000_0111;
self.current_frame_mut().interlaced = interlaced;
if self.check_frame_consistency {
// Consistency checks.
let (width, height) = (self.width, self.height);
let frame = self.current_frame_mut();
if width.checked_sub(frame.width) < Some(frame.left)
|| height.checked_sub(frame.height) < Some(frame.top)
{
return Err(DecodingError::format("frame descriptor is out-of-bounds"))
}
}
if local_table {
let entries = PLTE_CHANNELS * (1 << (table_size + 1));
self.current_frame_mut().palette =
Some(Vec::with_capacity(entries));
goto!(LocalPalette(entries))
} else {
goto!(Byte(CodeSize))
}
},
CodeSize => goto!(LzwInit(b))
}
}
GlobalPalette(left) => {
let n = cmp::min(left, buf.len());
if left > 0 {
self.global_color_table.extend_from_slice(&buf[..n]);
goto!(n, GlobalPalette(left - n))
} else {
let idx = self.background_color[0];
match self.global_color_table.chunks(PLTE_CHANNELS).nth(idx as usize) {
Some(chunk) => self.background_color[..PLTE_CHANNELS]
.copy_from_slice(&chunk[..PLTE_CHANNELS]),
None => self.background_color[0] = 0
}
goto!(BlockStart(Block::from_u8(b)), emit Decoded::GlobalPalette(
mem::replace(&mut self.global_color_table, Vec::new())
))
}
}
BlockStart(type_) => {
match type_ {
Some(Block::Image) => {
self.add_frame();
goto!(U16Byte1(U16Value::ImageLeft, b), emit Decoded::BlockStart(Block::Image))
}
Some(Block::Extension) => {
goto!(ExtensionBlock(AnyExtension(b)), emit Decoded::BlockStart(Block::Extension))
}
Some(Block::Trailer) => {
goto!(0, State::Trailer, emit Decoded::BlockStart(Block::Trailer))
}
None => {
if self.allow_unknown_blocks {
goto!(SkipBlock(b as usize))
} else {
Err(DecodingError::format("unknown block type encountered"))
}
}
}
}
BlockEnd(terminator) => {
if terminator == 0 {
if b == Block::Trailer as u8 {
goto!(0, BlockStart(Some(Block::Trailer)))
} else {
goto!(BlockStart(Block::from_u8(b)))
}
} else {
return Err(DecodingError::format(
"expected block terminator not found"
))
}
}
ExtensionBlock(id) => {
use Extension::*;
self.ext.id = id;
self.ext.data.clear();
self.ext.data.push(b);
if let Some(ext) = Extension::from_u8(id.0) {
match ext {
Control => {
goto!(self.read_control_extension(b)?)
}
Text | Comment | Application => {
goto!(SkipBlock(b as usize))
}
}
} else {
return Err(DecodingError::format(
"unknown extention block encountered"
))
}
}
SkipBlock(left) => {
let n = cmp::min(left, buf.len());
if left > 0 {
self.ext.data.extend_from_slice(&buf[..n]);
goto!(n, SkipBlock(left - n))
} else {
if b == 0 {
self.ext.is_block_end = true;
goto!(BlockEnd(b), emit Decoded::BlockFinished(self.ext.id, &self.ext.data))
} else {
self.ext.is_block_end = false;
goto!(SkipBlock(b as usize), emit Decoded::SubBlockFinished(self.ext.id, &self.ext.data))
}
}
}
LocalPalette(left) => {
let n = cmp::min(left, buf.len());
if left > 0 {
self.current_frame_mut().palette
.as_mut().unwrap().extend(buf[..n].iter().cloned());
goto!(n, LocalPalette(left - n))
} else {
goto!(LzwInit(b))
}
}
LzwInit(code_size) => {
// LZW spec: max 12 bits per code
if code_size > 11 {
return Err(DecodingError::format(
"invalid minimal code size"
))
}
self.lzw_reader = Some(LzwDecoder::new(BitOrder::Lsb, code_size));
goto!(DecodeSubBlock(b as usize), emit Decoded::Frame(self.current_frame_mut()))
}
DecodeSubBlock(left) => {
if left > 0 {
let n = cmp::min(left, buf.len());
let max_bytes = self.current_frame().required_bytes();
let decoder = self.lzw_reader.as_mut().unwrap();
if decoder.has_ended() {
debug_assert!(n > 0, "Made forward progress after LZW end");
return goto!(n, DecodeSubBlock(0), emit Decoded::Data(&[]));
}
let mut dummy_target;
let decode_target;
if self.decode_buffer.is_empty() {
let size = (1 << 14).min(max_bytes);
self.decode_buffer = vec![0; size];
}
if max_bytes == 0 {
dummy_target = [0; 16];
decode_target = &mut dummy_target[..];
} else {
decode_target = self.decode_buffer.as_mut_slice();
}
debug_assert!(!decode_target.is_empty(), "LZW decoding can make forward progress.");
let decoded = decoder.decode_bytes(&buf[..n], decode_target);
if let Err(err) = decoded.status {
return Err(io::Error::new(io::ErrorKind::InvalidData, &*format!("{:?}", err)).into());
}
let bytes = &self.decode_buffer[..decoded.consumed_out.min(max_bytes)];
let consumed = decoded.consumed_in;
goto!(consumed, DecodeSubBlock(left - consumed), emit Decoded::Data(bytes))
} else if b != 0 { // decode next sub-block
goto!(DecodeSubBlock(b as usize))
} else {
let max_bytes = self.current_frame().required_bytes();
// The end of the lzw stream is only reached if left == 0 and an additional call
// to `decode_bytes` results in an empty slice.
let decoder = self.lzw_reader.as_mut().unwrap();
// Some mutable bytes to decode into. We need this for forward progress in
// `lzw`. However, in some cases we do not actually need any bytes, when
// `max_bytes` is `0`.
let mut dummy_target;
let decode_target;
if self.decode_buffer.is_empty() {
let size = (1 << 14).min(max_bytes);
self.decode_buffer = vec![0; size];
}
if max_bytes == 0 {
dummy_target = [0; 16];
decode_target = &mut dummy_target[..];
} else {
decode_target = self.decode_buffer.as_mut_slice();
}
debug_assert!(!decode_target.is_empty(), "LZW decoding can make forward progress.");
let decoded = decoder.decode_bytes(&[], decode_target);
match decoded.status {
Ok(LzwStatus::Done) | Ok(LzwStatus::Ok) => {},
Ok(LzwStatus::NoProgress) => {
if self.check_for_end_code {
return Err(io::Error::new(io::ErrorKind::InvalidData, "No end code in lzw stream").into());
} else {
self.current = None;
return goto!(0, FrameDecoded, emit Decoded::DataEnd);
}
},
Err(err) => {
return Err(io::Error::new(io::ErrorKind::InvalidData, &*format!("{:?}", err)).into());
}
}
let bytes = &self.decode_buffer[..decoded.consumed_out.min(max_bytes)];
if bytes.len() > 0 {
goto!(0, DecodeSubBlock(0), emit Decoded::Data(bytes))
} else {
// end of image data reached
self.current = None;
goto!(0, FrameDecoded, emit Decoded::DataEnd)
}
}
}
FrameDecoded => {
goto!(BlockEnd(b))
}
Trailer => {
self.state = None;
Ok((1, Decoded::Trailer))
//panic!("EOF {:?}", self)
}
}
}
fn read_control_extension(&mut self, b: u8) -> Result<State, DecodingError> {
self.add_frame();
self.ext.data.push(b);
if b != 4 {
return Err(DecodingError::format(
"control extension has wrong length"
))
}
Ok(Byte(ByteValue::ControlFlags))
}
fn add_frame(&mut self) {
if self.current.is_none() {
self.current = Some(Frame::default())
}
}
}
#[test]
fn error_cast() {
let _ : Box<dyn error::Error> = DecodingError::Format(DecodingFormatError::new("testing")).into();
}

522
vendor/gif/src/reader/mod.rs vendored Normal file
View File

@ -0,0 +1,522 @@
use std::borrow::Cow;
use std::io;
use std::cmp;
use std::mem;
use std::iter;
use std::io::prelude::*;
use crate::common::{Block, Frame};
mod decoder;
pub use self::decoder::{
PLTE_CHANNELS, StreamingDecoder, Decoded, DecodingError, DecodingFormatError, Extensions,
Version
};
const N_CHANNELS: usize = 4;
/// Output mode for the image data
#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(u8)]
pub enum ColorOutput {
/// The decoder expands the image data to 32bit RGBA.
/// This affects:
///
/// - The buffer buffer of the `Frame` returned by `Decoder::read_next_frame`.
/// - `Decoder::fill_buffer`, `Decoder::buffer_size` and `Decoder::line_length`.
RGBA = 0,
/// The decoder returns the raw indexed data.
Indexed = 1,
}
#[derive(Clone, Debug)]
/// Memory limit in bytes. `MemoryLimit(0)` means
/// that there is no memory limit set.
pub struct MemoryLimit(pub u32);
impl MemoryLimit {
/// Enforce no memory limit.
///
/// If you intend to process images from unknown origins this is a potentially dangerous
/// constant to use, as your program could be vulnerable to decompression bombs. That is,
/// malicious images crafted specifically to require an enormous amount of memory to process
/// while having a disproportionately small file size.
///
/// The risks for modern machines are a bit smaller as the dimensions of each frame can not
/// exceed `u32::MAX` (~4Gb) but this is still a significant amount of memory.
pub const NONE: MemoryLimit = MemoryLimit(0);
fn buffer_size(&self, color: ColorOutput, width: u16, height: u16) -> Option<usize> {
let pixels = u32::from(width) * u32::from(height);
let bytes_per_pixel = match color {
ColorOutput::Indexed => 1,
ColorOutput::RGBA => 4,
};
if self.0 > 0 && pixels > self.0 / bytes_per_pixel {
None
} else {
Some(pixels as usize * bytes_per_pixel as usize)
}
}
}
/// Options for opening a GIF decoder.
#[derive(Clone, Debug)]
pub struct DecodeOptions {
memory_limit: MemoryLimit,
color_output: ColorOutput,
check_frame_consistency: bool,
check_for_end_code: bool,
allow_unknown_blocks: bool,
}
impl DecodeOptions {
/// Creates a new decoder builder
pub fn new() -> DecodeOptions {
DecodeOptions {
memory_limit: MemoryLimit(50_000_000), // 50 MB
color_output: ColorOutput::Indexed,
check_frame_consistency: false,
check_for_end_code: false,
allow_unknown_blocks: false,
}
}
/// Configure how color data is decoded.
pub fn set_color_output(&mut self, color: ColorOutput) {
self.color_output = color;
}
/// Configure a memory limit for decoding.
pub fn set_memory_limit(&mut self, limit: MemoryLimit) {
self.memory_limit = limit;
}
/// Configure if frames must be within the screen descriptor.
///
/// The default is `false`.
///
/// When turned on, all frame descriptors being read must fit within the screen descriptor or
/// otherwise an error is returned and the stream left in an unspecified state.
///
/// When turned off, frames may be arbitrarily larger or offset in relation to the screen. Many
/// other decoder libraries handle this in highly divergent ways. This moves all checks to the
/// caller, for example to emulate a specific style.
pub fn check_frame_consistency(&mut self, check: bool) {
self.check_frame_consistency = check;
}
/// Configure if LZW encoded blocks must end with a marker end code.
///
/// The default is `false`.
///
/// When turned on, all image data blocks—which are LZW encoded—must contain a special bit
/// sequence signalling the end of the data. LZW processing terminates when this code is
/// encountered. The specification states that it must be the last code output by the encoder
/// for an image.
///
/// When turned off then image data blocks can simply end. Note that this might silently ignore
/// some bits of the last or second to last byte.
pub fn check_lzw_end_code(&mut self, check: bool) {
self.check_for_end_code = check;
}
/// Configure if unknown blocks are allowed to be decoded.
///
/// The default is `false`.
///
/// When turned on, the decoder will allow unknown blocks to be in the
/// `BlockStart` position.
///
/// When turned off, decoded block starts must mark an `Image`, `Extension`,
/// or `Trailer` block. Otherwise, the decoded image will return an error.
/// If an unknown block error is returned from decoding, enabling this
/// setting may allow for a further state of decoding on the next attempt.
pub fn allow_unknown_blocks(&mut self, check: bool) {
self.allow_unknown_blocks = check;
}
/// Reads the logical screen descriptor including the global color palette
///
/// Returns a `Decoder`. All decoder configuration has to be done beforehand.
pub fn read_info<R: Read>(self, r: R) -> Result<Decoder<R>, DecodingError> {
Decoder::with_no_init(r, StreamingDecoder::with_options(&self), self).init()
}
}
struct ReadDecoder<R: Read> {
reader: io::BufReader<R>,
decoder: StreamingDecoder,
at_eof: bool
}
impl<R: Read> ReadDecoder<R> {
fn decode_next(&mut self) -> Result<Option<Decoded>, DecodingError> {
while !self.at_eof {
let (consumed, result) = {
let buf = self.reader.fill_buf()?;
if buf.len() == 0 {
return Err(DecodingError::format(
"unexpected EOF"
))
}
self.decoder.update(buf)?
};
self.reader.consume(consumed);
match result {
Decoded::Nothing => (),
Decoded::BlockStart(Block::Trailer) => {
self.at_eof = true
},
result => return Ok(unsafe{
// FIXME: #6393
Some(mem::transmute::<Decoded, Decoded>(result))
}),
}
}
Ok(None)
}
}
#[allow(dead_code)]
/// GIF decoder
pub struct Decoder<R: Read> {
decoder: ReadDecoder<R>,
color_output: ColorOutput,
memory_limit: MemoryLimit,
bg_color: Option<u8>,
global_palette: Option<Vec<u8>>,
current_frame: Frame<'static>,
buffer: Vec<u8>,
}
impl<R> Decoder<R> where R: Read {
/// Create a new decoder with default options.
pub fn new(reader: R) -> Result<Self, DecodingError> {
DecodeOptions::new().read_info(reader)
}
/// Return a builder that allows configuring limits etc.
pub fn build() -> DecodeOptions {
DecodeOptions::new()
}
fn with_no_init(reader: R, decoder: StreamingDecoder, options: DecodeOptions) -> Decoder<R> {
Decoder {
decoder: ReadDecoder {
reader: io::BufReader::new(reader),
decoder,
at_eof: false
},
bg_color: None,
global_palette: None,
buffer: Vec::with_capacity(32),
color_output: options.color_output,
memory_limit: options.memory_limit,
current_frame: Frame::default(),
}
}
fn init(mut self) -> Result<Self, DecodingError> {
loop {
match self.decoder.decode_next()? {
Some(Decoded::BackgroundColor(bg_color)) => {
self.bg_color = Some(bg_color)
}
Some(Decoded::GlobalPalette(palette)) => {
self.global_palette = if palette.len() > 0 {
Some(palette)
} else {
None
};
break
},
Some(_) => {
// Unreachable since this loop exists after the global
// palette has been read.
unreachable!()
},
None => return Err(DecodingError::format(
"file does not contain any image data"
))
}
}
// If the background color is invalid, ignore it
if let Some(ref palette) = self.global_palette {
if self.bg_color.unwrap_or(0) as usize >= (palette.len() / PLTE_CHANNELS) {
self.bg_color = None;
}
}
Ok(self)
}
/// Returns the next frame info
pub fn next_frame_info(&mut self) -> Result<Option<&Frame<'static>>, DecodingError> {
if !self.buffer.is_empty() {
// FIXME: Warn about discarding data?
self.buffer.clear();
}
loop {
match self.decoder.decode_next()? {
Some(Decoded::Frame(frame)) => {
self.current_frame = frame.clone();
if frame.palette.is_none() && self.global_palette.is_none() {
return Err(DecodingError::format(
"no color table available for current frame"
))
}
break
},
Some(_) => (),
None => return Ok(None)
}
}
Ok(Some(&self.current_frame))
}
/// Reads the next frame from the image.
///
/// Do not call `Self::next_frame_info` beforehand.
/// Deinterlaces the result.
pub fn read_next_frame(&mut self) -> Result<Option<&Frame<'static>>, DecodingError> {
if let Some(frame) = self.next_frame_info()? {
let (width, height) = (frame.width, frame.height);
let pixel_bytes = self.memory_limit
.buffer_size(self.color_output, width, height)
.ok_or_else(|| {
DecodingError::format("image is too large to decode")
})?;
debug_assert_eq!(
pixel_bytes, self.buffer_size(),
"Checked computation diverges from required buffer size"
);
let mut vec = vec![0; pixel_bytes];
self.read_into_buffer(&mut vec)?;
self.current_frame.buffer = Cow::Owned(vec);
self.current_frame.interlaced = false;
Ok(Some(&self.current_frame))
} else {
Ok(None)
}
}
/// Reads the data of the current frame into a pre-allocated buffer.
///
/// `Self::next_frame_info` needs to be called beforehand.
/// The length of `buf` must be at least `Self::buffer_size`.
/// Deinterlaces the result.
pub fn read_into_buffer(&mut self, buf: &mut [u8]) -> Result<(), DecodingError> {
if self.current_frame.interlaced {
let width = self.line_length();
let height = self.current_frame.height as usize;
for row in (InterlaceIterator { len: height, next: 0, pass: 0 }) {
if !self.fill_buffer(&mut buf[row*width..][..width])? {
return Err(DecodingError::format("image truncated"))
}
}
} else {
let buf = &mut buf[..self.buffer_size()];
if !self.fill_buffer(buf)? {
return Err(DecodingError::format("image truncated"))
}
};
Ok(())
}
/// Reads data of the current frame into a pre-allocated buffer until the buffer has been
/// filled completely.
///
/// `Self::next_frame_info` needs to be called beforehand. Returns `true` if the supplied
/// buffer could be filled completely. Should not be called after `false` had been returned.
pub fn fill_buffer(&mut self, mut buf: &mut [u8]) -> Result<bool, DecodingError> {
use self::ColorOutput::*;
const PLTE_CHANNELS: usize = 3;
macro_rules! handle_data(
($data:expr) => {
match self.color_output {
RGBA => {
let transparent = self.current_frame.transparent;
let palette: &[u8] = match self.current_frame.palette {
Some(ref table) => &*table,
None => &*self.global_palette.as_ref().unwrap(),
};
let len = cmp::min(buf.len()/N_CHANNELS, $data.len());
for (rgba, &idx) in buf[..len*N_CHANNELS].chunks_mut(N_CHANNELS).zip($data.iter()) {
let plte_offset = PLTE_CHANNELS * idx as usize;
if palette.len() >= plte_offset + PLTE_CHANNELS {
let colors = &palette[plte_offset..];
rgba[0] = colors[0];
rgba[1] = colors[1];
rgba[2] = colors[2];
rgba[3] = if let Some(t) = transparent {
if t == idx { 0x00 } else { 0xFF }
} else {
0xFF
}
}
}
(len, N_CHANNELS)
},
Indexed => {
let len = cmp::min(buf.len(), $data.len());
buf[..len].copy_from_slice(&$data[..len]);
(len, 1)
}
}
}
);
let buf_len = self.buffer.len();
if buf_len > 0 {
let (len, channels) = handle_data!(&self.buffer);
let _ = self.buffer.drain(..len);
buf = &mut buf[len*channels..];
if buf.len() == 0 {
return Ok(true)
}
}
loop {
match self.decoder.decode_next()? {
Some(Decoded::Data(data)) => {
let (len, channels) = handle_data!(data);
buf = &mut buf[len*channels..]; // shorten buf
if buf.len() > 0 {
continue
} else if len < data.len() {
self.buffer.extend_from_slice(&data[len..]);
}
return Ok(true)
},
Some(_) => return Ok(false), // make sure that no important result is missed
None => return Ok(false)
}
}
}
/// Output buffer size
pub fn buffer_size(&self) -> usize {
self.line_length() * self.current_frame.height as usize
}
/// Line length of the current frame
pub fn line_length(&self) -> usize {
use self::ColorOutput::*;
match self.color_output {
RGBA => self.current_frame.width as usize * N_CHANNELS,
Indexed => self.current_frame.width as usize
}
}
/// Returns the color palette relevant for the current (next) frame
pub fn palette(&self) -> Result<&[u8], DecodingError> {
// TODO prevent planic
Ok(match self.current_frame.palette {
Some(ref table) => &*table,
None => &*self.global_palette.as_ref().ok_or(DecodingError::format(
"no color table available for current frame"
))?,
})
}
/// The global color palette
pub fn global_palette(&self) -> Option<&[u8]> {
self.global_palette.as_ref().map(|v| &**v)
}
/// Width of the image
pub fn width(&self) -> u16 {
self.decoder.decoder.width()
}
/// Height of the image
pub fn height(&self) -> u16 {
self.decoder.decoder.height()
}
/// Index of the background color in the global palette
pub fn bg_color(&self) -> Option<usize> {
self.bg_color.map(|v| v as usize)
}
}
struct InterlaceIterator {
len: usize,
next: usize,
pass: usize
}
impl iter::Iterator for InterlaceIterator {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
if self.len == 0 || self.pass > 3 {
return None
}
let mut next = self.next + [8, 8, 4, 2][self.pass];
while next >= self.len {
next = [4, 2, 1, 0][self.pass];
self.pass += 1;
}
mem::swap(&mut next, &mut self.next);
Some(next)
}
}
#[cfg(test)]
mod test {
use std::fs::File;
use super::{Decoder, InterlaceIterator};
#[test]
fn test_simple_indexed() {
let mut decoder = Decoder::new(File::open("tests/samples/sample_1.gif").unwrap()).unwrap();
let frame = decoder.read_next_frame().unwrap().unwrap();
assert_eq!(&*frame.buffer, &[
1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
1, 1, 1, 0, 0, 0, 0, 2, 2, 2,
1, 1, 1, 0, 0, 0, 0, 2, 2, 2,
2, 2, 2, 0, 0, 0, 0, 1, 1, 1,
2, 2, 2, 0, 0, 0, 0, 1, 1, 1,
2, 2, 2, 2, 2, 1, 1, 1, 1, 1,
2, 2, 2, 2, 2, 1, 1, 1, 1, 1,
2, 2, 2, 2, 2, 1, 1, 1, 1, 1
][..])
}
#[test]
fn test_interlace_iterator() {
for &(len, expect) in &[
(0, &[][..]),
(1, &[0][..]),
(2, &[0, 1][..]),
(3, &[0, 2, 1][..]),
(4, &[0, 2, 1, 3][..]),
(5, &[0, 4, 2, 1, 3][..]),
(6, &[0, 4, 2, 1, 3, 5][..]),
(7, &[0, 4, 2, 6, 1, 3, 5][..]),
(8, &[0, 4, 2, 6, 1, 3, 5, 7][..]),
(9, &[0, 8, 4, 2, 6, 1, 3, 5, 7][..]),
(10, &[0, 8, 4, 2, 6, 1, 3, 5, 7, 9][..]),
(11, &[0, 8, 4, 2, 6, 10, 1, 3, 5, 7, 9][..]),
(12, &[0, 8, 4, 2, 6, 10, 1, 3, 5, 7, 9, 11][..]),
(13, &[0, 8, 4, 12, 2, 6, 10, 1, 3, 5, 7, 9, 11][..]),
(14, &[0, 8, 4, 12, 2, 6, 10, 1, 3, 5, 7, 9, 11, 13][..]),
(15, &[0, 8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13][..]),
(16, &[0, 8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15][..]),
(17, &[0, 8, 16, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15][..]),
] {
let iter = InterlaceIterator { len: len, next: 0, pass: 0 };
let lines = iter.collect::<Vec<_>>();
assert_eq!(lines, expect);
}
}
}

49
vendor/gif/src/traits.rs vendored Normal file
View File

@ -0,0 +1,49 @@
//! Traits used in this library
use std::io;
/// Writer extension to write little endian data
pub trait WriteBytesExt<T> {
/// Writes `T` to a bytes stream. Least significant byte first.
fn write_le(&mut self, n: T) -> io::Result<()>;
/*
#[inline]
fn write_byte(&mut self, n: u8) -> io::Result<()> where Self: Write {
self.write_all(&[n])
}
*/
}
impl<W: io::Write + ?Sized> WriteBytesExt<u8> for W {
#[inline]
fn write_le(&mut self, n: u8) -> io::Result<()> {
self.write_all(&[n])
}
}
impl<W: io::Write + ?Sized> WriteBytesExt<u16> for W {
#[inline]
fn write_le(&mut self, n: u16) -> io::Result<()> {
self.write_all(&[n as u8, (n>>8) as u8])
}
}
impl<W: io::Write + ?Sized> WriteBytesExt<u32> for W {
#[inline]
fn write_le(&mut self, n: u32) -> io::Result<()> {
self.write_le(n as u16)?;
self.write_le((n >> 16) as u16)
}
}
impl<W: io::Write + ?Sized> WriteBytesExt<u64> for W {
#[inline]
fn write_le(&mut self, n: u64) -> io::Result<()> {
self.write_le(n as u32)?;
self.write_le((n >> 32) as u32)
}
}

170
vendor/gif/tests/check_testimages.rs vendored Normal file
View File

@ -0,0 +1,170 @@
extern crate gif;
extern crate glob;
use std::collections::HashMap;
use std::fs::File;
use std::path::PathBuf;
use std::io::BufReader;
use std::io::prelude::*;
const BASE_PATH: [&'static str; 2] = [".", "tests"];
fn process_images<F>(func: F)
where F: Fn(PathBuf) -> Result<u32, gif::DecodingError> {
let base: PathBuf = BASE_PATH.iter().collect();
let test_suites = &["samples"];
let mut results = HashMap::new();
let mut expected_failures = 0;
for suite in test_suites {
let mut path = base.clone();
path.push(suite);
path.push("*.gif");
let pattern = &*format!("{}", path.display());
for path in glob::glob(pattern).unwrap().filter_map(Result::ok) {
print!("{}: ", path.to_string_lossy());
match func(path.clone()) {
Ok(crc) => {
results.insert(path, format!("{}", crc));
println!("{}", crc)
},
Err(_) if path.file_name().unwrap().to_str().unwrap().starts_with("x") => {
expected_failures += 1;
println!("Expected failure")
},
err => panic!("{:?}", err)
}
}
}
let mut path = base.clone();
path.push("results.txt");
let mut ref_results = HashMap::new();
let mut failures = 0;
for line in BufReader::new(File::open(path).unwrap()).lines() {
let line = line.unwrap();
let parts: Vec<_> = line.split(": ").collect();
if parts[1] == "Expected failure" {
failures += 1;
} else {
ref_results.insert(PathBuf::from(parts[0]), parts[1].to_string());
}
}
assert_eq!(expected_failures, failures);
for (path, crc) in results.iter() {
assert_eq!(
ref_results.get(path).expect(&format!("reference for {:?} is missing", path)),
crc
)
}
}
#[test]
fn render_images() {
process_images(|path| {
let mut decoder = gif::DecodeOptions::new();
decoder.set_color_output(gif::ColorOutput::RGBA);
let file = File::open(path)?;
let mut decoder = decoder.read_info(file)?;
let mut crc = Crc32::new();
while let Some(frame) = decoder.read_next_frame()? {
// First sanity check:
assert_eq!(
frame.buffer.len(),
frame.width as usize
* frame.height as usize
* 4
);
crc.update(&*frame.buffer);
}
Ok(crc.checksum())
})
}
const CRC_TABLE: [u32; 256] = [
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
0x2d02ef8d
];
/// Crc-32 checksum calculation
#[derive(Debug)]
pub struct Crc32 {
crc: u32,
}
impl Crc32 {
/// Create a new hasher.
pub fn new() -> Crc32 {
Crc32 {crc: 0xFFFFFFFF}
}
/// Resets the hasher.
pub fn reset(&mut self) {
*self = Self::new()
}
/// Update the internal hasher with the bytes from ```buf```
pub fn update(&mut self, buf: &[u8]) {
for &byte in buf {
let a = (self.crc ^ byte as u32) & 0xFF;
let b = self.crc >> 8;
self.crc = CRC_TABLE[a as usize] ^ b;
}
}
/// Return the computed hash.
pub fn checksum(&self) -> u32 {
self.crc ^ 0xFFFFFFFF
}
}

28
vendor/gif/tests/crashtest.rs vendored Normal file
View File

@ -0,0 +1,28 @@
use std::{fs, io};
use gif::DecodeOptions;
#[test]
fn try_decode_crash_regression() {
let files = fs::read_dir(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/crashtest")).unwrap();
let options = DecodeOptions::new();
for entry in files {
let entry = entry.unwrap();
if let Some(ext) = entry.path().extension() {
if ext.to_str() != Some("gif") {
panic!("Unexpected file {} in crashtests, should end with .gif", entry.path().display());
}
} else {
panic!("Unexpected file {} in crashtests, should end with .gif", entry.path().display());
}
let file_data = fs::read(entry.path()).unwrap();
let _ = try_decode_file(&options, file_data);
}
}
fn try_decode_file(options: &DecodeOptions, data: Vec<u8>) -> Result<(), gif::DecodingError> {
let mut reader = options.clone().read_info(io::Cursor::new(data))?;
while reader.read_next_frame()?.is_some() {}
Ok(())
}

84
vendor/gif/tests/decode.rs vendored Normal file
View File

@ -0,0 +1,84 @@
use gif::{DecodeOptions, DisposalMethod, Encoder, Frame};
#[test]
fn frame_consistency_is_configurable() {
let image = create_image_with_oob_frames();
{
let options = DecodeOptions::new();
let mut data = image.as_slice();
let mut decoder = options.clone().read_info(&mut data).unwrap();
assert!(decoder.read_next_frame().is_ok());
assert!(decoder.read_next_frame().is_ok());
}
{
let mut options = DecodeOptions::new();
options.check_frame_consistency(true);
let mut data = image.as_slice();
let mut decoder = options.clone().read_info(&mut data).unwrap();
assert!(decoder.read_next_frame().is_ok());
assert!(decoder.read_next_frame().is_err());
}
{
let mut options = DecodeOptions::new();
options.check_frame_consistency(false);
let mut data = image.as_slice();
let mut decoder = options.clone().read_info(&mut data).unwrap();
assert!(decoder.read_next_frame().is_ok());
assert!(decoder.read_next_frame().is_ok());
}
}
fn create_image_with_oob_frames() -> Vec<u8> {
let mut data = vec![];
let mut encoder = Encoder::new(&mut data, 2, 2, &[0, 0, 0]).unwrap();
let mut frame = Frame {
delay: 1,
dispose: DisposalMethod::Any,
transparent: None,
needs_user_input: false,
top: 0,
left: 0,
width: 2,
height: 2,
interlaced: false,
palette: None,
buffer: vec![0, 0, 0, 0].into(),
};
encoder.write_frame(&frame).unwrap();
frame.top = 1;
frame.left = 1;
encoder.write_frame(&frame).unwrap();
drop(encoder);
data
}
#[test]
fn check_for_end_code_is_configurable() {
// In this particular image, the image data of the 62nd frame has no end code.
let image: &[u8] = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/samples/gifplayer-muybridge.gif"));
{
let options = DecodeOptions::new();
let mut decoder = options.clone().read_info(&image[..]).unwrap();
for _ in 0..61 {
assert!(decoder.read_next_frame().is_ok());
}
assert!(decoder.read_next_frame().is_ok());
}
{
let mut options = DecodeOptions::new();
options.check_lzw_end_code(true);
let mut decoder = options.clone().read_info(&image[..]).unwrap();
for _ in 0..61 {
assert!(decoder.read_next_frame().is_ok());
}
assert!(decoder.read_next_frame().is_err());
}
}

10
vendor/gif/tests/results.txt vendored Normal file
View File

@ -0,0 +1,10 @@
tests/samples/alpha_gif_a.gif: 3871893825
tests/samples/anim-gr.gif: 291646878
tests/samples/beacon.gif: 2462153529
tests/samples/interlaced.gif: 2115495372
tests/samples/moon_impact.gif: 2438689726
tests/samples/sample_1.gif: 3275424619
tests/samples/2x2.gif: 3802149240
tests/samples/sample_big.gif: 4184562096
tests/samples/gifplayer-muybridge.gif: 4267078865
tests/samples/set_hsts.gif: 224161812

103
vendor/gif/tests/roundtrip.rs vendored Normal file
View File

@ -0,0 +1,103 @@
use gif::{ColorOutput, Decoder, Encoder, Frame};
#[test]
fn encode_roundtrip() {
const ORIGINAL: &'static [u8] = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/samples/2x2.gif"));
round_trip_from_image(ORIGINAL);
}
fn round_trip_from_image(original: &[u8]) {
let (width, height, global_palette);
let frames: Vec<Frame> = {
let mut decoder = Decoder::new(original).unwrap();
width = decoder.width();
height = decoder.height();
global_palette = decoder
.global_palette()
.unwrap_or_default()
.to_vec();
core::iter::from_fn(move || {
decoder.read_next_frame().unwrap().cloned()
}).collect()
};
let mut encoder = Encoder::new(vec![], width, height, &global_palette).unwrap();
for frame in &frames {
encoder.write_frame(frame).unwrap();
}
let buffer = encoder.into_inner().unwrap();
{
let mut decoder = Decoder::new(&buffer[..]).expect("Invalid info encoded");
assert_eq!(decoder.width(), width);
assert_eq!(decoder.height(), height);
assert_eq!(global_palette, decoder.global_palette().unwrap_or_default());
let new_frames: Vec<_> = core::iter::from_fn(move || {
decoder.read_next_frame().unwrap().cloned()
}).collect();
assert_eq!(new_frames.len(), frames.len(), "Diverging number of frames");
for (new, reference) in new_frames.iter().zip(&frames) {
assert_eq!(new.delay, reference.delay);
assert_eq!(new.dispose, reference.dispose);
assert_eq!(new.transparent, reference.transparent);
assert_eq!(new.needs_user_input, reference.needs_user_input);
assert_eq!(new.top, reference.top);
assert_eq!(new.left, reference.left);
assert_eq!(new.width, reference.width);
assert_eq!(new.height, reference.height);
assert_eq!(new.interlaced, reference.interlaced);
assert_eq!(new.palette, reference.palette);
assert_eq!(new.buffer, reference.buffer);
}
}
}
#[test]
#[cfg(feature = "color_quant")]
fn encode_roundtrip_few_colors() {
const WIDTH: u16 = 128;
const HEIGHT: u16 = 128;
// Build an image with a single red pixel, that NeuQuant won't
// sample, in order to check that we do appropriatelyq specialise the
// few-colors case.
let mut pixels: Vec<u8> = vec![255; WIDTH as usize * HEIGHT as usize * 4];
// Top-left pixel is always sampled, so use the second pixel.
pixels[5] = 0;
pixels[6] = 0;
// Set speed to 30 to handily avoid sampling that one pixel.
//
// We clone "pixels", since the parameter is replaced with a
// paletted version, and later we want to compare the output with
// the original RGBA image.
let mut frame = Frame::from_rgba_speed(WIDTH, HEIGHT, &mut pixels.clone(), 30);
let mut buffer = vec![];
{
let mut encoder = Encoder::new(&mut buffer, WIDTH, HEIGHT, &[]).unwrap();
encoder.write_frame(&frame).unwrap();
frame.make_lzw_pre_encoded();
encoder.write_lzw_pre_encoded_frame(&frame).unwrap();
}
{
let mut decoder = {
let mut builder = Decoder::<&[u8]>::build();
builder.set_color_output(ColorOutput::RGBA);
builder.read_info(&buffer[..]).expect("Invalid info encoded")
};
// Only check key fields, assuming "round_trip_from_image"
// covers the rest. We are primarily concerned with quantisation.
assert_eq!(decoder.width(), WIDTH);
assert_eq!(decoder.height(), HEIGHT);
let new_frames: Vec<_> = core::iter::from_fn(move || {
decoder.read_next_frame().unwrap().cloned()
}).collect();
assert_eq!(new_frames.len(), 2, "Diverging number of frames");
// NB: reference.buffer can't be used as it contains the palette version.
assert_eq!(new_frames[0].buffer, pixels);
assert_eq!(new_frames[1].buffer, pixels);
}
}

42
vendor/gif/tests/stall.rs vendored Normal file
View File

@ -0,0 +1,42 @@
use std::{fs, sync::mpsc, thread, time::Duration};
#[test]
fn try_decode_crash_regression() {
let files = fs::read_dir(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/stall")).unwrap();
for entry in files {
let entry = entry.unwrap();
if let Some(ext) = entry.path().extension() {
if ext.to_str() != Some("gif") {
panic!("Unexpected file {} in crashtests, should end with .gif", entry.path().display());
}
} else {
panic!("Unexpected file {} in crashtests, should end with .gif", entry.path().display());
}
let file_data = fs::read(entry.path()).unwrap();
let _ = decode_on_timer(file_data);
}
}
fn decode_on_timer(data: Vec<u8>) {
let (send, recv) = mpsc::channel();
thread::spawn(move || {
let result = decode(&data);
send.send(result).expect("still waiting");
});
let _ = recv.recv_timeout(Duration::from_secs(1))
.expect("any result");
}
fn decode(data: &[u8]) -> Result<(), gif::DecodingError> {
let mut options = gif::DecodeOptions::new();
options.set_color_output(gif::ColorOutput::RGBA);
let mut decoder = options.read_info(data)?;
while let Some(_frame) = decoder.read_next_frame()? {}
Ok(())
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 B