- Commit
- 0bf8476078c1f52bce5bac346906c479085b6855
- Parent
- aa47386e22095f4c286014be29648d7893a0a707
- Author
- Pablo <pablo-pie@riseup.net>
- Date
Reimplemented the functionality of the image crate
Custum build of stapix for tikz.pablopie.xyz
Reimplemented the functionality of the image crate
5 files changed, 431 insertions, 984 deletions
| Status | Name | Changes | Insertions | Deletions |
| Modified | .gitignore | 2 files changed | 2 | 0 |
| Modified | Cargo.lock | 2 files changed | 20 | 937 |
| Modified | Cargo.toml | 2 files changed | 4 | 2 |
| Added | src/image.rs | 1 file changed | 373 | 0 |
| Modified | src/main.rs | 2 files changed | 32 | 45 |
diff --git a/.gitignore b/.gitignore @@ -1,2 +1,4 @@ /target /examples/site +.project.gf +tags
diff --git a/Cargo.lock b/Cargo.lock @@ -3,162 +3,6 @@ version = 4 [[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] -name = "aligned" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee4508988c62edf04abd8d92897fca0c2995d907ce1dfeaf369dac3716a40685" -dependencies = [ - "as-slice", -] - -[[package]] -name = "aligned-vec" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" -dependencies = [ - "equator", -] - -[[package]] -name = "anyhow" -version = "1.0.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" - -[[package]] -name = "arbitrary" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" - -[[package]] -name = "arg_enum_proc_macro" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" - -[[package]] -name = "as-slice" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" -dependencies = [ - "stable_deref_trait", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "av-scenechange" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394" -dependencies = [ - "aligned", - "anyhow", - "arg_enum_proc_macro", - "arrayvec", - "log", - "num-rational", - "num-traits", - "pastey", - "rayon", - "thiserror", - "v_frame", - "y4m", -] - -[[package]] -name = "av1-grain" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cfddb07216410377231960af4fcab838eaa12e013417781b78bd95ee22077f8" -dependencies = [ - "anyhow", - "arrayvec", - "log", - "nom", - "num-rational", - "v_frame", -] - -[[package]] -name = "avif-serialize" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47c8fbc0f831f4519fe8b810b6a7a91410ec83031b8233f730a0480029f6a23f" -dependencies = [ - "arrayvec", -] - -[[package]] -name = "bit_field" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" - -[[package]] -name = "bitflags" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" - -[[package]] -name = "bitstream-io" -version = "4.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60d4bd9d1db2c6bdf285e223a7fa369d5ce98ec767dec949c6ca62863ce61757" -dependencies = [ - "core2", -] - -[[package]] -name = "built" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64" - -[[package]] -name = "bumpalo" -version = "3.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" - -[[package]] -name = "bytemuck" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" - -[[package]] -name = "byteorder-lite" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" - -[[package]] name = "cc" version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -169,200 +13,18 @@ dependencies = [ ] [[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - -[[package]] -name = "core2" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" -dependencies = [ - "memchr", -] - -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" -dependencies = [ - "autocfg", - "cfg-if", - "crossbeam-utils", - "memoffset", - "scopeguard", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "either" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" - -[[package]] -name = "equator" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" -dependencies = [ - "equator-macro", -] - -[[package]] -name = "equator-macro" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] -name = "exr" -version = "1.74.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be" -dependencies = [ - "bit_field", - "half", - "lebe", - "miniz_oxide", - "rayon-core", - "smallvec", - "zune-inflate", -] - -[[package]] -name = "fax" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab" -dependencies = [ - "fax_derive", -] - -[[package]] -name = "fax_derive" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "fdeflate" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "flate2" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "libc", - "r-efi", - "wasip2", -] - -[[package]] -name = "gif" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5df2ba84018d80c213569363bdcd0c64e6933c67fe4c1d60ecf822971a3c35e" -dependencies = [ - "color_quant", - "weezl", -] - -[[package]] name = "glob" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] -name = "half" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" -dependencies = [ - "cfg-if", - "crunchy", - "zerocopy", -] - -[[package]] name = "hashbrown" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -375,46 +37,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" [[package]] -name = "image" -version = "0.25.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a" -dependencies = [ - "bytemuck", - "byteorder-lite", - "color_quant", - "exr", - "gif", - "image-webp", - "moxcms", - "num-traits", - "png", - "qoi", - "ravif", - "rayon", - "rgb", - "tiff", - "zune-core 0.5.0", - "zune-jpeg 0.5.8", -] - -[[package]] -name = "image-webp" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" -dependencies = [ - "byteorder-lite", - "quick-error", -] - -[[package]] -name = "imgref" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8" - -[[package]] name = "indexmap" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -425,26 +47,6 @@ dependencies = [ ] [[package]] -name = "interpolate_name" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "itertools" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" -dependencies = [ - "either", -] - -[[package]] name = "itoa" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -460,168 +62,20 @@ dependencies = [ ] [[package]] -name = "lebe" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" - -[[package]] name = "libc" version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] -name = "libfuzzer-sys" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404" -dependencies = [ - "arbitrary", - "cc", -] - -[[package]] name = "libwebp-sys" -version = "0.9.4" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0df0a0f9444d52aee6335cd724d21a2ee3285f646291799a72be518ec8ee3c" +checksum = "39f019895d5064a40a6d1e8859d8093c3dfdaee8d4a15fa08c80405a856658f0" dependencies = [ "cc", "glob", -] - -[[package]] -name = "log" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" - -[[package]] -name = "loop9" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" -dependencies = [ - "imgref", -] - -[[package]] -name = "maybe-rayon" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" -dependencies = [ - "cfg-if", - "rayon", -] - -[[package]] -name = "memchr" -version = "2.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" - -[[package]] -name = "memoffset" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" -dependencies = [ - "autocfg", -] - -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", - "simd-adler32", -] - -[[package]] -name = "moxcms" -version = "0.7.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97" -dependencies = [ - "num-traits", - "pxfm", -] - -[[package]] -name = "new_debug_unreachable" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" - -[[package]] -name = "nom" -version = "8.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" -dependencies = [ - "memchr", -] - -[[package]] -name = "noop_proc_macro" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" - -[[package]] -name = "num-bigint" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-derive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" -dependencies = [ - "num-bigint", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", + "pkg-config", ] [[package]] @@ -635,44 +89,10 @@ dependencies = [ ] [[package]] -name = "once_cell" -version = "1.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[package]] -name = "pastey" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" - -[[package]] -name = "png" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0" -dependencies = [ - "bitflags", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.21" +name = "pkg-config" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "proc-macro2" @@ -684,49 +104,6 @@ dependencies = [ ] [[package]] -name = "profiling" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" -dependencies = [ - "profiling-procmacros", -] - -[[package]] -name = "profiling-procmacros" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" -dependencies = [ - "quote", - "syn", -] - -[[package]] -name = "pxfm" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7186d3822593aa4393561d186d1393b3923e9d6163d3fbfd6e825e3e6cf3e6a8" -dependencies = [ - "num-traits", -] - -[[package]] -name = "qoi" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "quick-error" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" - -[[package]] name = "quote" version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -736,135 +113,12 @@ dependencies = [ ] [[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "rand" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" -dependencies = [ - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rav1e" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b" -dependencies = [ - "aligned-vec", - "arbitrary", - "arg_enum_proc_macro", - "arrayvec", - "av-scenechange", - "av1-grain", - "bitstream-io", - "built", - "cfg-if", - "interpolate_name", - "itertools", - "libc", - "libfuzzer-sys", - "log", - "maybe-rayon", - "new_debug_unreachable", - "noop_proc_macro", - "num-derive", - "num-traits", - "paste", - "profiling", - "rand", - "rand_chacha", - "simd_helpers", - "thiserror", - "v_frame", - "wasm-bindgen", -] - -[[package]] -name = "ravif" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef69c1990ceef18a116855938e74793a5f7496ee907562bd0857b6ac734ab285" -dependencies = [ - "avif-serialize", - "imgref", - "loop9", - "quick-error", - "rav1e", - "rayon", - "rgb", -] - -[[package]] -name = "rayon" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - -[[package]] -name = "rgb" -version = "0.8.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - -[[package]] name = "ryu" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] name = "serde" version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -904,27 +158,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" [[package]] -name = "simd_helpers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" -dependencies = [ - "quote", -] - -[[package]] -name = "smallvec" -version = "1.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" - -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - -[[package]] name = "syn" version = "2.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -936,26 +169,6 @@ dependencies = [ ] [[package]] -name = "thiserror" -version = "2.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] name = "threadpool" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -965,29 +178,17 @@ dependencies = [ ] [[package]] -name = "tiff" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af9605de7fee8d9551863fd692cce7637f548dbd9db9180fcc07ccc6d26c336f" -dependencies = [ - "fax", - "flate2", - "half", - "quick-error", - "weezl", - "zune-jpeg 0.4.21", -] - -[[package]] name = "tikz_gallery_generator" version = "1.0.0" dependencies = [ - "image", + "libwebp-sys", "num_cpus", "serde", "serde_yaml", "threadpool", - "webp", + "zune-core", + "zune-jpeg", + "zune-png", ] [[package]] @@ -1003,129 +204,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" [[package]] -name = "v_frame" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2" -dependencies = [ - "aligned-vec", - "num-traits", - "wasm-bindgen", -] - -[[package]] -name = "wasip2" -version = "1.0.1+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "webp" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c071456adef4aca59bf6a583c46b90ff5eb0b4f758fc347cea81290288f37ce1" -dependencies = [ - "image", - "libwebp-sys", -] - -[[package]] -name = "weezl" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" - -[[package]] -name = "wit-bindgen" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" - -[[package]] -name = "y4m" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448" - -[[package]] -name = "zerocopy" -version = "0.8.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "zune-core" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" - -[[package]] name = "zune-core" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "111f7d9820f05fd715df3144e254d6fc02ee4088b0644c0ffd0efc9e6d9d2773" +checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" [[package]] name = "zune-inflate" @@ -1138,18 +220,19 @@ dependencies = [ [[package]] name = "zune-jpeg" -version = "0.4.21" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" +checksum = "e35aee689668bf9bd6f6f3a6c60bb29ba1244b3b43adfd50edd554a371da37d5" dependencies = [ - "zune-core 0.4.12", + "zune-core", ] [[package]] -name = "zune-jpeg" -version = "0.5.8" +name = "zune-png" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e35aee689668bf9bd6f6f3a6c60bb29ba1244b3b43adfd50edd554a371da37d5" +checksum = "af6dc406edc296451b9597ee98643df8606396066d8f0864682c266eda16f310" dependencies = [ - "zune-core 0.5.0", + "zune-core", + "zune-inflate", ]
diff --git a/Cargo.toml b/Cargo.toml @@ -9,8 +9,10 @@ license = "GPLv3" [dependencies] serde = { version = "1.0", features = ["derive"] } serde_yaml = "0.9" -webp = "0.3" -image = "0.25" +libwebp-sys = "0.14" +zune-core = "0.5" +zune-jpeg = "0.5" +zune-png = "0.5" threadpool = "1.8.1" num_cpus = "1.16.0"
diff --git /dev/null b/src/image.rs @@ -0,0 +1,373 @@ +//! Efficient-ish routines for parsing JPEG/PNG and encoding WebP +//! +//! We avoid using the `image` crate because it is bloated, yet we do use some +//! libraries: +//! +//! * `libwebp-sys` (we don't use `webp` because it requires `image` +//! * `zune-jpeg` (used internaly by `image`) +//! * `zune-png` +//! +//! The `zune-` crates implement SIMD optimizations. +//! +//! Working with these crates directly also allows us the oportunity to +//! streamline pixel format conversions. For example, when parsing JPEG we now +//! feed the YUV pixel data directly to `libwebp` instead of first converting +//! it to RGB and then converting it back to YUV (which is what `image` would +//! have done). + +use std::{ + slice, + ptr, + io::{self, Write, Read, BufReader}, + path::Path, + fs::File, +}; +use zune_core::{ + colorspace::ColorSpace, + options::DecoderOptions, + bytestream::ZCursor, + result::DecodingResult, +}; +use zune_jpeg::JpegDecoder; +use zune_png::PngDecoder; +use libwebp_sys::WebPEncodingError; + +use crate::create_file; + +#[derive(Clone, Debug)] +pub struct Image { + pixels: PixelBuffer, + pub width: u32, + pub height: u32, +} + +#[derive(Clone, Debug)] +enum PixelBuffer { + Yuv { y: Vec<u8>, u: Vec<u8>, v: Vec<u8>, uv_stride: u32, }, + Grayscale { level: Vec<u8>, alpha: Option<Vec<u8>>, }, + Rgb(Vec<u8>), + Rgba(Vec<u8>), +} + +pub fn parse_jpeg(path: &Path) -> Result<Image, ()> { + let options = DecoderOptions::default() + .jpeg_set_out_colorspace(ColorSpace::YCbCr); + + let f = BufReader::new(open_file(path).map_err(|_| ())?); + let mut decoder = JpegDecoder::new_with_options(f, options); + + let pixels = match decoder.decode() { + Ok(pixels) => pixels, + Err(e) => { + errorln!("Couldn't parse {path:?}: {e}"); + return Err(()); + } + }; + + let info = decoder.info().expect("should have already parsed JPEG header"); + + let options = decoder.options(); + + let width = info.width as u32; + let height = info.height as u32; + let uv_width = width.div_ceil(2); + let uv_height = height.div_ceil(2); + + assert_expected_pixels_len( + width as usize, + height as usize, + 3, + pixels.len() + ); + let y_len: usize = (width * height) as _; + let uv_len: usize = (uv_width * uv_height) as _; + + let colorspace = options.jpeg_get_out_colorspace(); + debug_assert!(colorspace == ColorSpace::YCbCr, + "unexpected colorspace when parsing JPEG: {colorspace:?}"); + + let mut y = vec![0; y_len]; + let mut u = vec![0; uv_len]; + let mut v = vec![0; uv_len]; + + // TODO: [optmize]: can we optimize these loops? + for i in 0..y_len { y[i] = pixels[i*3]; } + for uv_x in 0..uv_width { + for uv_y in 0..uv_height { + let x = uv_x * 2; + let y = uv_y * 2; + + let i: usize = (y * width + x) as _; + let j: usize = (uv_y * uv_width + uv_x) as _; + + // NOTE: using an approximation of nearest neighbor for efficient + let u_val = pixels[i*3+1]; + let v_val = pixels[i*3+2]; + u[j] = u_val; + v[j] = v_val; + } + } + + Ok(Image { + pixels: PixelBuffer::Yuv { y, u, v, uv_stride: uv_width, }, + width, + height, + }) +} + +pub fn parse_png(path: &Path) -> Result<Image, ()> { + let options = DecoderOptions::default() + .png_set_strip_to_8bit(true); + + let mut f = open_file(path).map_err(|_| ())?; + let size: usize = f.metadata() + .expect("OS should support file metadata") + .len() as _; + let mut f_contents = Vec::with_capacity(size); + f.read_to_end(&mut f_contents) + .map_err(|e| errorln!("Could not read {path:?}: {e}"))?; + let f_contents = ZCursor::new(f_contents); + + let mut decoder = PngDecoder::new_with_options(f_contents, options); + + let pixels = match decoder.decode() { + Ok(DecodingResult::U8(pixels)) => pixels, + Ok(DecodingResult::U16(_)) => { + unreachable!("PNG strip was set to 8 bits") + } + Ok(_) => { + unreachable!("PngDecoder::decode should always return DecodingResult::U8 or DecodingResult::U16") + } + Err(e) => { + errorln!("Couldn't parse {path:?}: {e}"); + return Err(()); + } + }; + + let (width, height) = decoder + .dimensions() + .expect("should already have decoded PNG headers"); + let colorspace = decoder + .colorspace() + .expect("should already have decoded PNG headers"); + + match colorspace { + ColorSpace::RGB => { + assert_expected_pixels_len(width, height, 3, pixels.len()); + + Ok(Image { + pixels: PixelBuffer::Rgb(pixels), + width: width as u32, + height: height as u32, + }) + } + ColorSpace::RGBA => { + assert_expected_pixels_len(width, height, 4, pixels.len()); + + Ok(Image { + pixels: PixelBuffer::Rgba(pixels), + width: width as u32, + height: height as u32, + }) + } + ColorSpace::Luma => { + assert_expected_pixels_len(width, height, 1, pixels.len()); + + Ok(Image { + pixels: PixelBuffer::Grayscale { level: pixels, alpha: None, }, + width: width as u32, + height: height as u32, + }) + } + ColorSpace::LumaA => { + assert_expected_pixels_len(width, height, 2, pixels.len()); + + let len = pixels.len()/2; + let mut level = vec![0; len]; + let mut alpha = vec![0; len]; + + #[allow(clippy::identity_op)] + for i in 0..len { + level[i] = pixels[i*2+0]; + alpha[i] = pixels[i*2+1]; + } + + Ok(Image { + pixels: PixelBuffer::Grayscale { level, alpha: Some(alpha), }, + width: width as u32, + height: height as u32, + }) + } + colorspace => { + unreachable!("unexpected colorspace when parsing PNG: {colorspace:?}") + } + } +} + +pub fn encode_webp(img: &Image, output_path: &Path) -> Result<(), ()> { + use std::mem::MaybeUninit; + use libwebp_sys::*; + const WEBP_IMAGE_QUALITY: f32 = 90.0; + + unsafe { + // ====================================================================== + let mut config = MaybeUninit::uninit(); + let init = WebPInitConfig(config.as_mut_ptr()); + let mut config: WebPConfig = config.assume_init(); + debug_assert!(init, "libwebp version mismatch"); + + config.lossless = 0; // using lossy compression + config.alpha_compression = 1; // using lossy compression + config.quality = WEBP_IMAGE_QUALITY; + + debug_assert!(WebPValidateConfig(&config) != 0, + "WebP config should be valid"); + + // ====================================================================== + // NOTE: it makes no sense to re-use ww because WebPMemoryWriterClear(ww) + // literally frees ww.mem and sets it NULL + let mut ww = MaybeUninit::uninit(); + WebPMemoryWriterInit(ww.as_mut_ptr()); + let mut ww: WebPMemoryWriter = ww.assume_init(); + + // NOTE: we cannot re-use picture because WebPPictureAlloc, + // WebPPictureImportRGB & WebPPictureImportRGBA all free the pixel + // buffers before reallocating them + let mut picture = MaybeUninit::uninit(); + let init = WebPPictureInit(picture.as_mut_ptr()); + let mut picture: WebPPicture = picture.assume_init(); + debug_assert!(init, "libwebp version mismatch"); + + // NOTE: the libwebp documentation recommends using YUV (which is the + // default in WebPPictureInit) when doing lossy compression + // picture.use_argb = 0; + picture.width = img.width as i32; + picture.height = img.height as i32; + let import_status = match &img.pixels { + PixelBuffer::Yuv { y, u, v, uv_stride, } => { + // NOTE: setting picture.y, picture.u & picture.v manually to avoid + // recopying the pixel buffers + // + // we must indicate picture is a "view" (i.e. does not own the + // pixel buffer memory) by setting picture.memory_ to NULL: + // libwebp/src/enc/picture_rescale_enc.c:97 + picture.memory_ = ptr::null_mut(); + picture.y_stride = img.width as i32; + picture.uv_stride = *uv_stride as i32; + picture.y = y.as_ptr() as *mut _; + picture.u = u.as_ptr() as *mut _; + picture.v = v.as_ptr() as *mut _; + + debug_assert!(WebPPictureIsView(&picture) != 0, + "picture should have been configured as \"view\""); + true + } + PixelBuffer::Grayscale { level, alpha, } => { + if alpha.is_some() { picture.colorspace = WebPEncCSP::WEBP_YUV420A; } + let status = WebPPictureAlloc(&mut picture) != 0; + if status { + let uv_width: usize = img.width.div_ceil(2) as _; + let uv_height: usize = img.height.div_ceil(2) as _; + let uv_len = uv_width * uv_height; + + ptr::copy_nonoverlapping(level.as_ptr(), picture.y, level.len()); + ptr::write_bytes(picture.u, 128, uv_len); + ptr::write_bytes(picture.v, 128, uv_len); + if let Some(alpha) = alpha { + ptr::copy_nonoverlapping(alpha.as_ptr(), picture.a, alpha.len()); + } + } + + status + } + PixelBuffer::Rgb(pixels) => { + WebPPictureImportRGB( + &mut picture, + pixels.as_ptr(), + img.width as i32 * 3, + ) != 0 + } + PixelBuffer::Rgba(pixels) => { + WebPPictureImportRGBA( + &mut picture, + pixels.as_ptr(), + img.width as i32 * 4, + ) != 0 + } + }; + if !import_status { + log_webp_memmory_error(picture.error_code, output_path); + return Err(()); + } + picture.writer = Some(WebPMemoryWrite); + picture.custom_ptr = &mut ww as *mut WebPMemoryWriter as _; + + // ====================================================================== + let status = WebPEncode(&config, &mut picture) != 0; + + let bytes = if status { + slice::from_raw_parts(ww.mem, ww.size) + } else { + log_webp_memmory_error(picture.error_code, output_path); + return Err(()); + }; + + // ====================================================================== + let mut thumb_file = create_file(output_path).map_err(|_| ())?; + if let Err(e) = thumb_file.write_all(bytes) { + errorln!("Couldn't write thumnail to file {output_path:?}: {e}"); + return Err(()); + } + + WebPPictureFree(&mut picture); + } + + Ok(()) +} + +#[inline] +fn assert_expected_pixels_len( + width: usize, + height: usize, + bpp: usize, + pixels_len: usize, +) { + let expected_pixels_len = width * height * bpp; + debug_assert!( + pixels_len == expected_pixels_len, + "unexpected pixel buffer length: expected {width} × {height} × {bpp} = {expected_pixels_len} but got {pixels_len}", + ); +} + +#[inline] +fn log_webp_memmory_error(e: WebPEncodingError, output_path: &Path) { + match e { + WebPEncodingError::VP8_ENC_ERROR_OUT_OF_MEMORY => { + errorln!("Could not generate {output_path:?}: out of memmory"); + } + WebPEncodingError::VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY => { + errorln!("Could not generate {output_path:?}: memory error while flushing bits"); + } + WebPEncodingError::VP8_ENC_ERROR_PARTITION0_OVERFLOW => { + errorln!("Could not generate {output_path:?}: partition is bigger than 512k"); + } + WebPEncodingError::VP8_ENC_ERROR_PARTITION_OVERFLOW => { + errorln!("Could not generate {output_path:?}: partition is bigger than 16M"); + } + WebPEncodingError::VP8_ENC_ERROR_BAD_WRITE => { + errorln!("Could not generate {output_path:?}: error while flushing bytes"); + } + WebPEncodingError::VP8_ENC_ERROR_FILE_TOO_BIG => { + errorln!("Could not generate {output_path:?}: file is bigger than 4G"); + } + WebPEncodingError::VP8_ENC_ERROR_USER_ABORT => { + errorln!("Could not generate {output_path:?}: abort requested by the user"); + } + e => unreachable!("unexpected WebPEncodingError variant: {e:?}"), + } +} + +fn open_file(path: &Path) -> io::Result<File> { + File::open(path) + .map_err(|e| { errorln!("Could not parse {path:?}: {e}"); e }) +}
diff --git a/src/main.rs b/src/main.rs @@ -1,4 +1,3 @@ -use image::{DynamicImage, ImageReader}; use std::{ cmp, env, @@ -10,15 +9,16 @@ use std::{ sync::mpsc, time::Instant, }; -use gallery_entry::{GalleryEntry, FileFormat, LicenseType}; use threadpool::ThreadPool; +use gallery_entry::{GalleryEntry, FileFormat, LicenseType}; use escape::Escaped; #[macro_use] mod log; -mod escape; +mod image; mod gallery_entry; +mod escape; /// A wrapper for displaying the path for the thumbnail of a given path pub struct ThumbPath<'a>(pub &'a GalleryEntry); @@ -87,7 +87,7 @@ fn main() -> ExitCode { } } } - + if !is_valid_arg { if arg.starts_with("-") { errorln!("Unknown option: {arg:?}"); @@ -434,6 +434,18 @@ fn write_license(f: &mut File) -> io::Result<()> { } fn render_thumbnail(pic: &GalleryEntry, thumb_path: &Path) -> Result<(), ()> { + const TARGET_HEIGHT: u32 = 500; + + #[inline] + fn warn_img_size(src_width: u32, src_height: u32, src_path: &Path) { + let target_width = src_width * TARGET_HEIGHT / src_height; + let scale_x = src_width / target_width; + + if scale_x > 1 { + warnln!("Thumbnail for {src_path:?}? {src_width}×{src_height}: thumbnails are not downsampled"); + } + } + match pic.file_format { FileFormat::TeX => { // tikztosvg -o thumb_path @@ -469,52 +481,27 @@ fn render_thumbnail(pic: &GalleryEntry, thumb_path: &Path) -> Result<(), ()> { ); return Err(()); } - }, + } FileFormat::Svg => { let mut src_path = PathBuf::from(TARGET_PATH); src_path.push(IMAGES_PATH); src_path.push(&pic.file_name); copy(&src_path, thumb_path)?; - }, - FileFormat::Jpeg | FileFormat::Png => { - /// Target height of the thumbnails - const THUMB_HEIGHT: u32 = 500; - const WEBP_IMAGE_QUALITY: f32 = 90.0; - - let mut thumb_file = create_file(thumb_path).map_err(|_| ())?; - - let img_reader = ImageReader::open(&pic.path) - .map_err(|e| { - errorln!( - "Couldn't open file {path:?} to render thumbnail: {e}", - path = pic.file_name, - ); - })?; - - let img = img_reader - .decode() - .map_err(|e| { - errorln!( - "Faileded to decode image file {name:?}: {e}", - name = pic.file_name, - ); - })?; - - let h = THUMB_HEIGHT; - let w = (h * img.width()) / img.height(); - - // we should make sure that the image is in the RGBA8 format so that - // the webp crate can encode it - let img = DynamicImage::from(img.thumbnail(w, h).into_rgba8()); - let mem = webp::Encoder::from_image(&img) - .expect("image should be in the RGBA8 format") - .encode(WEBP_IMAGE_QUALITY); - - if let Err(e) = thumb_file.write_all(&mem) { - errorln!( "Couldn't write thumnail to file {thumb_path:?}: {e}"); - return Err(()); - } + } + FileFormat::Jpeg => { + // NOTE: we do not rescale the images since the scale-factor for the + // actual images included in our gallery should always be 1 + let img = image::parse_jpeg(&pic.path)?; + warn_img_size(img.width, img.height, &pic.path); + image::encode_webp(&img, thumb_path)?; + } + FileFormat::Png => { + // NOTE: we do not rescale the images since the scale-factor for the + // actual images included in our gallery should always be 1 + let img = image::parse_png(&pic.path)?; + warn_img_size(img.width, img.height, &pic.path); + image::encode_webp(&img, thumb_path)?; } } @@ -534,7 +521,7 @@ fn needs_update(pic: &GalleryEntry, dst: &Path) -> bool { fn create_file(path: &Path) -> io::Result<File> { File::create(path) - .map_err(|e| { errorln!("Could not open file {path:?}: {e}"); e }) + .map_err(|e| { errorln!("Could not open {path:?}: {e}"); e }) } fn copy(from: &Path, to: &Path) -> Result<(), ()> {