Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ImageFormat::from_extension #1361

Merged
merged 3 commits into from Nov 15, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
43 changes: 39 additions & 4 deletions src/image.rs
Expand Up @@ -8,7 +8,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 +65,46 @@ pub enum ImageFormat {
}

impl ImageFormat {
/// Return the image format specified by a path's file extension.
a1phyr marked this conversation as resolved.
Show resolved Hide resolved
pub fn from_extension(ext: &str) -> Option<Self> {
a1phyr marked this conversation as resolved.
Show resolved Hide resolved
let ext = ext.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,
})
}

/// Return the image format specified by the path's file extension.
#[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(|s| s.to_str())
.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())
}
}