use std::io::BufWriter; use image::{ codecs::jpeg::JpegEncoder, DynamicImage::*, EncodableLayout, ExtendedColorType::*, ImageBuffer, }; use photon_rs::{ multiple::watermark, native::open_image, transform::{resize, SamplingFilter}, PhotonImage, }; // TODO: Add option for 4:5 export and 1:1 export pub fn process_image(path: &std::path::Path) -> PhotonImage { let mut image = open_image(path.to_str().expect("to parse path to string")) .expect("to open image with Photon"); println!("opened image"); let padding = 36; let intended_size_in_frame = 1080 - padding; let mut background = PhotonImage::new_from_byteslice({ let mut image = Image::create(1080, 1080, false, Format::RGB8).expect("to be able to create an image"); image.fill(Color::WHITE); image.save_jpg_to_buffer().to_vec() }); let width = image.get_width(); let height = image.get_height(); let biggest_dimension = std::cmp::max(width, height); println!("Original dimensions {width}x{height}"); let scaled_other_dimension = if width == biggest_dimension { (height as f32 / (width as f32 / intended_size_in_frame as f32)) as u32 } else { (width as f32 / (height as f32 / intended_size_in_frame as f32)) as u32 }; let image = if width == biggest_dimension { println!("Width is bigger, setting to {intended_size_in_frame}x{scaled_other_dimension}"); resize( &mut image, intended_size_in_frame, scaled_other_dimension, SamplingFilter::Lanczos3, ) } else { println!("Height is bigger, setting to {scaled_other_dimension}x{intended_size_in_frame}"); resize( &mut image, scaled_other_dimension, intended_size_in_frame, SamplingFilter::Lanczos3, ) }; if width == biggest_dimension { watermark( &mut background, &image, (1080 - intended_size_in_frame) / 2, (1080 - scaled_other_dimension) / 2, ); } else { watermark( &mut background, &image, (1080 - scaled_other_dimension) / 2, (1080 - intended_size_in_frame) / 2, ); } // println!("done!"); background } pub fn save_image(image: PhotonImage, path: &str) { let raw_pixels = image.get_raw_pixels(); let width = image.get_width(); let height = image.get_height(); println!("attempting to make image buffer"); let buffer = ImageBuffer::from_vec(width, height, raw_pixels) .expect("to be able to build an image buffer"); let image = ImageRgba8(buffer).into_rgb8(); println!("attempting to save to {path:#?}"); let file = std::fs::File::create(path).expect("to able to create a file"); let ref mut buff = BufWriter::new(file); let mut encoder = JpegEncoder::new_with_quality(buff, 100); encoder .encode(&image.as_bytes(), width, height, Rgb8) .expect("to encode the JPEG to a file"); println!("saved to {path:#?}"); } use godot::{ engine::{image::Format, Image}, prelude::*, }; struct PhotoProcessingExtension; #[gdextension] unsafe impl ExtensionLibrary for PhotoProcessingExtension {} #[derive(GodotClass)] #[class(base=Node)] struct PhotoProcess { base: Base, } #[godot_api] impl INode for PhotoProcess { fn init(base: Base) -> Self { Self { base } } } #[godot_api] impl PhotoProcess { #[signal] fn done_with_photo(path: GString); #[func] fn process_files(&mut self, files: PackedStringArray) { godot_print!("attempting to process files: {files:#?}! ✨"); let mut threads = vec![]; let instance_id = self.base().instance_id(); for file in files.to_vec() { godot_print!("attempting to process file: {file:#?}! 🤔"); let file = file.to_string().clone(); threads.push(std::thread::spawn(move || { let path = std::path::Path::new(&file); println!( "🎞️ Processing {:?}", path.file_name().expect("to turn path into a filename") ); let image = process_image(path); println!("Done processing!"); let mut new_file_path = std::path::PathBuf::from(path); new_file_path.set_file_name(format!( "{} - instagram compatible.jpg", path.file_name() .expect("to get path filename") .to_str() .expect("to convert filename to string") .replace(".jpg", "") )); save_image( image, new_file_path .to_str() .expect("to turn new_path into a string"), ); Gd::::from_instance_id(instance_id).call_deferred( "emit_signal".into(), &[ Variant::from("done_with_photo"), Variant::from(new_file_path.to_str().unwrap()), ], ); println!("🖼️ done w/ file, wrote to: {new_file_path:#?}"); })); } // for handle in threads { // handle.join().expect("to join threads"); // } } }