diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 2ded3d353a..26fc30833d 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -414,6 +414,12 @@ dependencies = [ "syn 2.0.52", ] +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + [[package]] name = "bitflags" version = "1.3.2" @@ -687,18 +693,22 @@ dependencies = [ "base64 0.22.0", "chrono", "ctrlc", + "dashmap 5.5.3", "deelevate", "delay_timer", "dirs 5.0.1", + "display-info", "dunce", "dyn-clone", + "fast_image_resize", "glob", "gunzip", + "image", "indexmap 2.2.5", "log", "nanoid", "once_cell", - "open 5.1.0", + "open 5.0.2", "parking_lot", "port_scanner", "reqwest", @@ -950,6 +960,12 @@ version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto-common" version = "0.1.6" @@ -1007,6 +1023,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "cursor-icon" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" + [[package]] name = "darling" version = "0.20.8" @@ -1052,6 +1074,19 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.3", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "data-encoding" version = "2.5.0" @@ -1083,7 +1118,7 @@ dependencies = [ "autocfg", "concat-idents", "cron_clock", - "dashmap", + "dashmap 4.0.2", "event-listener 2.5.3", "futures", "log", @@ -1219,6 +1254,36 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" +[[package]] +name = "display-info" +version = "0.4.9" +source = "git+https://github.com/greenhat616/display-info.git#093a0c2174edbeb1f5b275b9ae9061c6e9c91cac" +dependencies = [ + "anyhow", + "core-graphics 0.23.1", + "fxhash", + "log", + "smithay-client-toolkit", + "widestring 1.0.2", + "windows 0.54.0", + "xcb", +] + +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading 0.8.2", +] + +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + [[package]] name = "dtoa" version = "1.0.9" @@ -1377,6 +1442,33 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "exr" +version = "1.72.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" +dependencies = [ + "bit_field", + "flume", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "fast_image_resize" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9d450fac8a334ad72825596173f0f7767ff04dd6e3d59c49c894c4bc2957e8b" +dependencies = [ + "cfg-if", + "num-traits", + "thiserror", +] + [[package]] name = "fastrand" version = "1.9.0" @@ -1459,6 +1551,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "spin", +] + [[package]] name = "fnv" version = "1.0.7" @@ -1786,6 +1887,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "gif" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "gimli" version = "0.28.1" @@ -1990,6 +2101,16 @@ dependencies = [ "tracing", ] +[[package]] +name = "half" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e" +dependencies = [ + "cfg-if", + "crunchy", +] + [[package]] name = "halfbrown" version = "0.2.4" @@ -2286,7 +2407,13 @@ dependencies = [ "bytemuck", "byteorder", "color_quant", + "exr", + "gif", + "jpeg-decoder", "num-traits", + "png", + "qoi", + "tiff", ] [[package]] @@ -2487,6 +2614,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +[[package]] +name = "jpeg-decoder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" +dependencies = [ + "rayon", +] + [[package]] name = "js-sys" version = "0.3.68" @@ -2533,6 +2669,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + [[package]] name = "lexical-core" version = "0.8.5" @@ -2825,6 +2967,15 @@ version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +[[package]] +name = "memmap2" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" +dependencies = [ + "libc", +] + [[package]] name = "memmem" version = "0.1.1" @@ -3241,9 +3392,9 @@ dependencies = [ [[package]] name = "open" -version = "5.1.0" +version = "5.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f2588edf622de56e7a1fed57bf203344f63c03f3d43472ba0434a92373c8f27" +checksum = "eedff767bc49d336bff300224f73307ae36963c843e38dc9312a22171b012cbc" dependencies = [ "is-wsl", "libc", @@ -3836,6 +3987,15 @@ dependencies = [ "unicode-ident", ] +[[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" @@ -4765,7 +4925,7 @@ dependencies = [ "libc", "nix 0.23.2", "thiserror", - "widestring", + "widestring 0.4.3", "winapi", ] @@ -4790,6 +4950,29 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +[[package]] +name = "smithay-client-toolkit" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "922fd3eeab3bd820d76537ce8f582b1cf951eceb5475c28500c7457d9d17f53a" +dependencies = [ + "bitflags 2.4.2", + "cursor-icon", + "libc", + "log", + "memmap2", + "rustix 0.38.31", + "thiserror", + "wayland-backend", + "wayland-client", + "wayland-csd-frame", + "wayland-cursor", + "wayland-protocols", + "wayland-protocols-wlr", + "wayland-scanner", + "xkeysym", +] + [[package]] name = "smol" version = "1.3.0" @@ -4860,6 +5043,9 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] [[package]] name = "stable_deref_trait" @@ -5462,6 +5648,17 @@ dependencies = [ "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]] name = "time" version = "0.3.34" @@ -6151,6 +6348,101 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wayland-backend" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d50fa61ce90d76474c87f5fc002828d81b32677340112b4ef08079a9d459a40" +dependencies = [ + "cc", + "downcast-rs", + "rustix 0.38.31", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82fb96ee935c2cea6668ccb470fb7771f6215d1691746c2d896b447a00ad3f1f" +dependencies = [ + "bitflags 2.4.2", + "rustix 0.38.31", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-csd-frame" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" +dependencies = [ + "bitflags 2.4.2", + "cursor-icon", + "wayland-backend", +] + +[[package]] +name = "wayland-cursor" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71ce5fa868dd13d11a0d04c5e2e65726d0897be8de247c0c5a65886e283231ba" +dependencies = [ + "rustix 0.38.31", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4" +dependencies = [ + "bitflags 2.4.2", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" +dependencies = [ + "bitflags 2.4.2", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b3a62929287001986fb58c789dce9b67604a397c15c611ad9f747300b6c283" +dependencies = [ + "proc-macro2", + "quick-xml 0.31.0", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15a0c8eaff5216d07f226cb7a549159267f3467b289d9a2e52fd3ef5aae2b7af" +dependencies = [ + "dlib", + "log", + "pkg-config", +] + [[package]] name = "web-sys" version = "0.3.68" @@ -6252,6 +6544,12 @@ dependencies = [ "windows-metadata", ] +[[package]] +name = "weezl" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" + [[package]] name = "which" version = "4.4.2" @@ -6283,6 +6581,12 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c" +[[package]] +name = "widestring" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" + [[package]] name = "winapi" version = "0.3.9" @@ -6894,6 +7198,23 @@ dependencies = [ "rustix 0.38.31", ] +[[package]] +name = "xcb" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d27b37e69b8c05bfadcd968eb1a4fe27c9c52565b727f88512f43b89567e262" +dependencies = [ + "bitflags 1.3.2", + "libc", + "quick-xml 0.30.0", +] + +[[package]] +name = "xcursor" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a0ccd7b4a5345edfcd0c3535718a4e9ff7798ffc536bb5b5a0e26ff84732911" + [[package]] name = "xdg-home" version = "1.1.0" @@ -6904,6 +7225,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "xkeysym" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "054a8e68b76250b253f671d1268cb7f1ae089ec35e195b2efb2a4e9a836d0621" + [[package]] name = "yaml-rust" version = "0.4.5" @@ -7057,6 +7384,15 @@ dependencies = [ "pkg-config", ] +[[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 = "zvariant" version = "3.15.2" diff --git a/backend/tauri/Cargo.toml b/backend/tauri/Cargo.toml index e07b77bcd5..e5599909e4 100644 --- a/backend/tauri/Cargo.toml +++ b/backend/tauri/Cargo.toml @@ -84,6 +84,10 @@ base64 = "0.22" single-instance = "0.3.3" tauri-plugin-deep-link = { path = "../tauri-plugin-deep-link", version = "0.1.2" } uuid = "1.7.0" +image = "0.24.9" +fast_image_resize = "3.0.4" +display-info = { git = "https://github.com/greenhat616/display-info.git" } # should be removed after upgrading to tauri v2 +dashmap = "5.5.3" [target.'cfg(windows)'.dependencies] deelevate = "0.2.0" diff --git a/backend/tauri/src/core/tray/icon.rs b/backend/tauri/src/core/tray/icon.rs new file mode 100644 index 0000000000..20e96b6cdf --- /dev/null +++ b/backend/tauri/src/core/tray/icon.rs @@ -0,0 +1,95 @@ +use dashmap::DashMap; +use once_cell::sync::Lazy; +use std::{ + fmt::{Display, Formatter}, + sync::Arc, +}; + +#[derive(Debug, Clone, Default)] +pub enum TrayIcon { + #[default] + Normal, + Tun, + SystemProxy, +} + +impl Display for TrayIcon { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Self::Normal => write!(f, "normal"), + Self::Tun => write!(f, "tun"), + Self::SystemProxy => write!(f, "system_proxy"), + } + } +} + +impl From for &'static str { + fn from(icon: TrayIcon) -> Self { + match icon { + TrayIcon::Normal => "normal", + TrayIcon::Tun => "tun", + TrayIcon::SystemProxy => "system_proxy", + } + } +} + +impl From<&TrayIcon> for &'static str { + fn from(icon: &TrayIcon) -> Self { + match icon { + TrayIcon::Normal => "normal", + TrayIcon::Tun => "tun", + TrayIcon::SystemProxy => "system_proxy", + } + } +} + +// TODO: use the `icon` module to load the tray icon, and support macos, linux +static RAW_ICON: Lazy>>> = Lazy::new(|| { + let m = DashMap::new(); + #[cfg(windows)] + { + m.insert( + TrayIcon::Tun.into(), + include_bytes!("../../../icons/win-tray-icon-blue.png").to_vec(), + ); + m.insert( + TrayIcon::SystemProxy.into(), + include_bytes!("../../../icons/win-tray-icon-pink.png").to_vec(), + ); + m.insert( + TrayIcon::Normal.into(), + include_bytes!("../../../icons/win-tray-icon.png").to_vec(), + ); + } + Arc::new(m) +}); + +static RESIZED_ICON_CACHE: Lazy>>> = Lazy::new(|| { + let m = DashMap::new(); + #[cfg(windows)] + { + let scale_factor = crate::utils::help::get_max_scale_factor(); + resize_images(&m, scale_factor); + } + Arc::new(m) +}); + +fn resize_images(map: &DashMap<&'static str, Vec>, scale_factor: f64) { + for item in RAW_ICON.iter() { + let (mode, icon) = item.pair(); + let icon = crate::utils::help::resize_tray_image(icon, scale_factor).unwrap(); + map.insert(*mode, icon); + } +} + +pub fn on_scale_factor_changed(scale_factor: f64) { + resize_images(&RESIZED_ICON_CACHE, scale_factor); +} + +pub fn get_icon(mode: &TrayIcon) -> Vec { + RESIZED_ICON_CACHE + .clone() + .get::<&'static str>(&Into::<&'static str>::into(mode)) + .unwrap() + .clone() +} diff --git a/backend/tauri/src/core/tray/mod.rs b/backend/tauri/src/core/tray/mod.rs index 1ce536eb2f..e79517534d 100644 --- a/backend/tauri/src/core/tray/mod.rs +++ b/backend/tauri/src/core/tray/mod.rs @@ -1,3 +1,4 @@ +use super::storage; use crate::{cmds, config::Config, feat, utils::resolve}; use anyhow::Result; use rust_i18n::t; @@ -5,16 +6,18 @@ use tauri::{ api, AppHandle, CustomMenuItem, Manager, SystemTrayEvent, SystemTrayMenu, SystemTrayMenuItem, SystemTraySubmenu, }; +use tracing_attributes::instrument; -use super::storage; - -pub struct Tray {} - +mod icon; pub mod proxies; - +pub use self::icon::on_scale_factor_changed; +use self::icon::TrayIcon; use self::proxies::SystemTrayMenuProxiesExt; +pub struct Tray {} + impl Tray { + #[instrument(skip(_app_handle))] pub fn tray_menu(_app_handle: &AppHandle) -> SystemTrayMenu { let version = env!("NYANPASU_VERSION"); @@ -68,6 +71,7 @@ impl Tray { .add_item(CustomMenuItem::new("quit", t!("tray.quit")).accelerator("CmdOrControl+Q")) } + #[instrument(skip(app_handle))] pub fn update_systray(app_handle: &AppHandle) -> Result<()> { app_handle .tray_handle() @@ -76,6 +80,7 @@ impl Tray { Ok(()) } + #[instrument(skip(app_handle))] pub fn update_part(app_handle: &AppHandle) -> Result<()> { let mode = crate::utils::config::get_current_clash_mode(); @@ -93,15 +98,15 @@ impl Tray { #[cfg(target_os = "windows")] { - let indication_icon = if *tun_mode { - include_bytes!("../../../icons/win-tray-icon-blue.png").to_vec() + let mode = if *tun_mode { + TrayIcon::Tun } else if *system_proxy { - include_bytes!("../../../icons/win-tray-icon-pink.png").to_vec() + TrayIcon::SystemProxy } else { - include_bytes!("../../../icons/win-tray-icon.png").to_vec() + TrayIcon::Normal }; - - let _ = tray.set_icon(tauri::Icon::Raw(indication_icon)); + let icon = icon::get_icon(&mode); + let _ = tray.set_icon(tauri::Icon::Raw(icon)); } let _ = tray.get_item("system_proxy").set_selected(*system_proxy); @@ -128,6 +133,7 @@ impl Tray { Ok(()) } + #[instrument(skip(app_handle, event))] pub fn on_system_tray_event(app_handle: &AppHandle, event: SystemTrayEvent) { match event { SystemTrayEvent::MenuItemClick { id, .. } => match id.as_str() { diff --git a/backend/tauri/src/main.rs b/backend/tauri/src/main.rs index 3f52c28248..d7d8cd2b09 100644 --- a/backend/tauri/src/main.rs +++ b/backend/tauri/src/main.rs @@ -229,6 +229,9 @@ fn main() -> std::io::Result<()> { tauri::RunEvent::WindowEvent { label, event, .. } => { if label == "main" { match event { + tauri::WindowEvent::ScaleFactorChanged { scale_factor, .. } => { + core::tray::on_scale_factor_changed(scale_factor); + } tauri::WindowEvent::CloseRequested { .. } | tauri::WindowEvent::Destroyed => { // log::info!(target: "app", "window close requested"); let _ = resolve::save_window_state(app_handle, true); diff --git a/backend/tauri/src/utils/help.rs b/backend/tauri/src/utils/help.rs index 6e2d2e0d2c..0bd90cae03 100644 --- a/backend/tauri/src/utils/help.rs +++ b/backend/tauri/src/utils/help.rs @@ -1,13 +1,24 @@ use crate::config::nyanpasu::ExternalControllerPortStrategy; use anyhow::{anyhow, bail, Context, Result}; +use display_info::DisplayInfo; +use fast_image_resize as fr; +use image::{codecs::png::PngEncoder, io::Reader as ImageReader, ColorType, ImageEncoder}; use nanoid::nanoid; use serde::{de::DeserializeOwned, Serialize}; use serde_yaml::{Mapping, Value}; -use std::{fs, path::PathBuf, str::FromStr}; +use std::{ + fs, + io::{BufWriter, Cursor}, + num::NonZeroU32, + path::PathBuf, + str::FromStr, +}; use tauri::{ api::shell::{open, Program}, Manager, }; +use tracing::{debug, warn}; +use tracing_attributes::instrument; /// read data from yaml as struct T pub fn read_yaml(path: &PathBuf) -> Result { @@ -140,6 +151,73 @@ pub fn get_clash_external_port( Ok(port) } +pub fn resize_tray_image(img: &[u8], scale_factor: f64) -> Result> { + let img = ImageReader::new(Cursor::new(img)) + .with_guessed_format()? + .decode()?; + let width = NonZeroU32::new(img.width()).unwrap_or(NonZeroU32::new(16).unwrap()); + let height = NonZeroU32::new(img.height()).unwrap_or(NonZeroU32::new(16).unwrap()); + let mut src_image = fr::Image::from_vec_u8( + width, + height, + img.to_rgba8().into_raw(), + fr::PixelType::U8x4, + ) + .context("failed to parse image")?; + // Multiple RGB channels of source image by alpha channel + let alpha_mul_div = fr::MulDiv::default(); + alpha_mul_div + .multiply_alpha_inplace(&mut src_image.view_mut()) + .context("failed to multiply alpha")?; + // Create container for data of destination image + let size = (32_f64 * scale_factor).round() as u32; // 32px is the base tray size as the dpi is 96 + let dst_width = NonZeroU32::new(size).unwrap(); + let dst_height = NonZeroU32::new(size).unwrap(); + let mut dst_image = fr::Image::new(dst_width, dst_height, src_image.pixel_type()); + + // Get mutable view of destination image data + let mut dst_view = dst_image.view_mut(); + + // Create Resizer instance and resize source image + // into buffer of destination image + let mut resizer = fr::Resizer::new(fr::ResizeAlg::Convolution(fr::FilterType::Lanczos3)); + resizer + .resize(&src_image.view(), &mut dst_view) + .context("failed to resize image")?; + // Divide RGB channels of destination image by alpha + alpha_mul_div.divide_alpha_inplace(&mut dst_view).unwrap(); + + // Write destination image as PNG-file + let mut result_buf = BufWriter::new(Vec::new()); + PngEncoder::new(&mut result_buf).write_image( + dst_image.buffer(), + dst_width.get(), + dst_height.get(), + ColorType::Rgba8, + )?; + Ok(result_buf.buffer().to_vec()) +} + +#[instrument] +pub fn get_max_scale_factor() -> f64 { + match DisplayInfo::all() { + Ok(displays) => { + let mut scale_factor = 0.0; + debug!("displays: {:?}", displays); + for display in displays { + if display.scale_factor > scale_factor { + scale_factor = display.scale_factor; + } + } + scale_factor as f64 + } + Err(err) => { + warn!("failed to get display info: {:?}", err); + 1.0_f64 + } + } +} + #[macro_export] macro_rules! error { ($result: expr) => {