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

View File

@ -0,0 +1 @@
{"files":{"CHANGELOG.md":"ab750336ba92a7917c8cd95721467a3e5ad09b67128dcf9e8828a07344ddbe65","Cargo.lock":"52250936726e85e95f581650d9cbf5d3927c88cf33a7b9d69457a8cf5be4ac7c","Cargo.toml":"24ee6d3db1b9376f01c35af0eae3ec7249639c1b12dee48a19b169b4d18c4b4a","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"22b83fb92a50418c7df9564b9c179fa2a0e74f293bc58b72dd7e218fc0902143","README.md":"9627c2e0e1390599b3c53b6486074ad81899bfdc31b6c98230e5ab74125fbc14","appveyor.yml":"5861c1d97075d4a09aea38b0ade35c0a03498f41cdc4f8754122b3793ddb6493","benches/decoding_benchmark.rs":"1973c084e1af3d1d13662cdcc467a8c7711bfd75fee485d9a4387035b654507f","benches/large_image.jpg":"4cd902020e963fffd23e3a0d0be71207efe665265518fe5a3809ba77699a5fd0","benches/large_image.rs":"5c45235e625879dd8bbc8bd513864fba6aab513918a79114f4c3bf0fdd801cc0","benches/tower.jpg":"40b5ae0df66540ba3ac60edf2840b4b8edd0500706105f3b63083e3a8993119a","benches/tower_grayscale.jpg":"90519862a5bc59ca5702ff802a2f16e8cdcd28b124f891bb03556b920742d94e","benches/tower_progressive.jpg":"e87bac231e2c11b1aef6981358189ad5c51f67069a43131b1512ed823a7607c6","examples/decode.rs":"44731268ab63d532e18f4556ecd906622c19a5d55e900d2c7ddbb37c032df2fc","rust-toolchain":"b37685c6d7646baf7933322dcbdadf302fa6d575cf2be7c6c3a2402424453113","src/arch/mod.rs":"c962dcb6e5255c6c2d7b9745d1b99841f7edaed904193d46e48fe06dff36bce7","src/arch/neon.rs":"9ee5fbebb8621b69e2031162a61269cd0784883fbe574974250ac2a23b66dd74","src/arch/ssse3.rs":"b6a0f177bdb5c9f7cfc325c533fc48b16a620c9d868e48b070811d819c719066","src/decoder.rs":"a6061b29d0d1a09121ff7d883e11f83889a30d820c7a00baab781fc09e74e855","src/decoder/lossless.rs":"62245c58c6cdb256c49a60f93c39c339ac59a8ff1b88b0b7d672866aa43c8782","src/error.rs":"7c0deaa7c58b4f1c00a42616ce0b8b37c1040ac85fcdade36ca20aa0c2435a61","src/huffman.rs":"80df6386478375e99614c4639af004aede2927763645b0a49df6b6d083eef5f0","src/idct.rs":"d296b125388d71fc2abf8df812ba68b58712196748d3a07a0111e216d1c3d75e","src/lib.rs":"f8fc7484fa5e66728f248377eef89477b16ae0891850bdb8640a15566ca82cf1","src/marker.rs":"e68c3ffc47bfb16b2f08ed169abde7d95369e1a887ba3a6edd3d453ba0e1e2af","src/parser.rs":"9a47082dc14ba7a02536c17cae6d7551460156547f967696e16978cf7940e83e","src/upsampler.rs":"c4e1e2d93f4373933b0bb34ea0b488af70925dca974aa95713a44bf8ffe942d6","src/worker/immediate.rs":"1862e4e6ccd6dcf04fb3943e6f1a695066c4a40b0e7d9e6ec00f5c4a814077a4","src/worker/mod.rs":"c99107e5d7a468ecc787f00c03ba26bd9f7e239a0c5bf6e7a732f7f5176f6e8a","src/worker/multithreaded.rs":"1937b6b470d0f2e918a3a88cd782bec66a5c9a6876510dd37769faabe502d69c","src/worker/rayon.rs":"95ea76aa149c87d4f036020010248fca25a8a8dda7751072b54605f9764e712f","tests/lib.rs":"e7adbbf8b6585aee85cde1a22d37e126ebe4228714a1db33195db217199ba935","tests/rayon-0.rs":"09cd6f6f4cc6cb23e629a53801cbd981e4c84ce380b0206892ee2b7907edc004","tests/rayon-1.rs":"48c65a31da2d3891ac52191813351293907588d1f10549f53a649b287af15784","tests/rayon-2.rs":"f847b26b3363a6bc59665ecb0dfb9cc3a16356f2a7ffbdd60de49d8c649c071f","tests/rayon.rs":"fc3688f55a39a07cb54fe615bc4beb468d61bc3e0ef1bb87b5ca01c7b3d85d7e"},"package":"bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e"}

157
vendor/jpeg-decoder/CHANGELOG.md vendored Normal file
View File

@ -0,0 +1,157 @@
# Change Log
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## v0.3.0 (2022-10-17)
- The MSRV policy is now managed by the `rust-version` field in `Cargo.toml`.
- The color transform can now be overridden as well as hinted with
`Decoder::set_color_transform`.
## v0.2.6 (2022-05-09)
- Another fix to allow usage in WASM target.
- Decoding in the WASM target is now actively tested in CI.
## v0.2.5 (2022-05-02)
- Fix single threaded usage in WASM target.
## v0.2.4 (2022-04-01)
- Corrects minimal version requirements of dependency `rayon`.
## v0.2.3 (2022-02-14)
- Added `Decoder::set_max_decoding_buffer_size` which limits the bytes
allocated for the output of the decoding process.
- Added Arm64-Neon intrinsic implementation of idct and color conversion. This
depends on a Rust nightly compiler feature ([`aarch64_target_feature`]) and
it must be explicitly enabled. As soon as the minimum supported Rust version
includes the stabilization of this feature, the code will be enabled by
default and the feature changed to do nothing.
[`aarch64_target_feature`]: https://github.com/rust-lang/rust/issues/90620
## v0.2.2 (2022-02-12)
- Added and SSE3-specific SIMD intrinsic implementation for idct and color
conversion. It will run if applicable targets are detect at _runtime_.
- The SIMD implementation is not bit-for-bit compatible with non-SIMD output.
You can enable the `platform_independent` feature, to ensure that only
bit-for-bit equivalent code runs and output is the same on all platforms.
- Improved performance some more by avoiding bounds checks with array types.
- Multithreading is now used more frequently, without the rayon target, except
on an explicit list of unsupported platforms.
## v0.2.1 (2022-12-09)
- Fix decoding error due to conflict of lossless with some spectral selections.
## v0.2.0 (2021-12-04)
- Added Lossless JPEG support
- Added support for EXIF and ICC data
- Minimum supported rust version changed to 1.48 and no formal policy for bump releases for now
- Minor stability fixes on invalid jpeg images
## v0.1.22 (2021-01-27)
- Fix panic on jpeg without frames.
## v0.1.21 (2021-01-23)
- Fix incorrect order of MCUs in non-interleaved streams
- DCT Progressive images with incomplete coefficient blocks are now rendered
- Fix a panic on invalid dimensions
- Reduce allocations and runtime of decoding
- Rework multi-threading to run a thread per component
## v0.1.20 (2020-07-04)
- Fix decoding of some progressive images failing
- Several more performance improvements
- Add `PixelFormat::pixel_bytes` to determine the size of pixels
- Cleanup and clarification of the 8x8 idct implementation
- Updated fuzzing harnesses and helpers
## v0.1.19 (2020-04-27)
- Fix decoding returning too much image data
- Fix recognizing padding in marker segments
- Several decode performance improvements
- Remove use of deprecated `Error::description`
## v0.1.18 (2019-12-10)
- Fix two bugs causing panics introduced in 0.1.17.
## v0.1.17 (2019-12-08)
- Minimum supported rust version changed to 1.34
- Fix clippy::into_iter_on_array warning
- Ignore extraneous bytes after SOS
- Support IDCT Scaling
## v0.1.16 (2019-08-25)
- Minimum supported rust version changed to 1.28
- Allow zero length DHT segments
## v0.1.15 (2018-06-10)
- Added support for WebAssembly and asm.js (thanks @CryZe!)
- Bugfix for images with APP14 segments longer than 12 bytes.
## v0.1.14 (2018-02-15)
- Updated `rayon` to 1.0.
## v0.1.13 (2017-06-14)
- Updated `rayon` to 0.8.
## v0.1.12 (2017-04-07)
- Fixed an integer overflow in `derive_huffman_codes`.
- Updated `rayon` to 0.7.
## v0.1.11 (2017-01-09)
- Fixed an integer overflow.
- Updated `byteorder` to 1.0.
## v0.1.10 (2016-12-23)
- Updated `rayon` to 0.6
## v0.1.9 (2016-12-12)
- Added a generic integer upsampler, which brings support for some unusual subsampling schemes, e.g. 4:1:1 (thanks @iamrohit7!)
- Made rayon optional through the `rayon` cargo feature (thanks @jackpot51!)
## v0.1.8 (2016-11-05)
* Updated rayon to version 0.5.
## v0.1.7 (2016-10-04)
- Added `UnsupportedFeature::NonIntegerSubsamplingRatio` error
- Fixed a bug which could cause certain images to fail decoding
- Fixed decoding of JPEGs which has a final RST marker in their entropy-coded data
- Avoid allocating coefficients when calling `read_info()` on progressive JPEGs
## v0.1.6 (2016-07-12)
- Added support for 16-bit quantization tables (even though the JPEG spec explicitly
states "An 8-bit DCT-based process shall not use a 16-bit precision quantization table",
but since libjpeg allows it there is little choice...)
- Added support for decoding files with extraneous data (this violates the JPEG spec, but libjpeg allows it)
- Fixed panic when decoding files without SOF
- Fixed bug which caused files with certain APP marker segments to fail decoding
## v0.1.5 (2016-06-22)
- Removed `euclid` and `num-rational` dependencies
- Updated `rayon` to 0.4
## v0.1.4 (2016-04-20)
- Replaced `num` with `num-rational`
## v0.1.3 (2016-04-06)
- Updated `byteorder` to 0.5
## v0.1.2 (2016-03-08)
- Fixed a bug which was causing some progressive JPEGs to fail decoding
- Performance improvements
## v0.1.1 (2016-02-29)
- Performance improvements
## v0.1.0 (2016-02-13)
- Initial release

700
vendor/jpeg-decoder/Cargo.lock generated vendored Normal file
View File

@ -0,0 +1,700 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "adler32"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
[[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.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d"
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[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 = "console_error_panic_hook"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
dependencies = [
"cfg-if",
"wasm-bindgen",
]
[[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 = "deflate"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174"
dependencies = [
"adler32",
"byteorder",
]
[[package]]
name = "either"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
[[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 = "jpeg-decoder"
version = "0.3.0"
dependencies = [
"criterion",
"png",
"rayon",
"walkdir",
"wasm-bindgen",
"wasm-bindgen-test",
]
[[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.135"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c"
[[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.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435"
dependencies = [
"adler32",
]
[[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.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "once_cell"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
[[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.16.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6"
dependencies = [
"bitflags",
"crc32fast",
"deflate",
"miniz_oxide",
]
[[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.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
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.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
[[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 = "scoped-tls"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
[[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.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
[[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.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41feea4228a6f1cd09ec7a3593a682276702cd67b5273544757dae23c096f074"
dependencies = [
"itoa 1.0.4",
"ryu",
"serde",
]
[[package]]
name = "syn"
version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1"
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-futures"
version = "0.4.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d"
dependencies = [
"cfg-if",
"js-sys",
"wasm-bindgen",
"web-sys",
]
[[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 = "wasm-bindgen-test"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09d2fff962180c3fadf677438054b1db62bee4aa32af26a45388af07d1287e1d"
dependencies = [
"console_error_panic_hook",
"js-sys",
"scoped-tls",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-bindgen-test-macro",
]
[[package]]
name = "wasm-bindgen-test-macro"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4683da3dfc016f704c9f82cf401520c4f1cb3ee440f7f52b3d6ac29506a49ca7"
dependencies = [
"proc-macro2",
"quote",
]
[[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 = "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"

81
vendor/jpeg-decoder/Cargo.toml vendored Normal file
View File

@ -0,0 +1,81 @@
# 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"
rust-version = "1.61.0"
name = "jpeg-decoder"
version = "0.3.0"
authors = ["The image-rs Developers"]
exclude = [
"/tests/*",
"!/tests/*.rs",
]
description = "JPEG decoder"
documentation = "https://docs.rs/jpeg-decoder"
readme = "README.md"
keywords = [
"jpeg",
"jpg",
"decoder",
"image",
]
license = "MIT / Apache-2.0"
repository = "https://github.com/image-rs/jpeg-decoder"
resolver = "2"
[[test]]
name = "rayon"
required-features = ["rayon"]
[[test]]
name = "rayon-0"
required-features = ["rayon"]
[[test]]
name = "rayon-1"
required-features = ["rayon"]
[[test]]
name = "rayon-2"
required-features = ["rayon"]
[[bench]]
name = "decoding_benchmark"
harness = false
[[bench]]
name = "large_image"
harness = false
[dependencies.rayon]
version = "1.5.1"
optional = true
[dev-dependencies.criterion]
version = "0.3"
[dev-dependencies.png]
version = "0.16"
[dev-dependencies.walkdir]
version = "2.0"
[dev-dependencies.wasm-bindgen]
version = "=0.2.83"
[dev-dependencies.wasm-bindgen-test]
version = "0.3"
[features]
default = ["rayon"]
nightly_aarch64_neon = []
platform_independent = []

201
vendor/jpeg-decoder/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/jpeg-decoder/LICENSE-MIT vendored Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2016 The jpeg-decoder Developers
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.

38
vendor/jpeg-decoder/README.md vendored Normal file
View File

@ -0,0 +1,38 @@
# jpeg-decoder
[![Rust CI](https://github.com/image-rs/jpeg-decoder/workflows/Rust%20CI/badge.svg)](https://github.com/image-rs/jpeg-decoder/actions)
[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/k65rrkd0f8yb4o9w/branch/master?svg=true)](https://ci.appveyor.com/project/kaksmet/jpeg-decoder/branch/master)
[![Crates.io](https://img.shields.io/crates/v/jpeg-decoder.svg)](https://crates.io/crates/jpeg-decoder)
A Rust library for decoding JPEGs.
[Documentation](https://docs.rs/jpeg-decoder)
## Example
Cargo.toml:
```toml
[dependencies]
jpeg-decoder = "0.2"
```
main.rs:
```rust
extern crate jpeg_decoder as jpeg;
use std::fs::File;
use std::io::BufReader;
fn main() {
let file = File::open("hello_world.jpg").expect("failed to open file");
let mut decoder = jpeg::Decoder::new(BufReader::new(file));
let pixels = decoder.decode().expect("failed to decode image");
let metadata = decoder.info().unwrap();
}
```
## Requirements
This crate compiles with rust >= 1.48. Minimum Supported Rust Version:
- All releases `0.1.*` compile with rust >= 1.36.
- The releases `0.2.*` may bump Rust Version requirements (TBD).

20
vendor/jpeg-decoder/appveyor.yml vendored Normal file
View File

@ -0,0 +1,20 @@
environment:
matrix:
- TARGET: x86_64-pc-windows-msvc
- TARGET: i686-pc-windows-msvc
- TARGET: i686-pc-windows-gnu
install:
- ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-nightly-${env:TARGET}.exe"
- rust-nightly-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust"
- SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin
- SET PATH=%PATH%;C:\MinGW\bin
- rustc -V
- cargo -V
build: false
test_script:
- cargo build --verbose
- cargo test --tests --benches
- cargo bench --bench decoding_benchmark --no-default-features --features "$FEATURES" -- --warm-up-time 1 --measurement-time 1 --sample-size 25

View File

@ -0,0 +1,41 @@
extern crate criterion;
extern crate jpeg_decoder;
use criterion::{black_box, Criterion};
use jpeg_decoder as jpeg;
use jpeg_decoder::ImageInfo;
fn read_image(image: &[u8]) -> Vec<u8> {
jpeg::Decoder::new(black_box(image)).decode().unwrap()
}
fn read_metadata(image: &[u8]) -> ImageInfo {
let mut decoder = jpeg::Decoder::new(black_box(image));
decoder.read_info().unwrap();
decoder.info().unwrap()
}
fn main() {
let mut c = Criterion::default().configure_from_args();
c.bench_function("decode a 512x512 JPEG", |b| b.iter(|| {
read_image(include_bytes!("tower.jpg"))
}));
c.bench_function("decode a 512x512 progressive JPEG", |b| b.iter(|| {
read_image(include_bytes!("tower_progressive.jpg"))
}));
c.bench_function("decode a 512x512 grayscale JPEG", |b| b.iter(|| {
read_image(include_bytes!("tower_grayscale.jpg"))
}));
c.bench_function("extract metadata from an image", |b| b.iter(|| {
read_metadata(include_bytes!("tower.jpg"))
}));
c.bench_function("decode a 3072x2048 RGB Lossless JPEG", |b| b.iter(|| {
read_image(include_bytes!("../tests/reftest/images/lossless/jpeg_lossless_sel1-rgb.jpg"))
}));
c.final_summary();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 KiB

View File

@ -0,0 +1,18 @@
extern crate criterion;
extern crate jpeg_decoder;
use criterion::{black_box, Criterion};
use jpeg_decoder as jpeg;
fn read_image(image: &[u8]) -> Vec<u8> {
jpeg::Decoder::new(black_box(image)).decode().unwrap()
}
fn main() {
let mut c = Criterion::default().configure_from_args();
c.bench_function("decode a 2268x1512 JPEG", |b| {
b.iter(|| read_image(include_bytes!("large_image.jpg")))
});
c.final_summary();
}

BIN
vendor/jpeg-decoder/benches/tower.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

83
vendor/jpeg-decoder/examples/decode.rs vendored Normal file
View File

@ -0,0 +1,83 @@
extern crate jpeg_decoder as jpeg;
extern crate png;
use std::env;
use std::fs::File;
use std::io::{self, BufReader, Write};
use std::process;
fn usage() -> ! {
write!(io::stderr(), "usage: decode image.jpg image.png").unwrap();
process::exit(1)
}
fn main() {
let mut args = env::args().skip(1);
let input_path = args.next().unwrap_or_else(|| usage());
let output_path = args.next().unwrap_or_else(|| usage());
let input_file = File::open(input_path).expect("The specified input file could not be opened");
let mut decoder = jpeg::Decoder::new(BufReader::new(input_file));
let mut data = decoder.decode().expect("Decoding failed. If other software can successfully decode the specified JPEG image, then it's likely that there is a bug in jpeg-decoder");
let info = decoder.info().unwrap();
eprintln!("{:?}", info);
eprintln!("Exif: {}", decoder.exif_data().is_some());
eprintln!("ICC: {}", decoder.icc_profile().is_some());
let output_file = File::create(output_path).unwrap();
let mut encoder = png::Encoder::new(output_file, info.width as u32, info.height as u32);
match info.pixel_format {
jpeg::PixelFormat::L16 => {
encoder.set_depth(png::BitDepth::Sixteen);
encoder.set_color(png::ColorType::Grayscale);
},
jpeg::PixelFormat::RGB24 => {
encoder.set_depth(png::BitDepth::Eight);
encoder.set_color(png::ColorType::RGB);
},
jpeg::PixelFormat::CMYK32 => {
data = cmyk_to_rgb(&mut data);
encoder.set_depth(png::BitDepth::Eight);
encoder.set_color(png::ColorType::RGB)
},
jpeg::PixelFormat::L8 => {
encoder.set_depth(png::BitDepth::Eight);
encoder.set_color(png::ColorType::Grayscale);
},
}
encoder.write_header()
.expect("writing png header failed")
.write_image_data(&data)
.expect("png encoding failed");
}
fn cmyk_to_rgb(input: &[u8]) -> Vec<u8> {
let size = input.len() - input.len() / 4;
let mut output = Vec::with_capacity(size);
for pixel in input.chunks(4) {
let c = pixel[0] as f32 / 255.0;
let m = pixel[1] as f32 / 255.0;
let y = pixel[2] as f32 / 255.0;
let k = pixel[3] as f32 / 255.0;
// CMYK -> CMY
let c = c * (1.0 - k) + k;
let m = m * (1.0 - k) + k;
let y = y * (1.0 - k) + k;
// CMY -> RGB
let r = (1.0 - c) * 255.0;
let g = (1.0 - m) * 255.0;
let b = (1.0 - y) * 255.0;
output.push(r as u8);
output.push(g as u8);
output.push(b as u8);
}
output
}

1
vendor/jpeg-decoder/rust-toolchain vendored Normal file
View File

@ -0,0 +1 @@
1.61.0

46
vendor/jpeg-decoder/src/arch/mod.rs vendored Normal file
View File

@ -0,0 +1,46 @@
#![allow(unsafe_code)]
mod neon;
mod ssse3;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
use std::is_x86_feature_detected;
/// Arch-specific implementation of YCbCr conversion. Returns the number of pixels that were
/// converted.
pub fn get_color_convert_line_ycbcr() -> Option<unsafe fn(&[u8], &[u8], &[u8], &mut [u8]) -> usize>
{
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[allow(unsafe_code)]
{
if is_x86_feature_detected!("ssse3") {
return Some(ssse3::color_convert_line_ycbcr);
}
}
// Runtime detection is not needed on aarch64.
#[cfg(all(feature = "nightly_aarch64_neon", target_arch = "aarch64"))]
{
return Some(neon::color_convert_line_ycbcr);
}
#[allow(unreachable_code)]
None
}
/// Arch-specific implementation of 8x8 IDCT.
pub fn get_dequantize_and_idct_block_8x8(
) -> Option<unsafe fn(&[i16; 64], &[u16; 64], usize, &mut [u8])> {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[allow(unsafe_code)]
{
if is_x86_feature_detected!("ssse3") {
return Some(ssse3::dequantize_and_idct_block_8x8);
}
}
// Runtime detection is not needed on aarch64.
#[cfg(all(feature = "nightly_aarch64_neon", target_arch = "aarch64"))]
{
return Some(neon::dequantize_and_idct_block_8x8);
}
#[allow(unreachable_code)]
None
}

221
vendor/jpeg-decoder/src/arch/neon.rs vendored Normal file
View File

@ -0,0 +1,221 @@
#[cfg(all(feature = "nightly_aarch64_neon", target_arch = "aarch64"))]
use core::arch::aarch64::*;
#[cfg(all(feature = "nightly_aarch64_neon", target_arch = "aarch64"))]
#[target_feature(enable = "neon")]
unsafe fn idct8(data: &mut [int16x8_t; 8]) {
// The fixed-point constants here are obtained by taking the fractional part of the constants
// from the non-SIMD implementation and scaling them up by 1<<15. This is because
// vqrdmulhq_n_s16(a, b) is effectively equivalent to (a*b)>>15 (except for possibly some
// slight differences in rounding).
// The code here is effectively equivalent to the calls to "kernel" in idct.rs, except that it
// doesn't apply any further scaling and fixed point constants have a different precision.
let p2 = data[2];
let p3 = data[6];
let p1 = vqrdmulhq_n_s16(vqaddq_s16(p2, p3), 17734); // 0.5411961
let t2 = vqsubq_s16(
vqsubq_s16(p1, p3),
vqrdmulhq_n_s16(p3, 27779), // 0.847759065
);
let t3 = vqaddq_s16(p1, vqrdmulhq_n_s16(p2, 25079)); // 0.765366865
let p2 = data[0];
let p3 = data[4];
let t0 = vqaddq_s16(p2, p3);
let t1 = vqsubq_s16(p2, p3);
let x0 = vqaddq_s16(t0, t3);
let x3 = vqsubq_s16(t0, t3);
let x1 = vqaddq_s16(t1, t2);
let x2 = vqsubq_s16(t1, t2);
let t0 = data[7];
let t1 = data[5];
let t2 = data[3];
let t3 = data[1];
let p3 = vqaddq_s16(t0, t2);
let p4 = vqaddq_s16(t1, t3);
let p1 = vqaddq_s16(t0, t3);
let p2 = vqaddq_s16(t1, t2);
let p5 = vqaddq_s16(p3, p4);
let p5 = vqaddq_s16(p5, vqrdmulhq_n_s16(p5, 5763)); // 0.175875602
let t0 = vqrdmulhq_n_s16(t0, 9786); // 0.298631336
let t1 = vqaddq_s16(
vqaddq_s16(t1, t1),
vqrdmulhq_n_s16(t1, 1741), // 0.053119869
);
let t2 = vqaddq_s16(
vqaddq_s16(t2, vqaddq_s16(t2, t2)),
vqrdmulhq_n_s16(t2, 2383), // 0.072711026
);
let t3 = vqaddq_s16(t3, vqrdmulhq_n_s16(t3, 16427)); // 0.501321110
let p1 = vqsubq_s16(p5, vqrdmulhq_n_s16(p1, 29490)); // 0.899976223
let p2 = vqsubq_s16(
vqsubq_s16(vqsubq_s16(p5, p2), p2),
vqrdmulhq_n_s16(p2, 18446), // 0.562915447
);
let p3 = vqsubq_s16(
vqrdmulhq_n_s16(p3, -31509), // -0.961570560
p3,
);
let p4 = vqrdmulhq_n_s16(p4, -12785); // -0.390180644
let t3 = vqaddq_s16(vqaddq_s16(p1, p4), t3);
let t2 = vqaddq_s16(vqaddq_s16(p2, p3), t2);
let t1 = vqaddq_s16(vqaddq_s16(p2, p4), t1);
let t0 = vqaddq_s16(vqaddq_s16(p1, p3), t0);
data[0] = vqaddq_s16(x0, t3);
data[7] = vqsubq_s16(x0, t3);
data[1] = vqaddq_s16(x1, t2);
data[6] = vqsubq_s16(x1, t2);
data[2] = vqaddq_s16(x2, t1);
data[5] = vqsubq_s16(x2, t1);
data[3] = vqaddq_s16(x3, t0);
data[4] = vqsubq_s16(x3, t0);
}
#[cfg(all(feature = "nightly_aarch64_neon", target_arch = "aarch64"))]
#[target_feature(enable = "neon")]
unsafe fn transpose8(data: &mut [int16x8_t; 8]) {
// Use NEON's 2x2 matrix transposes (vtrn) to do the transposition in each 4x4 block, then
// combine the 4x4 blocks.
let a01 = vtrnq_s16(data[0], data[1]);
let a23 = vtrnq_s16(data[2], data[3]);
let four0 = vtrnq_s32(vreinterpretq_s32_s16(a01.0), vreinterpretq_s32_s16(a23.0));
let four1 = vtrnq_s32(vreinterpretq_s32_s16(a01.1), vreinterpretq_s32_s16(a23.1));
let a45 = vtrnq_s16(data[4], data[5]);
let a67 = vtrnq_s16(data[6], data[7]);
let four2 = vtrnq_s32(vreinterpretq_s32_s16(a45.0), vreinterpretq_s32_s16(a67.0));
let four3 = vtrnq_s32(vreinterpretq_s32_s16(a45.1), vreinterpretq_s32_s16(a67.1));
data[0] = vreinterpretq_s16_s32(vcombine_s32(vget_low_s32(four0.0), vget_low_s32(four2.0)));
data[1] = vreinterpretq_s16_s32(vcombine_s32(vget_low_s32(four1.0), vget_low_s32(four3.0)));
data[2] = vreinterpretq_s16_s32(vcombine_s32(vget_low_s32(four0.1), vget_low_s32(four2.1)));
data[3] = vreinterpretq_s16_s32(vcombine_s32(vget_low_s32(four1.1), vget_low_s32(four3.1)));
data[4] = vreinterpretq_s16_s32(vcombine_s32(vget_high_s32(four0.0), vget_high_s32(four2.0)));
data[5] = vreinterpretq_s16_s32(vcombine_s32(vget_high_s32(four1.0), vget_high_s32(four3.0)));
data[6] = vreinterpretq_s16_s32(vcombine_s32(vget_high_s32(four0.1), vget_high_s32(four2.1)));
data[7] = vreinterpretq_s16_s32(vcombine_s32(vget_high_s32(four1.1), vget_high_s32(four3.1)));
}
#[cfg(all(feature = "nightly_aarch64_neon", target_arch = "aarch64"))]
#[target_feature(enable = "neon")]
pub unsafe fn dequantize_and_idct_block_8x8(
coefficients: &[i16; 64],
quantization_table: &[u16; 64],
output_linestride: usize,
output: &mut [u8],
) {
// The loop below will write to positions [output_linestride * i, output_linestride * i + 8)
// for 0<=i<8. Thus, the last accessed position is at an offset of output_linestrade * 7 + 7,
// and if that position is in-bounds, so are all other accesses.
assert!(
output.len()
> output_linestride
.checked_mul(7)
.unwrap()
.checked_add(7)
.unwrap()
);
const SHIFT: i32 = 3;
// Read the DCT coefficients, scale them up and dequantize them.
let mut data = [vdupq_n_s16(0); 8];
for i in 0..8 {
data[i] = vshlq_n_s16(
vmulq_s16(
vld1q_s16(coefficients.as_ptr().wrapping_add(i * 8)),
vreinterpretq_s16_u16(vld1q_u16(quantization_table.as_ptr().wrapping_add(i * 8))),
),
SHIFT,
);
}
// Usual column IDCT - transpose - column IDCT - transpose approach.
idct8(&mut data);
transpose8(&mut data);
idct8(&mut data);
transpose8(&mut data);
for i in 0..8 {
// The two passes of the IDCT algorithm give us a factor of 8, so the shift here is
// increased by 3.
// As values will be stored in a u8, they need to be 128-centered and not 0-centered.
// We add 128 with the appropriate shift for that purpose.
const OFFSET: i16 = 128 << (SHIFT + 3);
// We want rounding right shift, so we should add (1/2) << (SHIFT+3) before shifting.
const ROUNDING_BIAS: i16 = (1 << (SHIFT + 3)) >> 1;
let data_with_offset = vqaddq_s16(data[i], vdupq_n_s16(OFFSET + ROUNDING_BIAS));
vst1_u8(
output.as_mut_ptr().wrapping_add(output_linestride * i),
vqshrun_n_s16(data_with_offset, SHIFT + 3),
);
}
}
#[cfg(all(feature = "nightly_aarch64_neon", target_arch = "aarch64"))]
#[target_feature(enable = "neon")]
pub unsafe fn color_convert_line_ycbcr(y: &[u8], cb: &[u8], cr: &[u8], output: &mut [u8]) -> usize {
assert!(output.len() % 3 == 0);
let num = output.len() / 3;
assert!(num <= y.len());
assert!(num <= cb.len());
assert!(num <= cr.len());
let num_vecs = num / 8;
for i in 0..num_vecs {
const SHIFT: i32 = 6;
// Load.
let y = vld1_u8(y.as_ptr().wrapping_add(i * 8));
let cb = vld1_u8(cb.as_ptr().wrapping_add(i * 8));
let cr = vld1_u8(cr.as_ptr().wrapping_add(i * 8));
// Convert to 16 bit and shift.
let y = vreinterpretq_s16_u16(vshll_n_u8(y, SHIFT));
let cb = vreinterpretq_s16_u16(vshll_n_u8(cb, SHIFT));
let cr = vreinterpretq_s16_u16(vshll_n_u8(cr, SHIFT));
// Add offsets
let y = vqaddq_s16(y, vdupq_n_s16((1 << SHIFT) >> 1));
let c128 = vdupq_n_s16(128 << SHIFT);
let cb = vqsubq_s16(cb, c128);
let cr = vqsubq_s16(cr, c128);
// Compute cr * 1.402, cb * 0.34414, cr * 0.71414, cb * 1.772
let cr_140200 = vqaddq_s16(vqrdmulhq_n_s16(cr, 13173), cr);
let cb_034414 = vqrdmulhq_n_s16(cb, 11276);
let cr_071414 = vqrdmulhq_n_s16(cr, 23401);
let cb_177200 = vqaddq_s16(vqrdmulhq_n_s16(cb, 25297), cb);
// Last conversion step.
let r = vqaddq_s16(y, cr_140200);
let g = vqsubq_s16(y, vqaddq_s16(cb_034414, cr_071414));
let b = vqaddq_s16(y, cb_177200);
// Shift back and convert to u8.
let r = vqshrun_n_s16(r, SHIFT);
let g = vqshrun_n_s16(g, SHIFT);
let b = vqshrun_n_s16(b, SHIFT);
// Shuffle + store.
vst3_u8(
output.as_mut_ptr().wrapping_add(24 * i),
uint8x8x3_t(r, g, b),
);
}
num_vecs * 8
}

288
vendor/jpeg-decoder/src/arch/ssse3.rs vendored Normal file
View File

@ -0,0 +1,288 @@
#[cfg(target_arch = "x86")]
use std::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[target_feature(enable = "ssse3")]
unsafe fn idct8(data: &mut [__m128i; 8]) {
// The fixed-point constants here are obtained by taking the fractional part of the constants
// from the non-SIMD implementation and scaling them up by 1<<15. This is because
// _mm_mulhrs_epi16(a, b) is effectively equivalent to (a*b)>>15 (except for possibly some
// slight differences in rounding).
// The code here is effectively equivalent to the calls to "kernel" in idct.rs, except that it
// doesn't apply any further scaling and fixed point constants have a different precision.
let p2 = data[2];
let p3 = data[6];
let p1 = _mm_mulhrs_epi16(_mm_adds_epi16(p2, p3), _mm_set1_epi16(17734)); // 0.5411961
let t2 = _mm_subs_epi16(
_mm_subs_epi16(p1, p3),
_mm_mulhrs_epi16(p3, _mm_set1_epi16(27779)), // 0.847759065
);
let t3 = _mm_adds_epi16(p1, _mm_mulhrs_epi16(p2, _mm_set1_epi16(25079))); // 0.765366865
let p2 = data[0];
let p3 = data[4];
let t0 = _mm_adds_epi16(p2, p3);
let t1 = _mm_subs_epi16(p2, p3);
let x0 = _mm_adds_epi16(t0, t3);
let x3 = _mm_subs_epi16(t0, t3);
let x1 = _mm_adds_epi16(t1, t2);
let x2 = _mm_subs_epi16(t1, t2);
let t0 = data[7];
let t1 = data[5];
let t2 = data[3];
let t3 = data[1];
let p3 = _mm_adds_epi16(t0, t2);
let p4 = _mm_adds_epi16(t1, t3);
let p1 = _mm_adds_epi16(t0, t3);
let p2 = _mm_adds_epi16(t1, t2);
let p5 = _mm_adds_epi16(p3, p4);
let p5 = _mm_adds_epi16(p5, _mm_mulhrs_epi16(p5, _mm_set1_epi16(5763))); // 0.175875602
let t0 = _mm_mulhrs_epi16(t0, _mm_set1_epi16(9786)); // 0.298631336
let t1 = _mm_adds_epi16(
_mm_adds_epi16(t1, t1),
_mm_mulhrs_epi16(t1, _mm_set1_epi16(1741)), // 0.053119869
);
let t2 = _mm_adds_epi16(
_mm_adds_epi16(t2, _mm_adds_epi16(t2, t2)),
_mm_mulhrs_epi16(t2, _mm_set1_epi16(2383)), // 0.072711026
);
let t3 = _mm_adds_epi16(t3, _mm_mulhrs_epi16(t3, _mm_set1_epi16(16427))); // 0.501321110
let p1 = _mm_subs_epi16(p5, _mm_mulhrs_epi16(p1, _mm_set1_epi16(29490))); // 0.899976223
let p2 = _mm_subs_epi16(
_mm_subs_epi16(_mm_subs_epi16(p5, p2), p2),
_mm_mulhrs_epi16(p2, _mm_set1_epi16(18446)), // 0.562915447
);
let p3 = _mm_subs_epi16(
_mm_mulhrs_epi16(p3, _mm_set1_epi16(-31509)), // -0.961570560
p3,
);
let p4 = _mm_mulhrs_epi16(p4, _mm_set1_epi16(-12785)); // -0.390180644
let t3 = _mm_adds_epi16(_mm_adds_epi16(p1, p4), t3);
let t2 = _mm_adds_epi16(_mm_adds_epi16(p2, p3), t2);
let t1 = _mm_adds_epi16(_mm_adds_epi16(p2, p4), t1);
let t0 = _mm_adds_epi16(_mm_adds_epi16(p1, p3), t0);
data[0] = _mm_adds_epi16(x0, t3);
data[7] = _mm_subs_epi16(x0, t3);
data[1] = _mm_adds_epi16(x1, t2);
data[6] = _mm_subs_epi16(x1, t2);
data[2] = _mm_adds_epi16(x2, t1);
data[5] = _mm_subs_epi16(x2, t1);
data[3] = _mm_adds_epi16(x3, t0);
data[4] = _mm_subs_epi16(x3, t0);
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[target_feature(enable = "ssse3")]
unsafe fn transpose8(data: &mut [__m128i; 8]) {
// Transpose a 8x8 matrix with a sequence of interleaving operations.
// Naming: dABl contains elements from the *l*ower halves of vectors A and B, interleaved, i.e.
// A0 B0 A1 B1 ...
// dABCDll contains elements from the lower quarter (ll) of vectors A, B, C, D, interleaved -
// A0 B0 C0 D0 A1 B1 C1 D1 ...
let d01l = _mm_unpacklo_epi16(data[0], data[1]);
let d23l = _mm_unpacklo_epi16(data[2], data[3]);
let d45l = _mm_unpacklo_epi16(data[4], data[5]);
let d67l = _mm_unpacklo_epi16(data[6], data[7]);
let d01h = _mm_unpackhi_epi16(data[0], data[1]);
let d23h = _mm_unpackhi_epi16(data[2], data[3]);
let d45h = _mm_unpackhi_epi16(data[4], data[5]);
let d67h = _mm_unpackhi_epi16(data[6], data[7]);
// Operating on 32-bits will interleave *consecutive pairs* of 16-bit integers.
let d0123ll = _mm_unpacklo_epi32(d01l, d23l);
let d0123lh = _mm_unpackhi_epi32(d01l, d23l);
let d4567ll = _mm_unpacklo_epi32(d45l, d67l);
let d4567lh = _mm_unpackhi_epi32(d45l, d67l);
let d0123hl = _mm_unpacklo_epi32(d01h, d23h);
let d0123hh = _mm_unpackhi_epi32(d01h, d23h);
let d4567hl = _mm_unpacklo_epi32(d45h, d67h);
let d4567hh = _mm_unpackhi_epi32(d45h, d67h);
// Operating on 64-bits will interleave *consecutive quadruples* of 16-bit integers.
data[0] = _mm_unpacklo_epi64(d0123ll, d4567ll);
data[1] = _mm_unpackhi_epi64(d0123ll, d4567ll);
data[2] = _mm_unpacklo_epi64(d0123lh, d4567lh);
data[3] = _mm_unpackhi_epi64(d0123lh, d4567lh);
data[4] = _mm_unpacklo_epi64(d0123hl, d4567hl);
data[5] = _mm_unpackhi_epi64(d0123hl, d4567hl);
data[6] = _mm_unpacklo_epi64(d0123hh, d4567hh);
data[7] = _mm_unpackhi_epi64(d0123hh, d4567hh);
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[target_feature(enable = "ssse3")]
pub unsafe fn dequantize_and_idct_block_8x8(
coefficients: &[i16; 64],
quantization_table: &[u16; 64],
output_linestride: usize,
output: &mut [u8],
) {
// The loop below will write to positions [output_linestride * i, output_linestride * i + 8)
// for 0<=i<8. Thus, the last accessed position is at an offset of output_linestrade * 7 + 7,
// and if that position is in-bounds, so are all other accesses.
assert!(
output.len()
> output_linestride
.checked_mul(7)
.unwrap()
.checked_add(7)
.unwrap()
);
#[cfg(target_arch = "x86")]
use std::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
const SHIFT: i32 = 3;
// Read the DCT coefficients, scale them up and dequantize them.
let mut data = [_mm_setzero_si128(); 8];
for i in 0..8 {
data[i] = _mm_slli_epi16(
_mm_mullo_epi16(
_mm_loadu_si128(coefficients.as_ptr().wrapping_add(i * 8) as *const _),
_mm_loadu_si128(quantization_table.as_ptr().wrapping_add(i * 8) as *const _),
),
SHIFT,
);
}
// Usual column IDCT - transpose - column IDCT - transpose approach.
idct8(&mut data);
transpose8(&mut data);
idct8(&mut data);
transpose8(&mut data);
for i in 0..8 {
let mut buf = [0u8; 16];
// The two passes of the IDCT algorithm give us a factor of 8, so the shift here is
// increased by 3.
// As values will be stored in a u8, they need to be 128-centered and not 0-centered.
// We add 128 with the appropriate shift for that purpose.
const OFFSET: i16 = 128 << (SHIFT + 3);
// We want rounding right shift, so we should add (1/2) << (SHIFT+3) before shifting.
const ROUNDING_BIAS: i16 = (1 << (SHIFT + 3)) >> 1;
let data_with_offset = _mm_adds_epi16(data[i], _mm_set1_epi16(OFFSET + ROUNDING_BIAS));
_mm_storeu_si128(
buf.as_mut_ptr() as *mut _,
_mm_packus_epi16(
_mm_srai_epi16(data_with_offset, SHIFT + 3),
_mm_setzero_si128(),
),
);
std::ptr::copy_nonoverlapping::<u8>(
buf.as_ptr(),
output.as_mut_ptr().wrapping_add(output_linestride * i) as *mut _,
8,
);
}
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[target_feature(enable = "ssse3")]
pub unsafe fn color_convert_line_ycbcr(y: &[u8], cb: &[u8], cr: &[u8], output: &mut [u8]) -> usize {
assert!(output.len() % 3 == 0);
let num = output.len() / 3;
assert!(num <= y.len());
assert!(num <= cb.len());
assert!(num <= cr.len());
// _mm_loadu_si64 generates incorrect code for Rust <1.58. To circumvent this, we use a full
// 128-bit load, but that requires leaving an extra vector of border to the scalar code.
// From Rust 1.58 on, the _mm_loadu_si128 can be replaced with _mm_loadu_si64 and this
// .saturating_sub() can be removed.
let num_vecs = (num / 8).saturating_sub(1);
for i in 0..num_vecs {
const SHIFT: i32 = 6;
// Load.
let y = _mm_loadu_si128(y.as_ptr().wrapping_add(i * 8) as *const _);
let cb = _mm_loadu_si128(cb.as_ptr().wrapping_add(i * 8) as *const _);
let cr = _mm_loadu_si128(cr.as_ptr().wrapping_add(i * 8) as *const _);
// Convert to 16 bit.
let shuf16 = _mm_setr_epi8(
0, -0x7F, 1, -0x7F, 2, -0x7F, 3, -0x7F, 4, -0x7F, 5, -0x7F, 6, -0x7F, 7, -0x7F,
);
let y = _mm_slli_epi16(_mm_shuffle_epi8(y, shuf16), SHIFT);
let cb = _mm_slli_epi16(_mm_shuffle_epi8(cb, shuf16), SHIFT);
let cr = _mm_slli_epi16(_mm_shuffle_epi8(cr, shuf16), SHIFT);
// Add offsets
let c128 = _mm_set1_epi16(128 << SHIFT);
let y = _mm_adds_epi16(y, _mm_set1_epi16((1 << SHIFT) >> 1));
let cb = _mm_subs_epi16(cb, c128);
let cr = _mm_subs_epi16(cr, c128);
// Compute cr * 1.402, cb * 0.34414, cr * 0.71414, cb * 1.772
let cr_140200 = _mm_adds_epi16(_mm_mulhrs_epi16(cr, _mm_set1_epi16(13173)), cr);
let cb_034414 = _mm_mulhrs_epi16(cb, _mm_set1_epi16(11276));
let cr_071414 = _mm_mulhrs_epi16(cr, _mm_set1_epi16(23401));
let cb_177200 = _mm_adds_epi16(_mm_mulhrs_epi16(cb, _mm_set1_epi16(25297)), cb);
// Last conversion step.
let r = _mm_adds_epi16(y, cr_140200);
let g = _mm_subs_epi16(y, _mm_adds_epi16(cb_034414, cr_071414));
let b = _mm_adds_epi16(y, cb_177200);
// Shift back and convert to u8.
let zero = _mm_setzero_si128();
let r = _mm_packus_epi16(_mm_srai_epi16(r, SHIFT), zero);
let g = _mm_packus_epi16(_mm_srai_epi16(g, SHIFT), zero);
let b = _mm_packus_epi16(_mm_srai_epi16(b, SHIFT), zero);
// Shuffle rrrrrrrrggggggggbbbbbbbb to rgbrgbrgb...
// Control vectors for _mm_shuffle_epi8. -0x7F is selected so that the resulting position
// after _mm_shuffle_epi8 will be filled with 0, so that the r, g, and b vectors can then
// be OR-ed together.
let shufr = _mm_setr_epi8(
0, -0x7F, -0x7F, 1, -0x7F, -0x7F, 2, -0x7F, -0x7F, 3, -0x7F, -0x7F, 4, -0x7F, -0x7F, 5,
);
let shufg = _mm_setr_epi8(
-0x7F, 0, -0x7F, -0x7F, 1, -0x7F, -0x7F, 2, -0x7F, -0x7F, 3, -0x7F, -0x7F, 4, -0x7F,
-0x7F,
);
let shufb = _mm_alignr_epi8(shufg, shufg, 15);
let rgb_low = _mm_or_si128(
_mm_shuffle_epi8(r, shufr),
_mm_or_si128(_mm_shuffle_epi8(g, shufg), _mm_shuffle_epi8(b, shufb)),
);
// For the next part of the rgb vectors, we need to select R values from 6 up, G and B from
// 5 up. The highest bit of -0x7F + 6 is still set, so the corresponding location will
// still be 0.
let shufr1 = _mm_add_epi8(shufb, _mm_set1_epi8(6));
let shufg1 = _mm_add_epi8(shufr, _mm_set1_epi8(5));
let shufb1 = _mm_add_epi8(shufg, _mm_set1_epi8(5));
let rgb_hi = _mm_or_si128(
_mm_shuffle_epi8(r, shufr1),
_mm_or_si128(_mm_shuffle_epi8(g, shufg1), _mm_shuffle_epi8(b, shufb1)),
);
let mut data = [0u8; 32];
_mm_storeu_si128(data.as_mut_ptr() as *mut _, rgb_low);
_mm_storeu_si128(data.as_mut_ptr().wrapping_add(16) as *mut _, rgb_hi);
std::ptr::copy_nonoverlapping::<u8>(
data.as_ptr(),
output.as_mut_ptr().wrapping_add(24 * i),
24,
);
}
num_vecs * 8
}

1493
vendor/jpeg-decoder/src/decoder.rs vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,259 @@
use std::io::Read;
use crate::decoder::{Decoder, MAX_COMPONENTS};
use crate::error::{Error, Result};
use crate::huffman::HuffmanDecoder;
use crate::marker::Marker;
use crate::parser::Predictor;
use crate::parser::{Component, FrameInfo, ScanInfo};
impl<R: Read> Decoder<R> {
/// decode_scan_lossless
pub fn decode_scan_lossless(
&mut self,
frame: &FrameInfo,
scan: &ScanInfo,
) -> Result<(Option<Marker>, Vec<Vec<u16>>)> {
let ncomp = scan.component_indices.len();
let npixel = frame.image_size.height as usize * frame.image_size.width as usize;
assert!(ncomp <= MAX_COMPONENTS);
let mut results = vec![vec![0u16; npixel]; ncomp];
let components: Vec<Component> = scan
.component_indices
.iter()
.map(|&i| frame.components[i].clone())
.collect();
// Verify that all required huffman tables has been set.
if scan
.dc_table_indices
.iter()
.any(|&i| self.dc_huffman_tables[i].is_none())
{
return Err(Error::Format(
"scan makes use of unset dc huffman table".to_owned(),
));
}
let mut huffman = HuffmanDecoder::new();
let reader = &mut self.reader;
let mut mcus_left_until_restart = self.restart_interval;
let mut expected_rst_num = 0;
let mut ra = [0u16; MAX_COMPONENTS];
let mut rb = [0u16; MAX_COMPONENTS];
let mut rc = [0u16; MAX_COMPONENTS];
let width = frame.image_size.width as usize;
let height = frame.image_size.height as usize;
let mut differences = vec![Vec::with_capacity(npixel); ncomp];
for _mcu_y in 0..height {
for _mcu_x in 0..width {
if self.restart_interval > 0 {
if mcus_left_until_restart == 0 {
match huffman.take_marker(reader)? {
Some(Marker::RST(n)) => {
if n != expected_rst_num {
return Err(Error::Format(format!(
"found RST{} where RST{} was expected",
n, expected_rst_num
)));
}
huffman.reset();
expected_rst_num = (expected_rst_num + 1) % 8;
mcus_left_until_restart = self.restart_interval;
}
Some(marker) => {
return Err(Error::Format(format!(
"found marker {:?} inside scan where RST{} was expected",
marker, expected_rst_num
)))
}
None => {
return Err(Error::Format(format!(
"no marker found where RST{} was expected",
expected_rst_num
)))
}
}
}
mcus_left_until_restart -= 1;
}
for (i, _component) in components.iter().enumerate() {
let dc_table = self.dc_huffman_tables[scan.dc_table_indices[i]]
.as_ref()
.unwrap();
let value = huffman.decode(reader, dc_table)?;
let diff = match value {
0 => 0,
1..=15 => huffman.receive_extend(reader, value)? as i32,
16 => 32768,
_ => {
// Section F.1.2.1.1
// Table F.1
return Err(Error::Format(
"invalid DC difference magnitude category".to_owned(),
));
}
};
differences[i].push(diff);
}
}
}
if scan.predictor_selection == Predictor::Ra {
for (i, _component) in components.iter().enumerate() {
// calculate the top left pixel
let diff = differences[i][0];
let prediction = 1 << (frame.precision - scan.point_transform - 1) as i32;
let result = ((prediction + diff) & 0xFFFF) as u16; // modulo 2^16
let result = result << scan.point_transform;
results[i][0] = result;
// calculate leftmost column, using top pixel as predictor
let mut previous = result;
for mcu_y in 1..height {
let diff = differences[i][mcu_y * width];
let prediction = previous as i32;
let result = ((prediction + diff) & 0xFFFF) as u16; // modulo 2^16
let result = result << scan.point_transform;
results[i][mcu_y * width] = result;
previous = result;
}
// calculate rows, using left pixel as predictor
for mcu_y in 0..height {
for mcu_x in 1..width {
let diff = differences[i][mcu_y * width + mcu_x];
let prediction = results[i][mcu_y * width + mcu_x - 1] as i32;
let result = ((prediction + diff) & 0xFFFF) as u16; // modulo 2^16
let result = result << scan.point_transform;
results[i][mcu_y * width + mcu_x] = result;
}
}
}
} else {
for mcu_y in 0..height {
for mcu_x in 0..width {
for (i, _component) in components.iter().enumerate() {
let diff = differences[i][mcu_y * width + mcu_x];
// The following lines could be further optimized, e.g. moving the checks
// and updates of the previous values into the prediction function or
// iterating such that diagonals with mcu_x + mcu_y = const are computed at
// the same time to exploit independent predictions in this case
if mcu_x > 0 {
ra[i] = results[i][mcu_y * frame.image_size.width as usize + mcu_x - 1];
}
if mcu_y > 0 {
rb[i] =
results[i][(mcu_y - 1) * frame.image_size.width as usize + mcu_x];
if mcu_x > 0 {
rc[i] = results[i]
[(mcu_y - 1) * frame.image_size.width as usize + (mcu_x - 1)];
}
}
let prediction = predict(
ra[i] as i32,
rb[i] as i32,
rc[i] as i32,
scan.predictor_selection,
scan.point_transform,
frame.precision,
mcu_x,
mcu_y,
self.restart_interval > 0
&& mcus_left_until_restart == self.restart_interval - 1,
);
let result = ((prediction + diff) & 0xFFFF) as u16; // modulo 2^16
results[i][mcu_y * width + mcu_x] = result << scan.point_transform;
}
}
}
}
let mut marker = huffman.take_marker(&mut self.reader)?;
while let Some(Marker::RST(_)) = marker {
marker = self.read_marker().ok();
}
Ok((marker, results))
}
}
/// H.1.2.1
fn predict(
ra: i32,
rb: i32,
rc: i32,
predictor: Predictor,
point_transform: u8,
input_precision: u8,
ix: usize,
iy: usize,
restart: bool,
) -> i32 {
if (ix == 0 && iy == 0) || restart {
// start of first line or restart
if input_precision > 1 + point_transform {
1 << (input_precision - point_transform - 1)
} else {
0
}
} else if iy == 0 {
// rest of first line
ra
} else if ix == 0 {
// start of other line
rb
} else {
// use predictor Table H.1
match predictor {
Predictor::NoPrediction => 0,
Predictor::Ra => ra,
Predictor::Rb => rb,
Predictor::Rc => rc,
Predictor::RaRbRc1 => ra + rb - rc,
Predictor::RaRbRc2 => ra + ((rb - rc) >> 1),
Predictor::RaRbRc3 => rb + ((ra - rc) >> 1),
Predictor::RaRb => (ra + rb) / 2,
}
}
}
pub fn compute_image_lossless(frame: &FrameInfo, mut data: Vec<Vec<u16>>) -> Result<Vec<u8>> {
if data.is_empty() || data.iter().any(Vec::is_empty) {
return Err(Error::Format("not all components have data".to_owned()));
}
let output_size = frame.output_size;
let components = &frame.components;
let ncomp = components.len();
if ncomp == 1 {
let decoded = convert_to_u8(frame, data.remove(0));
Ok(decoded)
} else {
let mut decoded: Vec<u16> =
vec![0u16; ncomp * output_size.width as usize * output_size.height as usize];
for (x, chunk) in decoded.chunks_mut(ncomp).enumerate() {
for (i, (component_data, _)) in data.iter().zip(components.iter()).enumerate() {
chunk[i] = component_data[x];
}
}
let decoded = convert_to_u8(frame, decoded);
Ok(decoded)
}
}
fn convert_to_u8(frame: &FrameInfo, data: Vec<u16>) -> Vec<u8> {
if frame.precision == 8 {
data.iter().map(|x| *x as u8).collect()
} else {
// we output native endian, which is the standard for image-rs
let ne_bytes: Vec<_> = data.iter().map(|x| x.to_ne_bytes()).collect();
ne_bytes.concat()
}
}

75
vendor/jpeg-decoder/src/error.rs vendored Normal file
View File

@ -0,0 +1,75 @@
use alloc::boxed::Box;
use alloc::fmt;
use alloc::string::String;
use core::result;
use std::error::Error as StdError;
use std::io::Error as IoError;
use crate::ColorTransform;
pub type Result<T> = result::Result<T, Error>;
/// An enumeration over JPEG features (currently) unsupported by this library.
///
/// Support for features listed here may be included in future versions of this library.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum UnsupportedFeature {
/// Hierarchical JPEG.
Hierarchical,
/// JPEG using arithmetic entropy coding instead of Huffman coding.
ArithmeticEntropyCoding,
/// Sample precision in bits. 8 bit sample precision is what is currently supported in non-lossless coding process.
SamplePrecision(u8),
/// Number of components in an image. 1, 3 and 4 components are currently supported.
ComponentCount(u8),
/// An image can specify a zero height in the frame header and use the DNL (Define Number of
/// Lines) marker at the end of the first scan to define the number of lines in the frame.
DNL,
/// Subsampling ratio.
SubsamplingRatio,
/// A subsampling ratio not representable as an integer.
NonIntegerSubsamplingRatio,
/// Colour transform
ColorTransform(ColorTransform),
}
/// Errors that can occur while decoding a JPEG image.
#[derive(Debug)]
pub enum Error {
/// The image is not formatted properly. The string contains detailed information about the
/// error.
Format(String),
/// The image makes use of a JPEG feature not (currently) supported by this library.
Unsupported(UnsupportedFeature),
/// An I/O error occurred while decoding the image.
Io(IoError),
/// An internal error occurred while decoding the image.
Internal(Box<dyn StdError + Send + Sync + 'static>), //TODO: not used, can be removed with the next version bump
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::Format(ref desc) => write!(f, "invalid JPEG format: {}", desc),
Error::Unsupported(ref feat) => write!(f, "unsupported JPEG feature: {:?}", feat),
Error::Io(ref err) => err.fmt(f),
Error::Internal(ref err) => err.fmt(f),
}
}
}
impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match *self {
Error::Io(ref err) => Some(err),
Error::Internal(ref err) => Some(&**err),
_ => None,
}
}
}
impl From<IoError> for Error {
fn from(err: IoError) -> Error {
Error::Io(err)
}
}

346
vendor/jpeg-decoder/src/huffman.rs vendored Normal file
View File

@ -0,0 +1,346 @@
use alloc::borrow::ToOwned;
use alloc::vec;
use alloc::vec::Vec;
use core::iter;
use std::io::Read;
use crate::read_u8;
use crate::error::{Error, Result};
use crate::marker::Marker;
use crate::parser::ScanInfo;
const LUT_BITS: u8 = 8;
#[derive(Debug)]
pub struct HuffmanDecoder {
bits: u64,
num_bits: u8,
marker: Option<Marker>,
}
impl HuffmanDecoder {
pub fn new() -> HuffmanDecoder {
HuffmanDecoder {
bits: 0,
num_bits: 0,
marker: None,
}
}
// Section F.2.2.3
// Figure F.16
pub fn decode<R: Read>(&mut self, reader: &mut R, table: &HuffmanTable) -> Result<u8> {
if self.num_bits < 16 {
self.read_bits(reader)?;
}
let (value, size) = table.lut[self.peek_bits(LUT_BITS) as usize];
if size > 0 {
self.consume_bits(size);
Ok(value)
}
else {
let bits = self.peek_bits(16);
for i in LUT_BITS .. 16 {
let code = (bits >> (15 - i)) as i32;
if code <= table.maxcode[i as usize] {
self.consume_bits(i + 1);
let index = (code + table.delta[i as usize]) as usize;
return Ok(table.values[index]);
}
}
Err(Error::Format("failed to decode huffman code".to_owned()))
}
}
pub fn decode_fast_ac<R: Read>(&mut self, reader: &mut R, table: &HuffmanTable) -> Result<Option<(i16, u8)>> {
if let Some(ref ac_lut) = table.ac_lut {
if self.num_bits < LUT_BITS {
self.read_bits(reader)?;
}
let (value, run_size) = ac_lut[self.peek_bits(LUT_BITS) as usize];
if run_size != 0 {
let run = run_size >> 4;
let size = run_size & 0x0f;
self.consume_bits(size);
return Ok(Some((value, run)));
}
}
Ok(None)
}
#[inline]
pub fn get_bits<R: Read>(&mut self, reader: &mut R, count: u8) -> Result<u16> {
if self.num_bits < count {
self.read_bits(reader)?;
}
let bits = self.peek_bits(count);
self.consume_bits(count);
Ok(bits)
}
#[inline]
pub fn receive_extend<R: Read>(&mut self, reader: &mut R, count: u8) -> Result<i16> {
let value = self.get_bits(reader, count)?;
Ok(extend(value, count))
}
pub fn reset(&mut self) {
self.bits = 0;
self.num_bits = 0;
}
pub fn take_marker<R: Read>(&mut self, reader: &mut R) -> Result<Option<Marker>> {
self.read_bits(reader).map(|_| self.marker.take())
}
#[inline]
fn peek_bits(&mut self, count: u8) -> u16 {
debug_assert!(count <= 16);
debug_assert!(self.num_bits >= count);
((self.bits >> (64 - count)) & ((1 << count) - 1)) as u16
}
#[inline]
fn consume_bits(&mut self, count: u8) {
debug_assert!(self.num_bits >= count);
self.bits <<= count as usize;
self.num_bits -= count;
}
fn read_bits<R: Read>(&mut self, reader: &mut R) -> Result<()> {
while self.num_bits <= 56 {
// Fill with zero bits if we have reached the end.
let byte = match self.marker {
Some(_) => 0,
None => read_u8(reader)?,
};
if byte == 0xFF {
let mut next_byte = read_u8(reader)?;
// Check for byte stuffing.
if next_byte != 0x00 {
// We seem to have reached the end of entropy-coded data and encountered a
// marker. Since we can't put data back into the reader, we have to continue
// reading to identify the marker so we can pass it on.
// Section B.1.1.2
// "Any marker may optionally be preceded by any number of fill bytes, which are bytes assigned code XFF."
while next_byte == 0xFF {
next_byte = read_u8(reader)?;
}
match next_byte {
0x00 => return Err(Error::Format("FF 00 found where marker was expected".to_owned())),
_ => self.marker = Some(Marker::from_u8(next_byte).unwrap()),
}
continue;
}
}
self.bits |= (byte as u64) << (56 - self.num_bits);
self.num_bits += 8;
}
Ok(())
}
}
// Section F.2.2.1
// Figure F.12
fn extend(value: u16, count: u8) -> i16 {
let vt = 1 << (count as u16 - 1);
if value < vt {
value as i16 + (-1 << count as i16) + 1
} else {
value as i16
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum HuffmanTableClass {
DC,
AC,
}
pub struct HuffmanTable {
values: Vec<u8>,
delta: [i32; 16],
maxcode: [i32; 16],
lut: [(u8, u8); 1 << LUT_BITS],
ac_lut: Option<[(i16, u8); 1 << LUT_BITS]>,
}
impl HuffmanTable {
pub fn new(bits: &[u8; 16], values: &[u8], class: HuffmanTableClass) -> Result<HuffmanTable> {
let (huffcode, huffsize) = derive_huffman_codes(bits)?;
// Section F.2.2.3
// Figure F.15
// delta[i] is set to VALPTR(I) - MINCODE(I)
let mut delta = [0i32; 16];
let mut maxcode = [-1i32; 16];
let mut j = 0;
for i in 0 .. 16 {
if bits[i] != 0 {
delta[i] = j as i32 - huffcode[j] as i32;
j += bits[i] as usize;
maxcode[i] = huffcode[j - 1] as i32;
}
}
// Build a lookup table for faster decoding.
let mut lut = [(0u8, 0u8); 1 << LUT_BITS];
for (i, &size) in huffsize.iter().enumerate().filter(|&(_, &size)| size <= LUT_BITS) {
let bits_remaining = LUT_BITS - size;
let start = (huffcode[i] << bits_remaining) as usize;
let val = (values[i], size);
for b in &mut lut[start..][..1 << bits_remaining] {
*b = val;
}
}
// Build a lookup table for small AC coefficients which both decodes the value and does the
// equivalent of receive_extend.
let ac_lut = match class {
HuffmanTableClass::DC => None,
HuffmanTableClass::AC => {
let mut table = [(0i16, 0u8); 1 << LUT_BITS];
for (i, &(value, size)) in lut.iter().enumerate() {
let run_length = value >> 4;
let magnitude_category = value & 0x0f;
if magnitude_category > 0 && size + magnitude_category <= LUT_BITS {
let unextended_ac_value = (((i << size) & ((1 << LUT_BITS) - 1)) >> (LUT_BITS - magnitude_category)) as u16;
let ac_value = extend(unextended_ac_value, magnitude_category);
table[i] = (ac_value, (run_length << 4) | (size + magnitude_category));
}
}
Some(table)
},
};
Ok(HuffmanTable {
values: values.to_vec(),
delta,
maxcode,
lut,
ac_lut,
})
}
}
// Section C.2
fn derive_huffman_codes(bits: &[u8; 16]) -> Result<(Vec<u16>, Vec<u8>)> {
// Figure C.1
let huffsize = bits.iter()
.enumerate()
.fold(Vec::new(), |mut acc, (i, &value)| {
acc.extend(iter::repeat((i + 1) as u8).take(value as usize));
acc
});
// Figure C.2
let mut huffcode = vec![0u16; huffsize.len()];
let mut code_size = huffsize[0];
let mut code = 0u32;
for (i, &size) in huffsize.iter().enumerate() {
while code_size < size {
code <<= 1;
code_size += 1;
}
if code >= (1u32 << size) {
return Err(Error::Format("bad huffman code length".to_owned()));
}
huffcode[i] = code as u16;
code += 1;
}
Ok((huffcode, huffsize))
}
// https://www.loc.gov/preservation/digital/formats/fdd/fdd000063.shtml
// "Avery Lee, writing in the rec.video.desktop newsgroup in 2001, commented that "MJPEG, or at
// least the MJPEG in AVIs having the MJPG fourcc, is restricted JPEG with a fixed -- and
// *omitted* -- Huffman table. The JPEG must be YCbCr colorspace, it must be 4:2:2, and it must
// use basic Huffman encoding, not arithmetic or progressive.... You can indeed extract the
// MJPEG frames and decode them with a regular JPEG decoder, but you have to prepend the DHT
// segment to them, or else the decoder won't have any idea how to decompress the data.
// The exact table necessary is given in the OpenDML spec.""
pub fn fill_default_mjpeg_tables(scan: &ScanInfo,
dc_huffman_tables: &mut[Option<HuffmanTable>],
ac_huffman_tables: &mut[Option<HuffmanTable>]) {
// Section K.3.3
if dc_huffman_tables[0].is_none() && scan.dc_table_indices.iter().any(|&i| i == 0) {
// Table K.3
dc_huffman_tables[0] = Some(HuffmanTable::new(
&[0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
&[0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B], HuffmanTableClass::DC).unwrap());
}
if dc_huffman_tables[1].is_none() && scan.dc_table_indices.iter().any(|&i| i == 1) {
// Table K.4
dc_huffman_tables[1] = Some(HuffmanTable::new(
&[0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00],
&[0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B], HuffmanTableClass::DC).unwrap());
}
if ac_huffman_tables[0].is_none() && scan.ac_table_indices.iter().any(|&i| i == 0) {
// Table K.5
ac_huffman_tables[0] = Some(HuffmanTable::new(
&[0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D],
&[0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0,
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5,
0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2,
0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
0xF9, 0xFA
], HuffmanTableClass::AC).unwrap());
}
if ac_huffman_tables[1].is_none() && scan.ac_table_indices.iter().any(|&i| i == 1) {
// Table K.6
ac_huffman_tables[1] = Some(HuffmanTable::new(
&[0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77],
&[0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0,
0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26,
0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5,
0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3,
0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA,
0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
0xF9, 0xFA
], HuffmanTableClass::AC).unwrap());
}
}

657
vendor/jpeg-decoder/src/idct.rs vendored Normal file
View File

@ -0,0 +1,657 @@
// Malicious JPEG files can cause operations in the idct to overflow.
// One example is tests/crashtest/images/imagetestsuite/b0b8914cc5f7a6eff409f16d8cc236c5.jpg
// That's why wrapping operators are needed.
// Note: we have many values that are straight from a reference.
// Do not warn on them or try to automatically change them.
#![allow(clippy::excessive_precision)]
// Note: consistency for unrolled, scaled offset loops
#![allow(clippy::erasing_op)]
#![allow(clippy::identity_op)]
use crate::parser::Dimensions;
use core::{convert::TryFrom, num::Wrapping};
pub(crate) fn choose_idct_size(full_size: Dimensions, requested_size: Dimensions) -> usize {
fn scaled(len: u16, scale: usize) -> u16 {
((len as u32 * scale as u32 - 1) / 8 + 1) as u16
}
for &scale in &[1, 2, 4] {
if scaled(full_size.width, scale) >= requested_size.width
|| scaled(full_size.height, scale) >= requested_size.height
{
return scale;
}
}
8
}
#[test]
fn test_choose_idct_size() {
assert_eq!(
choose_idct_size(
Dimensions {
width: 5472,
height: 3648
},
Dimensions {
width: 200,
height: 200
}
),
1
);
assert_eq!(
choose_idct_size(
Dimensions {
width: 5472,
height: 3648
},
Dimensions {
width: 500,
height: 500
}
),
1
);
assert_eq!(
choose_idct_size(
Dimensions {
width: 5472,
height: 3648
},
Dimensions {
width: 684,
height: 456
}
),
1
);
assert_eq!(
choose_idct_size(
Dimensions {
width: 5472,
height: 3648
},
Dimensions {
width: 999,
height: 456
}
),
1
);
assert_eq!(
choose_idct_size(
Dimensions {
width: 5472,
height: 3648
},
Dimensions {
width: 684,
height: 999
}
),
1
);
assert_eq!(
choose_idct_size(
Dimensions {
width: 500,
height: 333
},
Dimensions {
width: 63,
height: 42
}
),
1
);
assert_eq!(
choose_idct_size(
Dimensions {
width: 5472,
height: 3648
},
Dimensions {
width: 685,
height: 999
}
),
2
);
assert_eq!(
choose_idct_size(
Dimensions {
width: 5472,
height: 3648
},
Dimensions {
width: 1000,
height: 1000
}
),
2
);
assert_eq!(
choose_idct_size(
Dimensions {
width: 5472,
height: 3648
},
Dimensions {
width: 1400,
height: 1400
}
),
4
);
assert_eq!(
choose_idct_size(
Dimensions {
width: 5472,
height: 3648
},
Dimensions {
width: 5472,
height: 3648
}
),
8
);
assert_eq!(
choose_idct_size(
Dimensions {
width: 5472,
height: 3648
},
Dimensions {
width: 16384,
height: 16384
}
),
8
);
assert_eq!(
choose_idct_size(
Dimensions {
width: 1,
height: 1
},
Dimensions {
width: 65535,
height: 65535
}
),
8
);
assert_eq!(
choose_idct_size(
Dimensions {
width: 5472,
height: 3648
},
Dimensions {
width: 16384,
height: 16384
}
),
8
);
}
pub(crate) fn dequantize_and_idct_block(
scale: usize,
coefficients: &[i16; 64],
quantization_table: &[u16; 64],
output_linestride: usize,
output: &mut [u8],
) {
match scale {
8 => dequantize_and_idct_block_8x8(
coefficients,
quantization_table,
output_linestride,
output,
),
4 => dequantize_and_idct_block_4x4(
coefficients,
quantization_table,
output_linestride,
output,
),
2 => dequantize_and_idct_block_2x2(
coefficients,
quantization_table,
output_linestride,
output,
),
1 => dequantize_and_idct_block_1x1(
coefficients,
quantization_table,
output_linestride,
output,
),
_ => panic!("Unsupported IDCT scale {}/8", scale),
}
}
pub fn dequantize_and_idct_block_8x8(
coefficients: &[i16; 64],
quantization_table: &[u16; 64],
output_linestride: usize,
output: &mut [u8],
) {
#[cfg(not(feature = "platform_independent"))]
if let Some(idct) = crate::arch::get_dequantize_and_idct_block_8x8() {
#[allow(unsafe_code)]
unsafe {
return idct(coefficients, quantization_table, output_linestride, output);
}
}
let output = output.chunks_mut(output_linestride);
dequantize_and_idct_block_8x8_inner(coefficients, quantization_table, output)
}
// This is based on stb_image's 'stbi__idct_block'.
fn dequantize_and_idct_block_8x8_inner<'a, I>(
coefficients: &[i16; 64],
quantization_table: &[u16; 64],
output: I,
) where
I: IntoIterator<Item = &'a mut [u8]>,
I::IntoIter: ExactSizeIterator<Item = &'a mut [u8]>,
{
let output = output.into_iter();
debug_assert!(
output.len() >= 8,
"Output iterator has the wrong length: {}",
output.len()
);
let mut temp = [Wrapping(0); 64];
// columns
for i in 0..8 {
if coefficients[i + 8] == 0
&& coefficients[i + 16] == 0
&& coefficients[i + 24] == 0
&& coefficients[i + 32] == 0
&& coefficients[i + 40] == 0
&& coefficients[i + 48] == 0
&& coefficients[i + 56] == 0
{
let dcterm = dequantize(coefficients[i], quantization_table[i]) << 2;
temp[i] = dcterm;
temp[i + 8] = dcterm;
temp[i + 16] = dcterm;
temp[i + 24] = dcterm;
temp[i + 32] = dcterm;
temp[i + 40] = dcterm;
temp[i + 48] = dcterm;
temp[i + 56] = dcterm;
} else {
let s0 = dequantize(coefficients[i], quantization_table[i]);
let s1 = dequantize(coefficients[i + 8], quantization_table[i + 8]);
let s2 = dequantize(coefficients[i + 16], quantization_table[i + 16]);
let s3 = dequantize(coefficients[i + 24], quantization_table[i + 24]);
let s4 = dequantize(coefficients[i + 32], quantization_table[i + 32]);
let s5 = dequantize(coefficients[i + 40], quantization_table[i + 40]);
let s6 = dequantize(coefficients[i + 48], quantization_table[i + 48]);
let s7 = dequantize(coefficients[i + 56], quantization_table[i + 56]);
let Kernel {
xs: [x0, x1, x2, x3],
ts: [t0, t1, t2, t3],
} = kernel(
[s0, s1, s2, s3, s4, s5, s6, s7],
// constants scaled things up by 1<<12; let's bring them back
// down, but keep 2 extra bits of precision
512,
);
temp[i] = (x0 + t3) >> 10;
temp[i + 56] = (x0 - t3) >> 10;
temp[i + 8] = (x1 + t2) >> 10;
temp[i + 48] = (x1 - t2) >> 10;
temp[i + 16] = (x2 + t1) >> 10;
temp[i + 40] = (x2 - t1) >> 10;
temp[i + 24] = (x3 + t0) >> 10;
temp[i + 32] = (x3 - t0) >> 10;
}
}
for (chunk, output_chunk) in temp.chunks_exact(8).zip(output) {
let chunk = <&[_; 8]>::try_from(chunk).unwrap();
// constants scaled things up by 1<<12, plus we had 1<<2 from first
// loop, plus horizontal and vertical each scale by sqrt(8) so together
// we've got an extra 1<<3, so 1<<17 total we need to remove.
// so we want to round that, which means adding 0.5 * 1<<17,
// aka 65536. Also, we'll end up with -128 to 127 that we want
// to encode as 0..255 by adding 128, so we'll add that before the shift
const X_SCALE: i32 = 65536 + (128 << 17);
// eliminate downstream bounds checks
let output_chunk = &mut output_chunk[..8];
// TODO When the minimum rust version supports it
// let [s0, rest @ ..] = chunk;
let (s0, rest) = chunk.split_first().unwrap();
if *rest == [Wrapping(0); 7] {
let dcterm = stbi_clamp((stbi_fsh(*s0) + Wrapping(X_SCALE)) >> 17);
output_chunk[0] = dcterm;
output_chunk[1] = dcterm;
output_chunk[2] = dcterm;
output_chunk[3] = dcterm;
output_chunk[4] = dcterm;
output_chunk[5] = dcterm;
output_chunk[6] = dcterm;
output_chunk[7] = dcterm;
} else {
let Kernel {
xs: [x0, x1, x2, x3],
ts: [t0, t1, t2, t3],
} = kernel(*chunk, X_SCALE);
output_chunk[0] = stbi_clamp((x0 + t3) >> 17);
output_chunk[7] = stbi_clamp((x0 - t3) >> 17);
output_chunk[1] = stbi_clamp((x1 + t2) >> 17);
output_chunk[6] = stbi_clamp((x1 - t2) >> 17);
output_chunk[2] = stbi_clamp((x2 + t1) >> 17);
output_chunk[5] = stbi_clamp((x2 - t1) >> 17);
output_chunk[3] = stbi_clamp((x3 + t0) >> 17);
output_chunk[4] = stbi_clamp((x3 - t0) >> 17);
}
}
}
struct Kernel {
xs: [Wrapping<i32>; 4],
ts: [Wrapping<i32>; 4],
}
#[inline]
fn kernel_x([s0, s2, s4, s6]: [Wrapping<i32>; 4], x_scale: i32) -> [Wrapping<i32>; 4] {
// Even `chunk` indicies
let (t2, t3);
{
let p2 = s2;
let p3 = s6;
let p1 = (p2 + p3) * stbi_f2f(0.5411961);
t2 = p1 + p3 * stbi_f2f(-1.847759065);
t3 = p1 + p2 * stbi_f2f(0.765366865);
}
let (t0, t1);
{
let p2 = s0;
let p3 = s4;
t0 = stbi_fsh(p2 + p3);
t1 = stbi_fsh(p2 - p3);
}
let x0 = t0 + t3;
let x3 = t0 - t3;
let x1 = t1 + t2;
let x2 = t1 - t2;
let x_scale = Wrapping(x_scale);
[x0 + x_scale, x1 + x_scale, x2 + x_scale, x3 + x_scale]
}
#[inline]
fn kernel_t([s1, s3, s5, s7]: [Wrapping<i32>; 4]) -> [Wrapping<i32>; 4] {
// Odd `chunk` indicies
let mut t0 = s7;
let mut t1 = s5;
let mut t2 = s3;
let mut t3 = s1;
let p3 = t0 + t2;
let p4 = t1 + t3;
let p1 = t0 + t3;
let p2 = t1 + t2;
let p5 = (p3 + p4) * stbi_f2f(1.175875602);
t0 *= stbi_f2f(0.298631336);
t1 *= stbi_f2f(2.053119869);
t2 *= stbi_f2f(3.072711026);
t3 *= stbi_f2f(1.501321110);
let p1 = p5 + p1 * stbi_f2f(-0.899976223);
let p2 = p5 + p2 * stbi_f2f(-2.562915447);
let p3 = p3 * stbi_f2f(-1.961570560);
let p4 = p4 * stbi_f2f(-0.390180644);
t3 += p1 + p4;
t2 += p2 + p3;
t1 += p2 + p4;
t0 += p1 + p3;
[t0, t1, t2, t3]
}
#[inline]
fn kernel([s0, s1, s2, s3, s4, s5, s6, s7]: [Wrapping<i32>; 8], x_scale: i32) -> Kernel {
Kernel {
xs: kernel_x([s0, s2, s4, s6], x_scale),
ts: kernel_t([s1, s3, s5, s7]),
}
}
#[inline(always)]
fn dequantize(c: i16, q: u16) -> Wrapping<i32> {
Wrapping(i32::from(c) * i32::from(q))
}
// 4x4 and 2x2 IDCT based on Rakesh Dugad and Narendra Ahuja: "A Fast Scheme for Image Size Change in the Compressed Domain" (2001).
// http://sylvana.net/jpegcrop/jidctred/
fn dequantize_and_idct_block_4x4(
coefficients: &[i16; 64],
quantization_table: &[u16; 64],
output_linestride: usize,
output: &mut [u8],
) {
debug_assert_eq!(coefficients.len(), 64);
let mut temp = [Wrapping(0i32); 4 * 4];
const CONST_BITS: usize = 12;
const PASS1_BITS: usize = 2;
const FINAL_BITS: usize = CONST_BITS + PASS1_BITS + 3;
// columns
for i in 0..4 {
let s0 = Wrapping(coefficients[i + 8 * 0] as i32 * quantization_table[i + 8 * 0] as i32);
let s1 = Wrapping(coefficients[i + 8 * 1] as i32 * quantization_table[i + 8 * 1] as i32);
let s2 = Wrapping(coefficients[i + 8 * 2] as i32 * quantization_table[i + 8 * 2] as i32);
let s3 = Wrapping(coefficients[i + 8 * 3] as i32 * quantization_table[i + 8 * 3] as i32);
let x0 = (s0 + s2) << PASS1_BITS;
let x2 = (s0 - s2) << PASS1_BITS;
let p1 = (s1 + s3) * stbi_f2f(0.541196100);
let t0 = (p1 + s3 * stbi_f2f(-1.847759065) + Wrapping(512)) >> (CONST_BITS - PASS1_BITS);
let t2 = (p1 + s1 * stbi_f2f(0.765366865) + Wrapping(512)) >> (CONST_BITS - PASS1_BITS);
temp[i + 4 * 0] = x0 + t2;
temp[i + 4 * 3] = x0 - t2;
temp[i + 4 * 1] = x2 + t0;
temp[i + 4 * 2] = x2 - t0;
}
for i in 0..4 {
let s0 = temp[i * 4 + 0];
let s1 = temp[i * 4 + 1];
let s2 = temp[i * 4 + 2];
let s3 = temp[i * 4 + 3];
let x0 = (s0 + s2) << CONST_BITS;
let x2 = (s0 - s2) << CONST_BITS;
let p1 = (s1 + s3) * stbi_f2f(0.541196100);
let t0 = p1 + s3 * stbi_f2f(-1.847759065);
let t2 = p1 + s1 * stbi_f2f(0.765366865);
// constants scaled things up by 1<<12, plus we had 1<<2 from first
// loop, plus horizontal and vertical each scale by sqrt(8) so together
// we've got an extra 1<<3, so 1<<17 total we need to remove.
// so we want to round that, which means adding 0.5 * 1<<17,
// aka 65536. Also, we'll end up with -128 to 127 that we want
// to encode as 0..255 by adding 128, so we'll add that before the shift
let x0 = x0 + Wrapping(1 << (FINAL_BITS - 1)) + Wrapping(128 << FINAL_BITS);
let x2 = x2 + Wrapping(1 << (FINAL_BITS - 1)) + Wrapping(128 << FINAL_BITS);
let output = &mut output[i * output_linestride..][..4];
output[0] = stbi_clamp((x0 + t2) >> FINAL_BITS);
output[3] = stbi_clamp((x0 - t2) >> FINAL_BITS);
output[1] = stbi_clamp((x2 + t0) >> FINAL_BITS);
output[2] = stbi_clamp((x2 - t0) >> FINAL_BITS);
}
}
fn dequantize_and_idct_block_2x2(
coefficients: &[i16; 64],
quantization_table: &[u16; 64],
output_linestride: usize,
output: &mut [u8],
) {
debug_assert_eq!(coefficients.len(), 64);
const SCALE_BITS: usize = 3;
// Column 0
let s00 = Wrapping(coefficients[8 * 0] as i32 * quantization_table[8 * 0] as i32);
let s10 = Wrapping(coefficients[8 * 1] as i32 * quantization_table[8 * 1] as i32);
let x0 = s00 + s10;
let x2 = s00 - s10;
// Column 1
let s01 = Wrapping(coefficients[8 * 0 + 1] as i32 * quantization_table[8 * 0 + 1] as i32);
let s11 = Wrapping(coefficients[8 * 1 + 1] as i32 * quantization_table[8 * 1 + 1] as i32);
let x1 = s01 + s11;
let x3 = s01 - s11;
let x0 = x0 + Wrapping(1 << (SCALE_BITS - 1)) + Wrapping(128 << SCALE_BITS);
let x2 = x2 + Wrapping(1 << (SCALE_BITS - 1)) + Wrapping(128 << SCALE_BITS);
// Row 0
output[0] = stbi_clamp((x0 + x1) >> SCALE_BITS);
output[1] = stbi_clamp((x0 - x1) >> SCALE_BITS);
// Row 1
output[output_linestride + 0] = stbi_clamp((x2 + x3) >> SCALE_BITS);
output[output_linestride + 1] = stbi_clamp((x2 - x3) >> SCALE_BITS);
}
fn dequantize_and_idct_block_1x1(
coefficients: &[i16; 64],
quantization_table: &[u16; 64],
_output_linestride: usize,
output: &mut [u8],
) {
debug_assert_eq!(coefficients.len(), 64);
let s0 = (Wrapping(coefficients[0] as i32 * quantization_table[0] as i32) + Wrapping(128 * 8)) / Wrapping(8);
output[0] = stbi_clamp(s0);
}
// take a -128..127 value and stbi__clamp it and convert to 0..255
fn stbi_clamp(x: Wrapping<i32>) -> u8 {
x.0.max(0).min(255) as u8
}
fn stbi_f2f(x: f32) -> Wrapping<i32> {
Wrapping((x * 4096.0 + 0.5) as i32)
}
fn stbi_fsh(x: Wrapping<i32>) -> Wrapping<i32> {
x << 12
}
#[test]
fn test_dequantize_and_idct_block_8x8() {
#[cfg_attr(rustfmt, rustfmt_skip)]
let coefficients: [i16; 8 * 8] = [
-14, -39, 58, -2, 3, 3, 0, 1,
11, 27, 4, -3, 3, 0, 1, 0,
-6, -13, -9, -1, -2, -1, 0, 0,
-4, 0, -1, -2, 0, 0, 0, 0,
3, 0, 0, 0, 0, 0, 0, 0,
-3, -2, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
];
#[cfg_attr(rustfmt, rustfmt_skip)]
let quantization_table: [u16; 8 * 8] = [
8, 6, 5, 8, 12, 20, 26, 31,
6, 6, 7, 10, 13, 29, 30, 28,
7, 7, 8, 12, 20, 29, 35, 28,
7, 9, 11, 15, 26, 44, 40, 31,
9, 11, 19, 28, 34, 55, 52, 39,
12, 18, 28, 32, 41, 52, 57, 46,
25, 32, 39, 44, 52, 61, 60, 51,
36, 46, 48, 49, 56, 50, 52, 50
];
let output_linestride: usize = 8;
let mut output = [0u8; 8 * 8];
dequantize_and_idct_block_8x8(
&coefficients,
&quantization_table,
output_linestride,
&mut output,
);
#[cfg_attr(rustfmt, rustfmt_skip)]
let expected_output = [
118, 92, 110, 83, 77, 93, 144, 198,
172, 116, 114, 87, 78, 93, 146, 191,
194, 107, 91, 76, 71, 93, 160, 198,
196, 100, 80, 74, 67, 92, 174, 209,
182, 104, 88, 81, 68, 89, 178, 206,
105, 64, 59, 59, 63, 94, 183, 201,
35, 27, 28, 37, 72, 121, 203, 204,
37, 45, 41, 47, 98, 154, 223, 208
];
for i in 0..64 {
assert!((output[i] as i16 - expected_output[i] as i16).abs() <= 1);
}
}
#[test]
fn test_dequantize_and_idct_block_8x8_all_zero() {
let mut output = [0u8; 8 * 8];
dequantize_and_idct_block_8x8(&[0; 8 * 8], &[666; 8 * 8], 8, &mut output);
assert_eq!(&output[..], &[128; 8 * 8][..]);
}
#[test]
fn test_dequantize_and_idct_block_8x8_saturated() {
// Arch-specific IDCT implementations need not handle i16::MAX values.
#[cfg(not(feature = "platform_independent"))]
if crate::arch::get_dequantize_and_idct_block_8x8().is_some() {
return;
}
let mut output = [0u8; 8 * 8];
dequantize_and_idct_block_8x8(&[i16::MAX; 8 * 8], &[u16::MAX; 8 * 8], 8, &mut output);
#[cfg_attr(rustfmt, rustfmt_skip)]
let expected = [
0, 0, 0, 255, 255, 0, 0, 255,
0, 0, 215, 0, 0, 255, 255, 0,
255, 255, 255, 255, 255, 0, 0, 255,
0, 0, 255, 0, 255, 0, 255, 255,
0, 0, 255, 255, 0, 255, 0, 0,
255, 255, 0, 255, 255, 255, 170, 0,
0, 255, 0, 0, 0, 0, 0, 255,
255, 255, 0, 255, 0, 255, 0, 0
];
assert_eq!(&output[..], &expected[..]);
}

66
vendor/jpeg-decoder/src/lib.rs vendored Normal file
View File

@ -0,0 +1,66 @@
//! This crate contains a JPEG decoder.
//!
//! # Examples
//!
//! ```
//! use jpeg_decoder::Decoder;
//! use std::fs::File;
//! use std::io::BufReader;
//!
//! let file = File::open("tests/reftest/images/extraneous-data.jpg").expect("failed to open file");
//! let mut decoder = Decoder::new(BufReader::new(file));
//! let pixels = decoder.decode().expect("failed to decode image");
//! let metadata = decoder.info().unwrap();
//! ```
//!
//! Get metadata from a file without decoding it:
//!
//! ```
//! use jpeg_decoder::Decoder;
//! use std::fs::File;
//! use std::io::BufReader;
//!
//! let file = File::open("tests/reftest/images/extraneous-data.jpg").expect("failed to open file");
//! let mut decoder = Decoder::new(BufReader::new(file));
//! decoder.read_info().expect("failed to read metadata");
//! let metadata = decoder.info().unwrap();
//! ```
#![deny(missing_docs)]
#![deny(unsafe_code)]
#![cfg_attr(feature = "platform_independent", forbid(unsafe_code))]
extern crate alloc;
extern crate core;
#[cfg(feature = "rayon")]
extern crate rayon;
pub use decoder::{ColorTransform, Decoder, ImageInfo, PixelFormat};
pub use error::{Error, UnsupportedFeature};
pub use parser::CodingProcess;
use std::io;
#[cfg(not(feature = "platform_independent"))]
mod arch;
mod decoder;
mod error;
mod huffman;
mod idct;
mod marker;
mod parser;
mod upsampler;
mod worker;
fn read_u8<R: io::Read>(reader: &mut R) -> io::Result<u8> {
let mut buf = [0];
reader.read_exact(&mut buf)?;
Ok(buf[0])
}
fn read_u16_from_be<R: io::Read>(reader: &mut R) -> io::Result<u16> {
let mut buf = [0, 0];
reader.read_exact(&mut buf)?;
Ok(u16::from_be_bytes(buf))
}

136
vendor/jpeg-decoder/src/marker.rs vendored Normal file
View File

@ -0,0 +1,136 @@
// Table B.1
#[derive(Clone, Copy, Debug, PartialEq)]
// Note: Established names.
#[allow(clippy::upper_case_acronyms)]
pub enum Marker {
/// Start Of Frame markers
///
/// - SOF(0): Baseline DCT (Huffman coding)
/// - SOF(1): Extended sequential DCT (Huffman coding)
/// - SOF(2): Progressive DCT (Huffman coding)
/// - SOF(3): Lossless (sequential) (Huffman coding)
/// - SOF(5): Differential sequential DCT (Huffman coding)
/// - SOF(6): Differential progressive DCT (Huffman coding)
/// - SOF(7): Differential lossless (sequential) (Huffman coding)
/// - SOF(9): Extended sequential DCT (arithmetic coding)
/// - SOF(10): Progressive DCT (arithmetic coding)
/// - SOF(11): Lossless (sequential) (arithmetic coding)
/// - SOF(13): Differential sequential DCT (arithmetic coding)
/// - SOF(14): Differential progressive DCT (arithmetic coding)
/// - SOF(15): Differential lossless (sequential) (arithmetic coding)
SOF(u8),
/// Reserved for JPEG extensions
JPG,
/// Define Huffman table(s)
DHT,
/// Define arithmetic coding conditioning(s)
DAC,
/// Restart with modulo 8 count `m`
RST(u8),
/// Start of image
SOI,
/// End of image
EOI,
/// Start of scan
SOS,
/// Define quantization table(s)
DQT,
/// Define number of lines
DNL,
/// Define restart interval
DRI,
/// Define hierarchical progression
DHP,
/// Expand reference component(s)
EXP,
/// Reserved for application segments
APP(u8),
/// Reserved for JPEG extensions
JPGn(u8),
/// Comment
COM,
/// For temporary private use in arithmetic coding
TEM,
/// Reserved
RES,
}
impl Marker {
pub fn has_length(self) -> bool {
use self::Marker::*;
! matches!(self, RST(..) | SOI | EOI | TEM)
}
pub fn from_u8(n: u8) -> Option<Marker> {
use self::Marker::*;
match n {
0x00 => None, // Byte stuffing
0x01 => Some(TEM),
0x02 ..= 0xBF => Some(RES),
0xC0 => Some(SOF(0)),
0xC1 => Some(SOF(1)),
0xC2 => Some(SOF(2)),
0xC3 => Some(SOF(3)),
0xC4 => Some(DHT),
0xC5 => Some(SOF(5)),
0xC6 => Some(SOF(6)),
0xC7 => Some(SOF(7)),
0xC8 => Some(JPG),
0xC9 => Some(SOF(9)),
0xCA => Some(SOF(10)),
0xCB => Some(SOF(11)),
0xCC => Some(DAC),
0xCD => Some(SOF(13)),
0xCE => Some(SOF(14)),
0xCF => Some(SOF(15)),
0xD0 => Some(RST(0)),
0xD1 => Some(RST(1)),
0xD2 => Some(RST(2)),
0xD3 => Some(RST(3)),
0xD4 => Some(RST(4)),
0xD5 => Some(RST(5)),
0xD6 => Some(RST(6)),
0xD7 => Some(RST(7)),
0xD8 => Some(SOI),
0xD9 => Some(EOI),
0xDA => Some(SOS),
0xDB => Some(DQT),
0xDC => Some(DNL),
0xDD => Some(DRI),
0xDE => Some(DHP),
0xDF => Some(EXP),
0xE0 => Some(APP(0)),
0xE1 => Some(APP(1)),
0xE2 => Some(APP(2)),
0xE3 => Some(APP(3)),
0xE4 => Some(APP(4)),
0xE5 => Some(APP(5)),
0xE6 => Some(APP(6)),
0xE7 => Some(APP(7)),
0xE8 => Some(APP(8)),
0xE9 => Some(APP(9)),
0xEA => Some(APP(10)),
0xEB => Some(APP(11)),
0xEC => Some(APP(12)),
0xED => Some(APP(13)),
0xEE => Some(APP(14)),
0xEF => Some(APP(15)),
0xF0 => Some(JPGn(0)),
0xF1 => Some(JPGn(1)),
0xF2 => Some(JPGn(2)),
0xF3 => Some(JPGn(3)),
0xF4 => Some(JPGn(4)),
0xF5 => Some(JPGn(5)),
0xF6 => Some(JPGn(6)),
0xF7 => Some(JPGn(7)),
0xF8 => Some(JPGn(8)),
0xF9 => Some(JPGn(9)),
0xFA => Some(JPGn(10)),
0xFB => Some(JPGn(11)),
0xFC => Some(JPGn(12)),
0xFD => Some(JPGn(13)),
0xFE => Some(COM),
0xFF => None, // Fill byte
}
}
}

685
vendor/jpeg-decoder/src/parser.rs vendored Normal file
View File

@ -0,0 +1,685 @@
use alloc::borrow::ToOwned;
use alloc::{format, vec};
use alloc::vec::Vec;
use core::ops::{self, Range};
use std::io::{self, Read};
use crate::{read_u16_from_be, read_u8};
use crate::error::{Error, Result, UnsupportedFeature};
use crate::huffman::{HuffmanTable, HuffmanTableClass};
use crate::marker::Marker;
use crate::marker::Marker::*;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Dimensions {
pub width: u16,
pub height: u16,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum EntropyCoding {
Huffman,
Arithmetic,
}
/// Represents the coding process of an image.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum CodingProcess {
/// Sequential Discrete Cosine Transform
DctSequential,
/// Progressive Discrete Cosine Transform
DctProgressive,
/// Lossless
Lossless,
}
// Table H.1
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Predictor {
NoPrediction,
Ra,
Rb,
Rc,
RaRbRc1, // Ra + Rb - Rc
RaRbRc2, // Ra + ((Rb - Rc) >> 1)
RaRbRc3, // Rb + ((Ra - Rb) >> 1)
RaRb, // (Ra + Rb)/2
}
#[derive(Clone)]
pub struct FrameInfo {
pub is_baseline: bool,
pub is_differential: bool,
pub coding_process: CodingProcess,
pub entropy_coding: EntropyCoding,
pub precision: u8,
pub image_size: Dimensions,
pub output_size: Dimensions,
pub mcu_size: Dimensions,
pub components: Vec<Component>,
}
#[derive(Debug)]
pub struct ScanInfo {
pub component_indices: Vec<usize>,
pub dc_table_indices: Vec<usize>,
pub ac_table_indices: Vec<usize>,
pub spectral_selection: Range<u8>,
pub predictor_selection: Predictor, // for lossless
pub successive_approximation_high: u8,
pub successive_approximation_low: u8,
pub point_transform: u8, // for lossless
}
#[derive(Clone, Debug)]
pub struct Component {
pub identifier: u8,
pub horizontal_sampling_factor: u8,
pub vertical_sampling_factor: u8,
pub quantization_table_index: usize,
pub dct_scale: usize,
pub size: Dimensions,
pub block_size: Dimensions,
}
#[derive(Debug)]
pub enum AppData {
Adobe(AdobeColorTransform),
Jfif,
Avi1,
Icc(IccChunk),
Exif(Vec<u8>),
}
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum AdobeColorTransform {
// RGB or CMYK
Unknown,
YCbCr,
// YCbCrK
YCCK,
}
#[derive(Debug)]
pub struct IccChunk {
pub num_markers: u8,
pub seq_no: u8,
pub data: Vec<u8>,
}
impl FrameInfo {
pub(crate) fn update_idct_size(&mut self, idct_size: usize) -> Result<()> {
for component in &mut self.components {
component.dct_scale = idct_size;
}
update_component_sizes(self.image_size, &mut self.components)?;
self.output_size = Dimensions {
width: (self.image_size.width as f32 * idct_size as f32 / 8.0).ceil() as u16,
height: (self.image_size.height as f32 * idct_size as f32 / 8.0).ceil() as u16
};
Ok(())
}
}
fn read_length<R: Read>(reader: &mut R, marker: Marker) -> Result<usize> {
assert!(marker.has_length());
// length is including itself.
let length = usize::from(read_u16_from_be(reader)?);
if length < 2 {
return Err(Error::Format(format!("encountered {:?} with invalid length {}", marker, length)));
}
Ok(length - 2)
}
fn skip_bytes<R: Read>(reader: &mut R, length: usize) -> Result<()> {
let length = length as u64;
let to_skip = &mut reader.by_ref().take(length);
let copied = io::copy(to_skip, &mut io::sink())?;
if copied < length {
Err(Error::Io(io::ErrorKind::UnexpectedEof.into()))
} else {
Ok(())
}
}
// Section B.2.2
pub fn parse_sof<R: Read>(reader: &mut R, marker: Marker) -> Result<FrameInfo> {
let length = read_length(reader, marker)?;
if length <= 6 {
return Err(Error::Format("invalid length in SOF".to_owned()));
}
let is_baseline = marker == SOF(0);
let is_differential = match marker {
SOF(0 ..= 3) | SOF(9 ..= 11) => false,
SOF(5 ..= 7) | SOF(13 ..= 15) => true,
_ => panic!(),
};
let coding_process = match marker {
SOF(0) | SOF(1) | SOF(5) | SOF(9) | SOF(13) => CodingProcess::DctSequential,
SOF(2) | SOF(6) | SOF(10) | SOF(14) => CodingProcess::DctProgressive,
SOF(3) | SOF(7) | SOF(11) | SOF(15) => CodingProcess::Lossless,
_ => panic!(),
};
let entropy_coding = match marker {
SOF(0 ..= 3) | SOF(5 ..= 7) => EntropyCoding::Huffman,
SOF(9 ..= 11) | SOF(13 ..= 15) => EntropyCoding::Arithmetic,
_ => panic!(),
};
let precision = read_u8(reader)?;
match precision {
8 => {},
12 => {
if is_baseline {
return Err(Error::Format("12 bit sample precision is not allowed in baseline".to_owned()));
}
},
_ => {
if coding_process != CodingProcess::Lossless || precision > 16 {
return Err(Error::Format(format!("invalid precision {} in frame header", precision)))
}
},
}
let height = read_u16_from_be(reader)?;
let width = read_u16_from_be(reader)?;
// height:
// "Value 0 indicates that the number of lines shall be defined by the DNL marker and
// parameters at the end of the first scan (see B.2.5)."
if height == 0 {
return Err(Error::Unsupported(UnsupportedFeature::DNL));
}
if width == 0 {
return Err(Error::Format("zero width in frame header".to_owned()));
}
let component_count = read_u8(reader)?;
if component_count == 0 {
return Err(Error::Format("zero component count in frame header".to_owned()));
}
if coding_process == CodingProcess::DctProgressive && component_count > 4 {
return Err(Error::Format("progressive frame with more than 4 components".to_owned()));
}
if length != 6 + 3 * component_count as usize {
return Err(Error::Format("invalid length in SOF".to_owned()));
}
let mut components: Vec<Component> = Vec::with_capacity(component_count as usize);
for _ in 0 .. component_count {
let identifier = read_u8(reader)?;
// Each component's identifier must be unique.
if components.iter().any(|c| c.identifier == identifier) {
return Err(Error::Format(format!("duplicate frame component identifier {}", identifier)));
}
let byte = read_u8(reader)?;
let horizontal_sampling_factor = byte >> 4;
let vertical_sampling_factor = byte & 0x0f;
if horizontal_sampling_factor == 0 || horizontal_sampling_factor > 4 {
return Err(Error::Format(format!("invalid horizontal sampling factor {}", horizontal_sampling_factor)));
}
if vertical_sampling_factor == 0 || vertical_sampling_factor > 4 {
return Err(Error::Format(format!("invalid vertical sampling factor {}", vertical_sampling_factor)));
}
let quantization_table_index = read_u8(reader)?;
if quantization_table_index > 3 || (coding_process == CodingProcess::Lossless && quantization_table_index != 0) {
return Err(Error::Format(format!("invalid quantization table index {}", quantization_table_index)));
}
components.push(Component {
identifier,
horizontal_sampling_factor,
vertical_sampling_factor,
quantization_table_index: quantization_table_index as usize,
dct_scale: 8,
size: Dimensions {width: 0, height: 0},
block_size: Dimensions {width: 0, height: 0},
});
}
let mcu_size = update_component_sizes(Dimensions { width, height }, &mut components)?;
Ok(FrameInfo {
is_baseline,
is_differential,
coding_process,
entropy_coding,
precision,
image_size: Dimensions { width, height },
output_size: Dimensions { width, height },
mcu_size,
components,
})
}
/// Returns ceil(x/y), requires x>0
fn ceil_div(x: u32, y: u32) -> Result<u16> {
if x == 0 || y == 0 {
// TODO Determine how this error is reached. Can we validate input
// earlier and error out then?
return Err(Error::Format("invalid dimensions".to_owned()));
}
Ok((1 + ((x - 1) / y)) as u16)
}
fn update_component_sizes(size: Dimensions, components: &mut [Component]) -> Result<Dimensions> {
let h_max = components.iter().map(|c| c.horizontal_sampling_factor).max().unwrap() as u32;
let v_max = components.iter().map(|c| c.vertical_sampling_factor).max().unwrap() as u32;
let mcu_size = Dimensions {
width: ceil_div(size.width as u32, h_max * 8)?,
height: ceil_div(size.height as u32, v_max * 8)?,
};
for component in components {
component.size.width = ceil_div(size.width as u32 * component.horizontal_sampling_factor as u32 * component.dct_scale as u32, h_max * 8)?;
component.size.height = ceil_div(size.height as u32 * component.vertical_sampling_factor as u32 * component.dct_scale as u32, v_max * 8)?;
component.block_size.width = mcu_size.width * component.horizontal_sampling_factor as u16;
component.block_size.height = mcu_size.height * component.vertical_sampling_factor as u16;
}
Ok(mcu_size)
}
#[test]
fn test_update_component_sizes() {
let mut components = [Component {
identifier: 1,
horizontal_sampling_factor: 2,
vertical_sampling_factor: 2,
quantization_table_index: 0,
dct_scale: 8,
size: Dimensions { width: 0, height: 0 },
block_size: Dimensions { width: 0, height: 0 },
}];
let mcu = update_component_sizes(
Dimensions { width: 800, height: 280 },
&mut components).unwrap();
assert_eq!(mcu, Dimensions { width: 50, height: 18 });
assert_eq!(components[0].block_size, Dimensions { width: 100, height: 36 });
assert_eq!(components[0].size, Dimensions { width: 800, height: 280 });
}
// Section B.2.3
pub fn parse_sos<R: Read>(reader: &mut R, frame: &FrameInfo) -> Result<ScanInfo> {
let length = read_length(reader, SOS)?;
if 0 == length {
return Err(Error::Format("zero length in SOS".to_owned()));
}
let component_count = read_u8(reader)?;
if component_count == 0 || component_count > 4 {
return Err(Error::Format(format!("invalid component count {} in scan header", component_count)));
}
if length != 4 + 2 * component_count as usize {
return Err(Error::Format("invalid length in SOS".to_owned()));
}
let mut component_indices = Vec::with_capacity(component_count as usize);
let mut dc_table_indices = Vec::with_capacity(component_count as usize);
let mut ac_table_indices = Vec::with_capacity(component_count as usize);
for _ in 0 .. component_count {
let identifier = read_u8(reader)?;
let component_index = match frame.components.iter().position(|c| c.identifier == identifier) {
Some(value) => value,
None => return Err(Error::Format(format!("scan component identifier {} does not match any of the component identifiers defined in the frame", identifier))),
};
// Each of the scan's components must be unique.
if component_indices.contains(&component_index) {
return Err(Error::Format(format!("duplicate scan component identifier {}", identifier)));
}
// "... the ordering in the scan header shall follow the ordering in the frame header."
if component_index < *component_indices.iter().max().unwrap_or(&0) {
return Err(Error::Format("the scan component order does not follow the order in the frame header".to_owned()));
}
let byte = read_u8(reader)?;
let dc_table_index = byte >> 4;
let ac_table_index = byte & 0x0f;
if dc_table_index > 3 || (frame.is_baseline && dc_table_index > 1) {
return Err(Error::Format(format!("invalid dc table index {}", dc_table_index)));
}
if ac_table_index > 3 || (frame.is_baseline && ac_table_index > 1) {
return Err(Error::Format(format!("invalid ac table index {}", ac_table_index)));
}
component_indices.push(component_index);
dc_table_indices.push(dc_table_index as usize);
ac_table_indices.push(ac_table_index as usize);
}
let blocks_per_mcu = component_indices.iter().map(|&i| {
frame.components[i].horizontal_sampling_factor as u32 * frame.components[i].vertical_sampling_factor as u32
}).fold(0, ops::Add::add);
if component_count > 1 && blocks_per_mcu > 10 {
return Err(Error::Format("scan with more than one component and more than 10 blocks per MCU".to_owned()));
}
// Also utilized as 'Predictor' in lossless coding, as MEAN in JPEG-LS etc.
let spectral_selection_start = read_u8(reader)?;
// Also utilized as ILV parameter in JPEG-LS.
let mut spectral_selection_end = read_u8(reader)?;
let byte = read_u8(reader)?;
let successive_approximation_high = byte >> 4;
let successive_approximation_low = byte & 0x0f;
// The Differential Pulse-Mode prediction used (similar to png). Only utilized in Lossless
// coding. Don't confuse with the JPEG-LS parameter coded using the same scan info portion.
let predictor_selection;
let point_transform = successive_approximation_low;
if frame.coding_process == CodingProcess::DctProgressive {
predictor_selection = Predictor::NoPrediction;
if spectral_selection_end > 63 || spectral_selection_start > spectral_selection_end ||
(spectral_selection_start == 0 && spectral_selection_end != 0) {
return Err(Error::Format(format!("invalid spectral selection parameters: ss={}, se={}", spectral_selection_start, spectral_selection_end)));
}
if spectral_selection_start != 0 && component_count != 1 {
return Err(Error::Format("spectral selection scan with AC coefficients can't have more than one component".to_owned()));
}
if successive_approximation_high > 13 || successive_approximation_low > 13 {
return Err(Error::Format(format!("invalid successive approximation parameters: ah={}, al={}", successive_approximation_high, successive_approximation_low)));
}
// Section G.1.1.1.2
// "Each scan which follows the first scan for a given band progressively improves
// the precision of the coefficients by one bit, until full precision is reached."
if successive_approximation_high != 0 && successive_approximation_high != successive_approximation_low + 1 {
return Err(Error::Format("successive approximation scan with more than one bit of improvement".to_owned()));
}
}
else if frame.coding_process == CodingProcess::Lossless {
if spectral_selection_end != 0 {
return Err(Error::Format("spectral selection end shall be zero in lossless scan".to_owned()));
}
if successive_approximation_high != 0 {
return Err(Error::Format("successive approximation high shall be zero in lossless scan".to_owned()));
}
predictor_selection = match spectral_selection_start {
0 => Predictor::NoPrediction,
1 => Predictor::Ra,
2 => Predictor::Rb,
3 => Predictor::Rc,
4 => Predictor::RaRbRc1,
5 => Predictor::RaRbRc2,
6 => Predictor::RaRbRc3,
7 => Predictor::RaRb,
_ => {
return Err(Error::Format(format!("invalid predictor selection value: {}", spectral_selection_start)));
},
};
}
else {
predictor_selection = Predictor::NoPrediction;
if spectral_selection_end == 0 {
spectral_selection_end = 63;
}
if spectral_selection_start != 0 || spectral_selection_end != 63 {
return Err(Error::Format("spectral selection is not allowed in non-progressive scan".to_owned()));
}
if successive_approximation_high != 0 || successive_approximation_low != 0 {
return Err(Error::Format("successive approximation is not allowed in non-progressive scan".to_owned()));
}
}
Ok(ScanInfo {
component_indices,
dc_table_indices,
ac_table_indices,
spectral_selection: Range {
start: spectral_selection_start,
end: spectral_selection_end + 1,
},
predictor_selection,
successive_approximation_high,
successive_approximation_low,
point_transform,
})
}
// Section B.2.4.1
pub fn parse_dqt<R: Read>(reader: &mut R) -> Result<[Option<[u16; 64]>; 4]> {
let mut length = read_length(reader, DQT)?;
let mut tables = [None; 4];
// Each DQT segment may contain multiple quantization tables.
while length > 0 {
let byte = read_u8(reader)?;
let precision = (byte >> 4) as usize;
let index = (byte & 0x0f) as usize;
// The combination of 8-bit sample precision and 16-bit quantization tables is explicitly
// disallowed by the JPEG spec:
// "An 8-bit DCT-based process shall not use a 16-bit precision quantization table."
// "Pq: Quantization table element precision Specifies the precision of the Qk
// values. Value 0 indicates 8-bit Qk values; value 1 indicates 16-bit Qk values. Pq
// shall be zero for 8 bit sample precision P (see B.2.2)."
// libjpeg allows this behavior though, and there are images in the wild using it. So to
// match libjpeg's behavior we are deviating from the JPEG spec here.
if precision > 1 {
return Err(Error::Format(format!("invalid precision {} in DQT", precision)));
}
if index > 3 {
return Err(Error::Format(format!("invalid destination identifier {} in DQT", index)));
}
if length < 65 + 64 * precision {
return Err(Error::Format("invalid length in DQT".to_owned()));
}
let mut table = [0u16; 64];
for item in table.iter_mut() {
*item = match precision {
0 => u16::from(read_u8(reader)?),
1 => read_u16_from_be(reader)?,
_ => unreachable!(),
};
}
if table.iter().any(|&val| val == 0) {
return Err(Error::Format("quantization table contains element with a zero value".to_owned()));
}
tables[index] = Some(table);
length -= 65 + 64 * precision;
}
Ok(tables)
}
// Section B.2.4.2
pub fn parse_dht<R: Read>(reader: &mut R, is_baseline: Option<bool>) -> Result<(Vec<Option<HuffmanTable>>, Vec<Option<HuffmanTable>>)> {
let mut length = read_length(reader, DHT)?;
let mut dc_tables = vec![None, None, None, None];
let mut ac_tables = vec![None, None, None, None];
// Each DHT segment may contain multiple huffman tables.
while length > 17 {
let byte = read_u8(reader)?;
let class = byte >> 4;
let index = (byte & 0x0f) as usize;
if class != 0 && class != 1 {
return Err(Error::Format(format!("invalid class {} in DHT", class)));
}
if is_baseline == Some(true) && index > 1 {
return Err(Error::Format("a maximum of two huffman tables per class are allowed in baseline".to_owned()));
}
if index > 3 {
return Err(Error::Format(format!("invalid destination identifier {} in DHT", index)));
}
let mut counts = [0u8; 16];
reader.read_exact(&mut counts)?;
let size = counts.iter().map(|&val| val as usize).fold(0, ops::Add::add);
if size == 0 {
return Err(Error::Format("encountered table with zero length in DHT".to_owned()));
}
else if size > 256 {
return Err(Error::Format("encountered table with excessive length in DHT".to_owned()));
}
else if size > length - 17 {
return Err(Error::Format("invalid length in DHT".to_owned()));
}
let mut values = vec![0u8; size];
reader.read_exact(&mut values)?;
match class {
0 => dc_tables[index] = Some(HuffmanTable::new(&counts, &values, HuffmanTableClass::DC)?),
1 => ac_tables[index] = Some(HuffmanTable::new(&counts, &values, HuffmanTableClass::AC)?),
_ => unreachable!(),
}
length -= 17 + size;
}
if length != 0 {
return Err(Error::Format("invalid length in DHT".to_owned()));
}
Ok((dc_tables, ac_tables))
}
// Section B.2.4.4
pub fn parse_dri<R: Read>(reader: &mut R) -> Result<u16> {
let length = read_length(reader, DRI)?;
if length != 2 {
return Err(Error::Format("DRI with invalid length".to_owned()));
}
Ok(read_u16_from_be(reader)?)
}
// Section B.2.4.5
pub fn parse_com<R: Read>(reader: &mut R) -> Result<Vec<u8>> {
let length = read_length(reader, COM)?;
let mut buffer = vec![0u8; length];
reader.read_exact(&mut buffer)?;
Ok(buffer)
}
// Section B.2.4.6
pub fn parse_app<R: Read>(reader: &mut R, marker: Marker) -> Result<Option<AppData>> {
let length = read_length(reader, marker)?;
let mut bytes_read = 0;
let mut result = None;
match marker {
APP(0) => {
if length >= 5 {
let mut buffer = [0u8; 5];
reader.read_exact(&mut buffer)?;
bytes_read = buffer.len();
// http://www.w3.org/Graphics/JPEG/jfif3.pdf
if buffer[0..5] == *b"JFIF\0" {
result = Some(AppData::Jfif);
// https://sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#AVI1
} else if buffer[0..5] == *b"AVI1\0" {
result = Some(AppData::Avi1);
}
}
}
// Exif Data
APP(1) => {
if length >= 6 {
let mut buffer = [0u8; 6];
reader.read_exact(&mut buffer)?;
bytes_read = buffer.len();
// https://web.archive.org/web/20190624045241if_/http://www.cipa.jp:80/std/documents/e/DC-008-Translation-2019-E.pdf
// 4.5.4 Basic Structure of JPEG Compressed Data
if buffer == *b"Exif\x00\x00" {
let mut data = vec![0; length - bytes_read];
reader.read_exact(&mut data)?;
bytes_read += data.len();
result = Some(AppData::Exif(data));
}
}
}
APP(2) => {
if length > 14 {
let mut buffer = [0u8; 14];
reader.read_exact(&mut buffer)?;
bytes_read = buffer.len();
// http://www.color.org/ICC_Minor_Revision_for_Web.pdf
// B.4 Embedding ICC profiles in JFIF files
if buffer[0..12] == *b"ICC_PROFILE\0" {
let mut data = vec![0; length - bytes_read];
reader.read_exact(&mut data)?;
bytes_read += data.len();
result = Some(AppData::Icc(IccChunk {
seq_no: buffer[12],
num_markers: buffer[13],
data,
}));
}
}
}
APP(14) => {
if length >= 12 {
let mut buffer = [0u8; 12];
reader.read_exact(&mut buffer)?;
bytes_read = buffer.len();
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe
if buffer[0 .. 6] == *b"Adobe\0" {
let color_transform = match buffer[11] {
0 => AdobeColorTransform::Unknown,
1 => AdobeColorTransform::YCbCr,
2 => AdobeColorTransform::YCCK,
_ => return Err(Error::Format("invalid color transform in adobe app segment".to_owned())),
};
result = Some(AppData::Adobe(color_transform));
}
}
},
_ => {},
}
skip_bytes(reader, length - bytes_read)?;
Ok(result)
}

252
vendor/jpeg-decoder/src/upsampler.rs vendored Normal file
View File

@ -0,0 +1,252 @@
use alloc::boxed::Box;
use alloc::vec;
use alloc::vec::Vec;
use crate::error::{Error, Result, UnsupportedFeature};
use crate::parser::Component;
pub struct Upsampler {
components: Vec<UpsamplerComponent>,
line_buffer_size: usize
}
struct UpsamplerComponent {
upsampler: Box<dyn Upsample + Sync>,
width: usize,
height: usize,
row_stride: usize,
}
impl Upsampler {
pub fn new(components: &[Component], output_width: u16, output_height: u16) -> Result<Upsampler> {
let h_max = components.iter().map(|c| c.horizontal_sampling_factor).max().unwrap();
let v_max = components.iter().map(|c| c.vertical_sampling_factor).max().unwrap();
let mut upsampler_components = Vec::with_capacity(components.len());
for component in components {
let upsampler = choose_upsampler((component.horizontal_sampling_factor,
component.vertical_sampling_factor),
(h_max, v_max),
output_width,
output_height)?;
upsampler_components.push(UpsamplerComponent {
upsampler,
width: component.size.width as usize,
height: component.size.height as usize,
row_stride: component.block_size.width as usize * component.dct_scale,
});
}
let buffer_size = components.iter().map(|c| c.size.width).max().unwrap() as usize * h_max as usize;
Ok(Upsampler {
components: upsampler_components,
line_buffer_size: buffer_size
})
}
pub fn upsample_and_interleave_row(&self, component_data: &[Vec<u8>], row: usize, output_width: usize, output: &mut [u8], color_convert: fn(&[Vec<u8>], &mut [u8])) {
let component_count = component_data.len();
let mut line_buffers = vec![vec![0u8; self.line_buffer_size]; component_count];
debug_assert_eq!(component_count, self.components.len());
for (i, component) in self.components.iter().enumerate() {
component.upsampler.upsample_row(&component_data[i],
component.width,
component.height,
component.row_stride,
row,
output_width,
&mut line_buffers[i]);
}
color_convert(&line_buffers, output);
}
}
struct UpsamplerH1V1;
struct UpsamplerH2V1;
struct UpsamplerH1V2;
struct UpsamplerH2V2;
struct UpsamplerGeneric {
horizontal_scaling_factor: u8,
vertical_scaling_factor: u8
}
fn choose_upsampler(sampling_factors: (u8, u8),
max_sampling_factors: (u8, u8),
output_width: u16,
output_height: u16) -> Result<Box<dyn Upsample + Sync>> {
let h1 = sampling_factors.0 == max_sampling_factors.0 || output_width == 1;
let v1 = sampling_factors.1 == max_sampling_factors.1 || output_height == 1;
let h2 = sampling_factors.0 * 2 == max_sampling_factors.0;
let v2 = sampling_factors.1 * 2 == max_sampling_factors.1;
if h1 && v1 {
Ok(Box::new(UpsamplerH1V1))
}
else if h2 && v1 {
Ok(Box::new(UpsamplerH2V1))
}
else if h1 && v2 {
Ok(Box::new(UpsamplerH1V2))
}
else if h2 && v2 {
Ok(Box::new(UpsamplerH2V2))
}
else {
if max_sampling_factors.0 % sampling_factors.0 != 0 || max_sampling_factors.1 % sampling_factors.1 != 0 {
Err(Error::Unsupported(UnsupportedFeature::NonIntegerSubsamplingRatio))
}
else {
Ok(Box::new(UpsamplerGeneric {
horizontal_scaling_factor: max_sampling_factors.0 / sampling_factors.0,
vertical_scaling_factor: max_sampling_factors.1 / sampling_factors.1
}))
}
}
}
trait Upsample {
fn upsample_row(&self,
input: &[u8],
input_width: usize,
input_height: usize,
row_stride: usize,
row: usize,
output_width: usize,
output: &mut [u8]);
}
impl Upsample for UpsamplerH1V1 {
fn upsample_row(&self,
input: &[u8],
_input_width: usize,
_input_height: usize,
row_stride: usize,
row: usize,
output_width: usize,
output: &mut [u8]) {
let input = &input[row * row_stride ..];
output[..output_width].copy_from_slice(&input[..output_width]);
}
}
impl Upsample for UpsamplerH2V1 {
fn upsample_row(&self,
input: &[u8],
input_width: usize,
_input_height: usize,
row_stride: usize,
row: usize,
_output_width: usize,
output: &mut [u8]) {
let input = &input[row * row_stride ..];
if input_width == 1 {
output[0] = input[0];
output[1] = input[0];
return;
}
output[0] = input[0];
output[1] = ((input[0] as u32 * 3 + input[1] as u32 + 2) >> 2) as u8;
for i in 1 .. input_width - 1 {
let sample = 3 * input[i] as u32 + 2;
output[i * 2] = ((sample + input[i - 1] as u32) >> 2) as u8;
output[i * 2 + 1] = ((sample + input[i + 1] as u32) >> 2) as u8;
}
output[(input_width - 1) * 2] = ((input[input_width - 1] as u32 * 3 + input[input_width - 2] as u32 + 2) >> 2) as u8;
output[(input_width - 1) * 2 + 1] = input[input_width - 1];
}
}
impl Upsample for UpsamplerH1V2 {
fn upsample_row(&self,
input: &[u8],
_input_width: usize,
input_height: usize,
row_stride: usize,
row: usize,
output_width: usize,
output: &mut [u8]) {
let row_near = row as f32 / 2.0;
// If row_near's fractional is 0.0 we want row_far to be the previous row and if it's 0.5 we
// want it to be the next row.
let row_far = (row_near + row_near.fract() * 3.0 - 0.25).min((input_height - 1) as f32);
let input_near = &input[row_near as usize * row_stride ..];
let input_far = &input[row_far as usize * row_stride ..];
let output = &mut output[..output_width];
let input_near = &input_near[..output_width];
let input_far = &input_far[..output_width];
for i in 0..output_width {
output[i] = ((3 * input_near[i] as u32 + input_far[i] as u32 + 2) >> 2) as u8;
}
}
}
impl Upsample for UpsamplerH2V2 {
fn upsample_row(&self,
input: &[u8],
input_width: usize,
input_height: usize,
row_stride: usize,
row: usize,
_output_width: usize,
output: &mut [u8]) {
let row_near = row as f32 / 2.0;
// If row_near's fractional is 0.0 we want row_far to be the previous row and if it's 0.5 we
// want it to be the next row.
let row_far = (row_near + row_near.fract() * 3.0 - 0.25).min((input_height - 1) as f32);
let input_near = &input[row_near as usize * row_stride ..];
let input_far = &input[row_far as usize * row_stride ..];
if input_width == 1 {
let value = ((3 * input_near[0] as u32 + input_far[0] as u32 + 2) >> 2) as u8;
output[0] = value;
output[1] = value;
return;
}
let mut t1 = 3 * input_near[0] as u32 + input_far[0] as u32;
output[0] = ((t1 + 2) >> 2) as u8;
for i in 1 .. input_width {
let t0 = t1;
t1 = 3 * input_near[i] as u32 + input_far[i] as u32;
output[i * 2 - 1] = ((3 * t0 + t1 + 8) >> 4) as u8;
output[i * 2] = ((3 * t1 + t0 + 8) >> 4) as u8;
}
output[input_width * 2 - 1] = ((t1 + 2) >> 2) as u8;
}
}
impl Upsample for UpsamplerGeneric {
// Uses nearest neighbor sampling
fn upsample_row(&self,
input: &[u8],
input_width: usize,
_input_height: usize,
row_stride: usize,
row: usize,
_output_width: usize,
output: &mut [u8]) {
let mut index = 0;
let start = (row / self.vertical_scaling_factor as usize) * row_stride;
let input = &input[start..(start + input_width)];
for val in input {
for _ in 0..self.horizontal_scaling_factor {
output[index] = *val;
index += 1;
}
}
}
}

View File

@ -0,0 +1,80 @@
use alloc::vec;
use alloc::vec::Vec;
use core::mem;
use core::convert::TryInto;
use crate::decoder::MAX_COMPONENTS;
use crate::error::Result;
use crate::idct::dequantize_and_idct_block;
use crate::alloc::sync::Arc;
use crate::parser::Component;
use super::{RowData, Worker};
pub struct ImmediateWorker {
offsets: [usize; MAX_COMPONENTS],
results: Vec<Vec<u8>>,
components: Vec<Option<Component>>,
quantization_tables: Vec<Option<Arc<[u16; 64]>>>,
}
impl Default for ImmediateWorker {
fn default() -> Self {
ImmediateWorker {
offsets: [0; MAX_COMPONENTS],
results: vec![Vec::new(); MAX_COMPONENTS],
components: vec![None; MAX_COMPONENTS],
quantization_tables: vec![None; MAX_COMPONENTS],
}
}
}
impl ImmediateWorker {
pub fn start_immediate(&mut self, data: RowData) {
assert!(self.results[data.index].is_empty());
self.offsets[data.index] = 0;
self.results[data.index].resize(data.component.block_size.width as usize * data.component.block_size.height as usize * data.component.dct_scale * data.component.dct_scale, 0u8);
self.components[data.index] = Some(data.component);
self.quantization_tables[data.index] = Some(data.quantization_table);
}
pub fn append_row_immediate(&mut self, (index, data): (usize, Vec<i16>)) {
// Convert coefficients from a MCU row to samples.
let component = self.components[index].as_ref().unwrap();
let quantization_table = self.quantization_tables[index].as_ref().unwrap();
let block_count = component.block_size.width as usize * component.vertical_sampling_factor as usize;
let line_stride = component.block_size.width as usize * component.dct_scale;
assert_eq!(data.len(), block_count * 64);
for i in 0..block_count {
let x = (i % component.block_size.width as usize) * component.dct_scale;
let y = (i / component.block_size.width as usize) * component.dct_scale;
let coefficients = data[i * 64..(i + 1) * 64].try_into().unwrap();
let output = &mut self.results[index][self.offsets[index] + y * line_stride + x..];
dequantize_and_idct_block(component.dct_scale, coefficients, quantization_table, line_stride, output);
}
self.offsets[index] += block_count * component.dct_scale * component.dct_scale;
}
pub fn get_result_immediate(&mut self, index: usize) -> Vec<u8> {
mem::take(&mut self.results[index])
}
}
impl Worker for ImmediateWorker {
fn start(&mut self, data: RowData) -> Result<()> {
self.start_immediate(data);
Ok(())
}
fn append_row(&mut self, row: (usize, Vec<i16>)) -> Result<()> {
self.append_row_immediate(row);
Ok(())
}
fn get_result(&mut self, index: usize) -> Result<Vec<u8>> {
Ok(self.get_result_immediate(index))
}
}

128
vendor/jpeg-decoder/src/worker/mod.rs vendored Normal file
View File

@ -0,0 +1,128 @@
mod immediate;
mod multithreaded;
#[cfg(all(
not(any(target_arch = "asmjs", target_arch = "wasm32")),
feature = "rayon"
))]
mod rayon;
use crate::decoder::{choose_color_convert_func, ColorTransform};
use crate::error::Result;
use crate::parser::{Component, Dimensions};
use crate::upsampler::Upsampler;
use alloc::sync::Arc;
use alloc::vec::Vec;
use core::cell::RefCell;
pub struct RowData {
pub index: usize,
pub component: Component,
pub quantization_table: Arc<[u16; 64]>,
}
pub trait Worker {
fn start(&mut self, row_data: RowData) -> Result<()>;
fn append_row(&mut self, row: (usize, Vec<i16>)) -> Result<()>;
fn get_result(&mut self, index: usize) -> Result<Vec<u8>>;
/// Default implementation for spawning multiple tasks.
fn append_rows(&mut self, row: &mut dyn Iterator<Item = (usize, Vec<i16>)>) -> Result<()> {
for item in row {
self.append_row(item)?;
}
Ok(())
}
}
#[allow(dead_code)]
pub enum PreferWorkerKind {
Immediate,
Multithreaded,
}
#[derive(Default)]
pub struct WorkerScope {
inner: core::cell::RefCell<Option<WorkerScopeInner>>,
}
enum WorkerScopeInner {
#[cfg(all(
not(any(target_arch = "asmjs", target_arch = "wasm32")),
feature = "rayon"
))]
Rayon(rayon::Scoped),
#[cfg(not(any(target_arch = "asmjs", target_arch = "wasm32")))]
Multithreaded(multithreaded::MpscWorker),
Immediate(immediate::ImmediateWorker),
}
impl WorkerScope {
pub fn with<T>(with: impl FnOnce(&Self) -> T) -> T {
with(&WorkerScope {
inner: RefCell::default(),
})
}
pub fn get_or_init_worker<T>(
&self,
prefer: PreferWorkerKind,
f: impl FnOnce(&mut dyn Worker) -> T,
) -> T {
let mut inner = self.inner.borrow_mut();
let inner = inner.get_or_insert_with(move || match prefer {
#[cfg(all(
not(any(target_arch = "asmjs", target_arch = "wasm32")),
feature = "rayon"
))]
PreferWorkerKind::Multithreaded => WorkerScopeInner::Rayon(Default::default()),
#[allow(unreachable_patterns)]
#[cfg(not(any(target_arch = "asmjs", target_arch = "wasm32")))]
PreferWorkerKind::Multithreaded => WorkerScopeInner::Multithreaded(Default::default()),
_ => WorkerScopeInner::Immediate(Default::default()),
});
f(match &mut *inner {
#[cfg(all(
not(any(target_arch = "asmjs", target_arch = "wasm32")),
feature = "rayon"
))]
WorkerScopeInner::Rayon(worker) => worker,
#[cfg(not(any(target_arch = "asmjs", target_arch = "wasm32")))]
WorkerScopeInner::Multithreaded(worker) => worker,
WorkerScopeInner::Immediate(worker) => worker,
})
}
}
pub fn compute_image_parallel(
components: &[Component],
data: Vec<Vec<u8>>,
output_size: Dimensions,
color_transform: ColorTransform,
) -> Result<Vec<u8>> {
#[cfg(all(
not(any(target_arch = "asmjs", target_arch = "wasm32")),
feature = "rayon"
))]
return rayon::compute_image_parallel(components, data, output_size, color_transform);
#[allow(unreachable_code)]
{
let color_convert_func = choose_color_convert_func(components.len(), color_transform)?;
let upsampler = Upsampler::new(components, output_size.width, output_size.height)?;
let line_size = output_size.width as usize * components.len();
let mut image = vec![0u8; line_size * output_size.height as usize];
for (row, line) in image.chunks_mut(line_size).enumerate() {
upsampler.upsample_and_interleave_row(
&data,
row,
output_size.width as usize,
line,
color_convert_func,
);
}
Ok(image)
}
}

View File

@ -0,0 +1,123 @@
//! This module implements per-component parallelism.
//! It should be possible to implement per-row parallelism as well,
//! which should also boost performance of grayscale images
//! and allow scaling to more cores.
//! However, that would be more complex, so we use this as a starting point.
use super::immediate::ImmediateWorker;
use super::{RowData, Worker};
use crate::decoder::MAX_COMPONENTS;
use crate::error::Result;
use std::{
mem,
sync::mpsc::{self, Receiver, Sender},
};
enum WorkerMsg {
Start(RowData),
AppendRow(Vec<i16>),
GetResult(Sender<Vec<u8>>),
}
#[derive(Default)]
pub struct MpscWorker {
senders: [Option<Sender<WorkerMsg>>; MAX_COMPONENTS],
}
impl MpscWorker {
fn start_with(
&mut self,
row_data: RowData,
spawn_worker: impl FnOnce(usize) -> Result<Sender<WorkerMsg>>,
) -> Result<()> {
// if there is no worker thread for this component yet, start one
let component = row_data.index;
if let None = self.senders[component] {
let sender = spawn_worker(component)?;
self.senders[component] = Some(sender);
}
// we do the "take out value and put it back in once we're done" dance here
// and in all other message-passing methods because there's not that many rows
// and this should be cheaper than spawning MAX_COMPONENTS many threads up front
let sender = self.senders[component].as_mut().unwrap();
sender
.send(WorkerMsg::Start(row_data))
.expect("jpeg-decoder worker thread error");
Ok(())
}
fn append_row(&mut self, row: (usize, Vec<i16>)) -> Result<()> {
let component = row.0;
let sender = self.senders[component].as_mut().unwrap();
sender
.send(WorkerMsg::AppendRow(row.1))
.expect("jpeg-decoder worker thread error");
Ok(())
}
fn get_result_with(
&mut self,
index: usize,
collect: impl FnOnce(Receiver<Vec<u8>>) -> Vec<u8>,
) -> Result<Vec<u8>> {
let (tx, rx) = mpsc::channel();
let sender = mem::take(&mut self.senders[index]).unwrap();
sender
.send(WorkerMsg::GetResult(tx))
.expect("jpeg-decoder worker thread error");
Ok(collect(rx))
}
}
impl Worker for MpscWorker {
fn start(&mut self, row_data: RowData) -> Result<()> {
self.start_with(row_data, spawn_worker_thread)
}
fn append_row(&mut self, row: (usize, Vec<i16>)) -> Result<()> {
MpscWorker::append_row(self, row)
}
fn get_result(&mut self, index: usize) -> Result<Vec<u8>> {
self.get_result_with(index, collect_worker_thread)
}
}
fn create_worker() -> (Sender<WorkerMsg>, impl FnOnce() + 'static) {
let (tx, rx) = mpsc::channel();
let closure = move || {
let mut worker = ImmediateWorker::default();
while let Ok(message) = rx.recv() {
match message {
WorkerMsg::Start(mut data) => {
// we always set component index to 0 for worker threads
// because they only ever handle one per thread and we don't want them
// to attempt to access nonexistent components
data.index = 0;
worker.start_immediate(data);
}
WorkerMsg::AppendRow(row) => {
worker.append_row_immediate((0, row));
}
WorkerMsg::GetResult(chan) => {
let _ = chan.send(worker.get_result_immediate(0));
break;
}
}
}
};
(tx, closure)
}
fn spawn_worker_thread(component: usize) -> Result<Sender<WorkerMsg>> {
let (tx, worker) = create_worker();
let thread_builder =
std::thread::Builder::new().name(format!("worker thread for component {}", component));
thread_builder.spawn(worker)?;
Ok(tx)
}
fn collect_worker_thread(rx: Receiver<Vec<u8>>) -> Vec<u8> {
rx.recv().expect("jpeg-decoder worker thread error")
}

221
vendor/jpeg-decoder/src/worker/rayon.rs vendored Normal file
View File

@ -0,0 +1,221 @@
use core::convert::TryInto;
use rayon::iter::{IndexedParallelIterator, ParallelIterator};
use rayon::slice::ParallelSliceMut;
use crate::decoder::{choose_color_convert_func, ColorTransform};
use crate::error::Result;
use crate::idct::dequantize_and_idct_block;
use crate::parser::Component;
use crate::upsampler::Upsampler;
use crate::{decoder::MAX_COMPONENTS, parser::Dimensions};
use std::sync::Arc;
use super::{RowData, Worker};
/// Technically similar to `immediate::ImmediateWorker` but we copy it since we may prefer
/// different style of managing the memory allocation, something that multiple actors can access in
/// parallel.
#[derive(Default)]
struct ImmediateWorker {
offsets: [usize; MAX_COMPONENTS],
results: [Vec<u8>; MAX_COMPONENTS],
components: [Option<Component>; MAX_COMPONENTS],
quantization_tables: [Option<Arc<[u16; 64]>>; MAX_COMPONENTS],
}
#[derive(Clone, Copy)]
struct ComponentMetadata {
block_width: usize,
block_count: usize,
line_stride: usize,
dct_scale: usize,
}
#[derive(Default)]
pub struct Scoped {
inner: ImmediateWorker,
}
impl ImmediateWorker {
pub fn start_immediate(&mut self, data: RowData) {
let elements = data.component.block_size.width as usize
* data.component.block_size.height as usize
* data.component.dct_scale
* data.component.dct_scale;
self.offsets[data.index] = 0;
self.results[data.index].resize(elements, 0u8);
self.components[data.index] = Some(data.component);
self.quantization_tables[data.index] = Some(data.quantization_table);
}
pub fn get_result_immediate(&mut self, index: usize) -> Vec<u8> {
core::mem::take(&mut self.results[index])
}
pub fn component_metadata(&self, index: usize) -> Option<ComponentMetadata> {
let component = self.components[index].as_ref()?;
let block_size = component.block_size;
let block_width = block_size.width as usize;
let block_count = block_size.width as usize * component.vertical_sampling_factor as usize;
let line_stride = block_size.width as usize * component.dct_scale;
let dct_scale = component.dct_scale;
Some(ComponentMetadata {
block_width,
block_count,
line_stride,
dct_scale,
})
}
pub fn append_row_locked(
quantization_table: Arc<[u16; 64]>,
metadata: ComponentMetadata,
data: Vec<i16>,
result_block: &mut [u8],
) {
// Convert coefficients from a MCU row to samples.
let ComponentMetadata {
block_count,
line_stride,
block_width,
dct_scale,
} = metadata;
assert_eq!(data.len(), block_count * 64);
let mut output_buffer = [0; 64];
for i in 0..block_count {
let x = (i % block_width) * dct_scale;
let y = (i / block_width) * dct_scale;
let coefficients: &[i16; 64] = &data[i * 64..(i + 1) * 64].try_into().unwrap();
// Write to a temporary intermediate buffer, a 8x8 'image'.
dequantize_and_idct_block(
dct_scale,
coefficients,
&*quantization_table,
8,
&mut output_buffer,
);
let write_back = &mut result_block[y * line_stride + x..];
let buffered_lines = output_buffer.chunks_mut(8);
let back_lines = write_back.chunks_mut(line_stride);
for (buf, back) in buffered_lines.zip(back_lines).take(dct_scale) {
back[..dct_scale].copy_from_slice(&buf[..dct_scale]);
}
}
}
}
impl Worker for Scoped {
fn start(&mut self, row_data: RowData) -> Result<()> {
self.inner.start_immediate(row_data);
Ok(())
}
fn append_row(&mut self, row: (usize, Vec<i16>)) -> Result<()> {
let inner = &mut self.inner;
let (index, data) = row;
let quantization_table = inner.quantization_tables[index].as_ref().unwrap().clone();
let metadata = inner.component_metadata(index).unwrap();
let result_block = &mut inner.results[index][inner.offsets[index]..];
inner.offsets[index] += metadata.bytes_used();
ImmediateWorker::append_row_locked(quantization_table, metadata, data, result_block);
Ok(())
}
fn get_result(&mut self, index: usize) -> Result<Vec<u8>> {
let result = self.inner.get_result_immediate(index);
Ok(result)
}
// Magic sauce, these _may_ run in parallel.
fn append_rows(&mut self, iter: &mut dyn Iterator<Item = (usize, Vec<i16>)>) -> Result<()> {
let inner = &mut self.inner;
rayon::in_place_scope(|scope| {
let metadatas = [
inner.component_metadata(0),
inner.component_metadata(1),
inner.component_metadata(2),
inner.component_metadata(3),
];
let [res0, res1, res2, res3] = &mut inner.results;
// Lazily get the blocks. Note: if we've already collected results from a component
// then the result vector has already been deallocated/taken. But no more tasks should
// be created for it.
let mut result_blocks = [
res0.get_mut(inner.offsets[0]..).unwrap_or(&mut []),
res1.get_mut(inner.offsets[1]..).unwrap_or(&mut []),
res2.get_mut(inner.offsets[2]..).unwrap_or(&mut []),
res3.get_mut(inner.offsets[3]..).unwrap_or(&mut []),
];
// First we schedule everything, making sure their index is right etc.
for (index, data) in iter {
let metadata = metadatas[index].unwrap();
let quantization_table = inner.quantization_tables[index].as_ref().unwrap().clone();
inner.offsets[index] += metadata.bytes_used();
let (result_block, tail) =
core::mem::take(&mut result_blocks[index]).split_at_mut(metadata.bytes_used());
result_blocks[index] = tail;
scope.spawn(move |_| {
ImmediateWorker::append_row_locked(
quantization_table,
metadata,
data,
result_block,
)
});
}
});
Ok(())
}
}
impl ComponentMetadata {
fn bytes_used(&self) -> usize {
self.block_count * self.dct_scale * self.dct_scale
}
}
pub fn compute_image_parallel(
components: &[Component],
data: Vec<Vec<u8>>,
output_size: Dimensions,
color_transform: ColorTransform,
) -> Result<Vec<u8>> {
let color_convert_func = choose_color_convert_func(components.len(), color_transform)?;
let upsampler = Upsampler::new(components, output_size.width, output_size.height)?;
let line_size = output_size.width as usize * components.len();
let mut image = vec![0u8; line_size * output_size.height as usize];
image
.par_chunks_mut(line_size)
.with_max_len(1)
.enumerate()
.for_each(|(row, line)| {
upsampler.upsample_and_interleave_row(
&data,
row,
output_size.width as usize,
line,
color_convert_func,
);
});
Ok(image)
}

155
vendor/jpeg-decoder/tests/lib.rs vendored Normal file
View File

@ -0,0 +1,155 @@
extern crate jpeg_decoder as jpeg;
extern crate png;
extern crate walkdir;
use std::path::Path;
use std::fs::File;
mod common;
mod crashtest;
mod reftest;
#[test]
#[wasm_bindgen_test::wasm_bindgen_test]
fn included_file() {
const FILE: &[u8] = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/reftest/images/mozilla/jpg-progressive.jpg"));
let mut data = FILE;
let mut decoder = jpeg::Decoder::new(&mut data);
let ref_data = decoder.decode().unwrap();
let ref_info = decoder.info().unwrap();
let mut data = FILE;
decoder = jpeg::Decoder::new(&mut data);
decoder.read_info().unwrap();
let info = decoder.info().unwrap();
let data = decoder.decode().unwrap();
assert_eq!(info, decoder.info().unwrap());
assert_eq!(info, ref_info);
assert_eq!(data, ref_data);
}
#[test]
fn read_info() {
let path = Path::new("tests").join("reftest").join("images").join("mozilla").join("jpg-progressive.jpg");
let mut decoder = jpeg::Decoder::new(File::open(&path).unwrap());
let ref_data = decoder.decode().unwrap();
let ref_info = decoder.info().unwrap();
decoder = jpeg::Decoder::new(File::open(&path).unwrap());
decoder.read_info().unwrap();
let info = decoder.info().unwrap();
let data = decoder.decode().unwrap();
assert_eq!(info, decoder.info().unwrap());
assert_eq!(info, ref_info);
assert_eq!(data, ref_data);
}
#[test]
fn read_icc_profile() {
let path = Path::new("tests")
.join("reftest")
.join("images")
.join("mozilla")
.join("jpg-srgb-icc.jpg");
let mut decoder = jpeg::Decoder::new(File::open(&path).unwrap());
decoder.decode().unwrap();
let profile = decoder.icc_profile().unwrap();
// "acsp" is a mandatory string in ICC profile headers.
assert_eq!(&profile[36..40], b"acsp");
}
// Test if chunks are concatenated in the correct order
#[test]
fn read_icc_profile_random_order() {
let path = Path::new("tests")
.join("icc")
.join("icc_chunk_order.jpeg");
let mut decoder = jpeg::Decoder::new(File::open(&path).unwrap());
decoder.decode().unwrap();
let profile = decoder.icc_profile().unwrap();
assert_eq!(profile.len(), 254);
for i in 1..=254 {
assert_eq!(profile[i - 1], i as u8);
}
}
// Check if ICC profiles with invalid chunk number 0 are discarded
#[test]
fn read_icc_profile_seq_no_0() {
let path = Path::new("tests")
.join("icc")
.join("icc_chunk_seq_no_0.jpeg");
let mut decoder = jpeg::Decoder::new(File::open(&path).unwrap());
decoder.decode().unwrap();
let profile = decoder.icc_profile();
assert!(profile.is_none());
}
// Check if ICC profiles with multiple chunks with the same number are discarded
#[test]
fn read_icc_profile_double_seq_no() {
let path = Path::new("tests")
.join("icc")
.join("icc_chunk_double_seq_no.jpeg");
let mut decoder = jpeg::Decoder::new(File::open(&path).unwrap());
decoder.decode().unwrap();
let profile = decoder.icc_profile();
assert!(profile.is_none());
}
// Check if ICC profiles with mismatching number of chunks and total chunk count are discarded
#[test]
fn read_icc_profile_chunk_count_mismatch() {
let path = Path::new("tests")
.join("icc")
.join("icc_chunk_count_mismatch.jpeg");
let mut decoder = jpeg::Decoder::new(File::open(&path).unwrap());
decoder.decode().unwrap();
let profile = decoder.icc_profile();
assert!(profile.is_none());
}
// Check if ICC profiles with missing chunk are discarded
#[test]
fn read_icc_profile_missing_chunk() {
let path = Path::new("tests")
.join("icc")
.join("icc_missing_chunk.jpeg");
let mut decoder = jpeg::Decoder::new(File::open(&path).unwrap());
decoder.decode().unwrap();
let profile = decoder.icc_profile();
assert!(profile.is_none());
}
#[test]
fn read_exif_data() {
let path = Path::new("tests")
.join("reftest")
.join("images")
.join("ycck.jpg");
let mut decoder = jpeg::Decoder::new(File::open(&path).unwrap());
decoder.decode().unwrap();
let exif_data = decoder.exif_data().unwrap();
// exif data start as a TIFF header
assert_eq!(&exif_data[0..8], b"\x49\x49\x2A\x00\x08\x00\x00\x00");
}

16
vendor/jpeg-decoder/tests/rayon-0.rs vendored Normal file
View File

@ -0,0 +1,16 @@
//! Must be a separate test because it modifies the _global_ rayon pool.
use std::{fs::File, path::Path};
use jpeg_decoder::Decoder;
#[test]
fn decoding_in_global_pool() {
let path = Path::new("tests").join("reftest").join("images").join("mozilla").join("jpg-progressive.jpg");
rayon::ThreadPoolBuilder::new()
.num_threads(1)
.build_global()
.unwrap();
let mut decoder = Decoder::new(File::open(&path).unwrap());
let _ = decoder.decode().unwrap();
}

18
vendor/jpeg-decoder/tests/rayon-1.rs vendored Normal file
View File

@ -0,0 +1,18 @@
//! Must be a separate test because it modifies the _global_ rayon pool.
use std::{fs::File, path::Path};
use jpeg_decoder::Decoder;
#[test]
fn decoding_in_fetched_global_pool() {
let path = Path::new("tests").join("reftest").join("images").join("mozilla").join("jpg-progressive.jpg");
rayon::ThreadPoolBuilder::new()
.num_threads(1)
.build_global()
.unwrap();
rayon::scope(|_| {
let mut decoder = Decoder::new(File::open(&path).unwrap());
let _ = decoder.decode().unwrap();
})
}

23
vendor/jpeg-decoder/tests/rayon-2.rs vendored Normal file
View File

@ -0,0 +1,23 @@
//! Must be a separate test because it modifies the _global_ rayon pool.
use std::{fs::File, path::Path};
use jpeg_decoder::Decoder;
#[test]
fn decoding_in_global_pool() {
let path = Path::new("tests/reftest/images/progressive3.jpg");
rayon::ThreadPoolBuilder::new()
.num_threads(2)
.build_global()
.unwrap();
let _: Vec<_> = (0..1024)
.map(|_| {
let path = path.clone();
std::thread::spawn(move || {
let mut decoder = Decoder::new(File::open(&path).unwrap());
let _ = decoder.decode().unwrap();
});
}).collect();
}

16
vendor/jpeg-decoder/tests/rayon.rs vendored Normal file
View File

@ -0,0 +1,16 @@
use std::{fs::File, path::Path};
use jpeg_decoder::Decoder;
#[test]
fn decoding_in_limited_threadpool_does_not_deadlock() {
let path = Path::new("tests").join("reftest").join("images").join("mozilla").join("jpg-progressive.jpg");
let pool = rayon::ThreadPoolBuilder::new()
.num_threads(1)
.build()
.unwrap();
pool.install(|| {
let mut decoder = Decoder::new(File::open(&path).unwrap());
let _ = decoder.decode().unwrap();
});
}