use std::collections::HashMap;
use std::fmt;
use std::io;
use std::iter;
use std::path::Path;
use image::{self, DynamicImage, GenericImage, ImageFormat};
use util::animated_gif::{self, GifAnimation, is_gif, is_gif_animated};
use super::Loader;
use super::filesystem::PathLoader;
pub const DEFAULT_IMAGE_FORMAT: ImageFormat = ImageFormat::PNG;
lazy_static! {
#[doc(hidden)]
pub static ref IMAGE_FORMAT_EXTENSIONS: HashMap<&'static str, ImageFormat> = hashmap!{
"gif" => ImageFormat::GIF,
"jpeg" => ImageFormat::JPEG,
"jpg" => ImageFormat::JPEG,
"png" => ImageFormat::PNG,
};
}
#[derive(Clone)]
pub enum Template {
Image(DynamicImage, ImageFormat),
Animation(GifAnimation),
}
impl Template {
pub fn for_image<P: AsRef<Path>>(img: DynamicImage, path: P) -> Self {
let extension = path.as_ref().extension().and_then(|e| e.to_str())
.map(|s| s.trim().to_lowercase());
let img_format = extension
.and_then(|ext| IMAGE_FORMAT_EXTENSIONS.get(ext.as_str()).map(|f| *f))
.unwrap_or(DEFAULT_IMAGE_FORMAT);
Template::Image(img, img_format)
}
#[inline]
pub fn for_gif_animation(gif_anim: GifAnimation) -> Self {
Template::Animation(gif_anim)
}
}
impl Template {
#[inline]
pub fn is_animated(&self) -> bool {
match *self { Template::Animation(..) => true, _ => false, }
}
#[inline]
pub fn image_count(&self) -> usize {
match *self {
Template::Image(..) => 1,
Template::Animation(ref gif_anim) => gif_anim.frames_count(),
}
}
pub fn iter_images<'t>(&'t self) -> Box<Iterator<Item=&'t DynamicImage> + 't> {
match *self {
Template::Image(ref img, ..) => Box::new(iter::once(img)),
Template::Animation(ref gif_anim) => Box::new(
gif_anim.iter_frames().map(|f| &f.image)),
}
}
pub fn preferred_format(&self) -> ImageFormat {
match *self {
Template::Image(_, fmt) => match fmt {
ImageFormat::PNG | ImageFormat::JPEG => return fmt,
_ => {}
},
Template::Animation(..) => return ImageFormat::GIF,
}
DEFAULT_IMAGE_FORMAT
}
}
impl fmt::Debug for Template {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
Template::Image(ref img, f) => {
let (width, height) = img.dimensions();
write!(fmt, "Template::Image({}x{}, {:?})", width, height, f)
}
Template::Animation(ref gif_anim) => {
write!(fmt, "Template::Animation({} frame(s))", gif_anim.frames_count())
}
}
}
}
#[derive(Debug, Error)]
pub enum TemplateError {
#[error(msg = "I/O error while loading template")]
File(io::Error),
#[error(msg = "error while opening template image")]
OpenImage(image::ImageError),
#[error(msg = "error while opening animated GIF template")]
DecodeAnimatedGif(animated_gif::DecodeError),
}
#[derive(Debug)]
pub struct TemplateLoader {
inner: PathLoader<'static>,
}
impl TemplateLoader {
#[inline]
pub fn new<D: AsRef<Path>>(directory: D) -> Self {
TemplateLoader{
inner: PathLoader::for_extensions(directory, IMAGE_FORMAT_EXTENSIONS.keys()),
}
}
}
impl Loader for TemplateLoader {
type Item = Template;
type Err = TemplateError;
fn load<'n>(&self, name: &'n str) -> Result<Template, Self::Err> {
let path = self.inner.load(name)?;
if is_gif(&path) && is_gif_animated(&path).unwrap_or(false) {
trace!("Image {} is an animated GIF", path.display());
let gif_anim = animated_gif::decode_from_file(&path).map_err(|e| {
error!("Failed to open animated GIF template {}: {}",
path.display(), e); e
})?;
Ok(Template::for_gif_animation(gif_anim))
} else {
trace!("Opening image {}", path.display());
let img = image::open(&path)?;
Ok(Template::for_image(img, &path))
}
}
}