From 1a6bf92f649787508732a1c769b677997286ac1e Mon Sep 17 00:00:00 2001 From: Vladyslav Vladinov Date: Sun, 28 Apr 2024 19:10:30 +0200 Subject: [PATCH] fix(cli): fixed encoder function --- src/cli/pipeline.rs | 146 ++++++++++++++++++++++++++++++++------------ src/main.rs | 34 +++++++---- 2 files changed, 128 insertions(+), 52 deletions(-) diff --git a/src/cli/pipeline.rs b/src/cli/pipeline.rs index f84cf2bc..1a928463 100644 --- a/src/cli/pipeline.rs +++ b/src/cli/pipeline.rs @@ -1,7 +1,15 @@ -use std::{collections::BTreeMap, path::Path}; +use std::{collections::BTreeMap, fs::File, io::Read, path::Path}; use clap::ArgMatches; -use zune_core::options::EncoderOptions; +#[cfg(feature = "avif")] +use rimage::codecs::avif::AvifEncoder; +#[cfg(feature = "mozjpeg")] +use rimage::codecs::mozjpeg::MozJpegEncoder; +#[cfg(feature = "oxipng")] +use rimage::codecs::oxipng::OxiPngEncoder; +#[cfg(feature = "webp")] +use rimage::codecs::webp::WebPEncoder; +use zune_core::{bytestream::ZByteWriterTrait, options::EncoderOptions}; use zune_image::{ codecs::{ farbfeld::FarbFeldEncoder, jpeg::JpegEncoder, jpeg_xl::JxlEncoder, png::PngEncoder, @@ -18,22 +26,22 @@ pub fn decode>(f: P) -> Result { Image::open(f.as_ref()).or_else(|e| { if matches!(e, ImageErrors::ImageDecoderNotImplemented(_)) { #[cfg(any(feature = "avif", feature = "webp"))] - let file_content = std::fs::read("tests/files/avif/f1t.avif")?; + let mut file = File::open("tests/files/avif/f1t.avif")?; #[cfg(feature = "avif")] - if libavif::is_avif(&file_content) { - use rimage::codecs::avif::AvifDecoder; - use zune_core::bytestream::ZByteReader; - use zune_image::traits::DecoderTrait; + { + let mut file_content = vec![]; - let reader = ZByteReader::new(file_content); + file.read_to_end(&mut file_content)?; - let mut decoder = AvifDecoder::try_new(reader)?; + if libavif::is_avif(&file_content) { + use rimage::codecs::avif::AvifDecoder; - return >> as DecoderTrait>>::decode( - &mut decoder, - ); - }; + let decoder = AvifDecoder::try_new(file)?; + + return Image::from_decoder(decoder); + }; + } #[cfg(feature = "webp")] if f.as_ref() @@ -41,16 +49,10 @@ pub fn decode>(f: P) -> Result { .is_some_and(|f| f.eq_ignore_ascii_case("webp")) { use rimage::codecs::webp::WebPDecoder; - use zune_core::bytestream::ZByteReader; - use zune_image::traits::DecoderTrait; - let reader = ZByteReader::new(file_content); + let decoder = WebPDecoder::try_new(file)?; - let mut decoder = WebPDecoder::try_new(reader)?; - - return >> as DecoderTrait>>::decode( - &mut decoder, - ); + return Image::from_decoder(decoder); }; Err(ImageErrors::ImageDecoderNotImplemented( @@ -153,12 +155,68 @@ pub fn operations(matches: &ArgMatches, img: &Image) -> BTreeMap Result<(Box, &'static str), ImageErrors> { +pub enum AvailableEncoders { + FarbFeld(Box), + Jpeg(Box), + JpegXl(Box), + #[cfg(feature = "mozjpeg")] + MozJpeg(Box), + #[cfg(feature = "oxipng")] + OxiPng(Box), + #[cfg(feature = "avif")] + Avif(Box), + #[cfg(feature = "webp")] + Webp(Box), + Png(Box), + Ppm(Box), + Qoi(Box), +} + +impl AvailableEncoders { + pub fn to_extension(&self) -> &'static str { + match self { + AvailableEncoders::FarbFeld(_) => "ff", + AvailableEncoders::Jpeg(_) => "jpg", + AvailableEncoders::JpegXl(_) => "jxl", + #[cfg(feature = "mozjpeg")] + AvailableEncoders::MozJpeg(_) => "jpg", + #[cfg(feature = "oxipng")] + AvailableEncoders::OxiPng(_) => "png", + #[cfg(feature = "avif")] + AvailableEncoders::Avif(_) => "avif", + #[cfg(feature = "webp")] + AvailableEncoders::Webp(_) => "webp", + AvailableEncoders::Png(_) => "png", + AvailableEncoders::Ppm(_) => "ppm", + AvailableEncoders::Qoi(_) => "qoi", + } + } + + pub fn encode( + &mut self, + img: &Image, + sink: T, + ) -> Result { + match self { + AvailableEncoders::FarbFeld(enc) => enc.encode(img, sink), + AvailableEncoders::Jpeg(enc) => enc.encode(img, sink), + AvailableEncoders::JpegXl(enc) => enc.encode(img, sink), + AvailableEncoders::MozJpeg(enc) => enc.encode(img, sink), + AvailableEncoders::OxiPng(enc) => enc.encode(img, sink), + AvailableEncoders::Avif(enc) => enc.encode(img, sink), + AvailableEncoders::Webp(enc) => enc.encode(img, sink), + AvailableEncoders::Png(enc) => enc.encode(img, sink), + AvailableEncoders::Ppm(enc) => enc.encode(img, sink), + AvailableEncoders::Qoi(enc) => enc.encode(img, sink), + } + } +} + +pub fn encoder(name: &str, matches: &ArgMatches) -> Result { match name { - "farbfeld" => Ok((Box::new(FarbFeldEncoder::new()), "ff")), + "farbfeld" => Ok(AvailableEncoders::FarbFeld( + Box::new(FarbFeldEncoder::new()), + )), "jpeg" => { let options = EncoderOptions::default(); @@ -168,13 +226,15 @@ pub fn encoder( options.set_jpeg_encode_progressive(matches.get_flag("progressive")); - Ok((Box::new(JpegEncoder::new_with_options(options)), "jpg")) + Ok(AvailableEncoders::Jpeg(Box::new( + JpegEncoder::new_with_options(options), + ))) } - "jpeg_xl" => Ok((Box::new(JxlEncoder::new()), "jxl")), + "jpeg_xl" => Ok(AvailableEncoders::JpegXl(Box::new(JxlEncoder::new()))), #[cfg(feature = "mozjpeg")] "mozjpeg" => { use mozjpeg::qtable; - use rimage::codecs::mozjpeg::{MozJpegEncoder, MozJpegOptions}; + use rimage::codecs::mozjpeg::MozJpegOptions; let quality = *matches.get_one::("quality").unwrap() as f32; let chroma_quality = matches @@ -246,11 +306,13 @@ pub fn encoder( }), }; - Ok((Box::new(MozJpegEncoder::new_with_options(options)), "jpg")) + Ok(AvailableEncoders::MozJpeg(Box::new( + MozJpegEncoder::new_with_options(options), + ))) } #[cfg(feature = "oxipng")] "oxipng" => { - use rimage::codecs::oxipng::{OxiPngEncoder, OxiPngOptions}; + use rimage::codecs::oxipng::OxiPngOptions; let mut options = OxiPngOptions::from_preset(*matches.get_one::("effort").unwrap_or(&2)); @@ -261,11 +323,13 @@ pub fn encoder( None }; - Ok((Box::new(OxiPngEncoder::new_with_options(options)), "png")) + Ok(AvailableEncoders::OxiPng(Box::new( + OxiPngEncoder::new_with_options(options), + ))) } #[cfg(feature = "avif")] "avif" => { - use rimage::codecs::avif::{AvifEncoder, AvifOptions}; + use rimage::codecs::avif::AvifOptions; let options = AvifOptions { quality: *matches.get_one::("quality").unwrap() as f32, @@ -284,11 +348,13 @@ pub fn encoder( }, }; - Ok((Box::new(AvifEncoder::new_with_options(options)), "avif")) + Ok(AvailableEncoders::Avif(Box::new( + AvifEncoder::new_with_options(options), + ))) } #[cfg(feature = "webp")] "webp" => { - use rimage::codecs::webp::{WebPEncoder, WebPOptions}; + use rimage::codecs::webp::WebPOptions; let mut options = WebPOptions::new().unwrap(); @@ -297,11 +363,13 @@ pub fn encoder( options.near_lossless = 100 - *matches.get_one::("slight_loss").unwrap() as i32; options.exact = matches.get_flag("exact") as i32; - Ok((Box::new(WebPEncoder::new_with_options(options)), "webp")) + Ok(AvailableEncoders::Webp(Box::new( + WebPEncoder::new_with_options(options), + ))) } - "png" => Ok((Box::new(PngEncoder::new()), "png")), - "ppm" => Ok((Box::new(PPMEncoder::new()), "ppm")), - "qoi" => Ok((Box::new(QoiEncoder::new()), "qoi")), + "png" => Ok(AvailableEncoders::Png(Box::new(PngEncoder::new()))), + "ppm" => Ok(AvailableEncoders::Ppm(Box::new(PPMEncoder::new()))), + "qoi" => Ok(AvailableEncoders::Qoi(Box::new(QoiEncoder::new()))), name => Err(ImageErrors::GenericString(format!( "Encoder \"{name}\" not found", diff --git a/src/main.rs b/src/main.rs index 808c81cf..512212da 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,11 @@ -use std::{fs, path::PathBuf}; +use std::{ + fs::{self, File}, + path::PathBuf, +}; use cli::{ cli, - pipeline::{decode, encoder, operations}, + pipeline::{decode, operations}, utils::paths::{collect_files, get_paths}, }; use rayon::prelude::*; @@ -10,6 +13,8 @@ use zune_core::colorspace::ColorSpace; use zune_image::{core_filters::colorspace::ColorspaceConv, image::Image, pipelines::Pipeline}; use zune_imageprocs::auto_orient::AutoOrient; +use crate::cli::pipeline::encoder; + mod cli; macro_rules! handle_error { @@ -73,30 +78,28 @@ fn main() { let img = handle_error!(input, decode(&input)); - let (encoder, ext) = handle_error!(input, encoder(subcommand, matches)); - output.set_extension(ext); + let mut available_encoder = handle_error!(input, encoder(subcommand, matches)); + output.set_extension(available_encoder.to_extension()); - pipeline.add_operation(Box::new(AutoOrient)); + pipeline.chain_operations(Box::new(AutoOrient)); operations(matches, &img) .into_iter() .for_each(|(_, operations)| match operations.name() { "quantize" => { - pipeline.add_operation(Box::new(ColorspaceConv::new(ColorSpace::RGBA))); - pipeline.add_operation(operations); + pipeline + .chain_operations(Box::new(ColorspaceConv::new(ColorSpace::RGBA))); + pipeline.chain_operations(operations); } _ => { - pipeline.add_operation(operations); + pipeline.chain_operations(operations); } }); - pipeline.add_decoder(img); - pipeline.add_encoder(encoder); + pipeline.chain_image(img); handle_error!(input, pipeline.advance_to_end()); - let data = pipeline.get_results()[0].data(); - if backup { handle_error!( input, @@ -112,7 +115,12 @@ fn main() { } handle_error!(output, fs::create_dir_all(output.parent().unwrap())); - handle_error!(output, fs::write(&output, data)); + let output_file = handle_error!(output, File::create(&output)); + + handle_error!( + output, + available_encoder.encode(&pipeline.images()[0], output_file) + ); }); } None => unreachable!(),