use magick_rust::{bindings, MagickError};
use str_utils::EndsWithIgnoreAsciiCase;
use crate::{compute_output_size_sharpen, fetch_magic_wand, Crop, ImageConfig, ImageResource};
#[derive(Debug)]
struct ICOConfigInner {
remain_profile: bool,
width: u16,
height: u16,
crop: Option<Crop>,
shrink_only: bool,
sharpen: f64,
}
impl ICOConfigInner {
pub fn from(config: &ICOConfig) -> Vec<ICOConfigInner> {
let mut output = Vec::new();
for (width, height) in config.size.iter().copied() {
output.push(ICOConfigInner {
remain_profile: config.remain_profile,
width,
height,
crop: config.crop,
shrink_only: false,
sharpen: config.sharpen,
});
}
output
}
}
#[derive(Debug)]
pub struct ICOConfig {
pub remain_profile: bool,
pub size: Vec<(u16, u16)>,
pub crop: Option<Crop>,
pub sharpen: f64,
}
impl ICOConfig {
#[inline]
pub fn new() -> ICOConfig {
ICOConfig {
remain_profile: false,
size: Vec::with_capacity(1),
crop: None,
sharpen: -1f64,
}
}
}
impl Default for ICOConfig {
#[inline]
fn default() -> Self {
ICOConfig::new()
}
}
impl ImageConfig for ICOConfigInner {
#[inline]
fn is_remain_profile(&self) -> bool {
self.remain_profile
}
#[inline]
fn get_width(&self) -> u16 {
self.width
}
#[inline]
fn get_height(&self) -> u16 {
self.height
}
#[inline]
fn get_crop(&self) -> Option<Crop> {
self.crop
}
#[inline]
fn get_sharpen(&self) -> f64 {
self.sharpen
}
#[inline]
fn is_shrink_only(&self) -> bool {
self.shrink_only
}
}
pub fn to_ico(
output: &mut ImageResource,
input: &ImageResource,
config: &ICOConfig,
) -> Result<(), MagickError> {
let mut icon_dir = ico::IconDir::new(ico::ResourceType::Icon);
let ico_config_inner = ICOConfigInner::from(config);
let mut config_iter = ico_config_inner.iter();
let config = config_iter.next();
if let Some(config) = config {
let (mut mw, vector) = fetch_magic_wand(input, config)?;
if vector {
if !config.remain_profile {
mw.profile_image("*", None)?;
}
mw.set_image_format("RGBA")?;
mw.set_image_depth(8)?;
{
let temp = mw.write_image_blob("RGBA")?;
let icon_image = ico::IconImage::from_rgba_data(
u32::from(config.width),
u32::from(config.height),
temp,
);
icon_dir.add_entry(ico::IconDirEntry::encode_as_bmp(&icon_image).unwrap());
}
for config in config_iter {
let (mut mw, vector) = fetch_magic_wand(input, config)?;
if !vector {
return Err("The input image may not be a correct vector.".into());
}
mw.profile_image("*", None)?;
mw.set_image_format("RGBA")?;
mw.set_image_depth(8)?;
let temp = mw.write_image_blob("RGBA")?;
let icon_image = ico::IconImage::from_rgba_data(
u32::from(config.width),
u32::from(config.height),
temp,
);
icon_dir.add_entry(ico::IconDirEntry::encode_as_bmp(&icon_image).unwrap());
}
} else {
mw.profile_image("*", None)?;
mw.set_image_format("RGBA")?;
mw.set_image_depth(8)?;
{
let (width, height, sharpen) = compute_output_size_sharpen(&mw, config);
mw.resize_image(
width as usize,
height as usize,
bindings::FilterType_LanczosFilter,
);
mw.sharpen_image(0f64, sharpen)?;
let temp = mw.write_image_blob("RGBA")?;
let icon_image =
ico::IconImage::from_rgba_data(u32::from(width), u32::from(height), temp);
icon_dir.add_entry(ico::IconDirEntry::encode_as_bmp(&icon_image).unwrap());
}
for config in config_iter {
let mw = mw.clone();
let (width, height, sharpen) = compute_output_size_sharpen(&mw, config);
mw.resize_image(
width as usize,
height as usize,
bindings::FilterType_LanczosFilter,
);
mw.sharpen_image(0f64, sharpen)?;
let temp = mw.write_image_blob("RGBA")?;
let icon_image =
ico::IconImage::from_rgba_data(u32::from(width), u32::from(height), temp);
icon_dir.add_entry(ico::IconDirEntry::encode_as_bmp(&icon_image).unwrap());
}
}
}
match output {
ImageResource::Path(p) => {
if !p.ends_with_ignore_ascii_case_with_lowercase(".ico") {
return Err("The file extension name is not ico.".into());
}
let file = match std::fs::File::create(&p) {
Ok(f) => f,
Err(_) => return Err("Cannot create the icon file.".into()),
};
icon_dir.write(file).map_err(|_| "Cannot write the icon file.")?;
}
ImageResource::Data(b) => {
icon_dir.write(b).map_err(|_| "Cannot convert to icon data.")?;
}
ImageResource::MagickWand(_) => {
return Err("ICO cannot be output to a MagickWand instance.".into());
}
}
Ok(())
}