diff --git a/Cargo.lock b/Cargo.lock index b164cb9..20e11eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,17 +24,6 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" -[[package]] -name = "aes" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8eb277bec05f56a0e0591f155a484cbd0f4f07ff2905051a48c72f004f7ed58" -dependencies = [ - "cipher", - "cpubits", - "cpufeatures", -] - [[package]] name = "ahash" version = "0.8.12" @@ -66,21 +55,6 @@ dependencies = [ "equator", ] -[[package]] -name = "alloc-no-stdlib" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" - -[[package]] -name = "alloc-stdlib" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e76a019e91224d279006ff972f1e984179a6e9feb050adba6ce8274aef23195" -dependencies = [ - "alloc-no-stdlib", -] - [[package]] name = "android-activity" version = "0.6.1" @@ -136,27 +110,6 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" -[[package]] -name = "arboard" -version = "3.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf" -dependencies = [ - "clipboard-win", - "image", - "log", - "objc2 0.6.4", - "objc2-app-kit 0.3.2", - "objc2-core-foundation", - "objc2-core-graphics", - "objc2-foundation 0.3.2", - "parking_lot", - "percent-encoding", - "windows-sys 0.59.0", - "wl-clipboard-rs", - "x11rb", -] - [[package]] name = "arg_enum_proc_macro" version = "0.3.4" @@ -460,16 +413,6 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" -[[package]] -name = "block-buffer" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" -dependencies = [ - "hybrid-array", - "zeroize", -] - [[package]] name = "block2" version = "0.5.1" @@ -502,13 +445,28 @@ dependencies = [ ] [[package]] -name = "brotli-decompressor" -version = "5.0.3" +name = "bon" +version = "3.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a32acac15fe1967bc3986b2a6347dffc965602354ea6f450ad07e8bfd253583" +checksum = "a602c73c7b0148ec6d12af6fd5cc7a46e2eacc8878271a999abac56eed12f561" dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", + "bon-macros", + "rustversion", +] + +[[package]] +name = "bon-macros" +version = "3.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dee98b0db6a962de883bf5d20362dee4d7ca0d12fe39a7c6c73c844e1cd7c1f" +dependencies = [ + "darling", + "ident_case", + "prettyplease", + "proc-macro2", + "quote", + "rustversion", + "syn", ] [[package]] @@ -567,15 +525,6 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" -[[package]] -name = "bzip2" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a53fac24f34a81bc9954b5d6cfce0c21e18ec6959f44f56e8e90e4bb7c346c" -dependencies = [ - "libbz2-rs-sys", -] - [[package]] name = "calloop" version = "0.13.0" @@ -661,16 +610,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" -[[package]] -name = "cipher" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c" -dependencies = [ - "crypto-common", - "inout", -] - [[package]] name = "clipboard-win" version = "5.4.1" @@ -710,12 +649,6 @@ dependencies = [ "x11rb", ] -[[package]] -name = "cmov" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" - [[package]] name = "codespan-reporting" version = "0.12.0" @@ -752,18 +685,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "const-oid" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" - -[[package]] -name = "constant_time_eq" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" - [[package]] name = "core-foundation" version = "0.9.4" @@ -858,21 +779,6 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "cpubits" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06ae" - -[[package]] -name = "cpufeatures" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" -dependencies = [ - "libc", -] - [[package]] name = "crc32fast" version = "1.5.0" @@ -926,15 +832,6 @@ dependencies = [ "wgpu", ] -[[package]] -name = "crypto-common" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" -dependencies = [ - "hybrid-array", -] - [[package]] name = "ctor" version = "0.10.1" @@ -944,21 +841,46 @@ dependencies = [ "dtor", ] -[[package]] -name = "ctutils" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" -dependencies = [ - "cmov", -] - [[package]] name = "cursor-icon" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core", + "quote", + "syn", +] + [[package]] name = "dav1d" version = "0.11.1" @@ -981,31 +903,6 @@ dependencies = [ "system-deps", ] -[[package]] -name = "deflate64" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac6b926516df9c60bfa16e107b21086399f8285a44ca9711344b9e553c5146e2" - -[[package]] -name = "deranged" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" - -[[package]] -name = "digest" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" -dependencies = [ - "block-buffer", - "const-oid", - "crypto-common", - "ctutils", - "zeroize", -] - [[package]] name = "dirs" version = "6.0.0" @@ -1247,12 +1144,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" -[[package]] -name = "fixedbitset" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" - [[package]] name = "flate2" version = "1.1.9" @@ -1261,7 +1152,6 @@ checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" dependencies = [ "crc32fast", "miniz_oxide", - "zlib-rs", ] [[package]] @@ -1485,10 +1375,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", - "js-sys", "libc", "r-efi 6.0.0", - "wasm-bindgen", ] [[package]] @@ -1682,24 +1570,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" -[[package]] -name = "hmac" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" -dependencies = [ - "digest", -] - -[[package]] -name = "hybrid-array" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707114b52a152fa7bdb290cd7cd5912d9467273b6d74e21b8d81aca1f8533f6b" -dependencies = [ - "typenum", -] - [[package]] name = "iced" version = "0.14.0" @@ -1887,6 +1757,12 @@ dependencies = [ "winit", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "image" version = "0.25.10" @@ -1927,15 +1803,11 @@ dependencies = [ name = "imagey" version = "0.1.0" dependencies = [ - "arboard", "dirs", "iced", "image", - "jxl-oxide", - "kra-image-integration", + "jpegxl-rs", "rfd", - "size", - "zip", ] [[package]] @@ -1954,15 +1826,6 @@ dependencies = [ "hashbrown 0.17.1", ] -[[package]] -name = "inout" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7" -dependencies = [ - "hybrid-array", -] - [[package]] name = "interpolate_name" version = "0.2.4" @@ -2051,6 +1914,29 @@ dependencies = [ "libc", ] +[[package]] +name = "jpegxl-rs" +version = "0.14.0+libjxl-0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7d16fe73fb6d2e16b986392b716409c6ae6fabd35769d66bea391b016cdbc9" +dependencies = [ + "bon", + "byteorder", + "half", + "image", + "jpegxl-sys", + "thiserror 2.0.18", +] + +[[package]] +name = "jpegxl-sys" +version = "0.12.1+libjxl-0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00cb4f7ffb45ee4327e9ec6ca1a732f3abef470ee008f7d4be3afb414075cd7d" +dependencies = [ + "pkg-config", +] + [[package]] name = "js-sys" version = "0.3.103" @@ -2062,186 +1948,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "jxl-bitstream" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b480e752277e29eb4054f69546887a9b84656fe78c08f54ba5850ced98a378fe" -dependencies = [ - "tracing", -] - -[[package]] -name = "jxl-coding" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd972bcd125e776f1eb241ac50e39f956095a1c2770c64736c968f8946bd9a3c" -dependencies = [ - "jxl-bitstream", - "tracing", -] - -[[package]] -name = "jxl-color" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f316b1358c1711755b3ee8e8cb5c4a1dad12e796233088a7a513440782de80b2" -dependencies = [ - "jxl-bitstream", - "jxl-coding", - "jxl-grid", - "jxl-image", - "jxl-oxide-common", - "jxl-threadpool", - "tracing", -] - -[[package]] -name = "jxl-frame" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d967c6fd669c7c01060b5022d8835fa82fd46b06ffc98b549f17600a097c2b3" -dependencies = [ - "jxl-bitstream", - "jxl-coding", - "jxl-grid", - "jxl-image", - "jxl-modular", - "jxl-oxide-common", - "jxl-threadpool", - "jxl-vardct", - "tracing", -] - -[[package]] -name = "jxl-grid" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01671307879a033bfa52e6e8784b941aca770b3f3a7d33830b455b6844f793fb" -dependencies = [ - "tracing", -] - -[[package]] -name = "jxl-image" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f752d62577c702a94dbbce4045caf08cb58639e8a4d56464b40ecf33ffe565" -dependencies = [ - "jxl-bitstream", - "jxl-grid", - "jxl-oxide-common", - "tracing", -] - -[[package]] -name = "jxl-jbr" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e35d032bcec660647828527ff42c6f5776d2fd44b8357f9f6d9ac6dc07218e46" -dependencies = [ - "brotli-decompressor", - "jxl-bitstream", - "jxl-frame", - "jxl-grid", - "jxl-image", - "jxl-modular", - "jxl-oxide-common", - "jxl-threadpool", - "jxl-vardct", - "tracing", -] - -[[package]] -name = "jxl-modular" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2f045b24c738dd91d482be385512b512721ae08a671bd4b27bf1c47f215235" -dependencies = [ - "jxl-bitstream", - "jxl-coding", - "jxl-grid", - "jxl-oxide-common", - "jxl-threadpool", - "tracing", -] - -[[package]] -name = "jxl-oxide" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d36c662923f47586880211f3bc7c0d83fb3a9b410d278c7bde93450748abeef3" -dependencies = [ - "brotli-decompressor", - "bytemuck", - "image", - "jxl-bitstream", - "jxl-color", - "jxl-frame", - "jxl-grid", - "jxl-image", - "jxl-jbr", - "jxl-oxide-common", - "jxl-render", - "jxl-threadpool", - "tracing", -] - -[[package]] -name = "jxl-oxide-common" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62394c5021b3a9e7e0dbb2d639d555d019090c9946c39f6d3b09d390db4157b" -dependencies = [ - "jxl-bitstream", -] - -[[package]] -name = "jxl-render" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34386bfdb6a19b5a30cc9beb4d475d537422c31ae8c39bb69640fcce3fcaf19" -dependencies = [ - "bytemuck", - "jxl-bitstream", - "jxl-coding", - "jxl-color", - "jxl-frame", - "jxl-grid", - "jxl-image", - "jxl-modular", - "jxl-oxide-common", - "jxl-threadpool", - "jxl-vardct", - "tracing", -] - -[[package]] -name = "jxl-threadpool" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25f15eb830aa77a7f21148d72e153562a26bfe570139bd4922eab1908dd499d3" -dependencies = [ - "rayon", - "rayon-core", - "tracing", -] - -[[package]] -name = "jxl-vardct" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce72a18c6d3a47172ab6c479be2bdb56f22066b5d7092663f03b4490820b4511" -dependencies = [ - "jxl-bitstream", - "jxl-coding", - "jxl-grid", - "jxl-modular", - "jxl-oxide-common", - "jxl-threadpool", - "tracing", -] - [[package]] name = "kamadak-exif" version = "0.6.1" @@ -2268,16 +1974,6 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" -[[package]] -name = "kra-image-integration" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ab1db0e6caced9f4b26057c10ea190c447a8be4fcac03afd7213e35168ad700" -dependencies = [ - "image", - "zip", -] - [[package]] name = "kurbo" version = "0.10.4" @@ -2294,12 +1990,6 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" -[[package]] -name = "libbz2-rs-sys" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34b357333733e8260735ba5894eb928c02ecc69c78715f01a8019e7fa7f2db4c" - [[package]] name = "libc" version = "0.2.186" @@ -2407,15 +2097,6 @@ version = "0.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39" -[[package]] -name = "lzma-rust2" -version = "0.16.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca93e534d1142d1d0dcca6d25fe302508a5dfb40b302802904577725ea0b695b" -dependencies = [ - "sha2", -] - [[package]] name = "malloc_buf" version = "0.0.6" @@ -2635,12 +2316,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-conv" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" - [[package]] name = "num-derive" version = "0.4.2" @@ -3104,16 +2779,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "os_pipe" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - [[package]] name = "owned_ttf_parser" version = "0.25.1" @@ -3164,33 +2829,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" -[[package]] -name = "pbkdf2" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112d82ceb8c5bf524d9af484d4e4970c9fd5a0cc15ba14ad93dccd28873b0629" -dependencies = [ - "digest", - "hmac", -] - [[package]] name = "percent-encoding" version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" -[[package]] -name = "petgraph" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" -dependencies = [ - "fixedbitset", - "hashbrown 0.15.5", - "indexmap", -] - [[package]] name = "pin-project" version = "1.1.13" @@ -3294,18 +2938,6 @@ dependencies = [ "portable-atomic", ] -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppmd-rust" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efca4c95a19a79d1c98f791f10aebd5c1363b473244630bb7dbde1dc98455a24" - [[package]] name = "ppv-lite86" version = "0.2.21" @@ -3321,6 +2953,16 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro-crate" version = "3.5.0" @@ -3779,28 +3421,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "sha1" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha2" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "shlex" version = "2.0.1" @@ -3848,12 +3468,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" -[[package]] -name = "size" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b6709c7b6754dca1311b3c73e79fcce40dd414c782c66d88e8823030093b02b" - [[package]] name = "skrifa" version = "0.37.0" @@ -4025,6 +3639,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "svg_fmt" version = "0.4.5" @@ -4157,26 +3777,6 @@ dependencies = [ "zune-jpeg", ] -[[package]] -name = "time" -version = "0.3.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e1d5e639ff6bab73cb6885cc7e7b1de96c3f32c68ec55f3952614bec1092244" -dependencies = [ - "deranged", - "js-sys", - "num-conv", - "powerfmt", - "serde_core", - "time-core", -] - -[[package]] -name = "time-core" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" - [[package]] name = "tiny-skia" version = "0.11.4" @@ -4313,17 +3913,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "tree_magic_mini" -version = "3.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8765b90061cba6c22b5831f675da109ae5561588290f9fa2317adab2714d5a6" -dependencies = [ - "memchr", - "nom", - "petgraph", -] - [[package]] name = "ttf-parser" version = "0.25.1" @@ -4333,18 +3922,6 @@ dependencies = [ "core_maths", ] -[[package]] -name = "typed-path" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e" - -[[package]] -name = "typenum" -version = "1.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" - [[package]] name = "uds_windows" version = "1.2.1" @@ -5187,24 +4764,6 @@ version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" -[[package]] -name = "wl-clipboard-rs" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9651471a32e87d96ef3a127715382b2d11cc7c8bb9822ded8a7cc94072eb0a3" -dependencies = [ - "libc", - "log", - "os_pipe", - "rustix 1.1.4", - "thiserror 2.0.18", - "tree_magic_mini", - "wayland-backend", - "wayland-client", - "wayland-protocols", - "wayland-protocols-wlr", -] - [[package]] name = "x11-dl" version = "2.21.0" @@ -5367,85 +4926,6 @@ dependencies = [ "syn", ] -[[package]] -name = "zeroize" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" - -[[package]] -name = "zip" -version = "8.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d04a6b5381502aa6087c94c669499eb1602eb9c5e8198e534de571f7154809b" -dependencies = [ - "aes", - "bzip2", - "constant_time_eq", - "crc32fast", - "deflate64", - "flate2", - "getrandom 0.4.3", - "hmac", - "indexmap", - "lzma-rust2", - "memchr", - "pbkdf2", - "ppmd-rust", - "sha1", - "time", - "typed-path", - "zeroize", - "zopfli", - "zstd", -] - -[[package]] -name = "zlib-rs" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b142a20ec14a91d5bc708c1dc21b080c550113d8aa77afa29635673a65dd02c5" - -[[package]] -name = "zopfli" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" -dependencies = [ - "bumpalo", - "crc32fast", - "log", - "simd-adler32", -] - -[[package]] -name = "zstd" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "7.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" -dependencies = [ - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "2.0.16+zstd.1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" -dependencies = [ - "cc", - "pkg-config", -] - [[package]] name = "zune-core" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index 96d9fb8..c4f9f6f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,10 +6,8 @@ license = "AGPL-3.0-or-later" [dependencies] dirs = "6.0.0" -kra-image-integration = "0.1.0" rfd = "0.17.2" -size = "0.5.0" -zip = "8.6.0" +jpegxl-rs = "0.14.0" [dependencies.iced] version = "0.14.0" @@ -18,12 +16,3 @@ features = [ "image" ] [dependencies.image] version = "0.25.10" features = [ "avif-native" ] - -[dependencies.jxl-oxide] -version = "0.12.6" -# I really wish the image crate was named better -features = [ "image" ] - -[dependencies.arboard] -version = "3.6.1" -features = [ "wayland-data-control" ] diff --git a/README.md b/README.md deleted file mode 100644 index 545d7ef..0000000 --- a/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# imagey - -image viewer and maybe editor; inspired by mpv's simplicity - -name is subject to change, suggestions welcome :) - -## usage - -```sh -# try it out! -nix run git+https://git.federated.nexus/electria/imagey - -# install it imperatively -nix profile install git+https://git.federated.nexus/electria/imagey -``` - -keybinds are shown on startup if you didn't start it with an image - -## known issues - -### JXL - -1. encoding is not implemented (jxl-oxide is decoding-only) -2. oreintation is incorrect in some cases - - if the image has metadata oreintation AND - - it's a jxl without the right extension OR - it's a jpeg with the jxl extension - - this is due to the JXL decoder automatically rotating the image, - while the JPEG decoder (for instance) requires the extra step. - - my workaround is to check the path of the input file, - not changing the oreintation if it has the jxl extension; - causing these caveats for cases where the extension is wrong. - -### clipboard - -1. large images (eg photos) don't seem to copy on linux/wayland, - (despite set_image not returning any error) diff --git a/flake.nix b/flake.nix index 5a57299..ddb2efc 100644 --- a/flake.nix +++ b/flake.nix @@ -19,25 +19,19 @@ cargoToml = fromTOML (builtins.readFile ./Cargo.toml); name = cargoToml.package.name; - dlDeps = - with pkgs; - [ - # needed for both x11 and wayland - libxkbcommon - libGL + dlDeps = with pkgs; [ + # needed for both x11 and wayland + libxkbcommon + libGL + vulkan-loader - libx11 - libxcursor - libxi - libxcb - ] - ++ lib.optionals stdenv.hostPlatform.isLinux [ - # libdbus, for rfd - dbus.lib + wayland - vulkan-loader - wayland - ]; + libx11 + libxcursor + libxi + libxcb + ]; commonArgs = { # all that's needed for artifacts and checks @@ -45,12 +39,15 @@ pkg-config ]; buildInputs = with pkgs; [ + libjxl dav1d ]; src = ./.; }; + LD_LIBRARY_PATH = lib.makeLibraryPath dlDeps; + # Build *just* the cargo dependencies, # to reuse them for build and test derivations. cargoArtifacts = craneLib.buildDepsOnly commonArgs; @@ -74,7 +71,6 @@ "image/tiff" "image/bmp" "image/vnd.microsoft.icon" # .ico - "application/x-krita" ]; }; in @@ -83,13 +79,19 @@ // { inherit cargoArtifacts; - nativeBuildInputs = commonArgs.nativeBuildInputs ++ [ - pkgs.autoPatchelfHook - ]; + nativeBuildInputs = + commonArgs.nativeBuildInputs + ++ [ + pkgs.autoPatchelfHook + ] + ++ lib.optional pkgs.stdenv.hostPlatform.isLinux pkgs.makeBinaryWrapper; - buildInputs = commonArgs.buildInputs ++ [ - pkgs.libgcc - ]; + buildInputs = + commonArgs.buildInputs + ++ [ + pkgs.libgcc + ] + ++ lib.optional pkgs.stdenv.hostPlatform.isLinux pkgs.zenity; runtimeDependencies = dlDeps; @@ -98,6 +100,10 @@ postFixup = '' mkdir -p "$out/share/applications" ln -s "${desktopItem}"/share/applications/* "$out/share/applications/" + '' + + lib.optionalString pkgs.stdenv.hostPlatform.isLinux /* sh */ '' + wrapProgram $out/bin/${name} --inherit-argv0 \ + --prefix PATH : ${lib.makeBinPath [ pkgs.zenity ]} ''; } ); @@ -117,7 +123,7 @@ }; devShells.default = craneLib.devShell { - LD_LIBRARY_PATH = lib.makeLibraryPath dlDeps; + inherit LD_LIBRARY_PATH; inputsFrom = [ crate ]; packages = [ pkgs.rust-analyzer ]; diff --git a/src/main.rs b/src/main.rs index c676e44..3d8618d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,59 +1,49 @@ -use std::{ - borrow::Cow, - env, - ffi::OsStr, - os::unix::fs::MetadataExt, - path::{Path, PathBuf}, -}; +use std::{env, path::Path}; -use arboard::Clipboard; use iced::{ - Alignment, Color, Element, Length, Renderer, Subscription, Task, Theme, event, + Alignment, Color, Element, Length, Renderer, Subscription, Task, Theme, color, event, keyboard::{self, Key, key}, theme, widget, window, }; use image::{EncodableLayout, RgbaImage, imageops}; -use size::Size; mod utils; #[derive(Clone, Debug)] enum Message { - ImagePicked(Result), ImageDisplayReady(Result), - SavePathPicked(Result), Event(iced::Event), } -#[derive(Default)] +#[derive(Clone, Debug, Default)] struct State { image: Option, image_display: Option, - image_filter: widget::image::FilterMethod, error: Option, - info: Option, - infobar_shown: bool, - - clipboard: Option, } impl State { fn new() -> (Self, Task) { - let mut state = Self::default(); + let mut state = State::default(); - if let Some(path) = env::args().nth(1) { - match state.load_image(path) { - Err(e) => state.error = Some(e), - Ok(task) => return (state, task), - } - } + state.error = env::args().nth(1).and_then(|path| { + utils::load_image(path) + .map(|image| state.image = Some(image)) + .err() + }); + let allocate_image = state.allocate_image(); - (state, Task::none()) + (state, allocate_image) } fn view(&self) -> Element<'_, Message, Theme, Renderer> { - let main = self.image_display.as_ref().map_or_else( - || { + widget::column([ + if let Some(allocation) = self.image_display.as_ref() { + widget::image::viewer(allocation.handle().clone()) + .width(Length::Fill) + .height(Length::Fill) + .into() + } else { widget::container(widget::text(include_str!("usage.txt"))) .height(Length::Fill) .width(Length::Fill) @@ -61,59 +51,19 @@ impl State { .align_y(Alignment::Center) .into() }, - |allocation| { - widget::image::viewer(allocation.handle().clone()) - .filter_method(self.image_filter) - .max_scale(50.) - .min_scale(1.) - .width(Length::Fill) - .height(Length::Fill) - .into() + if let Some(error) = self.error.as_ref() { + widget::text(error).style(widget::text::danger).into() + } else { + widget::space().into() }, - ); - - let mut bar = Vec::new(); - if let Some(error) = self.error.as_ref() { - bar.push( - widget::container(widget::text(error).style(widget::text::danger)) - .align_x(Alignment::Start) - .width(Length::Fill) - .into(), - ); - } - if self.infobar_shown && self.info.is_some() { - bar.push( - widget::container(widget::text(self.title())) - .align_x(Alignment::End) - .width(Length::Fill) - .into(), - ); - } - - widget::column([main, widget::row(bar).into()]).into() + ]) + .into() } fn update(&mut self, message: Message) -> Task { match message { - Message::ImagePicked(result) => match result.and_then(|path| self.load_image(path)) { - Err(e) => self.error = Some(e), - Ok(task) => { - self.error = None; - return task; - } - }, Message::ImageDisplayReady(result) => { self.image_display = Some(result.unwrap()); } - Message::SavePathPicked(result) => { - self.error = result - .and_then(|path| { - self.image.as_ref().map_or_else( - || Err("no image to save".into()), - |image| image.save(path).map_err(|e| e.to_string()), - ) - }) - .err(); - } Message::Event(iced::Event::Keyboard(keyboard::Event::KeyPressed { key, @@ -124,93 +74,46 @@ impl State { Key::Named(key::Named::Tab) => { if modifiers.shift() { return widget::operation::focus_previous(); + } else { + return widget::operation::focus_next(); } - return widget::operation::focus_next(); } - Key::Character("o") => { - return Task::perform(utils::pick_image(), Message::ImagePicked); - } + Key::Character("o") => match self.pick_and_load_image() { + Ok(task) => { + self.error = None; + return task; + } + Err(e) => self.error = Some(e), + }, - Key::Character("r") => match self.image.as_ref() { - None => self.error = Some("no image to rotate".into()), - Some(image) => { - self.error = None; - self.image = Some(imageops::rotate90(image)); - return self.allocate_image(); - } - }, - Key::Character("i") => match self.image.as_mut() { - None => self.error = Some("no image to invert".into()), - Some(image) => { - self.error = None; - imageops::invert(image); - return self.allocate_image(); - } - }, - Key::Character("f") => { - self.image_filter = match self.image_filter { - widget::image::FilterMethod::Linear => widget::image::FilterMethod::Nearest, - widget::image::FilterMethod::Nearest => widget::image::FilterMethod::Linear, - } + Key::Character("r") => { + match self.image.as_ref() { + None => self.error = Some("no image to rotate".into()), + Some(image) => { + self.error = None; + self.image = Some(imageops::rotate90(image)); + return self.allocate_image(); + } + }; } - Key::Character("b") => { - self.infobar_shown = !self.infobar_shown; + Key::Character("i") => { + match self.image.as_mut() { + None => self.error = Some("no image to invert".into()), + Some(image) => { + self.error = None; + imageops::invert(image); + return self.allocate_image(); + } + }; } Key::Character("s") => { - if self.image.is_some() { - self.error = None; - return Task::perform(utils::pick_save_path(), Message::SavePathPicked); - } - self.error = Some("no image to save".into()); + self.save_image(); } Key::Character("q") => return window::latest().and_then(window::close), - Key::Character("y") | Key::Character("c") => { - if let Some(image) = self.image.as_ref() { - match match self.clipboard.take() { - Some(c) => Ok(c), - None => Clipboard::new(), - } { - Ok(mut clipboard) => { - self.error = clipboard - .set_image(utils::arboard_from_rgbaimage(image)) - .map_err(|e| e.to_string()) - .err(); - self.clipboard = Some(clipboard); - } - Err(e) => self.error = Some(e.to_string()), - } - } else { - self.error = Some("no image to yank".into()); - } - } - Key::Character("p") | Key::Character("v") => { - match match self.clipboard.take() { - Some(c) => Ok(c), - None => Clipboard::new(), - } { - Ok(mut clipboard) => { - let result = clipboard.get_image().map(utils::rgbaimage_from_arboard); - - self.error = result.as_ref().map_err(|e| e.to_string()).err(); - self.clipboard = Some(clipboard); - - if let Ok(image) = result { - self.info = Some(format!("{}x{}", image.width(), image.height(),)); - if image.dimensions() < (100, 100) { - self.image_filter = widget::image::FilterMethod::Nearest; - } - self.image = Some(image); - return self.allocate_image(); - } - } - Err(e) => self.error = Some(e.to_string()), - } - } - // ignore unused keys _ => {} }, @@ -233,29 +136,18 @@ impl State { } fn load_image(&mut self, path: impl AsRef) -> Result, String> { - utils::load_image(&path).map(|image| { - self.info = Some(format!( - "{} {} {}x{}", - path.as_ref() - .file_name() - .map_or(Cow::Borrowed("[no file]"), OsStr::to_string_lossy), - path.as_ref().metadata().map_or_else( - |e| { - eprintln!("failed to read metadata: {e}"); - Cow::Borrowed("[no size]") - }, - |m| Cow::Owned(Size::from_bytes(m.size()).to_string()) - ), - image.width(), - image.height(), - )); - if image.dimensions() < (100, 100) { - self.image_filter = widget::image::FilterMethod::Nearest; - } + utils::load_image(path).map(|image| { self.image = Some(image); self.allocate_image() }) } + fn pick_and_load_image(&mut self) -> Result, String> { + utils::pick_image().and_then(|path| self.load_image(path)) + } + + fn save_image(&mut self) { + self.error = utils::save_image(self.image.as_ref()).err(); + } fn allocate_image(&self) -> Task { let Some(image) = self.image.as_ref() else { @@ -266,18 +158,6 @@ impl State { widget::image::allocate(widget::image::Handle::from_rgba( image.width(), image.height(), - // SAFETY: this is a sort of race condition; - // will cause panics with large enough images, - // when they are edited consecutively. - // - // this can be reproduced by holding down 'i' with a 10k by 10k px image, - // which will invert the colors rapidly and eventually crash the app. - // - // however, that is not such a problem in this case, - // and I don't know a different way of doing this that doesn't copy - // (performance loss, which is can be pretty big) - // or likely cause flickering instead in such cases - // (like using Handle over Allocation to immediately drop the last one) unsafe { std::mem::transmute::<&[u8], &'static [u8]>(image.as_bytes()) }, )) .map(Message::ImageDisplayReady) @@ -287,31 +167,26 @@ impl State { event::listen().map(Message::Event) } fn title(&self) -> String { - self.info.as_ref().map_or_else( - || "imagey".into(), - |info| { - format!( - "imagey {info} {}", - utils::string_from_filter_type(self.image_filter), - ) - }, - ) + match self.image.as_ref() { + Some(image) => format!("imagey {}x{}", image.width(), image.height()), + None => "imagey".into(), + } } } fn main() -> Result<(), iced::Error> { - jxl_oxide::integration::register_image_decoding_hook(); - kra_image_integration::register(); - iced::application(State::new, State::update, State::view) .subscription(State::subscription) .title(State::title) .theme(Theme::custom( "custom", theme::Palette { - background: Color::BLACK, + background: color!(0x080808), text: Color::WHITE, - ..theme::Palette::DARK + primary: color!(0xff00ff), + success: color!(0x00ff00), + warning: color!(0x880000), + danger: color!(0xff0000), }, )) .run() diff --git a/src/usage.txt b/src/usage.txt index 9df1bc2..94cf3cf 100644 --- a/src/usage.txt +++ b/src/usage.txt @@ -1,10 +1,5 @@ -'o' to open a new image -'r' to rotate -'i' to invert colors -'f' to switch filter -'b' to toggle the bar -'s' to save the image -'q' to quit - -'y' or 'c' to yank -'p' or 'v' to put +"o" to open a new image +"r" to rotate +"i" to invert colors +"s" to save the image +"q" to quit diff --git a/src/utils.rs b/src/utils.rs index ae1dbb1..961a27d 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,84 +1,150 @@ use std::{ - borrow::Cow, ffi::OsStr, + fs, + io::Write, path::{Path, PathBuf}, }; -use iced::widget; -use image::{DynamicImage, ImageDecoder, ImageReader, ImageResult, RgbaImage}; -use rfd::AsyncFileDialog; +use image::{DynamicImage, ImageDecoder, ImageReader, RgbaImage}; +use jpegxl_rs::image::ToDynamic; +use rfd::FileDialog; pub fn load_image(path: impl AsRef) -> Result { - load_image_impl(path).map_err(|e| e.to_string()) -} -fn load_image_impl(path: impl AsRef) -> ImageResult { - let mut decoder = ImageReader::open(&path)? - .with_guessed_format()? - .into_decoder()?; + let path = path.as_ref(); - let oreintation = decoder.orientation()?; + let decoded_image = match path.extension().map(OsStr::to_string_lossy).as_deref() { + Some("jxl") => { + let data = match std::fs::read(path) { + Ok(d) => d, + Err(e) => { + return Err(format!( + "failed to read data from '{}': {e}", + path.display() + )); + } + }; - let mut decoded_image = DynamicImage::from_decoder(decoder)?; + match jpegxl_rs::decoder_builder() + .build() + .unwrap() + .decode_to_image(&data) + { + Ok(Some(decoded_image)) => decoded_image, + Ok(None) => { + return Err(format!( + "failed to convert jxl '{}' to DynamicImage", + path.display() + )); + } + Err(e) => { + return Err(format!("failed to decode jxl '{}': {e}", path.display())); + } + } + } - // the condition is a workaround to not rotate JXL images twice; - // since they are already rotated by the decoder - // (while jpegs for instance aren't) - if !path - .as_ref() - .extension() - .map(OsStr::to_string_lossy) - .is_some_and(|s| s == "jxl") - { - decoded_image.apply_orientation(oreintation); - } + _ => { + let reader = match ImageReader::open(path) { + Ok(r) => r, + Err(e) => { + return Err(format!( + "failed to create reader for '{}': {e}", + path.display() + )); + } + }; + + let mut decoder = match reader.into_decoder() { + Ok(d) => d, + Err(e) => { + return Err(format!( + "failed to create decoder for '{}': {e}", + path.display() + )); + } + }; + + let oreintation = match decoder.orientation() { + Ok(o) => o, + Err(e) => { + return Err(format!( + "failed to get oreintation of '{}': {e}", + path.display() + )); + } + }; + + let mut decoded_image = match DynamicImage::from_decoder(decoder) { + Ok(i) => i, + Err(e) => { + return Err(format!("failed to decode image '{}': {e}", path.display())); + } + }; + + decoded_image.apply_orientation(oreintation); + + decoded_image + } + }; Ok(decoded_image.into_rgba8()) } -pub async fn pick_image() -> Result { - let Some(filehandle) = AsyncFileDialog::new() +pub fn pick_image() -> Result { + let Some(path) = FileDialog::new() .add_filter( "image", &[ "png", "PNG", "jpg", "JPG", "jpeg", "JPEG", "avif", "jxl", "bmp", "exr", "ff", - "gif", "hdr", "ico", "pnm", "qoi", "tga", "tiff", "webp", "kra", + "gif", "hdr", "ico", "pnm", "qoi", "tga", "tiff", "webp", ], ) .pick_file() - .await else { return Err("no path to open provided".into()); }; - Ok(filehandle.path().to_owned()) + Ok(path) } -pub async fn pick_save_path() -> Result { - let Some(filehandle) = AsyncFileDialog::new().save_file().await else { +pub fn save_image(image: Option<&RgbaImage>) -> Result<(), String> { + let Some(image) = image else { + return Err("no image to save".into()); + }; + + let Some(path) = FileDialog::new().save_file() else { return Err("no path to save provided".into()); }; - Ok(filehandle.path().to_owned()) -} + let mut file = match fs::File::create(&path) { + Ok(f) => f, + Err(e) => return Err(format!("failed to create file '{}': {e}", path.display())), + }; -pub const fn string_from_filter_type(f: widget::image::FilterMethod) -> &'static str { - match f { - widget::image::FilterMethod::Linear => "bilinear", - widget::image::FilterMethod::Nearest => "nearest neighbor", - } -} + match path.extension().map(OsStr::to_string_lossy).as_deref() { + Some("jxl") => { + let mut encoder = jpegxl_rs::encoder_builder() + .speed(jpegxl_rs::encode::EncoderSpeed::Glacier) + .lossless(true) + .build() + .unwrap(); + let rgb_image = DynamicImage::from(image.to_owned()).into_rgb8(); + let jxl = + match encoder.encode::(&rgb_image, rgb_image.width(), rgb_image.height()) { + Ok(j) => j, + Err(e) => { + return Err(format!("failed to encode jxl '{}': {e}", path.display())); + } + }; -pub fn arboard_from_rgbaimage<'a>(image: &'a RgbaImage) -> arboard::ImageData<'a> { - arboard::ImageData { - width: image.width() as usize, - height: image.height() as usize, - bytes: Cow::Borrowed(image.as_ref()), - } -} -pub fn rgbaimage_from_arboard(image: arboard::ImageData) -> RgbaImage { - RgbaImage::from_raw( - image.width as u32, - image.height as u32, - image.bytes.into_owned(), - ) - .unwrap() + if let Err(e) = file.write(&jxl.data) { + return Err(format!("failed to write jxl to '{}': {e}", path.display())); + }; + } + _ => { + if let Err(e) = image.save(&path) { + return Err(format!("failed to save '{}': {e}", path.display())); + }; + } + }; + + Ok(()) }