diff --git a/Cargo.lock b/Cargo.lock index 6fa48998f..358ae6633 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,9 +10,9 @@ checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" [[package]] name = "addr2line" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ "gimli", ] @@ -156,9 +156,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a" +checksum = "0c378d78423fdad8089616f827526ee33c19f2fddbd5de1629152c9593ba4783" dependencies = [ "memchr", ] @@ -195,24 +195,23 @@ dependencies = [ [[package]] name = "anstream" -version = "0.3.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", - "is-terminal", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" +checksum = "15c4c2c83f81532e5845a733998b6971faca23490340a418e9b72a3ec9de12ea" [[package]] name = "anstyle-parse" @@ -234,9 +233,9 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "1.0.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c" +checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd" dependencies = [ "anstyle", "windows-sys 0.48.0", @@ -307,7 +306,7 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror", - "time 0.3.25", + "time 0.3.28", ] [[package]] @@ -323,7 +322,7 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror", - "time 0.3.25", + "time 0.3.28", ] [[package]] @@ -558,9 +557,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ "addr2line", "cc", @@ -624,9 +623,9 @@ checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5" [[package]] name = "base64" -version = "0.21.2" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" +checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53" [[package]] name = "base64ct" @@ -807,12 +806,12 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" [[package]] name = "bstr" -version = "1.6.0" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" +checksum = "4c2f7349907b712260e64b0afe2f84692af14a454be26187d9df565c7f69266a" dependencies = [ "memchr", - "regex-automata 0.3.6", + "regex-automata 0.3.7", "serde", ] @@ -1066,9 +1065,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.26" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +checksum = "95ed24df0632f708f5f6d8082675bef2596f7084dee3dd55f632290bf35bfe0f" dependencies = [ "android-tzdata", "iana-time-zone", @@ -1077,7 +1076,7 @@ dependencies = [ "serde", "time 0.1.43", "wasm-bindgen", - "winapi", + "windows-targets 0.48.5", ] [[package]] @@ -1122,20 +1121,19 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.23" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03aef18ddf7d879c15ce20f04826ef8418101c7e528014c3eeea13321047dca3" +checksum = "6a13b88d2c62ff462f88e4a121f17a82c1af05693a2f192b5c38d14de73c19f6" dependencies = [ "clap_builder", "clap_derive", - "once_cell", ] [[package]] name = "clap_builder" -version = "4.3.23" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ce6fffb678c9b80a70b6b6de0aad31df727623a70fd9a842c30cd573e2fa98" +checksum = "2bb9faaa7c2ef94b2743a21f5a29e6f0010dff4caa69ac8e9d6cf8b6fa74da08" dependencies = [ "anstream", "anstyle", @@ -1145,9 +1143,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.3.12" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" +checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" dependencies = [ "heck 0.4.1", "proc-macro2", @@ -1157,9 +1155,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" +checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" [[package]] name = "cocoa" @@ -1293,7 +1291,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" dependencies = [ "percent-encoding", - "time 0.3.25", + "time 0.3.28", "version_check", ] @@ -1532,7 +1530,7 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a011bbe2c35ce9c1f143b7af6f94f29a167beb4cd1d29e6740ce836f723120e" dependencies = [ - "nix 0.26.2", + "nix 0.26.4", "windows-sys 0.48.0", ] @@ -1591,6 +1589,7 @@ dependencies = [ "cfg-if", "cpufeatures", "curve25519-dalek-derive", + "digest 0.10.7", "fiat-crypto", "platforms", "rustc_version", @@ -1681,9 +1680,9 @@ dependencies = [ [[package]] name = "dashmap" -version = "5.5.0" +version = "5.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6943ae99c34386c84a470c499d3414f66502a41340aa895406e0d2e4a207b91d" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", "hashbrown 0.14.0", @@ -1770,6 +1769,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid", + "zeroize", +] + [[package]] name = "der-parser" version = "7.0.0" @@ -2006,10 +2015,10 @@ version = "0.14.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" dependencies = [ - "der", + "der 0.6.1", "elliptic-curve", "rfc6979", - "signature", + "signature 1.6.4", ] [[package]] @@ -2018,7 +2027,17 @@ version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" dependencies = [ - "signature", + "signature 1.6.4", +] + +[[package]] +name = "ed25519" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60f6d271ca33075c88028be6f04d502853d63a5ece419d269c15315d4fc1cf1d" +dependencies = [ + "pkcs8 0.10.2", + "signature 2.1.0", ] [[package]] @@ -2028,13 +2047,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" dependencies = [ "curve25519-dalek 3.2.0", - "ed25519", + "ed25519 1.5.3", "rand 0.7.3", "serde", "sha2 0.9.9", "zeroize", ] +[[package]] +name = "ed25519-dalek" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7277392b266383ef8396db7fdeb1e77b6c52fed775f5df15bb24f35b72156980" +dependencies = [ + "curve25519-dalek 4.0.0", + "ed25519 2.2.2", + "rand_core 0.6.4", + "serde", + "sha2 0.10.7", + "zeroize", +] + [[package]] name = "either" version = "1.9.0" @@ -2049,14 +2082,14 @@ checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" dependencies = [ "base16ct", "crypto-bigint 0.4.9", - "der", + "der 0.6.1", "digest 0.10.7", "ff", "generic-array", "group", "hkdf 0.12.3", "pem-rfc7468", - "pkcs8", + "pkcs8 0.9.0", "rand_core 0.6.4", "sec1", "subtle", @@ -2065,15 +2098,15 @@ dependencies = [ [[package]] name = "embed-resource" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7f1e82a60222fc67bfd50d752a9c89da5cce4c39ed39decc84a443b07bbd69a" +checksum = "fd0a2c9b742a980060d22545a7a83b573acd6b73045b9de6370c9530ce652f27" dependencies = [ "cc", "rustc_version", "toml 0.7.6", "vswhom", - "winreg 0.11.0", + "winreg 0.51.0", ] [[package]] @@ -2084,9 +2117,9 @@ checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" [[package]] name = "encoding_rs" -version = "0.8.32" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" dependencies = [ "cfg-if", ] @@ -2170,9 +2203,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" +checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" dependencies = [ "errno-dragonfly", "libc", @@ -2482,9 +2515,9 @@ dependencies = [ [[package]] name = "futures-concurrency" -version = "7.4.1" +version = "7.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cf49eababd62240534b6e7178bd8b20fac90318ed4295917b0565b6708ed670" +checksum = "d7cf7759a91582f72d30e68a2507feecac3dbcf6314ae91fa7046d6173d502e7" dependencies = [ "bitvec", "futures-core", @@ -2561,8 +2594,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2411eed028cdf8c8034eaf21f9915f956b6c3abec4d4c7949ee67f0721127bd" dependencies = [ "futures-io", - "rustls 0.20.8", - "webpki 0.22.0", + "rustls 0.20.9", + "webpki 0.22.1", ] [[package]] @@ -2775,9 +2808,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.27.3" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" [[package]] name = "gio" @@ -2866,7 +2899,7 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "759c97c1e17c55525b57192c06a267cda0ac5210b222d6b82189a2338fa1c13d" dependencies = [ - "aho-corasick 1.0.4", + "aho-corasick 1.0.5", "bstr", "fnv", "log", @@ -2963,9 +2996,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.20" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" +checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" dependencies = [ "bytes", "fnv", @@ -3024,12 +3057,11 @@ dependencies = [ [[package]] name = "headers" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" +checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" dependencies = [ - "base64 0.13.1", - "bitflags 1.3.2", + "base64 0.21.3", "bytes", "headers-core", "http", @@ -3558,7 +3590,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi", - "rustix 0.38.8", + "rustix 0.38.11", "windows-sys 0.48.0", ] @@ -3930,7 +3962,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70b34b6da8165c0bde35c82db8efda39b824776537e73973549e76cadb3a77c5" dependencies = [ "asynchronous-codec", - "base64 0.21.2", + "base64 0.21.3", "byteorder", "bytes", "either", @@ -3958,12 +3990,12 @@ dependencies = [ [[package]] name = "libp2p-identity" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e2d584751cecb2aabaa56106be6be91338a60a0f4e420cf2af639204f596fc1" +checksum = "276bb57e7af15d8f100d3c11cbdd32c6752b7eef4ba7a18ecf464972c07abcce" dependencies = [ "bs58", - "ed25519-dalek", + "ed25519-dalek 2.0.0", "log", "multiaddr", "multihash", @@ -4065,7 +4097,7 @@ dependencies = [ "parking_lot 0.12.1", "quinn-proto", "rand 0.8.5", - "rustls 0.20.8", + "rustls 0.20.9", "thiserror", "tokio", ] @@ -4118,9 +4150,9 @@ dependencies = [ "libp2p-identity", "rcgen 0.10.0", "ring", - "rustls 0.20.8", + "rustls 0.20.9", "thiserror", - "webpki 0.22.0", + "webpki 0.22.1", "x509-parser 0.14.0", "yasna", ] @@ -4382,9 +4414,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "5486aed0026218e61b8a01d5fbd5a0a134649abb71a0e53b7bc088529dced86e" [[package]] name = "memmap2" @@ -4567,9 +4599,9 @@ dependencies = [ [[package]] name = "mobc" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc79c4a77e312fee9c7bd4b957c12ad1196db73c4a81e5c0b13f02083c4f7f2f" +checksum = "0bdeff49b387edef305eccfe166af3e1483bb57902dbf369dddc42dc824df23b" dependencies = [ "async-trait", "futures-channel", @@ -4845,14 +4877,13 @@ dependencies = [ [[package]] name = "nix" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" dependencies = [ "bitflags 1.3.2", "cfg-if", "libc", - "static_assertions", ] [[package]] @@ -4953,9 +4984,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" dependencies = [ "autocfg", "num-integer", @@ -5085,9 +5116,9 @@ dependencies = [ [[package]] name = "object" -version = "0.31.1" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe" dependencies = [ "memchr", ] @@ -5147,11 +5178,11 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.56" +version = "0.10.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "729b745ad4a5575dd06a3e1af1414bd330ee561c01b3899eb584baeaa8def17e" +checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.0", "cfg-if", "foreign-types", "libc", @@ -5188,9 +5219,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.91" +version = "0.9.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "866b5f16f90776b9bb8dc1e1802ac6f0513de3a7a7465867bfbc563dc737faac" +checksum = "db7e971c2c2bba161b2d2fdf37080177eff520b3bc044787c7f1f5f9e78d869b" dependencies = [ "cc", "libc", @@ -5462,19 +5493,20 @@ checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pest" -version = "2.7.2" +version = "2.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1acb4a4365a13f749a93f1a094a7805e5cfa0955373a9de860d962eaa3a5fe5a" +checksum = "d7a4d085fd991ac8d5b05a147b437791b4260b76326baf0fc60cf7c9c27ecd33" dependencies = [ + "memchr", "thiserror", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.7.2" +version = "2.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "666d00490d4ac815001da55838c500eafb0320019bbaa44444137c48b443a853" +checksum = "a2bee7be22ce7918f641a33f08e3f43388c7656772244e2bbb2477f44cc9021a" dependencies = [ "pest", "pest_generator", @@ -5482,9 +5514,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.2" +version = "2.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ca01446f50dbda87c1786af8770d535423fa8a53aec03b8f4e3d7eb10e0929" +checksum = "d1511785c5e98d79a05e8a6bc34b4ac2168a0e3e92161862030ad84daa223141" dependencies = [ "pest", "pest_meta", @@ -5495,9 +5527,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.7.2" +version = "2.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56af0a30af74d0445c0bf6d9d051c979b516a1a5af790d251daee76005420a48" +checksum = "b42f0394d3123e33353ca5e1e89092e533d2cc490389f2bd6131c43c634ebc5f" dependencies = [ "once_cell", "pest", @@ -5656,8 +5688,18 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" dependencies = [ - "der", - "spki", + "der 0.6.1", + "spki 0.6.0", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der 0.7.8", + "spki 0.7.2", ] [[package]] @@ -5668,9 +5710,9 @@ checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "platforms" -version = "3.0.2" +version = "3.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630" +checksum = "4503fa043bf02cee09a9582e9554b4c6403b2ef55e4612e96561d294419429f8" [[package]] name = "plist" @@ -5678,12 +5720,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bdc0001cfea3db57a2e24bc0d818e9e20e554b5f97fabb9bc231dc240269ae06" dependencies = [ - "base64 0.21.2", + "base64 0.21.3", "indexmap 1.9.3", "line-wrap", "quick-xml", "serde", - "time 0.3.25", + "time 0.3.28", ] [[package]] @@ -6213,12 +6255,12 @@ dependencies = [ "rand 0.8.5", "ring", "rustc-hash", - "rustls 0.20.8", + "rustls 0.20.9", "slab", "thiserror", "tinyvec", "tracing 0.1.37", - "webpki 0.22.0", + "webpki 0.22.1", ] [[package]] @@ -6400,7 +6442,7 @@ checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" dependencies = [ "pem", "ring", - "time 0.3.25", + "time 0.3.28", "x509-parser 0.13.2", "yasna", ] @@ -6413,7 +6455,7 @@ checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" dependencies = [ "pem", "ring", - "time 0.3.25", + "time 0.3.28", "yasna", ] @@ -6463,14 +6505,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.9.3" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" +checksum = "12de2eff854e5fa4b1295edd650e227e9d8fb0c9e90b12e7f36d6a6811791a29" dependencies = [ - "aho-corasick 1.0.4", + "aho-corasick 1.0.5", "memchr", - "regex-automata 0.3.6", - "regex-syntax 0.7.4", + "regex-automata 0.3.7", + "regex-syntax 0.7.5", ] [[package]] @@ -6484,13 +6526,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" +checksum = "49530408a136e16e5b486e883fbb6ba058e8e4e8ae6621a77b048b314336e629" dependencies = [ - "aho-corasick 1.0.4", + "aho-corasick 1.0.5", "memchr", - "regex-syntax 0.7.4", + "regex-syntax 0.7.5", ] [[package]] @@ -6501,9 +6543,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "remove_dir_all" @@ -6540,11 +6582,11 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.18" +version = "0.11.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" +checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" dependencies = [ - "base64 0.21.2", + "base64 0.21.3", "bytes", "encoding_rs", "futures-core", @@ -6574,7 +6616,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "winreg 0.10.1", + "winreg 0.50.0", ] [[package]] @@ -6826,9 +6868,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.8" +version = "0.38.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" +checksum = "c0c3dde1fc030af041adc40e79c0e7fbcf431dd24870053d187d7c66e4b87453" dependencies = [ "bitflags 2.4.0", "errno", @@ -6852,14 +6894,14 @@ dependencies = [ [[package]] name = "rustls" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" dependencies = [ "log", "ring", "sct 0.7.0", - "webpki 0.22.0", + "webpki 0.22.1", ] [[package]] @@ -7039,7 +7081,7 @@ dependencies = [ "async-stream", "async-trait", "axum", - "base64 0.21.2", + "base64 0.21.3", "blake3", "bytes", "chrono", @@ -7075,11 +7117,10 @@ dependencies = [ "sd-crypto", "sd-ffmpeg", "sd-file-ext", - "sd-heif", + "sd-images", "sd-media-metadata", "sd-p2p", "sd-prisma", - "sd-svg", "sd-sync", "sd-utils", "serde", @@ -7231,15 +7272,14 @@ dependencies = [ ] [[package]] -name = "sd-heif" -version = "0.1.0" +name = "sd-images" +version = "0.0.0" dependencies = [ "image", "libheif-rs", "libheif-sys", - "once_cell", + "resvg", "thiserror", - "tokio", ] [[package]] @@ -7303,7 +7343,7 @@ name = "sd-p2p" version = "0.1.0" dependencies = [ "arc-swap", - "ed25519-dalek", + "ed25519-dalek 1.0.1", "flume", "hex", "if-watch", @@ -7348,17 +7388,6 @@ dependencies = [ "tracing 0.2.0", ] -[[package]] -name = "sd-svg" -version = "0.1.0" -dependencies = [ - "image", - "resvg", - "thiserror", - "tokio", - "tracing 0.1.37", -] - [[package]] name = "sd-sync" version = "0.1.0" @@ -7412,9 +7441,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" dependencies = [ "base16ct", - "der", + "der 0.6.1", "generic-array", - "pkcs8", + "pkcs8 0.9.0", "subtle", "zeroize", ] @@ -7493,9 +7522,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.185" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be9b6f69f1dfd54c3b568ffa45c310d6973a5e5148fd40cf515acaf38cf5bc31" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" dependencies = [ "serde_derive", ] @@ -7539,9 +7568,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.185" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc59dfdcbad1437773485e0367fea4b090a2e0a16d9ffc46af47764536a298ec" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", @@ -7615,7 +7644,7 @@ dependencies = [ "serde", "serde_json", "serde_with_macros 2.3.3", - "time 0.3.25", + "time 0.3.28", ] [[package]] @@ -7624,7 +7653,7 @@ version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ca3b16a3d82c4088f343b7480a93550b3eabe1a358569c2dfe38bbcead07237" dependencies = [ - "base64 0.21.2", + "base64 0.21.3", "chrono", "hex", "indexmap 1.9.3", @@ -7632,7 +7661,7 @@ dependencies = [ "serde", "serde_json", "serde_with_macros 3.3.0", - "time 0.3.25", + "time 0.3.28", ] [[package]] @@ -7793,6 +7822,12 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "signature" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" + [[package]] name = "simd-adler32" version = "0.3.7" @@ -7810,9 +7845,9 @@ dependencies = [ [[package]] name = "siphasher" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "skeptic" @@ -7837,9 +7872,9 @@ checksum = "04d2ecae5fcf33b122e2e6bd520a57ccf152d2dde3b38c71039df1a6867264ee" [[package]] name = "slab" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] @@ -7996,7 +8031,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" dependencies = [ "base64ct", - "der", + "der 0.6.1", +] + +[[package]] +name = "spki" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +dependencies = [ + "base64ct", + "der 0.7.8", ] [[package]] @@ -8241,7 +8286,7 @@ name = "swift-rs" version = "1.0.5" source = "git+https://github.com/Brendonovich/swift-rs?rev=973c22215734d1d5b97c496601d658371e537ece#973c22215734d1d5b97c496601d658371e537ece" dependencies = [ - "base64 0.21.2", + "base64 0.21.3", "serde", "serde_json", ] @@ -8455,7 +8500,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fbe522898e35407a8e60dc3870f7579fea2fc262a6a6072eccdd37ae1e1d91e" dependencies = [ "anyhow", - "base64 0.21.2", + "base64 0.21.3", "bytes", "cocoa", "dirs-next", @@ -8496,7 +8541,7 @@ dependencies = [ "tauri-utils", "tempfile", "thiserror", - "time 0.3.25", + "time 0.3.28", "tokio", "url", "uuid", @@ -8529,7 +8574,7 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54ad2d49fdeab4a08717f5b49a163bdc72efc3b1950b6758245fcde79b645e1a" dependencies = [ - "base64 0.21.2", + "base64 0.21.3", "brotli", "ico", "json-patch", @@ -8544,7 +8589,7 @@ dependencies = [ "sha2 0.10.7", "tauri-utils", "thiserror", - "time 0.3.25", + "time 0.3.28", "uuid", "walkdir", ] @@ -8676,7 +8721,7 @@ dependencies = [ "cfg-if", "fastrand 2.0.0", "redox_syscall 0.3.5", - "rustix 0.38.8", + "rustix 0.38.11", "windows-sys 0.48.0", ] @@ -8759,9 +8804,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fdd63d58b18d663fbdf70e049f00a22c8e42be082203be7f26589213cd75ea" +checksum = "17f6bb557fd245c28e6411aa56b6403c689ad95061f50e4be16c274e70a17e48" dependencies = [ "deranged", "itoa 1.0.9", @@ -8778,9 +8823,9 @@ checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] name = "time-macros" -version = "0.2.11" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb71511c991639bb078fd5bf97757e03914361c48100d52878b8e52b46fb92cd" +checksum = "1a942f44339478ef67935ab2bbaec2fb0322496cf3cbe84b261e06ac3814c572" dependencies = [ "time-core", ] @@ -9028,7 +9073,7 @@ source = "git+https://github.com/tokio-rs/tracing?rev=29146260fb4615d271d2e899ad dependencies = [ "crossbeam-channel", "thiserror", - "time 0.3.25", + "time 0.3.28", "tracing-subscriber 0.3.0", ] @@ -9342,9 +9387,9 @@ dependencies = [ [[package]] name = "unicase" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" dependencies = [ "version_check", ] @@ -9465,9 +9510,9 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" dependencies = [ "form_urlencoded", "idna 0.4.0", @@ -9506,7 +9551,7 @@ version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14d09ddfb0d93bf84824c09336d32e42f80961a9d1680832eb24fdf249ce11e6" dependencies = [ - "base64 0.21.2", + "base64 0.21.3", "log", "pico-args", "usvg-parser", @@ -9759,9 +9804,9 @@ checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "wasm-streams" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bbae3363c08332cadccd13b67db371814cd214c2524020932f0804b8cf7c078" +checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7" dependencies = [ "futures-util", "js-sys", @@ -9864,9 +9909,9 @@ dependencies = [ [[package]] name = "webpki" -version = "0.22.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +checksum = "f0e74f82d49d545ad128049b7e88f6576df2da6b02e9ce565c6f533be576957e" dependencies = [ "ring", "untrusted", @@ -9898,7 +9943,7 @@ dependencies = [ "sha2 0.10.7", "stun", "thiserror", - "time 0.3.25", + "time 0.3.28", "tokio", "turn", "url", @@ -9958,7 +10003,7 @@ dependencies = [ "serde", "sha1", "sha2 0.10.7", - "signature", + "signature 1.6.4", "subtle", "thiserror", "tokio", @@ -10493,32 +10538,13 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winnow" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d09770118a7eb1ccaf4a594a221334119a44a814fcb0d31c5b85e83e97227a97" +checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" -dependencies = [ - "winapi", -] - -[[package]] -name = "winreg" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a1a57ff50e9b408431e8f97d5456f2807f8eb2a2cd79b06068fc87f8ecf189" -dependencies = [ - "cfg-if", - "winapi", -] - [[package]] name = "winreg" version = "0.50.0" @@ -10529,6 +10555,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "winreg" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "937f3df7948156640f46aacef17a70db0de5917bda9c92b0f751f3a955b588fc" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "wry" version = "0.24.3" @@ -10636,7 +10672,7 @@ dependencies = [ "ring", "rusticata-macros", "thiserror", - "time 0.3.25", + "time 0.3.28", ] [[package]] @@ -10654,7 +10690,7 @@ dependencies = [ "oid-registry 0.6.1", "rusticata-macros", "thiserror", - "time 0.3.25", + "time 0.3.28", ] [[package]] @@ -10684,7 +10720,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" dependencies = [ - "time 0.3.25", + "time 0.3.28", ] [[package]] diff --git a/core/Cargo.toml b/core/Cargo.toml index 4e4651a18..ba62c4b1d 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -15,7 +15,7 @@ mobile = [] # This feature controls whether the Spacedrive Core contains functionality which requires FFmpeg. ffmpeg = ["dep:sd-ffmpeg"] location-watcher = ["dep:notify"] -heif = ["dep:sd-heif"] +heif = ["sd-images/heif"] [dependencies] sd-media-metadata = { path = "../crates/media-metadata" } @@ -27,8 +27,8 @@ sd-crypto = { path = "../crates/crypto", features = [ "serde", "keymanager", ] } -sd-svg = { path = "../crates/svg" } -sd-heif = { path = "../crates/heif", optional = true } + +sd-images = { path = "../crates/images" } sd-file-ext = { path = "../crates/file-ext" } sd-sync = { path = "../crates/sync" } sd-p2p = { path = "../crates/p2p", features = ["specta", "serde"] } diff --git a/core/src/object/media/thumbnail/mod.rs b/core/src/object/media/thumbnail/mod.rs index 3513359ad..46bf60273 100644 --- a/core/src/object/media/thumbnail/mod.rs +++ b/core/src/object/media/thumbnail/mod.rs @@ -9,25 +9,25 @@ use crate::{ }; use sd_file_ext::extensions::{Extension, ImageExtension, ALL_IMAGE_EXTENSIONS}; -use sd_media_metadata::image::{ExifReader, Orientation}; +use sd_images::format_image; +use sd_media_metadata::image::Orientation; #[cfg(feature = "ffmpeg")] use sd_file_ext::extensions::{VideoExtension, ALL_VIDEO_EXTENSIONS}; use std::{ - collections::{HashMap, HashSet}, + collections::HashMap, error::Error, ops::Deref, path::{Path, PathBuf}, - sync::Arc, }; use futures_concurrency::future::{Join, TryJoin}; -use image::{self, imageops, DynamicImage, GenericImageView, ImageFormat}; +use image::{self, imageops, DynamicImage, GenericImageView}; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; use thiserror::Error; -use tokio::{fs, io, task::spawn_blocking}; +use tokio::{fs, io}; use tracing::{error, trace, warn}; use webp::Encoder; @@ -37,8 +37,6 @@ mod shard; pub use directory::init_thumbnail_dir; pub use shard::get_shard_hex; -const THUMBNAIL_SIZE_FACTOR: f32 = 0.2; -const THUMBNAIL_QUALITY: f32 = 30.0; pub const THUMBNAIL_CACHE_DIR_NAME: &str = "thumbnails"; /// This does not check if a thumbnail exists, it just returns the path that it would exist at @@ -89,8 +87,24 @@ pub enum ThumbnailerError { VersionManager(#[from] VersionManagerError), #[error("failed to encode webp")] Encoding, - #[error("the image provided is too large")] - TooLarge, + #[error("error while converting the image: {0}")] + SdImages(#[from] sd_images::Error), +} + +/// This is the target pixel count for all thumbnails to be resized to, and it is eventually downscaled +/// to [`TARGET_QUALITY`]. +const TAGRET_PX: f32 = 262144_f32; + +/// This is the target quality that we render thumbnails at, it is a float between 0-100 +/// and is treated as a percentage (so 30% in this case, or it's the same as multiplying by `0.3`). +const TARGET_QUALITY: f32 = 30_f32; + +/// This takes in a width and a height, and returns a scaled width and height +/// It is scaled proportionally to the [`TARGET_PX`], so smaller images will be upscaled, +/// and larger images will be downscaled. This approach also maintains the aspect ratio of the image. +fn calculate_factor(w: f32, h: f32) -> (u32, u32) { + let sf = (TAGRET_PX / (w * h)).sqrt(); + ((w * sf).round() as u32, (h * sf).round() as u32) } #[derive(Debug, Serialize, Deserialize, Clone, Copy)] @@ -106,91 +120,30 @@ pub struct ThumbnailerMetadata { pub skipped: u32, } -static HEIF_EXTENSIONS: Lazy> = Lazy::new(|| { - ["heif", "heifs", "heic", "heics", "avif", "avci", "avcs"] - .into_iter() - .map(|s| s.to_string()) - .collect() -}); - -// The maximum file size that an image can be in order to have a thumbnail generated. -const MAXIMUM_FILE_SIZE: u64 = 50 * 1024 * 1024; // 50MB - pub async fn generate_image_thumbnail>( file_path: P, output_path: P, -) -> Result<(), Box> { - let file_path = file_path.as_ref(); +) -> Result<(), ThumbnailerError> { + let file_path = file_path.as_ref().to_path_buf(); - let ext = file_path - .extension() - .and_then(|ext| ext.to_str()) - .unwrap_or_default() - .to_ascii_lowercase(); - let ext = ext.as_str(); + let webp = tokio::task::block_in_place(move || -> Result<_, ThumbnailerError> { + let img = format_image(&file_path).map_err(|_| ThumbnailerError::Encoding)?; - let metadata = fs::metadata(file_path) - .await - .map_err(|e| FileIOError::from((file_path, e)))?; - - if metadata.len() - > (match ext { - "svg" => sd_svg::MAXIMUM_FILE_SIZE, - #[cfg(all(feature = "heif", not(target_os = "linux")))] - _ if HEIF_EXTENSIONS.contains(ext) => sd_heif::MAXIMUM_FILE_SIZE, - _ => MAXIMUM_FILE_SIZE, - }) { - return Err(ThumbnailerError::TooLarge.into()); - } - - #[cfg(all(feature = "heif", not(target_os = "linux")))] - if metadata.len() > sd_heif::MAXIMUM_FILE_SIZE && HEIF_EXTENSIONS.contains(ext) { - return Err(ThumbnailerError::TooLarge.into()); - } - - let data = Arc::new( - fs::read(file_path) - .await - .map_err(|e| FileIOError::from((file_path, e)))?, - ); - - let img = match ext { - "svg" => sd_svg::svg_to_dynamic_image(data.clone()).await?, - _ if HEIF_EXTENSIONS.contains(ext) => { - #[cfg(not(all(feature = "heif", not(target_os = "linux"))))] - return Err("HEIF not supported".into()); - #[cfg(all(feature = "heif", not(target_os = "linux")))] - sd_heif::heif_to_dynamic_image(data.clone()).await? - } - _ => image::load_from_memory_with_format( - &fs::read(file_path).await?, - ImageFormat::from_path(file_path)?, - )?, - }; - - let webp = spawn_blocking(move || -> Result<_, ThumbnailerError> { let (w, h) = img.dimensions(); + let (w_scale, h_scale) = calculate_factor(w as f32, h as f32); // Optionally, resize the existing photo and convert back into DynamicImage let mut img = DynamicImage::ImageRgba8(imageops::resize( &img, - // FIXME : Think of a better heuristic to get the thumbnail size - (w as f32 * THUMBNAIL_SIZE_FACTOR) as u32, - (h as f32 * THUMBNAIL_SIZE_FACTOR) as u32, + w_scale, + h_scale, imageops::FilterType::Triangle, )); - match ExifReader::from_slice(data.as_ref()) { - Ok(exif_reader) => { - // this corrects the rotation/flip of the image based on the available exif data - if let Some(orientation) = Orientation::from_reader(&exif_reader) { - img = orientation.correct_thumbnail(img); - } - } - Err(sd_media_metadata::Error::NoExifDataOnSlice) => { - // No can do if we don't have exif data - } - Err(e) => warn!("Unable to extract EXIF: {:?}", e), + // this corrects the rotation/flip of the image based on the *available* exif data + // not all images have exif data, so we don't error + if let Some(orientation) = Orientation::from_path(file_path) { + img = orientation.correct_thumbnail(img); } // Create the WebP encoder for the above image @@ -198,14 +151,11 @@ pub async fn generate_image_thumbnail>( return Err(ThumbnailerError::Encoding); }; - // Encode the image at a specified quality 0-100 - // Type WebPMemory is !Send, which makes the Future in this function !Send, // this make us `deref` to have a `&[u8]` and then `to_owned` to make a Vec // which implies on a unwanted clone... - Ok(encoder.encode(THUMBNAIL_QUALITY).deref().to_owned()) - }) - .await??; + Ok(encoder.encode(TARGET_QUALITY).deref().to_owned()) + })?; let output_path = output_path.as_ref(); @@ -214,11 +164,7 @@ pub async fn generate_image_thumbnail>( .await .map_err(|e| FileIOError::from((shard_dir, e)))?; } else { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "Cannot determine parent shard directory for thumbnail", - ) - .into()); + return Err(ThumbnailerError::Encoding); } fs::write(output_path, &webp) @@ -234,7 +180,7 @@ pub async fn generate_video_thumbnail + Send>( ) -> Result<(), Box> { use sd_ffmpeg::to_thumbnail; - to_thumbnail(file_path, output_path, 256, THUMBNAIL_QUALITY).await?; + to_thumbnail(file_path, output_path, 256, TARGET_QUALITY).await?; Ok(()) } @@ -249,16 +195,10 @@ pub const fn can_generate_thumbnail_for_video(video_extension: &VideoExtension) pub const fn can_generate_thumbnail_for_image(image_extension: &ImageExtension) -> bool { use ImageExtension::*; - #[cfg(all(feature = "heif", not(target_os = "linux")))] - let res = matches!( + matches!( image_extension, - Jpg | Jpeg | Png | Webp | Gif | Svg | Heic | Heics | Heif | Heifs | Avif - ); - - #[cfg(not(all(feature = "heif", not(target_os = "linux"))))] - let res = matches!(image_extension, Jpg | Jpeg | Png | Webp | Gif | Svg); - - res + Jpg | Jpeg | Png | Webp | Gif | Svg | Heic | Heics | Heif | Heifs | Avif | Bmp | Ico + ) } pub(super) async fn process( diff --git a/crates/heif/Cargo.toml b/crates/heif/Cargo.toml deleted file mode 100644 index 64dd597fc..000000000 --- a/crates/heif/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "sd-heif" -version = "0.1.0" -authors = ["Jake Robinson "] -license = { workspace = true } -repository = { workspace = true } -edition = { workspace = true } - -[dependencies] -libheif-rs = "0.19.2" -libheif-sys = "=1.14.2" -image = "0.24.6" -once_cell = "1.17.2" -tokio = { workspace = true, features = ["fs", "io-util"] } -thiserror = "1.0.40" diff --git a/crates/heif/src/lib.rs b/crates/heif/src/lib.rs deleted file mode 100644 index e7af00f08..000000000 --- a/crates/heif/src/lib.rs +++ /dev/null @@ -1,90 +0,0 @@ -use std::{ - io::{Cursor, SeekFrom}, - sync::Arc, -}; - -use image::DynamicImage; -use libheif_rs::{ColorSpace, HeifContext, LibHeif, RgbChroma}; -use once_cell::sync::Lazy; -use thiserror::Error; -use tokio::{ - io::{AsyncReadExt, AsyncSeekExt, BufReader}, - task::{spawn_blocking, JoinError}, -}; - -type HeifResult = Result; - -// The maximum file size that an image can be in order to have a thumbnail generated. -pub const MAXIMUM_FILE_SIZE: u64 = 20 * 1024 * 1024; // 20MB - -#[derive(Error, Debug)] -pub enum HeifError { - #[error("error with libheif: {0}")] - LibHeif(#[from] libheif_rs::HeifError), - #[error("error while loading the image (via the `image` crate): {0}")] - Image(#[from] image::ImageError), - #[error("Blocking task failed to execute to completion.")] - Join(#[from] JoinError), - #[error("there was an error while converting the image to an `RgbImage`")] - RgbImageConversion, - #[error("the image provided is unsupported")] - Unsupported, - #[error("the provided bit depth is invalid")] - InvalidBitDepth, - #[error("invalid path provided (non UTF-8)")] - InvalidPath, -} - -static HEIF: Lazy = Lazy::new(LibHeif::new); - -pub async fn heif_to_dynamic_image(data: Arc>) -> HeifResult { - let (img_data, stride, height, width) = spawn_blocking(move || -> Result<_, HeifError> { - let ctx = HeifContext::read_from_bytes(&data)?; - let handle = ctx.primary_image_handle()?; - let img = HEIF.decode(&handle, ColorSpace::Rgb(RgbChroma::Rgb), None)?; - - // TODO(brxken128): add support for images with individual r/g/b channels - // i'm unable to find a sample to test with, but it should follow the same principles as this one - let Some(planes) = img.planes().interleaved else { - return Err(HeifError::Unsupported); - }; - - if planes.bits_per_pixel != 8 { - return Err(HeifError::InvalidBitDepth); - } - - Ok(( - planes.data.to_vec(), - planes.stride, - img.height(), - img.width(), - )) - }) - .await??; - - let mut buffer = [0u8; 3]; // [r, g, b] - let mut reader = BufReader::new(Cursor::new(img_data)); - let mut sequence = vec![]; - - // this is the interpolation stuff, it essentially just makes the image correct - // in regards to stretching/resolution, etc - for y in 0..height { - reader - .seek(SeekFrom::Start((stride * y as usize) as u64)) - .await - .map_err(|_| HeifError::RgbImageConversion)?; - - for _ in 0..width { - reader - .read_exact(&mut buffer) - .await - .map_err(|_| HeifError::RgbImageConversion)?; - sequence.extend_from_slice(&buffer); - } - } - - let rgb_img = - image::RgbImage::from_raw(width, height, sequence).ok_or(HeifError::RgbImageConversion)?; - - Ok(DynamicImage::ImageRgb8(rgb_img)) -} diff --git a/crates/images/Cargo.toml b/crates/images/Cargo.toml new file mode 100644 index 000000000..38a4b1db5 --- /dev/null +++ b/crates/images/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "sd-images" +version = "0.0.0" +authors = [ + "Jake Robinson ", + "Vítor Vasconcellos ", +] +license = { workspace = true } +repository = { workspace = true } +edition = { workspace = true } + +[features] +heif = ["dep:libheif-rs", "dep:libheif-sys"] + +[dependencies] +libheif-rs = { version = "0.19.2", optional = true } +libheif-sys = { version = "=1.14.2", optional = true } +image = "0.24.7" +thiserror = "1.0.45" +resvg = "0.35.0" diff --git a/crates/images/src/consts.rs b/crates/images/src/consts.rs new file mode 100644 index 000000000..6aeb8ed51 --- /dev/null +++ b/crates/images/src/consts.rs @@ -0,0 +1,26 @@ +/// The size of 1MiB in bytes +const MIB: u64 = 1_048_576; + +pub const HEIF_EXTENSIONS: [&str; 7] = ["heif", "heifs", "heic", "heics", "avif", "avci", "avcs"]; + +/// The maximum file size that an image can be in order to have a thumbnail generated. +/// +/// This value is in MiB. +pub const HEIF_MAXIMUM_FILE_SIZE: u64 = MIB * 32; + +pub const SVG_EXTENSIONS: [&str; 2] = ["svg", "svgz"]; + +/// The maximum file size that an image can be in order to have a thumbnail generated. +/// +/// This value is in MiB. +pub const SVG_MAXIMUM_FILE_SIZE: u64 = MIB * 24; + +/// The size that SVG images are rendered at, assuming they are square. +// TODO(brxken128): check for non-1:1 SVG images and create a function to resize +// them while maintaining the aspect ratio. +pub const SVG_RENDER_SIZE: u32 = 512; + +/// The maximum file size that an image can be in order to have a thumbnail generated. +/// +/// This value is in MiB. +pub const GENERIC_MAXIMUM_FILE_SIZE: u64 = MIB * 64; diff --git a/crates/images/src/error.rs b/crates/images/src/error.rs new file mode 100644 index 000000000..e95a6dbdc --- /dev/null +++ b/crates/images/src/error.rs @@ -0,0 +1,37 @@ +use std::num::TryFromIntError; + +pub type Result = std::result::Result; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[cfg(feature = "heif")] + #[error("error with libheif: {0}")] + LibHeif(#[from] libheif_rs::HeifError), + + #[error("error with usvg: {0}")] + USvg(#[from] resvg::usvg::Error), + #[error("failed to allocate `Pixbuf` while converting an SVG")] + Pixbuf, + #[error("error while loading the image (via the `image` crate): {0}")] + Image(#[from] image::ImageError), + #[error("there was an i/o error: {0}")] + Io(#[from] std::io::Error), + #[error("there was an error while converting the image to an `RgbImage`")] + RgbImageConversion, + #[error("the image provided is unsupported")] + Unsupported, + #[error("the image provided is too large (over 20MiB)")] + TooLarge, + #[error("the provided bit depth is invalid")] + InvalidBitDepth, + #[error("invalid path provided (non UTF-8)")] + InvalidPath, + #[error("the image has an invalid length to be RGB")] + InvalidLength, + #[error("invalid path provided (it had no file extension)")] + NoExtension, + #[error("error while converting from raw")] + RawConversion, + #[error("error while parsing integers")] + TryFromInt(#[from] TryFromIntError), +} diff --git a/crates/images/src/formatter.rs b/crates/images/src/formatter.rs new file mode 100644 index 000000000..04516f8ed --- /dev/null +++ b/crates/images/src/formatter.rs @@ -0,0 +1,47 @@ +use crate::{ + consts, + error::{Error, Result}, + generic::GenericHandler, + svg::SvgHandler, + ImageHandler, +}; +use image::DynamicImage; +use std::{ + ffi::{OsStr, OsString}, + path::Path, +}; + +#[cfg(feature = "heif")] +use crate::heif::HeifHandler; + +pub fn format_image(path: impl AsRef) -> Result { + let ext = path + .as_ref() + .extension() + .map_or_else(|| Err(Error::NoExtension), |e| Ok(e.to_ascii_lowercase()))?; + match_to_handler(&ext).handle_image(path.as_ref()) +} + +#[allow(clippy::useless_let_if_seq)] +fn match_to_handler(ext: &OsStr) -> Box { + let mut handler: Box = Box::new(GenericHandler {}); + + #[cfg(feature = "heif")] + if consts::HEIF_EXTENSIONS + .iter() + .map(OsString::from) + .any(|x| x == ext) + { + handler = Box::new(HeifHandler {}); + } + + if consts::SVG_EXTENSIONS + .iter() + .map(OsString::from) + .any(|x| x == ext) + { + handler = Box::new(SvgHandler {}); + } + + handler +} diff --git a/crates/images/src/generic.rs b/crates/images/src/generic.rs new file mode 100644 index 000000000..9d401bb54 --- /dev/null +++ b/crates/images/src/generic.rs @@ -0,0 +1,22 @@ +use crate::consts::GENERIC_MAXIMUM_FILE_SIZE; +pub use crate::error::{Error, Result}; +use crate::ImageHandler; +use image::DynamicImage; +use std::path::Path; + +pub struct GenericHandler {} + +impl ImageHandler for GenericHandler { + fn maximum_size(&self) -> u64 { + GENERIC_MAXIMUM_FILE_SIZE + } + + fn validate_image(&self, _bits_per_pixel: u8, _length: usize) -> Result<()> { + Ok(()) + } + + fn handle_image(&self, path: &Path) -> Result { + let data = self.get_data(path)?; // this also makes sure the file isn't above the maximum size + Ok(image::load_from_memory(&data)?) + } +} diff --git a/crates/images/src/heif.rs b/crates/images/src/heif.rs new file mode 100644 index 000000000..d7a817b80 --- /dev/null +++ b/crates/images/src/heif.rs @@ -0,0 +1,122 @@ +pub use crate::consts::HEIF_EXTENSIONS; +use crate::consts::HEIF_MAXIMUM_FILE_SIZE; +pub use crate::error::{Error, Result}; +use crate::ImageHandler; +use image::DynamicImage; +use libheif_rs::{ColorSpace, HeifContext, LibHeif, RgbChroma}; +use std::io::{Cursor, SeekFrom}; +use std::io::{Read, Seek}; +use std::path::Path; + +pub struct HeifHandler {} + +impl ImageHandler for HeifHandler { + fn maximum_size(&self) -> u64 { + HEIF_MAXIMUM_FILE_SIZE + } + + fn validate_image(&self, bits_per_pixel: u8, length: usize) -> Result<()> { + if bits_per_pixel != 8 { + return Err(Error::InvalidBitDepth); + } else if length % 3 != 0 || length % 4 != 0 { + return Err(Error::InvalidLength); + } + + Ok(()) + } + + fn handle_image(&self, path: &Path) -> Result { + let img = { + let data = self.get_data(path)?; + let handle = HeifContext::read_from_bytes(&data)?.primary_image_handle()?; + LibHeif::new().decode(&handle, ColorSpace::Rgb(RgbChroma::Rgb), None) + }?; + + let planes = img.planes(); + + if let Some(i) = planes.interleaved { + self.validate_image(i.bits_per_pixel, i.data.len())?; + + let mut reader = Cursor::new(i.data); + let mut sequence = vec![]; + let mut buffer = [0u8; 3]; // [r, g, b] + + // this is the interpolation stuff, it essentially just makes the image correct + // in regards to stretching/resolution, etc + (0..img.height()).try_for_each(|x| { + let x: usize = x.try_into()?; + let start: u64 = (i.stride * x).try_into()?; + reader.seek(SeekFrom::Start(start))?; + (0..img.width()).try_for_each(|_| { + reader.read_exact(&mut buffer)?; + sequence.extend_from_slice(&buffer); + Ok::<(), Error>(()) + })?; + Ok::<(), Error>(()) + })?; + + image::RgbImage::from_raw(img.width(), img.height(), sequence).map_or_else( + || Err(Error::RgbImageConversion), + |x| Ok(DynamicImage::ImageRgb8(x)), + ) + } else if let (Some(r), Some(g), Some(b)) = (planes.r, planes.g, planes.b) { + // This implementation is **ENTIRELY** untested, as I'm unable to source + // a HEIF image that has separate r/g/b channels, let alone r/g/b/a. + // This was hand-crafted using my best judgement, and I think it should work. + // I'm sure we'll get a GH issue opened regarding it if not - brxken128 + + self.validate_image(r.bits_per_pixel, r.data.len())?; + self.validate_image(g.bits_per_pixel, g.data.len())?; + self.validate_image(b.bits_per_pixel, b.data.len())?; + + let mut red = Cursor::new(r.data); + let mut green = Cursor::new(g.data); + let mut blue = Cursor::new(b.data); + + let (mut alpha, has_alpha) = if let Some(a) = planes.a { + self.validate_image(a.bits_per_pixel, a.data.len())?; + (Cursor::new(a.data), true) + } else { + (Cursor::new([].as_ref()), false) + }; + + let mut sequence = vec![]; + let mut buffer: [u8; 4] = [0u8; 4]; + + // this is the interpolation stuff, it essentially just makes the image correct + // in regards to stretching/resolution, etc + (0..img.height()).try_for_each(|x| { + let x: usize = x.try_into()?; + let start: u64 = (r.stride * x).try_into()?; + red.seek(SeekFrom::Start(start))?; + (0..img.width()).try_for_each(|_| { + red.read_exact(&mut buffer[0..1])?; + green.read_exact(&mut buffer[1..2])?; + blue.read_exact(&mut buffer[2..3])?; + sequence.extend_from_slice(&buffer[..3]); + + if has_alpha { + alpha.read_exact(&mut buffer[3..4])?; + sequence.extend_from_slice(&buffer[3..4]); + } + Ok::<(), Error>(()) + })?; + Ok::<(), Error>(()) + })?; + + if has_alpha { + image::RgbaImage::from_raw(img.width(), img.height(), sequence).map_or_else( + || Err(Error::RgbImageConversion), + |x| Ok(DynamicImage::ImageRgba8(x)), + ) + } else { + image::RgbImage::from_raw(img.width(), img.height(), sequence).map_or_else( + || Err(Error::RgbImageConversion), + |x| Ok(DynamicImage::ImageRgb8(x)), + ) + } + } else { + Err(Error::Unsupported) + } + } +} diff --git a/crates/images/src/lib.rs b/crates/images/src/lib.rs new file mode 100644 index 000000000..60149c0c7 --- /dev/null +++ b/crates/images/src/lib.rs @@ -0,0 +1,60 @@ +#![warn( + clippy::all, + clippy::pedantic, + clippy::correctness, + clippy::perf, + clippy::style, + clippy::suspicious, + clippy::complexity, + clippy::nursery, + clippy::unwrap_used, + unused_qualifications, + rust_2018_idioms, + clippy::expect_used, + trivial_casts, + trivial_numeric_casts, + unused_allocation, + clippy::as_conversions, + clippy::dbg_macro +)] +#![forbid(unsafe_code)] +#![allow(clippy::missing_errors_doc, clippy::module_name_repetitions)] + +mod consts; +mod error; +mod formatter; +mod generic; +#[cfg(feature = "heif")] +mod heif; +mod svg; + +pub use error::{Error, Result}; +pub use formatter::format_image; +pub use image::DynamicImage; +use std::{fs, io::Read, path::Path}; + +pub trait ImageHandler { + fn maximum_size(&self) -> u64 + where + Self: Sized; // thanks vtables + + fn get_data(&self, path: &Path) -> Result> + where + Self: Sized, + { + let mut file = fs::File::open(path)?; + if file.metadata()?.len() > self.maximum_size() { + Err(Error::TooLarge) + } else { + let mut data = vec![]; + file.read_to_end(&mut data)?; + Ok(data) + } + } + + fn validate_image(&self, bits_per_pixel: u8, length: usize) -> Result<()> + where + Self: Sized; + + fn handle_image(&self, path: &Path) -> Result; +} diff --git a/crates/images/src/svg.rs b/crates/images/src/svg.rs new file mode 100644 index 000000000..975a20d5f --- /dev/null +++ b/crates/images/src/svg.rs @@ -0,0 +1,63 @@ +use std::path::Path; + +use crate::{ + consts::{SVG_MAXIMUM_FILE_SIZE, SVG_RENDER_SIZE}, + Error, ImageHandler, Result, +}; +use image::DynamicImage; +use resvg::{ + tiny_skia::{self}, + usvg, +}; +use usvg::{fontdb, TreeParsing, TreeTextToPath}; + +pub struct SvgHandler {} + +impl ImageHandler for SvgHandler { + fn maximum_size(&self) -> u64 { + SVG_MAXIMUM_FILE_SIZE + } + + fn validate_image(&self, _bits_per_pixel: u8, _length: usize) -> Result<()> { + Ok(()) + } + + fn handle_image(&self, path: &Path) -> Result { + let data = self.get_data(path)?; + let rtree = usvg::Tree::from_data(&data, &usvg::Options::default()).map(|mut tree| { + let mut fontdb = fontdb::Database::new(); + fontdb.load_system_fonts(); + tree.convert_text(&fontdb); + resvg::Tree::from_usvg(&tree) + })?; + + let size = if rtree.size.width() > rtree.size.height() { + rtree.size.to_int_size().scale_to_width(SVG_RENDER_SIZE) // make this a const + } else { + rtree.size.to_int_size().scale_to_height(SVG_RENDER_SIZE) + } + .ok_or(Error::InvalidLength)?; + + #[allow(clippy::cast_precision_loss)] + #[allow(clippy::as_conversions)] + let transform = tiny_skia::Transform::from_scale( + size.width() as f32 / rtree.size.width(), + size.height() as f32 / rtree.size.height(), + ); + + #[allow(clippy::cast_possible_truncation)] + #[allow(clippy::cast_sign_loss)] + #[allow(clippy::as_conversions)] + let Some(mut pixmap) = tiny_skia::Pixmap::new(size.width(), size.height()) else { + return Err(Error::Pixbuf); + }; + + rtree.render(transform, &mut pixmap.as_mut()); + + image::RgbaImage::from_raw(pixmap.width(), pixmap.height(), pixmap.data().into()) + .map_or_else( + || Err(Error::RgbImageConversion), + |x| Ok(DynamicImage::ImageRgba8(x)), + ) + } +} diff --git a/crates/media-metadata/src/image/orientation.rs b/crates/media-metadata/src/image/orientation.rs index 3cf0b5cff..0cf934e02 100644 --- a/crates/media-metadata/src/image/orientation.rs +++ b/crates/media-metadata/src/image/orientation.rs @@ -21,7 +21,7 @@ pub enum Orientation { impl Orientation { /// This is used for quickly sourcing [`Orientation`] data from a path, to be later used by one of the modification functions. #[allow(clippy::future_not_send)] - pub fn source_orientation(path: impl AsRef) -> Option { + pub fn from_path(path: impl AsRef) -> Option { let reader = ExifReader::from_path(path).ok()?; reader.get_tag_int(Tag::Orientation).map(Into::into) } diff --git a/crates/svg/Cargo.toml b/crates/svg/Cargo.toml deleted file mode 100644 index 4f7046347..000000000 --- a/crates/svg/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "sd-svg" -version = "0.1.0" -authors = ["Vítor Vasconcellos "] -license = { workspace = true } -repository = { workspace = true } -edition = { workspace = true } - -[dependencies] -image = "0.24.6" -resvg = "0.35.0" -thiserror = "1.0.40" -tokio = { workspace = true, features = ["fs", "io-util"] } -tracing = "0.1.37" diff --git a/crates/svg/src/lib.rs b/crates/svg/src/lib.rs deleted file mode 100644 index aa119e587..000000000 --- a/crates/svg/src/lib.rs +++ /dev/null @@ -1,76 +0,0 @@ -use std::sync::Arc; - -use image::DynamicImage; -use resvg::{ - tiny_skia::{self, Pixmap}, - usvg, -}; -use thiserror::Error; -use tokio::task::{spawn_blocking, JoinError}; -use tracing::error; -use usvg::{fontdb, TreeParsing, TreeTextToPath}; - -type SvgResult = Result; - -const THUMB_SIZE: u32 = 512; - -// The maximum file size that an image can be in order to have a thumbnail generated. -pub const MAXIMUM_FILE_SIZE: u64 = 20 * 1024 * 1024; // 20MB - -#[derive(Error, Debug)] -pub enum SvgError { - #[error("error with usvg: {0}")] - USvg(#[from] resvg::usvg::Error), - #[error("error while loading the image (via the `image` crate): {0}")] - Image(#[from] image::ImageError), - #[error("Blocking task failed to execute to completion")] - Join(#[from] JoinError), - #[error("failed to allocate `Pixbuf`")] - Pixbuf, - #[error("there was an error while converting the image to an `RgbImage`")] - RgbImageConversion, - #[error("failed to calculate thumbnail size")] - InvalidSize, -} - -pub async fn svg_to_dynamic_image(data: Arc>) -> SvgResult { - let mut pixmap = spawn_blocking(move || -> Result { - let rtree = usvg::Tree::from_data(&data, &usvg::Options::default()).map(|mut tree| { - let mut fontdb = fontdb::Database::new(); - fontdb.load_system_fonts(); - - tree.convert_text(&fontdb); - - resvg::Tree::from_usvg(&tree) - })?; - - let size = if rtree.size.width() > rtree.size.height() { - rtree.size.to_int_size().scale_to_width(THUMB_SIZE) - } else { - rtree.size.to_int_size().scale_to_height(THUMB_SIZE) - } - .ok_or(SvgError::InvalidSize)?; - - let transform = tiny_skia::Transform::from_scale( - size.width() as f32 / rtree.size.width(), - size.height() as f32 / rtree.size.height(), - ); - - let Some(mut pixmap) = tiny_skia::Pixmap::new(size.width(), size.height()) else { - return Err(SvgError::Pixbuf); - }; - - rtree.render(transform, &mut pixmap.as_mut()); - - Ok(pixmap) - }) - .await??; - - let Some(rgb_img) = - image::RgbaImage::from_raw(pixmap.width(), pixmap.height(), pixmap.data_mut().into()) - else { - return Err(SvgError::RgbImageConversion); - }; - - Ok(DynamicImage::ImageRgba8(rgb_img)) -}