Add jpegxl and avif support

This commit is contained in:
asonix 2023-06-21 17:05:35 -05:00
parent e925f2ba58
commit 6d2aef8cc0
5 changed files with 53 additions and 16 deletions

View file

@ -121,7 +121,7 @@ Options:
--media-filters <MEDIA_FILTERS>
Which media filters should be enabled on the `process` endpoint
--media-format <MEDIA_FORMAT>
Enforce uploaded media is transcoded to the provided format [possible values: jpeg, webp, png]
Enforce uploaded media is transcoded to the provided format [possible values: avif, jpeg, jxl, png, webp]
-h, --help
Print help information (use `--help` for more detail)
```
@ -383,7 +383,7 @@ pict-rs offers the following endpoints:
aspect ratio. For example, a 1600x900 image cropped with a 1x1 aspect ratio will become 900x900. A
1600x1100 image cropped with a 16x9 aspect ratio will become 1600x900.
Supported `ext` file extensions include `png`, `jpg`, and `webp`
Supported `ext` file extensions include `avif`, `jpg`, `jxl`, `png`, and `webp`
An example of usage could be
```

View file

@ -188,10 +188,10 @@ filters = ['blur', 'crop', 'identity', 'resize', 'thumbnail']
# environment variable: PICTRS__MEDIA__FORMAT
# default: empty
#
# available options: png, jpeg, webp
# When set, all uploaded still images will be converted to this file type. If you care about file
# size, setting this to 'webp' is probably the best option. By default, images are stored in their
# original file type.
# available options: avif, png, jpeg, jxl, webp
# When set, all uploaded still images will be converted to this file type. For balancing quality vs
# file size vs browser support, 'avif', 'jxl', and 'webp' should be considered. By default, images
# are stored in their original file type.
format = "webp"
## Optional: whether to validate images uploaded through the `import` endpoint

View file

@ -40,9 +40,11 @@ pub(crate) enum LogFormat {
)]
#[serde(rename_all = "snake_case")]
pub(crate) enum ImageFormat {
Avif,
Jpeg,
Webp,
Jxl,
Png,
Webp,
}
#[derive(
@ -158,7 +160,9 @@ impl ImageFormat {
pub(crate) fn as_magick_format(self) -> &'static str {
match self {
Self::Avif => "AVIF",
Self::Jpeg => "JPEG",
Self::Jxl => "JXL",
Self::Png => "PNG",
Self::Webp => "WEBP",
}
@ -166,7 +170,9 @@ impl ImageFormat {
pub(crate) fn as_ext(self) -> &'static str {
match self {
Self::Avif => ".avif",
Self::Jpeg => ".jpeg",
Self::Jxl => ".jxl",
Self::Png => ".png",
Self::Webp => ".webp",
}
@ -243,7 +249,9 @@ impl FromStr for ImageFormat {
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"avif" => Ok(Self::Avif),
"jpeg" | "jpg" => Ok(Self::Jpeg),
"jxl" => Ok(Self::Jxl),
"png" => Ok(Self::Png),
"webp" => Ok(Self::Webp),
other => Err(format!("Invalid variant: {other}")),

View file

@ -222,7 +222,9 @@ impl ValidInputType {
Self::Gif => FileFormat::Video(VideoFormat::Gif),
Self::Mp4 => FileFormat::Video(VideoFormat::Mp4),
Self::Webm => FileFormat::Video(VideoFormat::Webm),
Self::Avif => FileFormat::Image(ImageFormat::Avif),
Self::Jpeg => FileFormat::Image(ImageFormat::Jpeg),
Self::Jxl => FileFormat::Image(ImageFormat::Jxl),
Self::Png => FileFormat::Image(ImageFormat::Png),
Self::Webp => FileFormat::Image(ImageFormat::Webp),
}
@ -472,7 +474,7 @@ fn parse_details(output: std::borrow::Cow<'_, str>) -> Result<Option<Details>, E
for (k, v) in FORMAT_MAPPINGS {
if formats.contains(k) {
return Ok(Some(parse_details_inner(width, height, frames, *v)?));
return parse_details_inner(width, height, frames, *v);
}
}
@ -484,17 +486,22 @@ fn parse_details_inner(
height: &str,
frames: &str,
format: VideoFormat,
) -> Result<Details, Error> {
) -> Result<Option<Details>, Error> {
let width = width.parse().map_err(|_| UploadError::UnsupportedFormat)?;
let height = height.parse().map_err(|_| UploadError::UnsupportedFormat)?;
let frames = frames.parse().map_err(|_| UploadError::UnsupportedFormat)?;
Ok(Details {
// Probably a still image. ffmpeg thinks AVIF is an mp4
if frames == 1 {
return Ok(None);
}
Ok(Some(Details {
mime_type: format.to_mime(),
width,
height,
frames: Some(frames),
})
}))
}
async fn pixel_format(input_file: &str) -> Result<String, Error> {

View file

@ -22,6 +22,14 @@ pub(crate) fn details_hint(alias: &Alias) -> Option<ValidInputType> {
}
}
fn image_avif() -> mime::Mime {
"image/avif".parse().unwrap()
}
fn image_jxl() -> mime::Mime {
"image/jxl".parse().unwrap()
}
fn image_webp() -> mime::Mime {
"image/webp".parse().unwrap()
}
@ -39,8 +47,10 @@ pub(crate) enum ValidInputType {
Mp4,
Webm,
Gif,
Png,
Avif,
Jpeg,
Jxl,
Png,
Webp,
}
@ -50,8 +60,10 @@ impl ValidInputType {
Self::Mp4 => "MP4",
Self::Webm => "WEBM",
Self::Gif => "GIF",
Self::Png => "PNG",
Self::Avif => "AVIF",
Self::Jpeg => "JPEG",
Self::Jxl => "JXL",
Self::Png => "PNG",
Self::Webp => "WEBP",
}
}
@ -61,8 +73,10 @@ impl ValidInputType {
Self::Mp4 => ".mp4",
Self::Webm => ".webm",
Self::Gif => ".gif",
Self::Png => ".png",
Self::Avif => ".avif",
Self::Jpeg => ".jpeg",
Self::Jxl => ".jxl",
Self::Png => ".png",
Self::Webp => ".webp",
}
}
@ -89,7 +103,9 @@ impl ValidInputType {
pub(crate) const fn from_format(format: ImageFormat) -> Self {
match format {
ImageFormat::Avif => ValidInputType::Avif,
ImageFormat::Jpeg => ValidInputType::Jpeg,
ImageFormat::Jxl => ValidInputType::Jxl,
ImageFormat::Png => ValidInputType::Png,
ImageFormat::Webp => ValidInputType::Webp,
}
@ -97,7 +113,9 @@ impl ValidInputType {
pub(crate) const fn to_format(self) -> Option<ImageFormat> {
match self {
Self::Avif => Some(ImageFormat::Avif),
Self::Jpeg => Some(ImageFormat::Jpeg),
Self::Jxl => Some(ImageFormat::Jxl),
Self::Png => Some(ImageFormat::Png),
Self::Webp => Some(ImageFormat::Webp),
_ => None,
@ -273,8 +291,10 @@ fn parse_details(s: std::borrow::Cow<'_, str>) -> Result<Details, Error> {
"MP4" => video_mp4(),
"WEBM" => video_webm(),
"GIF" => mime::IMAGE_GIF,
"PNG" => mime::IMAGE_PNG,
"AVIF" => image_avif(),
"JPEG" => mime::IMAGE_JPEG,
"JXL" => image_jxl(),
"PNG" => mime::IMAGE_PNG,
"WEBP" => image_webp(),
_ => return Err(UploadError::UnsupportedFormat.into()),
};
@ -343,8 +363,10 @@ impl Details {
(mime::VIDEO, mime::MP4 | mime::MPEG) => ValidInputType::Mp4,
(mime::VIDEO, subtype) if subtype.as_str() == "webm" => ValidInputType::Webm,
(mime::IMAGE, mime::GIF) => ValidInputType::Gif,
(mime::IMAGE, mime::PNG) => ValidInputType::Png,
(mime::IMAGE, subtype) if subtype.as_str() == "avif" => ValidInputType::Avif,
(mime::IMAGE, mime::JPEG) => ValidInputType::Jpeg,
(mime::IMAGE, subtype) if subtype.as_str() == "jxl" => ValidInputType::Jxl,
(mime::IMAGE, mime::PNG) => ValidInputType::Png,
(mime::IMAGE, subtype) if subtype.as_str() == "webp" => ValidInputType::Webp,
_ => return Err(UploadError::UnsupportedFormat.into()),
};