mirror of
https://github.com/alexpasmantier/television.git
synced 2025-06-07 03:55:23 +00:00
Image preview (#363)
I initially didn't notice that an image previewer was already implemented but commented out—still, I wanted to finish mine! :) It works almost instantly on my side. I tested it in different terminals and only noticed some slowness in the RustRover-integrated terminal. So far, I’ve tested it with PNG, JPEG, ICO, GIF, and TIFF formats, and it works well. In theory, it should support all formats that the image crate can handle. I included them in the file list but commented out the ones I haven’t tested yet. To optimize memory usage, images are resized to a maximum of 128x128 before being cached. I’m not really sure what the best size is, since the image gets resized again when rendered to fit the preview window. Let me know if you have any feedback! 🚀    --------- Co-authored-by: Alexandre Pasmantier <47638216+alexpasmantier@users.noreply.github.com> Co-authored-by: alexpasmantier <alex.pasmant@gmail.com>
This commit is contained in:
parent
d47d6f7850
commit
e6c1a2a2a2
547
Cargo.lock
generated
547
Cargo.lock
generated
@ -26,6 +26,12 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aligned-vec"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "allocator-api2"
|
name = "allocator-api2"
|
||||||
version = "0.2.21"
|
version = "0.2.21"
|
||||||
@ -103,12 +109,58 @@ version = "1.0.95"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
|
checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arbitrary"
|
||||||
|
version = "1.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arg_enum_proc_macro"
|
||||||
|
version = "0.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arrayvec"
|
||||||
|
version = "0.7.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "av1-grain"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"arrayvec",
|
||||||
|
"log",
|
||||||
|
"nom",
|
||||||
|
"num-rational",
|
||||||
|
"v_frame",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "avif-serialize"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "98922d6a4cfbcb08820c69d8eeccc05bb1f29bfa06b4f5b1dbfe9a868bd7608e"
|
||||||
|
dependencies = [
|
||||||
|
"arrayvec",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
version = "0.3.74"
|
version = "0.3.74"
|
||||||
@ -186,6 +238,12 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bit_field"
|
||||||
|
version = "0.10.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
@ -201,6 +259,12 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitstream-io"
|
||||||
|
version = "2.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bstr"
|
name = "bstr"
|
||||||
version = "1.11.3"
|
version = "1.11.3"
|
||||||
@ -211,6 +275,12 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "built"
|
||||||
|
version = "0.7.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56ed6191a7e78c36abdb16ab65341eefd73d64d303fffccdbb00d51e4205967b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.17.0"
|
version = "3.17.0"
|
||||||
@ -223,6 +293,18 @@ version = "1.21.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3"
|
checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder-lite"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.9.0"
|
version = "1.9.0"
|
||||||
@ -262,9 +344,21 @@ version = "1.2.11"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e4730490333d58093109dc02c23174c3f4d490998c3fed3cc8e82d57afedb9cf"
|
checksum = "e4730490333d58093109dc02c23174c3f4d490998c3fed3cc8e82d57afedb9cf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"jobserver",
|
||||||
|
"libc",
|
||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-expr"
|
||||||
|
version = "0.15.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02"
|
||||||
|
dependencies = [
|
||||||
|
"smallvec",
|
||||||
|
"target-lexicon",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@ -357,6 +451,12 @@ dependencies = [
|
|||||||
"windows",
|
"windows",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "color_quant"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorchoice"
|
name = "colorchoice"
|
||||||
version = "1.0.3"
|
version = "1.0.3"
|
||||||
@ -622,12 +722,36 @@ version = "3.3.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f"
|
checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "exr"
|
||||||
|
version = "1.73.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0"
|
||||||
|
dependencies = [
|
||||||
|
"bit_field",
|
||||||
|
"half",
|
||||||
|
"lebe",
|
||||||
|
"miniz_oxide",
|
||||||
|
"rayon-core",
|
||||||
|
"smallvec",
|
||||||
|
"zune-inflate",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "2.3.0"
|
version = "2.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fdeflate"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
|
||||||
|
dependencies = [
|
||||||
|
"simd-adler32",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "filedescriptor"
|
name = "filedescriptor"
|
||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
@ -755,6 +879,16 @@ dependencies = [
|
|||||||
"windows-targets",
|
"windows-targets",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gif"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2"
|
||||||
|
dependencies = [
|
||||||
|
"color_quant",
|
||||||
|
"weezl",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gimli"
|
name = "gimli"
|
||||||
version = "0.31.1"
|
version = "0.31.1"
|
||||||
@ -854,6 +988,45 @@ dependencies = [
|
|||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "image"
|
||||||
|
version = "0.25.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
"byteorder-lite",
|
||||||
|
"color_quant",
|
||||||
|
"exr",
|
||||||
|
"gif",
|
||||||
|
"image-webp",
|
||||||
|
"num-traits",
|
||||||
|
"png",
|
||||||
|
"qoi",
|
||||||
|
"ravif",
|
||||||
|
"rayon",
|
||||||
|
"rgb",
|
||||||
|
"tiff",
|
||||||
|
"zune-core",
|
||||||
|
"zune-jpeg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "image-webp"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b77d01e822461baa8409e156015a1d91735549f0f2c17691bd2d996bef238f7f"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder-lite",
|
||||||
|
"quick-error",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "imgref"
|
||||||
|
version = "1.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.7.1"
|
version = "2.7.1"
|
||||||
@ -884,6 +1057,17 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "interpolate_name"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "is-terminal"
|
name = "is-terminal"
|
||||||
version = "0.4.15"
|
version = "0.4.15"
|
||||||
@ -910,6 +1094,15 @@ dependencies = [
|
|||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.13.0"
|
version = "0.13.0"
|
||||||
@ -925,6 +1118,21 @@ version = "1.0.14"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jobserver"
|
||||||
|
version = "0.1.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jpeg-decoder"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.77"
|
version = "0.3.77"
|
||||||
@ -941,12 +1149,28 @@ version = "1.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lebe"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.169"
|
version = "0.2.169"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libfuzzer-sys"
|
||||||
|
version = "0.4.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf78f52d400cf2d84a3a973a78a592b4adc535739e0a5597a0da6f0c357adc75"
|
||||||
|
dependencies = [
|
||||||
|
"arbitrary",
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libredox"
|
name = "libredox"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
@ -979,6 +1203,15 @@ version = "0.4.25"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
|
checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "loop9"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062"
|
||||||
|
dependencies = [
|
||||||
|
"imgref",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lru"
|
name = "lru"
|
||||||
version = "0.12.5"
|
version = "0.12.5"
|
||||||
@ -997,6 +1230,16 @@ dependencies = [
|
|||||||
"regex-automata 0.1.10",
|
"regex-automata 0.1.10",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "maybe-rayon"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"rayon",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.7.4"
|
version = "2.7.4"
|
||||||
@ -1016,6 +1259,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924"
|
checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"adler2",
|
"adler2",
|
||||||
|
"simd-adler32",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1030,6 +1274,12 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "new_debug_unreachable"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nom"
|
name = "nom"
|
||||||
version = "7.1.3"
|
version = "7.1.3"
|
||||||
@ -1040,6 +1290,12 @@ dependencies = [
|
|||||||
"minimal-lexical",
|
"minimal-lexical",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "noop_proc_macro"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-ansi-term"
|
name = "nu-ansi-term"
|
||||||
version = "0.46.0"
|
version = "0.46.0"
|
||||||
@ -1080,12 +1336,53 @@ dependencies = [
|
|||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-bigint"
|
||||||
|
version = "0.4.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
|
||||||
|
dependencies = [
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-conv"
|
name = "num-conv"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-derive"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-integer"
|
||||||
|
version = "0.1.46"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-rational"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
|
||||||
|
dependencies = [
|
||||||
|
"num-bigint",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.19"
|
version = "0.2.19"
|
||||||
@ -1258,12 +1555,34 @@ dependencies = [
|
|||||||
"plotters-backend",
|
"plotters-backend",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "png"
|
||||||
|
version = "0.17.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"crc32fast",
|
||||||
|
"fdeflate",
|
||||||
|
"flate2",
|
||||||
|
"miniz_oxide",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "powerfmt"
|
name = "powerfmt"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.93"
|
version = "1.0.93"
|
||||||
@ -1273,6 +1592,40 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "profiling"
|
||||||
|
version = "1.0.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d"
|
||||||
|
dependencies = [
|
||||||
|
"profiling-procmacros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "profiling-procmacros"
|
||||||
|
version = "1.0.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "qoi"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quick-error"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quick-xml"
|
name = "quick-xml"
|
||||||
version = "0.32.0"
|
version = "0.32.0"
|
||||||
@ -1291,6 +1644,36 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.2.15",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ratatui"
|
name = "ratatui"
|
||||||
version = "0.29.0"
|
version = "0.29.0"
|
||||||
@ -1313,6 +1696,56 @@ dependencies = [
|
|||||||
"unicode-width 0.2.0",
|
"unicode-width 0.2.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rav1e"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9"
|
||||||
|
dependencies = [
|
||||||
|
"arbitrary",
|
||||||
|
"arg_enum_proc_macro",
|
||||||
|
"arrayvec",
|
||||||
|
"av1-grain",
|
||||||
|
"bitstream-io",
|
||||||
|
"built",
|
||||||
|
"cfg-if",
|
||||||
|
"interpolate_name",
|
||||||
|
"itertools 0.12.1",
|
||||||
|
"libc",
|
||||||
|
"libfuzzer-sys",
|
||||||
|
"log",
|
||||||
|
"maybe-rayon",
|
||||||
|
"new_debug_unreachable",
|
||||||
|
"noop_proc_macro",
|
||||||
|
"num-derive",
|
||||||
|
"num-traits",
|
||||||
|
"once_cell",
|
||||||
|
"paste",
|
||||||
|
"profiling",
|
||||||
|
"rand",
|
||||||
|
"rand_chacha",
|
||||||
|
"simd_helpers",
|
||||||
|
"system-deps",
|
||||||
|
"thiserror 1.0.69",
|
||||||
|
"v_frame",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ravif"
|
||||||
|
version = "0.11.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2413fd96bd0ea5cdeeb37eaf446a22e6ed7b981d792828721e74ded1980a45c6"
|
||||||
|
dependencies = [
|
||||||
|
"avif-serialize",
|
||||||
|
"imgref",
|
||||||
|
"loop9",
|
||||||
|
"quick-error",
|
||||||
|
"rav1e",
|
||||||
|
"rayon",
|
||||||
|
"rgb",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rayon"
|
name = "rayon"
|
||||||
version = "1.10.0"
|
version = "1.10.0"
|
||||||
@ -1586,6 +2019,21 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simd-adler32"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simd_helpers"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "simdutf8"
|
name = "simdutf8"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
@ -1679,6 +2127,25 @@ dependencies = [
|
|||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "system-deps"
|
||||||
|
version = "6.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-expr",
|
||||||
|
"heck",
|
||||||
|
"pkg-config",
|
||||||
|
"toml",
|
||||||
|
"version-compare",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "target-lexicon"
|
||||||
|
version = "0.12.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "television"
|
name = "television"
|
||||||
version = "0.10.6"
|
version = "0.10.6"
|
||||||
@ -1696,6 +2163,7 @@ dependencies = [
|
|||||||
"gag",
|
"gag",
|
||||||
"human-panic",
|
"human-panic",
|
||||||
"ignore",
|
"ignore",
|
||||||
|
"image",
|
||||||
"nom",
|
"nom",
|
||||||
"nucleo",
|
"nucleo",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
@ -1818,6 +2286,17 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tiff"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e"
|
||||||
|
dependencies = [
|
||||||
|
"flate2",
|
||||||
|
"jpeg-decoder",
|
||||||
|
"weezl",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.3.37"
|
version = "0.3.37"
|
||||||
@ -2040,12 +2519,29 @@ dependencies = [
|
|||||||
"getrandom 0.2.15",
|
"getrandom 0.2.15",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "v_frame"
|
||||||
|
version = "0.3.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b"
|
||||||
|
dependencies = [
|
||||||
|
"aligned-vec",
|
||||||
|
"num-traits",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "valuable"
|
name = "valuable"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version-compare"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "walkdir"
|
name = "walkdir"
|
||||||
version = "2.5.0"
|
version = "2.5.0"
|
||||||
@ -2139,6 +2635,12 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "weezl"
|
||||||
|
version = "0.1.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
@ -2328,3 +2830,48 @@ name = "xterm-color"
|
|||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4de5f056fb9dc8b7908754867544e26145767187aaac5a98495e88ad7cb8a80f"
|
checksum = "4de5f056fb9dc8b7908754867544e26145767187aaac5a98495e88ad7cb8a80f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.7.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"zerocopy-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.7.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zune-core"
|
||||||
|
version = "0.4.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zune-inflate"
|
||||||
|
version = "0.2.54"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
|
||||||
|
dependencies = [
|
||||||
|
"simd-adler32",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zune-jpeg"
|
||||||
|
version = "0.4.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028"
|
||||||
|
dependencies = [
|
||||||
|
"zune-core",
|
||||||
|
]
|
||||||
|
@ -58,6 +58,8 @@ bat = { version = "0.25", default-features = false, features = ["regex-onig"] }
|
|||||||
gag = "1.0"
|
gag = "1.0"
|
||||||
nucleo = "0.5"
|
nucleo = "0.5"
|
||||||
toml = "0.8"
|
toml = "0.8"
|
||||||
|
image = "0.25"
|
||||||
|
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
winapi-util = "0.1.9"
|
winapi-util = "0.1.9"
|
||||||
|
@ -2,6 +2,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use crate::channels::entry::{Entry, PreviewType};
|
use crate::channels::entry::{Entry, PreviewType};
|
||||||
use devicons::FileIcon;
|
use devicons::FileIcon;
|
||||||
|
use ratatui::layout::Rect;
|
||||||
|
|
||||||
pub mod ansi;
|
pub mod ansi;
|
||||||
pub mod cache;
|
pub mod cache;
|
||||||
@ -9,6 +10,7 @@ pub mod previewers;
|
|||||||
|
|
||||||
// previewer types
|
// previewer types
|
||||||
use crate::utils::cache::RingSet;
|
use crate::utils::cache::RingSet;
|
||||||
|
use crate::utils::image::ImagePreviewWidget;
|
||||||
use crate::utils::syntax::HighlightedLines;
|
use crate::utils::syntax::HighlightedLines;
|
||||||
pub use previewers::basic::BasicPreviewer;
|
pub use previewers::basic::BasicPreviewer;
|
||||||
pub use previewers::basic::BasicPreviewerConfig;
|
pub use previewers::basic::BasicPreviewerConfig;
|
||||||
@ -30,6 +32,7 @@ pub enum PreviewContent {
|
|||||||
PlainText(Vec<String>),
|
PlainText(Vec<String>),
|
||||||
PlainTextWrapped(String),
|
PlainTextWrapped(String),
|
||||||
AnsiText(String),
|
AnsiText(String),
|
||||||
|
Image(ImagePreviewWidget),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PreviewContent {
|
impl PreviewContent {
|
||||||
@ -44,6 +47,9 @@ impl PreviewContent {
|
|||||||
PreviewContent::AnsiText(text) => {
|
PreviewContent::AnsiText(text) => {
|
||||||
text.lines().count().try_into().unwrap_or(u16::MAX)
|
text.lines().count().try_into().unwrap_or(u16::MAX)
|
||||||
}
|
}
|
||||||
|
PreviewContent::Image(image) => {
|
||||||
|
image.height().try_into().unwrap_or(u16::MAX)
|
||||||
|
}
|
||||||
_ => 0,
|
_ => 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -203,11 +209,15 @@ impl Previewer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dispatch_request(&mut self, entry: &Entry) -> Option<Arc<Preview>> {
|
fn dispatch_request(
|
||||||
|
&mut self,
|
||||||
|
entry: &Entry,
|
||||||
|
preview_window: Option<Rect>,
|
||||||
|
) -> Option<Arc<Preview>> {
|
||||||
match &entry.preview_type {
|
match &entry.preview_type {
|
||||||
PreviewType::Basic => Some(self.basic.preview(entry)),
|
PreviewType::Basic => Some(self.basic.preview(entry)),
|
||||||
PreviewType::EnvVar => Some(self.env_var.preview(entry)),
|
PreviewType::EnvVar => Some(self.env_var.preview(entry)),
|
||||||
PreviewType::Files => self.file.preview(entry),
|
PreviewType::Files => self.file.preview(entry, preview_window),
|
||||||
PreviewType::Command(cmd) => self.command.preview(entry, cmd),
|
PreviewType::Command(cmd) => self.command.preview(entry, cmd),
|
||||||
PreviewType::None => Some(Arc::new(Preview::default())),
|
PreviewType::None => Some(Arc::new(Preview::default())),
|
||||||
}
|
}
|
||||||
@ -217,17 +227,23 @@ impl Previewer {
|
|||||||
// faster, but since it's already running in the background and quite
|
// faster, but since it's already running in the background and quite
|
||||||
// fast for most standard file sizes, plus we're caching the previews,
|
// fast for most standard file sizes, plus we're caching the previews,
|
||||||
// I'm not sure the extra complexity is worth it.
|
// I'm not sure the extra complexity is worth it.
|
||||||
pub fn preview(&mut self, entry: &Entry) -> Option<Arc<Preview>> {
|
pub fn preview(
|
||||||
|
&mut self,
|
||||||
|
entry: &Entry,
|
||||||
|
preview_window: Option<Rect>,
|
||||||
|
) -> Option<Arc<Preview>> {
|
||||||
// if we haven't acknowledged the request yet, acknowledge it
|
// if we haven't acknowledged the request yet, acknowledge it
|
||||||
self.requests.push(entry.clone());
|
self.requests.push(entry.clone());
|
||||||
|
|
||||||
if let Some(preview) = self.dispatch_request(entry) {
|
if let Some(preview) = self.dispatch_request(entry, preview_window) {
|
||||||
return Some(preview);
|
return Some(preview);
|
||||||
}
|
}
|
||||||
|
|
||||||
// lookup request stack and return the most recent preview available
|
// lookup request stack and return the most recent preview available
|
||||||
for request in self.requests.back_to_front() {
|
for request in self.requests.back_to_front() {
|
||||||
if let Some(preview) = self.dispatch_request(&request) {
|
if let Some(preview) =
|
||||||
|
self.dispatch_request(&request, preview_window)
|
||||||
|
{
|
||||||
return Some(preview);
|
return Some(preview);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
use crate::utils::files::{read_into_lines_capped, ReadResult};
|
use crate::utils::files::{read_into_lines_capped, ReadResult};
|
||||||
use crate::utils::syntax::HighlightedLines;
|
use crate::utils::syntax::HighlightedLines;
|
||||||
|
use image::ImageReader;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
use ratatui::layout::Rect;
|
||||||
use rustc_hash::{FxBuildHasher, FxHashSet};
|
use rustc_hash::{FxBuildHasher, FxHashSet};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
@ -10,13 +12,13 @@ use std::sync::{
|
|||||||
atomic::{AtomicU8, Ordering},
|
atomic::{AtomicU8, Ordering},
|
||||||
Arc,
|
Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
use syntect::{highlighting::Theme, parsing::SyntaxSet};
|
use syntect::{highlighting::Theme, parsing::SyntaxSet};
|
||||||
use tracing::{debug, trace, warn};
|
use tracing::{debug, trace, warn};
|
||||||
|
|
||||||
use crate::channels::entry;
|
use crate::channels::entry;
|
||||||
use crate::preview::cache::PreviewCache;
|
use crate::preview::cache::PreviewCache;
|
||||||
use crate::preview::{previewers::meta, Preview, PreviewContent};
|
use crate::preview::{previewers::meta, Preview, PreviewContent};
|
||||||
|
use crate::utils::image::ImagePreviewWidget;
|
||||||
use crate::utils::{
|
use crate::utils::{
|
||||||
files::FileType,
|
files::FileType,
|
||||||
strings::preprocess_line,
|
strings::preprocess_line,
|
||||||
@ -78,20 +80,28 @@ impl FilePreviewer {
|
|||||||
self.cache.lock().get(&entry.name)
|
self.cache.lock().get(&entry.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn preview(&mut self, entry: &entry::Entry) -> Option<Arc<Preview>> {
|
pub fn preview(
|
||||||
|
&mut self,
|
||||||
|
entry: &entry::Entry,
|
||||||
|
preview_window: Option<Rect>,
|
||||||
|
) -> Option<Arc<Preview>> {
|
||||||
if let Some(preview) = self.cached(entry) {
|
if let Some(preview) = self.cached(entry) {
|
||||||
trace!("Preview cache hit for {:?}", entry.name);
|
trace!("Preview cache hit for {:?}", entry.name);
|
||||||
if preview.partial_offset.is_some() {
|
if preview.partial_offset.is_some() {
|
||||||
// preview is partial, spawn a task to compute the next chunk
|
// preview is partial, spawn a task to compute the next chunk
|
||||||
// and return the partial preview
|
// and return the partial preview
|
||||||
debug!("Spawning partial preview task for {:?}", entry.name);
|
debug!("Spawning partial preview task for {:?}", entry.name);
|
||||||
self.handle_preview_request(entry, Some(preview.clone()));
|
self.handle_preview_request(
|
||||||
|
entry,
|
||||||
|
Some(preview.clone()),
|
||||||
|
preview_window,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Some(preview)
|
Some(preview)
|
||||||
} else {
|
} else {
|
||||||
// preview is not in cache, spawn a task to compute the preview
|
// preview is not in cache, spawn a task to compute the preview
|
||||||
trace!("Preview cache miss for {:?}", entry.name);
|
trace!("Preview cache miss for {:?}", entry.name);
|
||||||
self.handle_preview_request(entry, None);
|
self.handle_preview_request(entry, None, preview_window);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -100,6 +110,7 @@ impl FilePreviewer {
|
|||||||
&mut self,
|
&mut self,
|
||||||
entry: &entry::Entry,
|
entry: &entry::Entry,
|
||||||
partial_preview: Option<Arc<Preview>>,
|
partial_preview: Option<Arc<Preview>>,
|
||||||
|
preview_window: Option<Rect>,
|
||||||
) {
|
) {
|
||||||
if self.in_flight_previews.lock().contains(&entry.name) {
|
if self.in_flight_previews.lock().contains(&entry.name) {
|
||||||
trace!("Preview already in flight for {:?}", entry.name);
|
trace!("Preview already in flight for {:?}", entry.name);
|
||||||
@ -126,6 +137,7 @@ impl FilePreviewer {
|
|||||||
&syntax_theme,
|
&syntax_theme,
|
||||||
&concurrent_tasks,
|
&concurrent_tasks,
|
||||||
&in_flight_previews,
|
&in_flight_previews,
|
||||||
|
preview_window,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -141,6 +153,7 @@ impl FilePreviewer {
|
|||||||
/// This ends up being the max size of partial previews.
|
/// This ends up being the max size of partial previews.
|
||||||
const PARTIAL_BUFREAD_SIZE: usize = 5 * 1024 * 1024;
|
const PARTIAL_BUFREAD_SIZE: usize = 5 * 1024 * 1024;
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn try_preview(
|
pub fn try_preview(
|
||||||
entry: &entry::Entry,
|
entry: &entry::Entry,
|
||||||
partial_preview: Option<Arc<Preview>>,
|
partial_preview: Option<Arc<Preview>>,
|
||||||
@ -149,6 +162,7 @@ pub fn try_preview(
|
|||||||
syntax_theme: &Arc<Theme>,
|
syntax_theme: &Arc<Theme>,
|
||||||
concurrent_tasks: &Arc<AtomicU8>,
|
concurrent_tasks: &Arc<AtomicU8>,
|
||||||
in_flight_previews: &Arc<Mutex<FxHashSet<String>>>,
|
in_flight_previews: &Arc<Mutex<FxHashSet<String>>>,
|
||||||
|
preview_window: Option<Rect>,
|
||||||
) {
|
) {
|
||||||
debug!("Computing preview for {:?}", entry.name);
|
debug!("Computing preview for {:?}", entry.name);
|
||||||
let path = PathBuf::from(&entry.name);
|
let path = PathBuf::from(&entry.name);
|
||||||
@ -236,8 +250,66 @@ pub fn try_preview(
|
|||||||
cache.lock().insert(entry.name.clone(), &p);
|
cache.lock().insert(entry.name.clone(), &p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if matches!(FileType::from(&path), FileType::Image) {
|
||||||
|
cache.lock().insert(
|
||||||
|
entry.name.clone(),
|
||||||
|
&meta::loading(&format!("Loading {}", entry.name)),
|
||||||
|
);
|
||||||
|
|
||||||
|
debug!("File {:?} is an image", entry.name);
|
||||||
|
let option_image = match ImageReader::open(path) {
|
||||||
|
Ok(reader) => match reader.with_guessed_format() {
|
||||||
|
Ok(reader) => match reader.decode() {
|
||||||
|
Ok(image) => Some(image),
|
||||||
|
Err(e) => {
|
||||||
|
warn!(
|
||||||
|
"Error impossible to decode {}: {:?}",
|
||||||
|
entry.name, e
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
warn!(
|
||||||
|
"Error impossible to guess the format of {}: {:?}",
|
||||||
|
entry.name, e
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Error opening image {}: {:?}", entry.name, e);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if let Some(image) = option_image {
|
||||||
|
let preview_window_dimension = preview_window.map(|rect| {
|
||||||
|
(
|
||||||
|
u32::from(rect.width.saturating_sub(2)),
|
||||||
|
u32::from(rect.height.saturating_sub(2)),
|
||||||
|
) // - 2 for the margin
|
||||||
|
});
|
||||||
|
let image_preview_widget = ImagePreviewWidget::from_dynamic_image(
|
||||||
|
image,
|
||||||
|
preview_window_dimension,
|
||||||
|
);
|
||||||
|
let total_lines =
|
||||||
|
image_preview_widget.height().try_into().unwrap_or(u16::MAX);
|
||||||
|
let content = PreviewContent::Image(image_preview_widget);
|
||||||
|
let preview = Arc::new(Preview::new(
|
||||||
|
entry.name.clone(),
|
||||||
|
content,
|
||||||
|
entry.icon,
|
||||||
|
None,
|
||||||
|
total_lines,
|
||||||
|
));
|
||||||
|
cache.lock().insert(entry.name.clone(), &preview);
|
||||||
|
} else {
|
||||||
|
let p = meta::not_supported(&entry.name);
|
||||||
|
cache.lock().insert(entry.name.clone(), &p);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
debug!("File isn't text-based: {:?}", entry.name);
|
debug!("File format isn't supported for preview: {:?}", entry.name);
|
||||||
let preview = meta::not_supported(&entry.name);
|
let preview = meta::not_supported(&entry.name);
|
||||||
cache.lock().insert(entry.name.clone(), &preview);
|
cache.lock().insert(entry.name.clone(), &preview);
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,17 @@ use crate::preview::{
|
|||||||
PREVIEW_NOT_SUPPORTED_MSG, TIMEOUT_MSG,
|
PREVIEW_NOT_SUPPORTED_MSG, TIMEOUT_MSG,
|
||||||
};
|
};
|
||||||
use crate::screen::colors::{Colorscheme, PreviewColorscheme};
|
use crate::screen::colors::{Colorscheme, PreviewColorscheme};
|
||||||
|
use crate::utils::image::ImagePreviewWidget;
|
||||||
use crate::utils::strings::{
|
use crate::utils::strings::{
|
||||||
replace_non_printable, shrink_with_ellipsis, ReplaceNonPrintableConfig,
|
replace_non_printable, shrink_with_ellipsis, ReplaceNonPrintableConfig,
|
||||||
EMPTY_STRING,
|
EMPTY_STRING,
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use devicons::FileIcon;
|
use devicons::FileIcon;
|
||||||
use ratatui::widgets::{Block, BorderType, Borders, Padding, Paragraph, Wrap};
|
use ratatui::buffer::Buffer;
|
||||||
|
use ratatui::widgets::{
|
||||||
|
Block, BorderType, Borders, Padding, Paragraph, Widget, Wrap,
|
||||||
|
};
|
||||||
use ratatui::Frame;
|
use ratatui::Frame;
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
layout::{Alignment, Rect},
|
layout::{Alignment, Rect},
|
||||||
@ -22,6 +26,22 @@ use std::str::FromStr;
|
|||||||
const FILL_CHAR_SLANTED: char = '╱';
|
const FILL_CHAR_SLANTED: char = '╱';
|
||||||
const FILL_CHAR_EMPTY: char = ' ';
|
const FILL_CHAR_EMPTY: char = ' ';
|
||||||
|
|
||||||
|
pub enum PreviewWidget<'a> {
|
||||||
|
Paragraph(Paragraph<'a>),
|
||||||
|
Image(ImagePreviewWidget),
|
||||||
|
}
|
||||||
|
impl Widget for PreviewWidget<'_> {
|
||||||
|
fn render(self, area: Rect, buf: &mut Buffer)
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
match self {
|
||||||
|
PreviewWidget::Paragraph(p) => p.render(area, buf),
|
||||||
|
PreviewWidget::Image(image) => image.render(area, buf),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn draw_preview_content_block(
|
pub fn draw_preview_content_block(
|
||||||
f: &mut Frame,
|
f: &mut Frame,
|
||||||
@ -38,9 +58,8 @@ pub fn draw_preview_content_block(
|
|||||||
&preview_state.preview.title,
|
&preview_state.preview.title,
|
||||||
use_nerd_font_icons,
|
use_nerd_font_icons,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// render the preview content
|
// render the preview content
|
||||||
let rp = build_preview_paragraph(
|
let rp = build_preview_widget(
|
||||||
inner,
|
inner,
|
||||||
&preview_state.preview.content,
|
&preview_state.preview.content,
|
||||||
preview_state.target_line,
|
preview_state.target_line,
|
||||||
@ -48,16 +67,17 @@ pub fn draw_preview_content_block(
|
|||||||
colorscheme,
|
colorscheme,
|
||||||
);
|
);
|
||||||
f.render_widget(rp, inner);
|
f.render_widget(rp, inner);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_preview_paragraph<'a>(
|
pub fn build_preview_widget<'a>(
|
||||||
inner: Rect,
|
inner: Rect,
|
||||||
preview_content: &'a PreviewContent,
|
preview_content: &'a PreviewContent,
|
||||||
target_line: Option<u16>,
|
target_line: Option<u16>,
|
||||||
preview_scroll: u16,
|
preview_scroll: u16,
|
||||||
colorscheme: &'a Colorscheme,
|
colorscheme: &'a Colorscheme,
|
||||||
) -> Paragraph<'a> {
|
) -> PreviewWidget<'a> {
|
||||||
let preview_block =
|
let preview_block =
|
||||||
Block::default().style(Style::default()).padding(Padding {
|
Block::default().style(Style::default()).padding(Padding {
|
||||||
top: 0,
|
top: 0,
|
||||||
@ -65,65 +85,77 @@ pub fn build_preview_paragraph<'a>(
|
|||||||
bottom: 0,
|
bottom: 0,
|
||||||
left: 1,
|
left: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
match preview_content {
|
match preview_content {
|
||||||
PreviewContent::AnsiText(text) => {
|
PreviewContent::AnsiText(text) => PreviewWidget::Paragraph(
|
||||||
build_ansi_text_paragraph(text, preview_block, preview_scroll)
|
build_ansi_text_paragraph(text, preview_block, preview_scroll),
|
||||||
}
|
|
||||||
PreviewContent::PlainText(content) => build_plain_text_paragraph(
|
|
||||||
content,
|
|
||||||
preview_block,
|
|
||||||
target_line,
|
|
||||||
preview_scroll,
|
|
||||||
colorscheme.preview,
|
|
||||||
),
|
),
|
||||||
PreviewContent::PlainTextWrapped(content) => {
|
PreviewContent::PlainText(content) => {
|
||||||
|
PreviewWidget::Paragraph(build_plain_text_paragraph(
|
||||||
|
content,
|
||||||
|
preview_block,
|
||||||
|
target_line,
|
||||||
|
preview_scroll,
|
||||||
|
colorscheme.preview,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
PreviewContent::PlainTextWrapped(content) => PreviewWidget::Paragraph(
|
||||||
build_plain_text_wrapped_paragraph(
|
build_plain_text_wrapped_paragraph(
|
||||||
content,
|
content,
|
||||||
preview_block,
|
preview_block,
|
||||||
colorscheme.preview,
|
colorscheme.preview,
|
||||||
)
|
)
|
||||||
.scroll((preview_scroll, 0))
|
.scroll((preview_scroll, 0)),
|
||||||
}
|
),
|
||||||
PreviewContent::SyntectHighlightedText(highlighted_lines) => {
|
PreviewContent::SyntectHighlightedText(highlighted_lines) => {
|
||||||
build_syntect_highlighted_paragraph(
|
PreviewWidget::Paragraph(build_syntect_highlighted_paragraph(
|
||||||
&highlighted_lines.lines,
|
&highlighted_lines.lines,
|
||||||
preview_block,
|
preview_block,
|
||||||
target_line,
|
target_line,
|
||||||
preview_scroll,
|
preview_scroll,
|
||||||
colorscheme.preview,
|
colorscheme.preview,
|
||||||
inner.height,
|
inner.height,
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
|
PreviewContent::Image(image) => PreviewWidget::Image(image.clone()),
|
||||||
|
|
||||||
// meta
|
// meta
|
||||||
PreviewContent::Loading => {
|
PreviewContent::Loading => PreviewWidget::Paragraph(
|
||||||
build_meta_preview_paragraph(inner, LOADING_MSG, FILL_CHAR_EMPTY)
|
build_meta_preview_paragraph(inner, LOADING_MSG, FILL_CHAR_EMPTY)
|
||||||
.block(preview_block)
|
.block(preview_block)
|
||||||
.alignment(Alignment::Left)
|
.alignment(Alignment::Left)
|
||||||
.style(Style::default().add_modifier(Modifier::ITALIC))
|
.style(Style::default().add_modifier(Modifier::ITALIC)),
|
||||||
}
|
),
|
||||||
PreviewContent::NotSupported => build_meta_preview_paragraph(
|
PreviewContent::NotSupported => PreviewWidget::Paragraph(
|
||||||
inner,
|
build_meta_preview_paragraph(
|
||||||
PREVIEW_NOT_SUPPORTED_MSG,
|
inner,
|
||||||
FILL_CHAR_EMPTY,
|
PREVIEW_NOT_SUPPORTED_MSG,
|
||||||
)
|
FILL_CHAR_EMPTY,
|
||||||
.block(preview_block)
|
)
|
||||||
.alignment(Alignment::Left)
|
.block(preview_block)
|
||||||
.style(Style::default().add_modifier(Modifier::ITALIC)),
|
.alignment(Alignment::Left)
|
||||||
PreviewContent::FileTooLarge => build_meta_preview_paragraph(
|
.style(Style::default().add_modifier(Modifier::ITALIC)),
|
||||||
inner,
|
),
|
||||||
FILE_TOO_LARGE_MSG,
|
PreviewContent::FileTooLarge => PreviewWidget::Paragraph(
|
||||||
FILL_CHAR_EMPTY,
|
build_meta_preview_paragraph(
|
||||||
)
|
inner,
|
||||||
.block(preview_block)
|
FILE_TOO_LARGE_MSG,
|
||||||
.alignment(Alignment::Left)
|
FILL_CHAR_EMPTY,
|
||||||
.style(Style::default().add_modifier(Modifier::ITALIC)),
|
)
|
||||||
PreviewContent::Timeout => {
|
.block(preview_block)
|
||||||
|
.alignment(Alignment::Left)
|
||||||
|
.style(Style::default().add_modifier(Modifier::ITALIC)),
|
||||||
|
),
|
||||||
|
|
||||||
|
PreviewContent::Timeout => PreviewWidget::Paragraph(
|
||||||
build_meta_preview_paragraph(inner, TIMEOUT_MSG, FILL_CHAR_EMPTY)
|
build_meta_preview_paragraph(inner, TIMEOUT_MSG, FILL_CHAR_EMPTY)
|
||||||
|
.block(preview_block)
|
||||||
|
.alignment(Alignment::Left)
|
||||||
|
.style(Style::default().add_modifier(Modifier::ITALIC)),
|
||||||
|
),
|
||||||
|
PreviewContent::Empty => {
|
||||||
|
PreviewWidget::Paragraph(Paragraph::new(Text::raw(EMPTY_STRING)))
|
||||||
}
|
}
|
||||||
.block(preview_block)
|
|
||||||
.alignment(Alignment::Left)
|
|
||||||
.style(Style::default().add_modifier(Modifier::ITALIC)),
|
|
||||||
PreviewContent::Empty => Paragraph::new(Text::raw(EMPTY_STRING)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,7 +304,10 @@ impl Television {
|
|||||||
&& !matches!(selected_entry.preview_type, PreviewType::None)
|
&& !matches!(selected_entry.preview_type, PreviewType::None)
|
||||||
{
|
{
|
||||||
// preview content
|
// preview content
|
||||||
if let Some(preview) = self.previewer.preview(selected_entry) {
|
if let Some(preview) = self
|
||||||
|
.previewer
|
||||||
|
.preview(selected_entry, self.ui_state.layout.preview_window)
|
||||||
|
{
|
||||||
// only update if the preview content has changed
|
// only update if the preview content has changed
|
||||||
if self.preview_state.preview.title != preview.title {
|
if self.preview_state.preview.title != preview.title {
|
||||||
self.preview_state.update(
|
self.preview_state.update(
|
||||||
|
@ -106,6 +106,7 @@ pub fn get_file_size(path: &Path) -> Option<u64> {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum FileType {
|
pub enum FileType {
|
||||||
Text,
|
Text,
|
||||||
|
Image,
|
||||||
Other,
|
Other,
|
||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
@ -117,6 +118,9 @@ where
|
|||||||
fn from(path: P) -> Self {
|
fn from(path: P) -> Self {
|
||||||
debug!("Getting file type for {:?}", path);
|
debug!("Getting file type for {:?}", path);
|
||||||
let p = path.as_ref();
|
let p = path.as_ref();
|
||||||
|
if is_accepted_image_extension(p) {
|
||||||
|
return FileType::Image;
|
||||||
|
}
|
||||||
if is_known_text_extension(p) {
|
if is_known_text_extension(p) {
|
||||||
return FileType::Text;
|
return FileType::Text;
|
||||||
}
|
}
|
||||||
@ -487,3 +491,28 @@ pub fn get_known_text_file_extensions() -> &'static FxHashSet<&'static str> {
|
|||||||
.collect()
|
.collect()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_accepted_image_extension<P>(path: P) -> bool
|
||||||
|
where
|
||||||
|
P: AsRef<Path>,
|
||||||
|
{
|
||||||
|
path.as_ref()
|
||||||
|
.extension()
|
||||||
|
.and_then(|ext| ext.to_str())
|
||||||
|
.is_some_and(|ext| get_known_image_file_extensions().contains(ext))
|
||||||
|
}
|
||||||
|
pub static KNOWN_IMAGE_FILE_EXTENSIONS: OnceLock<FxHashSet<&'static str>> =
|
||||||
|
OnceLock::new();
|
||||||
|
pub fn get_known_image_file_extensions() -> &'static FxHashSet<&'static str> {
|
||||||
|
KNOWN_IMAGE_FILE_EXTENSIONS.get_or_init(|| {
|
||||||
|
[
|
||||||
|
// "avif", requires the avif-native feature, uses the libdav1d C library.
|
||||||
|
// dds, dosen't work for some reason
|
||||||
|
"bmp", "ff", "gif", "hdr", "ico", "jpeg", "jpg", "exr", "png",
|
||||||
|
"pnm", "qoi", "tga", "tif", "webp",
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
185
television/utils/image.rs
Normal file
185
television/utils/image.rs
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
use image::imageops::FilterType;
|
||||||
|
use image::{DynamicImage, Pixel, Rgba};
|
||||||
|
use ratatui::buffer::{Buffer, Cell};
|
||||||
|
use ratatui::layout::{Position, Rect};
|
||||||
|
use ratatui::prelude::Color;
|
||||||
|
use ratatui::widgets::Widget;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::hash::Hash;
|
||||||
|
|
||||||
|
static PIXEL_STRING: &str = "▀";
|
||||||
|
const FILTER_TYPE: FilterType = FilterType::Lanczos3;
|
||||||
|
|
||||||
|
// use to reduce the size of the image before storing it
|
||||||
|
const DEFAULT_CACHED_WIDTH: u32 = 50;
|
||||||
|
const DEFAULT_CACHED_HEIGHT: u32 = 100;
|
||||||
|
|
||||||
|
const GRAY: Rgba<u8> = Rgba([242, 242, 242, 255]);
|
||||||
|
const WHITE: Rgba<u8> = Rgba([255, 255, 255, 255]);
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Hash, PartialEq)]
|
||||||
|
pub struct ImagePreviewWidget {
|
||||||
|
cells: Vec<Vec<Cell>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for &ImagePreviewWidget {
|
||||||
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
|
let height = self.height();
|
||||||
|
let width = self.width();
|
||||||
|
// offset of the left top corner where the image is centered
|
||||||
|
let total_width = usize::from(area.width) + 2 * usize::from(area.x);
|
||||||
|
let x_offset = total_width.saturating_sub(width) / 2 + 1;
|
||||||
|
let total_height = usize::from(area.height) + 2 * usize::from(area.y);
|
||||||
|
let y_offset = total_height.saturating_sub(height) / 2;
|
||||||
|
|
||||||
|
let (area_border_up, area_border_down) =
|
||||||
|
(area.y, area.y + area.height);
|
||||||
|
let (area_border_left, area_border_right) =
|
||||||
|
(area.x, area.x + area.width);
|
||||||
|
for (y, row) in self.cells.iter().enumerate() {
|
||||||
|
let pos_y = u16::try_from(y_offset + y).unwrap_or(u16::MAX);
|
||||||
|
if pos_y >= area_border_up && pos_y < area_border_down {
|
||||||
|
for (x, cell) in row.iter().enumerate() {
|
||||||
|
let pos_x =
|
||||||
|
u16::try_from(x_offset + x).unwrap_or(u16::MAX);
|
||||||
|
if pos_x >= area_border_left && pos_x <= area_border_right
|
||||||
|
{
|
||||||
|
if let Some(buf_cell) =
|
||||||
|
buf.cell_mut(Position::new(pos_x, pos_y))
|
||||||
|
{
|
||||||
|
*buf_cell = cell.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ImagePreviewWidget {
|
||||||
|
pub fn new(cells: Vec<Vec<Cell>>) -> ImagePreviewWidget {
|
||||||
|
ImagePreviewWidget { cells }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn height(&self) -> usize {
|
||||||
|
self.cells.len()
|
||||||
|
}
|
||||||
|
pub fn width(&self) -> usize {
|
||||||
|
if self.height() > 0 {
|
||||||
|
self.cells[0].len()
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_dynamic_image(
|
||||||
|
dynamic_image: DynamicImage,
|
||||||
|
dimension: Option<(u32, u32)>,
|
||||||
|
) -> Self {
|
||||||
|
let (window_width, window_height) =
|
||||||
|
dimension.unwrap_or((DEFAULT_CACHED_WIDTH, DEFAULT_CACHED_HEIGHT));
|
||||||
|
let (max_width, max_height) = (window_width, window_height * 2 - 2); // -2 to have some space with the title
|
||||||
|
|
||||||
|
// first quick resize
|
||||||
|
let big_resized_image = if dynamic_image.width() > max_width * 4
|
||||||
|
|| dynamic_image.height() > max_height * 4
|
||||||
|
{
|
||||||
|
dynamic_image.resize(
|
||||||
|
max_width * 4,
|
||||||
|
max_height * 4,
|
||||||
|
FilterType::Nearest,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
dynamic_image
|
||||||
|
};
|
||||||
|
|
||||||
|
// this time resize with the filter
|
||||||
|
let resized_image = if big_resized_image.width() > max_width
|
||||||
|
|| big_resized_image.height() > max_height
|
||||||
|
{
|
||||||
|
big_resized_image.resize(max_width, max_height, FILTER_TYPE)
|
||||||
|
} else {
|
||||||
|
big_resized_image
|
||||||
|
};
|
||||||
|
|
||||||
|
let cells = Self::cells_from_dynamic_image(resized_image);
|
||||||
|
ImagePreviewWidget::new(cells)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cells_from_dynamic_image(image: DynamicImage) -> Vec<Vec<Cell>> {
|
||||||
|
let image_rgba = image.into_rgba8();
|
||||||
|
|
||||||
|
//creation of the grid of cell
|
||||||
|
image_rgba
|
||||||
|
// iter over pair of rows
|
||||||
|
.rows()
|
||||||
|
.step_by(2)
|
||||||
|
.zip(image_rgba.rows().skip(1).step_by(2))
|
||||||
|
.enumerate()
|
||||||
|
.map(|(double_row_y, (row_1, row_2))| {
|
||||||
|
// create rows of cells
|
||||||
|
row_1
|
||||||
|
.into_iter()
|
||||||
|
.zip(row_2)
|
||||||
|
.enumerate()
|
||||||
|
.map(|(x, (color_up, color_down))| {
|
||||||
|
let position = (x, double_row_y);
|
||||||
|
DoublePixel::new(*color_up, *color_down)
|
||||||
|
.add_grid_background(position)
|
||||||
|
.into_cell()
|
||||||
|
})
|
||||||
|
.collect::<Vec<Cell>>()
|
||||||
|
})
|
||||||
|
.collect::<Vec<Vec<Cell>>>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// util to convert Rgba into ratatui's Cell
|
||||||
|
struct DoublePixel {
|
||||||
|
color_up: Rgba<u8>,
|
||||||
|
color_down: Rgba<u8>,
|
||||||
|
}
|
||||||
|
impl DoublePixel {
|
||||||
|
pub fn new(color_up: Rgba<u8>, color_down: Rgba<u8>) -> Self {
|
||||||
|
Self {
|
||||||
|
color_up,
|
||||||
|
color_down,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_grid_background(mut self, position: (usize, usize)) -> Self {
|
||||||
|
let color_up = self.color_up.0;
|
||||||
|
let color_down = self.color_down.0;
|
||||||
|
self.color_up = Self::blend_with_background(color_up, position, 0);
|
||||||
|
self.color_down = Self::blend_with_background(color_down, position, 1);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn blend_with_background(
|
||||||
|
color: impl Into<Rgba<u8>>,
|
||||||
|
position: (usize, usize),
|
||||||
|
offset: usize,
|
||||||
|
) -> Rgba<u8> {
|
||||||
|
let color = color.into();
|
||||||
|
if color[3] == 255 {
|
||||||
|
color
|
||||||
|
} else {
|
||||||
|
let is_white = (position.0 + position.1 * 2 + offset) % 2 == 0;
|
||||||
|
let mut base = if is_white { WHITE } else { GRAY };
|
||||||
|
base.blend(&color);
|
||||||
|
base
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_cell(self) -> Cell {
|
||||||
|
let mut cell = Cell::new(PIXEL_STRING);
|
||||||
|
cell.set_bg(Self::convert_image_color_to_ratatui_color(
|
||||||
|
self.color_down,
|
||||||
|
))
|
||||||
|
.set_fg(Self::convert_image_color_to_ratatui_color(self.color_up));
|
||||||
|
cell
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_image_color_to_ratatui_color(color: Rgba<u8>) -> Color {
|
||||||
|
Color::Rgb(color[0], color[1], color[2])
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@ pub mod clipboard;
|
|||||||
pub mod command;
|
pub mod command;
|
||||||
pub mod files;
|
pub mod files;
|
||||||
pub mod hashmaps;
|
pub mod hashmaps;
|
||||||
|
pub mod image;
|
||||||
pub mod indices;
|
pub mod indices;
|
||||||
pub mod input;
|
pub mod input;
|
||||||
pub mod metadata;
|
pub mod metadata;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user