182 lines
5.4 KiB
Rust
182 lines
5.4 KiB
Rust
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<Node>,
|
|
}
|
|
|
|
#[godot_api]
|
|
impl INode for PhotoProcess {
|
|
fn init(base: Base<Node>) -> 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::<Node>::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");
|
|
// }
|
|
}
|
|
}
|