Skip to content

Commit

Permalink
Merge pull request #1361 from a1phyr/format_from_extension
Browse files Browse the repository at this point in the history
Add `ImageFormat::from_extension`
  • Loading branch information
HeroicKatora committed Nov 15, 2020
2 parents e0261ce + b6507cf commit 085300e
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 54 deletions.
69 changes: 65 additions & 4 deletions src/image.rs
@@ -1,5 +1,6 @@
#![allow(clippy::too_many_arguments)]
use std::convert::TryFrom;
use std::ffi::OsStr;
use std::io;
use std::io::Read;
use std::ops::{Deref, DerefMut};
Expand All @@ -8,7 +9,7 @@ use std::usize;

use crate::ImageBuffer;
use crate::color::{ColorType, ExtendedColorType};
use crate::error::{ImageError, ImageResult, LimitError, LimitErrorKind, ParameterError, ParameterErrorKind};
use crate::error::{ImageError, ImageFormatHint, ImageResult, LimitError, LimitErrorKind, ParameterError, ParameterErrorKind};
use crate::math::Rect;
use crate::traits::Pixel;

Expand Down Expand Up @@ -65,11 +66,71 @@ pub enum ImageFormat {
}

impl ImageFormat {
/// Return the image format specified by a path's file extension.
///
/// # Example
///
/// ```
/// use image::ImageFormat;
///
/// let format = ImageFormat::from_extension("jpg");
/// assert_eq!(format, Some(ImageFormat::Jpeg));
/// ```
#[inline]
pub fn from_extension<S>(ext: S) -> Option<Self> where S: AsRef<OsStr> {
// thin wrapper function to strip generics
fn inner(ext: &OsStr) -> Option<ImageFormat> {
let ext = ext.to_str()?.to_ascii_lowercase();

Some(match ext.as_str() {
"jpg" | "jpeg" => ImageFormat::Jpeg,
"png" => ImageFormat::Png,
"gif" => ImageFormat::Gif,
"webp" => ImageFormat::WebP,
"tif" | "tiff" => ImageFormat::Tiff,
"tga" => ImageFormat::Tga,
"dds" => ImageFormat::Dds,
"bmp" => ImageFormat::Bmp,
"ico" => ImageFormat::Ico,
"hdr" => ImageFormat::Hdr,
"pbm" | "pam" | "ppm" | "pgm" => ImageFormat::Pnm,
"ff" | "farbfeld" => ImageFormat::Farbfeld,
_ => return None,
})
}

inner(ext.as_ref())
}

/// Return the image format specified by the path's file extension.
///
/// # Example
///
/// ```
/// use image::ImageFormat;
///
/// let format = ImageFormat::from_path("images/ferris.png")?;
/// assert_eq!(format, ImageFormat::Png);
///
/// # Ok::<(), image::error::ImageError>(())
/// ```
#[inline]
pub fn from_path<P>(path: P) -> ImageResult<Self> where P : AsRef<Path> {
// thin wrapper function to strip generics before calling from_path_impl
crate::io::free_functions::guess_format_from_path_impl(path.as_ref())
.map_err(Into::into)
// thin wrapper function to strip generics
fn inner(path: &Path) -> ImageResult<ImageFormat> {
let exact_ext = path.extension();
exact_ext
.and_then(|ext| ImageFormat::from_extension(ext))
.ok_or_else(|| {
let format_hint = match exact_ext {
None => ImageFormatHint::Unknown,
Some(os) => ImageFormatHint::PathExtension(os.into()),
};
ImageError::Unsupported(format_hint.into())
})
}

inner(path.as_ref())
}

/// Return a list of applicable extensions for this format.
Expand Down
50 changes: 0 additions & 50 deletions src/io/free_functions.rs
@@ -1,4 +1,3 @@
use std::ffi::OsString;
use std::fs::File;
use std::io::{BufRead, BufReader, BufWriter, Seek};
use std::path::Path;
Expand Down Expand Up @@ -39,14 +38,6 @@ use crate::image::ImageFormat;
#[allow(unused_imports)] // When no features are supported
use crate::image::{ImageDecoder, ImageEncoder};

/// Internal error type for guessing format from path.
pub(crate) enum PathError {
/// The extension did not fit a supported format.
UnknownExtension(OsString),
/// Extension could not be converted to `str`.
NoExtension,
}

pub(crate) fn open_impl(path: &Path) -> ImageResult<DynamicImage> {
let fin = match File::open(path) {
Ok(f) => f,
Expand Down Expand Up @@ -224,37 +215,6 @@ pub(crate) fn save_buffer_with_format_impl(
}
}

/// Guess format from a path.
///
/// Returns `PathError::NoExtension` if the path has no extension or returns a
/// `PathError::UnknownExtension` containing the extension if it can not be convert to a `str`.
pub(crate) fn guess_format_from_path_impl(path: &Path) -> Result<ImageFormat, PathError> {
let exact_ext = path.extension();
let ext = exact_ext
.and_then(|s| s.to_str())
.map_or("".to_string(), |s| s.to_ascii_lowercase());

Ok(match ext.as_str() {
"jpg" | "jpeg" => image::ImageFormat::Jpeg,
"png" => image::ImageFormat::Png,
"gif" => image::ImageFormat::Gif,
"webp" => image::ImageFormat::WebP,
"tif" | "tiff" => image::ImageFormat::Tiff,
"tga" => image::ImageFormat::Tga,
"dds" => image::ImageFormat::Dds,
"bmp" => image::ImageFormat::Bmp,
"ico" => image::ImageFormat::Ico,
"hdr" => image::ImageFormat::Hdr,
"pbm" | "pam" | "ppm" | "pgm" => image::ImageFormat::Pnm,
"ff" | "farbfeld" => image::ImageFormat::Farbfeld,
// The original extension is used, instead of _format
_ => return match exact_ext {
None => Err(PathError::NoExtension),
Some(os) => Err(PathError::UnknownExtension(os.to_owned())),
},
})
}

static MAGIC_BYTES: [(&[u8], ImageFormat); 19] = [
(b"\x89PNG\r\n\x1a\n", ImageFormat::Png),
(&[0xff, 0xd8, 0xff], ImageFormat::Jpeg),
Expand Down Expand Up @@ -298,13 +258,3 @@ pub(crate) fn guess_format_impl(buffer: &[u8]) -> Option<ImageFormat> {

None
}

impl From<PathError> for ImageError {
fn from(path: PathError) -> Self {
let format_hint = match path {
PathError::NoExtension => ImageFormatHint::Unknown,
PathError::UnknownExtension(ext) => ImageFormatHint::PathExtension(ext.into()),
};
ImageError::Unsupported(format_hint.into())
}
}

0 comments on commit 085300e

Please sign in to comment.