diff --git a/src-tauri/src/bin/testing.rs b/src-tauri/src/bin/testing.rs index 98c2e31..6ad8553 100644 --- a/src-tauri/src/bin/testing.rs +++ b/src-tauri/src/bin/testing.rs @@ -8,21 +8,6 @@ enum ImageType { Unsupported, } -// fn process_image(file_path_string: String) -> Result { -// // convert string to path to use in rust -// let file_path = std::path::Path::new(&file_path_string); - -// // get the data directory path -// let data_dir = tauri::api::path::data_dir().ok_or("Failed to get data directory")?; -// let mut destination = data_dir.clone(); -// // we can hardcode the file name since -// // 1. we are only dealing with one wallpaper image at a time -// // 2. we will be converting all images to the webp format -// destination.push("wallpaper.webp"); - -// // determine the file format -// } - /// determines the image type of a file that is passed in using the filepath fn determine_image_type(file_path: &std::path::Path) -> Result { // open the file @@ -70,49 +55,58 @@ fn is_animated_webp(file_path: &std::path::Path) -> Result { .map_err(|_| "File is not a valid WebP image".to_string()) } -/// crop static images (jpeg, png and still webp) to a maximum of 1280x800 and maintaining aspect ratio -fn crop_static_image( - file_path: &std::path::Path, - destination: &std::path::Path, -) -> Result<(), String> { - let img = image::open(file_path).map_err(|e| format!("Failed to open image: {e}"))?; - let (width, height) = image::GenericImageView::dimensions(&img); - - let resized_img = if width > 1280 || height > 800 { - img.resize(1280, 800, image::imageops::FilterType::Lanczos3) - } else { - img - }; - - let mut output_file = std::fs::File::create(destination) - .map_err(|e: std::io::Error| format!("Failed to create file: {e}"))?; - resized_img - .write_to(&mut output_file, image::ImageFormat::WebP) - .map_err(|e| format!("Failed to save image as WebP: {}", e))?; - Ok(()) +fn get_file_extension(file_path: &std::path::Path) -> Result { + file_path + .extension() + .and_then(|extension| extension.to_str()) + .map(|extension| extension.to_lowercase()) + .ok_or_else(|| "Failed to get file extension".to_string()) } -/// crop animated webp images -fn crop_animated_webp( - file_path: &std::path::Path, - destination: &std::path::Path, -) -> Result<(), String> { - let mut buffer = Vec::new(); - std::fs::File::open(file_path) - .and_then(|mut file| std::io::Read::read_to_end(&mut file, &mut buffer)) - .map_err(|e| format!("Failed to read file: {}", e))?; +/// function to interface with the tauri api on the javascript side +fn process_wallpaper_image(file_path_string: String) -> Result { + // convert the string to a path + let file_path = std::path::Path::new(&file_path_string); - let decoder = webp::AnimDecoder::new(&buffer); - let decoded = decoder - .decode() - .map_err(|e| format!("Failed to decode WebP animation: {e}"))?; + // determine the image type + match determine_image_type(file_path) { + Ok(image_type) => { + // convert the image type to a string extension for printing + let extension = match image_type { + ImageType::Jpeg => "jpeg", + ImageType::Png => "png", + ImageType::Gif => "gif", + ImageType::WebP => "webp", + ImageType::AnimatedWebP => "webp", + ImageType::Unsupported => "", + }; + Ok(extension.to_string()) + } + Err(e) => Err(format!("Error determining image type: {e}")), + } } fn main() { // Example usage - let path = std::path::Path::new("/home/vomitblood/Downloads/title.keys"); + let path = std::path::Path::new("/home/vomitblood/Downloads/background.jpg"); match determine_image_type(path) { - Ok(image_type) => println!("Image type: {:?}", image_type), - Err(e) => println!("Error determining image type: {}", e), + Ok(image_type) => { + // convert the image type to a string extension for printing + let extension = match image_type { + ImageType::Jpeg => "jpeg", + ImageType::Png => "png", + ImageType::Gif => "gif", + ImageType::WebP => "webp", + ImageType::AnimatedWebP => "webp", + ImageType::Unsupported => "", + }; + println!("Image extension: {extension}"); + } + Err(e) => println!("Error determining image type: {e}"), + } + + match get_file_extension(path) { + Ok(extension) => println!("File extension: {extension}"), + Err(e) => println!("Error getting file extension: {e}"), } } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs new file mode 100644 index 0000000..5a20f26 --- /dev/null +++ b/src-tauri/src/lib.rs @@ -0,0 +1 @@ +pub mod wallpaper; diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index e6ad770..923a243 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -1,8 +1,11 @@ // Prevents additional console window on Windows in release, DO NOT REMOVE!! #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] +use app::wallpaper; + fn main() { tauri::Builder::default() + .invoke_handler(tauri::generate_handler![wallpaper::process_wallpaper_image]) .run(tauri::generate_context!()) .expect("error while running tauri application"); } diff --git a/src-tauri/src/wallpaper.rs b/src-tauri/src/wallpaper.rs new file mode 100644 index 0000000..f5f08cc --- /dev/null +++ b/src-tauri/src/wallpaper.rs @@ -0,0 +1,86 @@ +#[derive(Debug, PartialEq)] +enum ImageType { + Jpeg, + Png, + Gif, + WebP, + AnimatedWebP, + Unsupported, +} + +/// function to interface with the tauri api on the javascript side +#[tauri::command] +pub fn process_wallpaper_image(file_path_string: String) -> Result { + // convert the string to a path + let file_path = std::path::Path::new(&file_path_string); + + // determine the image type + match determine_image_type(file_path) { + Ok(image_type) => match image_type { + ImageType::Jpeg => Ok("jpeg".to_string()), + ImageType::Png => Ok("png".to_string()), + ImageType::Gif => Ok("gif".to_string()), + ImageType::WebP => Ok("webp".to_string()), + ImageType::AnimatedWebP => Ok("webp".to_string()), + ImageType::Unsupported => { + Err("Unsupported image type (accepts webp, jpg, jpeg, png, gif)".to_string()) + } + }, + Err(e) => Err(format!("Error determining image type: {e}")), + } +} + +/// determines the image type of a file that is passed in using the filepath +fn determine_image_type(file_path: &std::path::Path) -> Result { + // open the file + let mut file = + std::fs::File::open(file_path).map_err(|e| format!("Failed to open file: {e}"))?; + + // read the first few bytes to determine the format + let mut buffer = [0; 12]; + std::io::Read::read_exact(&mut file, &mut buffer) + .map_err(|e| format!("Failed to read file: {e}"))?; + + // with hopes and prayers, try to guess the format and pray that it is correct haha + match image::guess_format(&buffer) { + Ok(image::ImageFormat::Jpeg) => Ok(ImageType::Jpeg), + Ok(image::ImageFormat::Png) => Ok(ImageType::Png), + Ok(image::ImageFormat::Gif) => Ok(ImageType::Gif), + Ok(image::ImageFormat::WebP) => { + // additional check for webp + is_animated_webp(file_path).map(|animated| { + if animated { + ImageType::AnimatedWebP + } else { + ImageType::WebP + } + }) + } + _ => Ok(ImageType::Unsupported), + } +} + +/// check if the file is a valid webp image and if it is animated +/// returns `Ok(true)` if it is animated, `Ok(false)` if it is not +/// and errors out if the file cannot be read or is not a webp +fn is_animated_webp(file_path: &std::path::Path) -> Result { + // open the file and read its contents into a buffer + let mut buffer = Vec::new(); + std::fs::File::open(file_path) + .and_then(|mut file| std::io::Read::read_to_end(&mut file, &mut buffer)) + .map_err(|e| format!("Failed to read file: {}", e))?; + + // use the webp crate to decode the image and check for animation + webp::AnimDecoder::new(&buffer) + .decode() + .map(|anim| anim.has_animation()) + .map_err(|_| "File is not a valid WebP image".to_string()) +} + +fn get_file_extension(file_path: &std::path::Path) -> Result { + file_path + .extension() + .and_then(|extension| extension.to_str()) + .map(|extension| extension.to_lowercase()) + .ok_or_else(|| "Failed to get file extension".to_string()) +} diff --git a/src/pages/testing.tsx b/src/pages/testing.tsx index da5c755..7b62afa 100644 --- a/src/pages/testing.tsx +++ b/src/pages/testing.tsx @@ -5,6 +5,7 @@ import { useState } from "react"; import { SettingsItem } from "../components/HeaderBar/Settings/SettingsItem"; import { useSettings } from "../contexts/SettingsContext"; import { testing } from "../lib/testing"; +import { invoke } from "@tauri-apps/api/tauri"; export default function Testing() { // contexts @@ -24,28 +25,42 @@ export default function Testing() { > - - - + - + + {text}