Upgrade dependencies and use fast_image_resize

This commit is contained in:
Rémi BERTHO 2023-04-11 23:56:06 +02:00
parent 83fa2c6864
commit 4c598da07a
Signed by: dalan
GPG key ID: EE3B917931C07B64
3 changed files with 1308 additions and 686 deletions

1908
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -7,14 +7,15 @@ edition = "2021"
anyhow = "1.0" anyhow = "1.0"
log = "0.4" log = "0.4"
simplelog = "0.12" simplelog = "0.12"
wry = "0.20" wry = "0.27"
native-dialog = "0.6" native-dialog = "0.6"
rust-embed = "6" rust-embed = "6"
base64 = "0.13" base64 = "0.21"
directories = "4" directories = "5"
clap = "3" clap = "4"
warp = "0.3" warp = "0.3"
tokio = { version = "1", features = ["full"] } tokio = { version = "1", features = ["full"] }
image = "0.24" image = "0.24"
rexiv2 = "0.9" rexiv2 = "0.10"
tempfile = "3" tempfile = "3"
fast_image_resize = "2"

View file

@ -1,14 +1,15 @@
#![windows_subsystem = "windows"] #![windows_subsystem = "windows"]
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use base64::Engine;
use clap::{Arg, Command}; use clap::{Arg, Command};
use image::{imageops::FilterType, io::Reader as ImageReader}; use image::{io::Reader as ImageReader, DynamicImage};
use log::info; use log::info;
use native_dialog::{FileDialog, MessageDialog}; use native_dialog::{FileDialog, MessageDialog};
use rexiv2::Metadata; use rexiv2::Metadata;
use rust_embed::RustEmbed; use rust_embed::RustEmbed;
use simplelog::{ColorChoice, ConfigBuilder, LevelFilter, TermLogger, TerminalMode}; use simplelog::{ColorChoice, ConfigBuilder, LevelFilter, TermLogger, TerminalMode};
use std::{fs::File, io::BufWriter, path::PathBuf, thread::JoinHandle}; use std::{fs::File, io::BufWriter, path::PathBuf, thread::JoinHandle, num::NonZeroU32};
use tempfile::tempdir; use tempfile::tempdir;
use warp::Filter; use warp::Filter;
use wry::{ use wry::{
@ -19,6 +20,7 @@ use wry::{
}, },
webview::WebViewBuilder, webview::WebViewBuilder,
}; };
use fast_image_resize as fr;
#[derive(RustEmbed)] #[derive(RustEmbed)]
#[folder = "embed/"] #[folder = "embed/"]
@ -39,15 +41,11 @@ fn main() -> Result<()> {
let tmp_dir = tempdir()?; let tmp_dir = tempdir()?;
let cmd = Command::new("Simple panorama viewer") let cmd = Command::new("Simple panorama viewer")
.arg( .arg(Arg::new("filename").value_parser(clap::value_parser!(PathBuf)).help("Image path"))
Arg::new("filename")
.allow_invalid_utf8(true)
.help("Image path"),
)
.get_matches(); .get_matches();
let img_path = if let Some(img_path) = cmd.value_of_os("filename") { let img_path = if let Some(img_path) = cmd.get_one::<PathBuf>("filename") {
PathBuf::from(img_path) img_path.clone()
} else { } else {
let user_dirs = directories::UserDirs::new().unwrap(); let user_dirs = directories::UserDirs::new().unwrap();
let dir = if let Some(img_dir) = user_dirs.picture_dir() { let dir = if let Some(img_dir) = user_dirs.picture_dir() {
@ -72,7 +70,7 @@ fn main() -> Result<()> {
const MAX_IMG_SIZE: u32 = 20_000_000; const MAX_IMG_SIZE: u32 = 20_000_000;
let img_data_path = if height * width > MAX_IMG_SIZE { let img_data_path = if height * width > MAX_IMG_SIZE {
// @todo Option pas de resize / taille en ligne de commande // @todo Option pas de resize / taille en ligne de commande
// @todo Option pas de resize / taille dans une config // @todo Option pas de resize / taille dans une config (confy)
// @todo Icon // @todo Icon
let ratio = (height * width) as f64 / MAX_IMG_SIZE as f64; let ratio = (height * width) as f64 / MAX_IMG_SIZE as f64;
let new_height = (height as f64 / ratio.sqrt()) as u32; let new_height = (height as f64 / ratio.sqrt()) as u32;
@ -80,13 +78,16 @@ fn main() -> Result<()> {
if MessageDialog::new() if MessageDialog::new()
.set_title("Resize") .set_title("Resize")
.set_text(&format!("Resize the file to {} × {}", new_width, new_height)) .set_text(&format!(
"Resize the file to {} × {}",
new_width, new_height
))
.show_confirm()? .show_confirm()?
{ {
// Resize image // Resize image
let img = ImageReader::open(&img_path)?.decode()?; let img = ImageReader::open(&img_path)?.decode()?;
info!("Resize image to {} × {}", new_width, new_height); info!("Resize image to {} × {}", new_width, new_height);
let image_resized = img.resize(new_width, new_height, FilterType::Triangle); let image_resized = fast_image_resize(&img, new_width, new_height)?;
// Save file and add metadata // Save file and add metadata
let tmp_img_path = tmp_dir.path().join("img.jpg"); let tmp_img_path = tmp_dir.path().join("img.jpg");
@ -105,7 +106,7 @@ fn main() -> Result<()> {
}; };
let img_data = std::fs::read(&img_data_path)?; let img_data = std::fs::read(&img_data_path)?;
tmp_dir.close()?; tmp_dir.close()?;
let img_base_64 = base64::encode(img_data); let img_base_64 = base64::engine::general_purpose::STANDARD.encode(img_data);
info!("Generate HTML"); info!("Generate HTML");
let html = std::str::from_utf8(Embed::get("index.html").unwrap().data.as_ref())? let html = std::str::from_utf8(Embed::get("index.html").unwrap().data.as_ref())?
@ -150,3 +151,49 @@ fn run_server(html: String) -> JoinHandle<()> {
async_runtime.block_on(warp::serve(hello).run(([127, 0, 0, 1], 62371))); async_runtime.block_on(warp::serve(hello).run(([127, 0, 0, 1], 62371)));
}) })
} }
/// Fast resize
pub fn fast_image_resize(
img: &DynamicImage,
max_width: u32,
max_height: u32,
) -> Result<DynamicImage> {
// Create source image
let width = NonZeroU32::new(img.width()).unwrap();
let height = NonZeroU32::new(img.height()).unwrap();
let src_image =
fr::Image::from_vec_u8(width, height, img.to_rgb8().into_raw(), fr::PixelType::U8x3)?;
let mut src_view = src_image.view();
// Create container for data of destination image
let (dst_width, dst_height) = if width > height {
(
NonZeroU32::new(max_width).unwrap(),
NonZeroU32::new((max_width * img.height()) / img.width()).unwrap(),
)
} else {
(
NonZeroU32::new((max_height * img.width()) / img.height()).unwrap(),
NonZeroU32::new(max_height).unwrap(),
)
};
src_view.set_crop_box_to_fit_dst_size(dst_width, dst_height, None);
let mut dst_image = fr::Image::new(dst_width, dst_height, src_view.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_view, &mut dst_view)?;
// Create Dynamic image
let img_buffer = image::RgbImage::from_raw(
dst_image.width().get(),
dst_image.height().get(),
dst_image.into_vec(),
)
.unwrap();
Ok(img_buffer.into())
}