Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ impl TryFrom<serde_json::Map<String, Value>> for Palette {
pub fn output_file_name(
dir_path: &Option<PathBuf>,
input_path: &Path,
extension: &str,
color_palette: &ColorPalette,
color_palette_variations: &[Palette],
method: deltae::DEMethod,
Expand Down Expand Up @@ -189,6 +190,6 @@ pub fn output_file_name(
}

output.push(output_file_name);
output.set_extension("png");
output.set_extension(extension);
output
}
133 changes: 133 additions & 0 deletions src/convert_image_format.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
use std::path::Path;

use image::AnimationDecoder;
use image::ImageDecoder;
use indicatif::{ParallelProgressIterator, ProgressBar, ProgressStyle};
use owo_colors::OwoColorize;
use rayon::{prelude::ParallelIterator, slice::ParallelSliceMut};

use crate::delta::{CLIDEMethod, Lab};

pub fn convert_default(
input_path: &Path,
output_path: &Path,
image_format: image::ImageFormat,
palettes_lab: &[Lab],
deltae_method: CLIDEMethod,
) {
const CHUNK: usize = 4;

// Open image
let mut image = match image::open(input_path) {
Ok(i) => i.into_rgba8(),
Err(err) => {
eprintln!(
"Encountered error while opening image at path {}: {}",
input_path.display(),
err.if_supports_color(owo_colors::Stream::Stderr, |text| text.red())
);
std::process::exit(127)
}
};

// LAB conversion moved into palette
// Apply palettes to image
let progress_bar = ProgressBar::new(
(image.len() / CHUNK)
.try_into()
.expect("Failed to convert usize to u64"),
);
progress_bar.set_style(
ProgressStyle::with_template(
"[{elapsed_precise}] [{wide_bar}] {pos}/{len} ({eta_precise})",
)
.expect("Failed to set progress bar style"),
);
let progress_bar_clone = progress_bar.clone();
image
.par_chunks_exact_mut(CHUNK)
.progress_with(progress_bar)
.for_each(|bytes| {
let pixel: [u8; CHUNK] = bytes.try_into().unwrap();
let lab = Lab::from(pixel);
let new_rgb = lab
.to_nearest_palette(palettes_lab, deltae::DEMethod::from(deltae_method))
.to_rgb();
bytes[..3].copy_from_slice(&new_rgb);
});
progress_bar_clone.finish();

match image.save_with_format(output_path, image_format) {
Ok(_) => println!("Saved image: {:?}", output_path.display()),
Err(err) => {
eprintln!(
"Encountered error while trying to save image \"{}\": {}",
output_path.display(),
err.if_supports_color(owo_colors::Stream::Stderr, |text| text.red())
);
std::process::exit(127)
}
};
}

fn gif_frame_count(input_path: &Path) -> usize {
let file = std::fs::File::open(input_path).expect("Error opening file");
let decoder = image::codecs::gif::GifDecoder::new(&file).expect("Error decoding GIF");
decoder.into_frames().count()
}

pub fn convert_gif(
input_path: &Path,
output_path: &Path,
palettes_lab: &[Lab],
deltae_method: CLIDEMethod,
) {
const CHUNK: usize = 4;
let file = std::fs::File::open(input_path).expect("Error opening file");
let decoder = image::codecs::gif::GifDecoder::new(&file).expect("Error decoding GIF");

println!("Dimensions {:?}", decoder.dimensions());

let frames = decoder.into_frames();
let frame_count = gif_frame_count(input_path);
let mut output_frames = Vec::new();

let progress_bar = ProgressBar::new(frame_count as u64);
progress_bar.set_style(
ProgressStyle::with_template(
"[{elapsed_precise}] [{wide_bar}] {pos}/{len} ({eta_precise})",
)
.expect("Failed to set progress bar style"),
);

for (_idx, frame) in frames.enumerate() {
match frame {
Ok(frame) => {
let mut image = frame.into_buffer();
image.par_chunks_mut(CHUNK).for_each(|bytes| {
let pixel: [u8; CHUNK] =
bytes.try_into().expect("Error converting bytes to pixel");
let lab = Lab::from(pixel);
let new_rgb = lab
.to_nearest_palette(palettes_lab, deltae::DEMethod::from(deltae_method))
.to_rgb();
bytes[..3].copy_from_slice(&new_rgb);
});

output_frames.push(image::Frame::new(image));
progress_bar.inc(1);
}
Err(e) => {
eprintln!("Error: {}", e);
}
}
}
progress_bar.finish();

let gif_out = std::fs::File::create(output_path).expect("Error creating output file");
let mut encoder = image::codecs::gif::GifEncoder::new(gif_out);
encoder
.set_repeat(image::codecs::gif::Repeat::Infinite)
.expect("Error setting gif encoder repeat");
encoder.encode_frames(output_frames).unwrap();
}
95 changes: 28 additions & 67 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
use std::io::{self, stdout, BufWriter, Write};

use clap::Parser;
use delta::Lab;
use indicatif::{ParallelProgressIterator, ProgressBar, ProgressStyle};
use owo_colors::{OwoColorize, Style};
use rayon::{
prelude::{IntoParallelIterator, ParallelIterator},
slice::ParallelSliceMut,
};
use rayon::prelude::{IntoParallelIterator, ParallelIterator};

use crate::{
cli::Cli,
config::{output_file_name, parse_palette},
convert_image_format::{convert_default, convert_gif},
delta::Lab,
};

mod cli;
mod config;
mod convert_image_format;
mod delta;
mod palettes;

Expand Down Expand Up @@ -150,69 +148,28 @@ fn main() -> io::Result<()> {

for (idx, path) in cli.process.iter().enumerate() {
let start = std::time::Instant::now();
// Open image
let mut image = match image::open(path) {
Ok(i) => i.into_rgba8(),
println!(
"[{}/{}] Converting image... (this may take a while)",
idx + 1,
cli.process.len()
);

let path_extension: image::ImageFormat = match image::ImageFormat::from_path(path) {
Ok(format) => format,
Err(err) => {
eprintln!(
"Encountered error while opening image at path {}: {}",
path.display()
.if_supports_color(owo_colors::Stream::Stderr, |text| text.blue()),
"Failed to get extension of file \"{}\" with error: {}",
path.display(),
err.if_supports_color(owo_colors::Stream::Stderr, |text| text.red())
);
std::process::exit(127)
}
};

println!(
"[{}/{}] Converting image... (this may take a while)",
idx + 1,
cli.process.len()
);

const CHUNK: usize = 4;
// Convert image to LAB representation
// let mut lab = Vec::with_capacity(image.as_raw().len() / CHUNK);
// image
// .par_chunks_exact(CHUNK)
// .map(|pixel| {
// let pixel: [u8; CHUNK] = pixel.try_into().unwrap();
// Lab::from(pixel)
// })
// .collect_into_vec(&mut lab);
//
// LAB conversion moved into palette

// Apply palettes to image
let progress_bar = ProgressBar::new(
(image.len() / CHUNK)
.try_into()
.expect("Failed to convert usize to u64"),
);
progress_bar.set_style(
ProgressStyle::with_template(
"[{elapsed_precise}] [{wide_bar}] {pos}/{len} ({eta_precise})",
)
.expect("Failed to set progress bar style"),
);
let progress_bar_clone = progress_bar.clone();
image
.par_chunks_exact_mut(CHUNK)
.progress_with(progress_bar)
.for_each(|bytes| {
let pixel: [u8; CHUNK] = bytes.try_into().unwrap();
let lab = Lab::from(pixel);
let new_rgb = lab
.to_nearest_palette(&palettes_lab, deltae::DEMethod::from(cli.method))
.to_rgb();
bytes[..3].copy_from_slice(&new_rgb);
});
progress_bar_clone.finish();

let output_file_name = match &cli.output {
Some(output_vec) => {
let mut name = output_vec[idx].clone();
name.set_extension("png");
name.set_extension(path_extension.extensions_str()[0]);
match &cli.dir_output {
Some(path) => {
let mut output = path.clone();
Expand All @@ -231,6 +188,7 @@ fn main() -> io::Result<()> {
output.push(output_file_name(
&cli.dir_output,
path,
path_extension.extensions_str()[0],
&cli.color_palette,
&palettes,
deltae::DEMethod::from(cli.method),
Expand All @@ -239,17 +197,20 @@ fn main() -> io::Result<()> {
}
};

match image.save_with_format(&output_file_name, image::ImageFormat::Png) {
Ok(_) => println!("Saved image: {:?}", output_file_name.display()),
Err(err) => {
eprintln!(
"Encountered error while trying to save image \"{}\": {}",
output_file_name.display(),
err.if_supports_color(owo_colors::Stream::Stderr, |text| text.red())
match path_extension {
image::ImageFormat::Gif => {
convert_gif(path, &output_file_name, &palettes_lab, cli.method);
}
_ => {
convert_default(
path,
&output_file_name,
path_extension,
&palettes_lab,
cli.method,
);
std::process::exit(127)
}
};
}

if cli.verbose >= 1 {
let duration = start.elapsed().as_secs_f32();
Expand Down