use std::fs;
use std::path::Path;
use image::ImageFormat;
use crate::{Error, Job, Result, extension_of};
pub fn convert(job: &Job) -> Result<()> {
let img = image::open(job.input)
.map_err(|e| Error::other(format!("image decode: {e}")))?;
save_to(&img, job.output)?;
Ok(())
}
pub fn from_psd(job: &Job) -> Result<()> {
let img = load_psd(job.input)?;
save_to(&img, job.output)?;
Ok(())
}
pub fn to_jp2(_job: &Job) -> Result<()> {
let from = extension_of(_job.input).unwrap_or_default();
let to = extension_of(_job.output).unwrap_or_default();
Err(Error::NotImplemented { from, to })
}
pub fn psd_to_jp2(job: &Job) -> Result<()> {
to_jp2(job)
}
pub fn from_jp2(job: &Job) -> Result<()> {
let img = load_jp2(job.input)?;
save_to(&img, job.output)?;
Ok(())
}
fn save_to(img: &image::DynamicImage, output: &Path) -> Result<()> {
let ext = extension_of(output)
.ok_or_else(|| Error::OutputNoExtension(output.to_path_buf()))?;
let fmt = match ext.as_str() {
"bmp" => ImageFormat::Bmp,
"gif" => ImageFormat::Gif,
"ico" => ImageFormat::Ico,
"jpeg" | "jpg" => ImageFormat::Jpeg,
"png" => ImageFormat::Png,
"tga" => ImageFormat::Tga,
"tif" | "tiff" => ImageFormat::Tiff,
"webp" => ImageFormat::WebP,
other => {
return Err(Error::other(format!("no image encoder for .{other}")));
}
};
let img = if matches!(fmt, ImageFormat::Ico) {
let (w, h) = (img.width(), img.height());
if w > 256 || h > 256 {
img.resize(256, 256, image::imageops::FilterType::Lanczos3)
} else {
img.clone()
}
} else {
img.clone()
};
img
.save_with_format(output, fmt)
.map_err(|e| Error::other(format!("image encode: {e}")))?;
Ok(())
}
fn load_psd(path: &Path) -> Result<image::DynamicImage> {
let bytes = fs::read(path)?;
let psd_doc = psd::Psd::from_bytes(&bytes)
.map_err(|e| Error::other(format!("psd parse: {e}")))?;
let rgba = psd_doc.rgba();
let buf = image::RgbaImage::from_raw(psd_doc.width(), psd_doc.height(), rgba)
.ok_or_else(|| Error::other("psd: failed to construct RGBA image"))?;
Ok(image::DynamicImage::ImageRgba8(buf))
}
fn load_jp2(path: &Path) -> Result<image::DynamicImage> {
let bytes = fs::read(path)?;
let img = jpeg2k::Image::from_bytes(&bytes)
.map_err(|e| Error::other(format!("jp2 decode: {e}")))?;
let dyn_img: image::DynamicImage = (&img)
.try_into()
.map_err(|e| Error::other(format!("jp2 convert: {e}")))?;
Ok(dyn_img)
}