diff --git a/Cargo.lock b/Cargo.lock index 889b79e..bc51d55 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -244,7 +244,7 @@ checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "common" -version = "0.9.5-masterV2" +version = "0.9.5-masterV3" dependencies = [ "criterion", "fastrand", @@ -853,7 +853,7 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "swww" -version = "0.9.5-masterV2" +version = "0.9.5-masterV3" dependencies = [ "assert_cmd", "clap", @@ -866,7 +866,7 @@ dependencies = [ [[package]] name = "swww-daemon" -version = "0.9.5-masterV2" +version = "0.9.5-masterV3" dependencies = [ "common", "keyframe", diff --git a/Cargo.toml b/Cargo.toml index 8a4e763..c0dfac0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ members = ["client", "daemon", "common"] default-members = ["client", "daemon"] [workspace.package] -version = "0.9.5-masterV2" +version = "0.9.5-masterV3" authors = ["Leonardo Gibrowski FaƩ "] edition = "2021" license-file = "LICENSE" diff --git a/client/src/cli.rs b/client/src/cli.rs index 22d97f7..d808179 100644 --- a/client/src/cli.rs +++ b/client/src/cli.rs @@ -1,6 +1,7 @@ /// Note: this file only has basic declarations and some definitions in order to be possible to /// import it in the build script, to automate shell completion use clap::{Parser, ValueEnum}; +use std::fmt::Display; use std::path::PathBuf; fn from_hex(hex: &str) -> Result<[u8; 3], String> { @@ -74,6 +75,19 @@ impl std::str::FromStr for Filter { } } +impl Display for Filter { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let str = match self { + Self::Nearest => "Nearest", + Self::Bilinear => "Bilinear", + Self::CatmullRom => "CatmullRom", + Self::Mitchell => "Mitchell", + Self::Lanczos3 => "Lanczos3", + }; + write!(f, "{}", str) + } +} + #[derive(Clone)] pub enum TransitionType { None, diff --git a/client/src/main.rs b/client/src/main.rs index f4d7712..6678056 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -1,15 +1,16 @@ -use std::{path::Path, time::Duration}; +use std::{path::Path, str::FromStr, time::Duration}; use clap::Parser; use common::cache; use common::ipc::{self, Answer, Client, IpcSocket, RequestSend}; use common::mmap::Mmap; +use image::Pixel; mod imgproc; use imgproc::*; mod cli; -use cli::{CliImage, ResizeStrategy, Swww}; +use cli::{CliImage, Filter, ResizeStrategy, Swww}; fn main() -> Result<(), String> { let swww = Swww::parse(); @@ -119,13 +120,18 @@ fn make_img_request( for (&dim, outputs) in dims.iter().zip(outputs) { img_req_builder.push( ipc::ImgSend { - img: image::RgbImage::from_pixel(dim.0, dim.1, image::Rgb(*color)) - .to_vec() - .into_boxed_slice(), + img: image::RgbaImage::from_pixel( + dim.0, + dim.1, + image::Rgb(*color).to_rgba(), + ) + .to_vec() + .into_boxed_slice(), path: format!("0x{:02x}{:02x}{:02x}", color[0], color[1], color[2]), dim, format: pixel_format, }, + Filter::Lanczos3.to_string(), outputs, None, ); @@ -176,6 +182,7 @@ fn make_img_request( None }; + let filter = img.filter.to_string(); let img = match img.resize { ResizeStrategy::No => img_pad(&img_raw, dim, &img.fill_color)?, ResizeStrategy::Crop => { @@ -196,6 +203,7 @@ fn make_img_request( dim, format: pixel_format, }, + filter, outputs, animation, ); @@ -265,32 +273,40 @@ fn restore_from_cache(requested_outputs: &[String]) -> Result<(), String> { let (_, _, outputs) = get_format_dims_and_outputs(requested_outputs)?; for output in outputs.iter().flatten() { - let img_path = common::cache::get_previous_image_path(output) - .map_err(|e| format!("failed to get previous image path: {e}"))?; - #[allow(deprecated)] - if let Err(e) = process_swww_args(&Swww::Img(cli::Img { - image: cli::parse_image(&img_path)?, - outputs: output.to_string(), - no_resize: false, - resize: ResizeStrategy::Crop, - fill_color: [0, 0, 0], - filter: cli::Filter::Lanczos3, - transition_type: cli::TransitionType::None, - transition_step: std::num::NonZeroU8::MAX, - transition_duration: 0.0, - transition_fps: 30, - transition_angle: 0.0, - transition_pos: cli::CliPosition { - x: cli::CliCoord::Pixel(0.0), - y: cli::CliCoord::Pixel(0.0), - }, - invert_y: false, - transition_bezier: (0.0, 0.0, 0.0, 0.0), - transition_wave: (0.0, 0.0), - })) { + if let Err(e) = restore_output(output) { eprintln!("WARNING: failed to load cache for output {output}: {e}"); } } Ok(()) } + +fn restore_output(output: &str) -> Result<(), String> { + let (filter, img_path) = common::cache::get_previous_image_path(output) + .map_err(|e| format!("failed to get previous image path: {e}"))?; + if img_path.is_empty() { + return Err("cache file does not exist".to_string()); + } + + #[allow(deprecated)] + process_swww_args(&Swww::Img(cli::Img { + image: cli::parse_image(&img_path)?, + outputs: output.to_string(), + no_resize: false, + resize: ResizeStrategy::Crop, + fill_color: [0, 0, 0], + filter: Filter::from_str(&filter).unwrap_or(Filter::Lanczos3), + transition_type: cli::TransitionType::None, + transition_step: std::num::NonZeroU8::MAX, + transition_duration: 0.0, + transition_fps: 30, + transition_angle: 0.0, + transition_pos: cli::CliPosition { + x: cli::CliCoord::Pixel(0.0), + y: cli::CliCoord::Pixel(0.0), + }, + invert_y: false, + transition_bezier: (0.0, 0.0, 0.0, 0.0), + transition_wave: (0.0, 0.0), + })) +} diff --git a/common/src/cache.rs b/common/src/cache.rs index e384f35..12d64be 100644 --- a/common/src/cache.rs +++ b/common/src/cache.rs @@ -14,10 +14,10 @@ use crate::ipc::Animation; use crate::ipc::PixelFormat; use crate::mmap::Mmap; -pub(crate) fn store(output_name: &str, img_path: &str) -> io::Result<()> { +pub(crate) fn store(output_name: &str, img_path: &str, filter: &str) -> io::Result<()> { let mut filepath = cache_dir()?; filepath.push(output_name); - File::create(filepath)?.write_all(img_path.as_bytes()) + File::create(filepath)?.write_all(format!("{filter}\n{img_path}").as_bytes()) } pub(crate) fn store_animation_frames( @@ -64,28 +64,35 @@ pub fn load_animation_frames( Ok(None) } -pub fn get_previous_image_path(output_name: &str) -> io::Result { +pub fn get_previous_image_path(output_name: &str) -> io::Result<(String, String)> { let mut filepath = cache_dir()?; clean_previous_verions(&filepath); filepath.push(output_name); if !filepath.is_file() { - return Ok("".to_string()); + return Ok(("".to_string(), "".to_string())); } let mut buf = Vec::with_capacity(64); File::open(filepath)?.read_to_end(&mut buf)?; - - String::from_utf8(buf).map_err(|e| { + let buf = String::from_utf8(buf).map_err(|e| { std::io::Error::new( std::io::ErrorKind::Other, format!("failed to decode bytes: {e}"), ) - }) + })?; + + match buf.split_once("\n") { + Some(buf) => Ok((buf.0.to_string(), buf.1.to_string())), + None => Err(std::io::Error::new( + std::io::ErrorKind::Other, + "failed to read image filter", + )), + } } pub fn load(output_name: &str) -> io::Result<()> { - let img_path = get_previous_image_path(output_name)?; + let (filter, img_path) = get_previous_image_path(output_name)?; if img_path.is_empty() { return Ok(()); } @@ -105,6 +112,7 @@ pub fn load(output_name: &str) -> io::Result<()> { .arg("img") .args([ &format!("--outputs={output_name}"), + &format!("--filter={filter}"), "--transition-type=none", &img_path, ]) diff --git a/common/src/ipc/mod.rs b/common/src/ipc/mod.rs index 6565261..07d4700 100644 --- a/common/src/ipc/mod.rs +++ b/common/src/ipc/mod.rs @@ -59,7 +59,13 @@ impl ImageRequestBuilder { } #[inline] - pub fn push(&mut self, img: ImgSend, outputs: &[String], animation: Option) { + pub fn push( + &mut self, + img: ImgSend, + filter: String, + outputs: &[String], + animation: Option, + ) { self.img_count += 1; let ImgSend { @@ -89,7 +95,7 @@ impl ImageRequestBuilder { // cache the request for output in outputs.iter() { - if let Err(e) = super::cache::store(output, path) { + if let Err(e) = super::cache::store(output, path, &filter) { eprintln!("ERROR: failed to store cache: {e}"); } }