#[cfg(feature = "dng-encode")]
use crate::formats::dng_export::DngExportConfig;
use super::standard::StandardFormat;
use crate::core::CodecId;
pub use crate::core::{BitDepth, MetadataEmbedOptions};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum OutputFormat {
Png,
Jpeg,
WebP,
Avif,
Jxl,
Dng,
}
impl OutputFormat {
pub fn name(self) -> &'static str {
match self {
OutputFormat::Png => "PNG",
OutputFormat::Jpeg => "JPEG",
OutputFormat::WebP => "WebP",
OutputFormat::Avif => "AVIF",
OutputFormat::Jxl => "JPEG XL",
OutputFormat::Dng => "DNG",
}
}
pub fn extension(self) -> &'static str {
match self {
OutputFormat::Png => "png",
OutputFormat::Jpeg => "jpg",
OutputFormat::WebP => "webp",
OutputFormat::Avif => "avif",
OutputFormat::Jxl => "jxl",
OutputFormat::Dng => "dng",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CommonEncodeOptions {
pub metadata: MetadataEmbedOptions,
pub bit_depth: BitDepth,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum WebPMode {
Lossless,
Lossy,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum JpegSubsampling {
#[default]
Yuv420,
Yuv422,
Yuv444,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum AvifRateControl {
#[default]
ConstantQuality,
Constrained,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ZunePngEncodeConfig {
pub common: CommonEncodeOptions,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct JpegEncEncodeConfig {
pub common: CommonEncodeOptions,
pub quality: u8,
}
impl Default for JpegEncEncodeConfig {
fn default() -> Self {
Self {
common: CommonEncodeOptions::default(),
quality: 90,
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct LibwebpEncodeConfig {
pub common: CommonEncodeOptions,
pub mode: WebPMode,
pub quality: f32,
pub method: u32,
pub near_lossless: u32,
}
impl LibwebpEncodeConfig {
pub fn lossy() -> Self {
Self {
common: CommonEncodeOptions::default(),
mode: WebPMode::Lossy,
quality: 75.0,
method: 4,
near_lossless: 100,
}
}
pub fn lossless() -> Self {
Self {
mode: WebPMode::Lossless,
..Self::lossy()
}
}
}
impl Default for LibwebpEncodeConfig {
fn default() -> Self {
Self::lossy()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RavifEncodeConfig {
pub common: CommonEncodeOptions,
pub quality: u8,
pub speed: u8,
}
impl Default for RavifEncodeConfig {
fn default() -> Self {
Self {
common: CommonEncodeOptions::default(),
quality: 80,
speed: 6,
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ZuneJxlEncodeConfig {
pub common: CommonEncodeOptions,
pub quality: f32,
pub effort: u8,
}
impl Default for ZuneJxlEncodeConfig {
fn default() -> Self {
Self {
common: CommonEncodeOptions::default(),
quality: 0.0,
effort: 7,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum LibjxlModular {
#[default]
Auto,
VarDct,
Modular,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum LibjxlColorTransform {
#[default]
Auto,
Xyb,
None,
YCbCr,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct LibjxlEncodeConfig {
pub common: CommonEncodeOptions,
pub distance: f32,
pub quality: Option<f32>,
pub lossless: bool,
pub effort: u8,
pub brotli_effort: i8,
pub decoding_speed: u8,
pub progressive: bool,
pub modular: LibjxlModular,
pub color_transform: LibjxlColorTransform,
pub epf: i8,
pub gaborish: Option<bool>,
pub noise: Option<bool>,
pub dots: Option<bool>,
pub patches: Option<bool>,
pub photon_noise_iso: f32,
pub resampling: i8,
pub use_container: bool,
pub codestream_level: i8,
pub extra_int_options: Vec<(i32, i64)>,
pub extra_float_options: Vec<(i32, f32)>,
}
impl Default for LibjxlEncodeConfig {
fn default() -> Self {
Self {
common: CommonEncodeOptions::default(),
distance: 1.0,
quality: None,
lossless: false,
effort: 7,
brotli_effort: -1,
decoding_speed: 0,
progressive: false,
modular: LibjxlModular::Auto,
color_transform: LibjxlColorTransform::Auto,
epf: -1,
gaborish: None,
noise: None,
dots: None,
patches: None,
photon_noise_iso: 0.0,
resampling: -1,
use_container: false,
codestream_level: -1,
extra_int_options: Vec::new(),
extra_float_options: Vec::new(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct LibjpegTurboEncodeConfig {
pub common: CommonEncodeOptions,
pub quality: u8,
pub progressive: bool,
pub subsampling: JpegSubsampling,
}
impl Default for LibjpegTurboEncodeConfig {
fn default() -> Self {
Self {
common: CommonEncodeOptions::default(),
quality: 90,
progressive: false,
subsampling: JpegSubsampling::Yuv420,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct MozjpegEncodeConfig {
pub common: CommonEncodeOptions,
pub quality: u8,
pub progressive: bool,
pub trellis: bool,
pub subsampling: JpegSubsampling,
}
impl Default for MozjpegEncodeConfig {
fn default() -> Self {
Self {
common: CommonEncodeOptions::default(),
quality: 85,
progressive: true,
trellis: true,
subsampling: JpegSubsampling::Yuv420,
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct JpegliEncodeConfig {
pub common: CommonEncodeOptions,
pub distance: f32,
pub quality: Option<u8>,
pub progressive: bool,
pub xyb: bool,
pub subsampling: JpegSubsampling,
}
impl Default for JpegliEncodeConfig {
fn default() -> Self {
Self {
common: CommonEncodeOptions::default(),
distance: 1.0,
quality: None,
progressive: true,
xyb: false,
subsampling: JpegSubsampling::Yuv420,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct LibaomEncodeConfig {
pub common: CommonEncodeOptions,
pub cq_level: u8,
pub min_quantizer: u8,
pub max_quantizer: u8,
pub cpu_used: u8,
pub rate_control: AvifRateControl,
}
impl Default for LibaomEncodeConfig {
fn default() -> Self {
Self {
common: CommonEncodeOptions::default(),
cq_level: 30,
min_quantizer: 0,
max_quantizer: 63,
cpu_used: 6,
rate_control: AvifRateControl::ConstantQuality,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SvtAv1EncodeConfig {
pub common: CommonEncodeOptions,
pub crf: u8,
pub preset: u8,
}
impl Default for SvtAv1EncodeConfig {
fn default() -> Self {
Self {
common: CommonEncodeOptions::default(),
crf: 35,
preset: 8,
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum EncodeOptions {
#[cfg(feature = "png-encode")]
PngZune(ZunePngEncodeConfig),
#[cfg(feature = "jpeg-encode")]
JpegJpegEnc(JpegEncEncodeConfig),
#[cfg(feature = "webp-encode")]
WebpLibwebp(LibwebpEncodeConfig),
#[cfg(feature = "avif-encode")]
AvifRavif(RavifEncodeConfig),
#[cfg(feature = "jxl-encode")]
JxlZune(ZuneJxlEncodeConfig),
#[cfg(feature = "jxl-encode-libjxl")]
JxlLibjxl(LibjxlEncodeConfig),
#[cfg(feature = "dng-encode")]
Dng(DngExportConfig),
}
#[cfg(feature = "png-encode")]
impl Default for EncodeOptions {
fn default() -> Self {
Self::png()
}
}
impl EncodeOptions {
#[cfg(feature = "png-encode")]
pub fn png() -> Self {
Self::PngZune(ZunePngEncodeConfig::default())
}
#[cfg(feature = "jpeg-encode")]
pub fn jpeg() -> Self {
Self::JpegJpegEnc(JpegEncEncodeConfig::default())
}
#[cfg(feature = "webp-encode")]
pub fn webp_lossy() -> Self {
Self::WebpLibwebp(LibwebpEncodeConfig::lossy())
}
#[cfg(feature = "webp-encode")]
pub fn webp_lossless() -> Self {
Self::WebpLibwebp(LibwebpEncodeConfig::lossless())
}
#[cfg(feature = "avif-encode")]
pub fn avif() -> Self {
Self::AvifRavif(RavifEncodeConfig::default())
}
#[cfg(feature = "jxl-encode")]
pub fn jxl() -> Self {
Self::JxlZune(ZuneJxlEncodeConfig::default())
}
#[cfg(feature = "jxl-encode-libjxl")]
pub fn jxl_libjxl() -> Self {
Self::JxlLibjxl(LibjxlEncodeConfig::default())
}
#[cfg(feature = "dng-encode")]
pub fn dng() -> Self {
Self::Dng(DngExportConfig::default())
}
pub fn format(&self) -> OutputFormat {
match self {
#[cfg(feature = "png-encode")]
EncodeOptions::PngZune(_) => OutputFormat::Png,
#[cfg(feature = "jpeg-encode")]
EncodeOptions::JpegJpegEnc(_) => OutputFormat::Jpeg,
#[cfg(feature = "webp-encode")]
EncodeOptions::WebpLibwebp(_) => OutputFormat::WebP,
#[cfg(feature = "avif-encode")]
EncodeOptions::AvifRavif(_) => OutputFormat::Avif,
#[cfg(feature = "jxl-encode")]
EncodeOptions::JxlZune(_) => OutputFormat::Jxl,
#[cfg(feature = "jxl-encode-libjxl")]
EncodeOptions::JxlLibjxl(_) => OutputFormat::Jxl,
#[cfg(feature = "dng-encode")]
EncodeOptions::Dng(_) => OutputFormat::Dng,
#[allow(unreachable_patterns)]
_ => unreachable!(),
}
}
pub fn codec_id(&self) -> CodecId {
match self {
#[cfg(feature = "png-encode")]
EncodeOptions::PngZune(_) => CodecId::new("png/zune"),
#[cfg(feature = "jpeg-encode")]
EncodeOptions::JpegJpegEnc(_) => CodecId::new("jpeg/jpeg-encoder"),
#[cfg(feature = "webp-encode")]
EncodeOptions::WebpLibwebp(_) => CodecId::new("webp/libwebp"),
#[cfg(feature = "avif-encode")]
EncodeOptions::AvifRavif(_) => CodecId::new("avif/ravif"),
#[cfg(feature = "jxl-encode")]
EncodeOptions::JxlZune(_) => CodecId::new("jxl/zune"),
#[cfg(feature = "jxl-encode-libjxl")]
EncodeOptions::JxlLibjxl(_) => CodecId::new("jxl/libjxl"),
#[cfg(feature = "dng-encode")]
EncodeOptions::Dng(_) => CodecId::new("dng/rawshift"),
#[allow(unreachable_patterns)]
_ => unreachable!(),
}
}
pub fn common(&self) -> CommonEncodeOptions {
match self {
#[cfg(feature = "png-encode")]
EncodeOptions::PngZune(c) => c.common,
#[cfg(feature = "jpeg-encode")]
EncodeOptions::JpegJpegEnc(c) => c.common,
#[cfg(feature = "webp-encode")]
EncodeOptions::WebpLibwebp(c) => c.common,
#[cfg(feature = "avif-encode")]
EncodeOptions::AvifRavif(c) => c.common,
#[cfg(feature = "jxl-encode")]
EncodeOptions::JxlZune(c) => c.common,
#[cfg(feature = "jxl-encode-libjxl")]
EncodeOptions::JxlLibjxl(c) => c.common,
#[cfg(feature = "dng-encode")]
EncodeOptions::Dng(c) => CommonEncodeOptions {
metadata: MetadataEmbedOptions {
embed_exif: c.embed_exif,
embed_icc: false,
embed_xmp: false,
},
bit_depth: BitDepth::Sixteen,
},
#[allow(unreachable_patterns)]
_ => unreachable!(),
}
}
pub fn default_for(format: StandardFormat) -> Option<EncodeOptions> {
match format {
#[cfg(feature = "png-encode")]
StandardFormat::Png => Some(EncodeOptions::png()),
#[cfg(feature = "jpeg-encode")]
StandardFormat::Jpeg => Some(EncodeOptions::jpeg()),
#[cfg(feature = "webp-encode")]
StandardFormat::WebP => Some(EncodeOptions::webp_lossy()),
#[cfg(feature = "avif-encode")]
StandardFormat::Avif => Some(EncodeOptions::avif()),
#[cfg(feature = "jxl-encode")]
StandardFormat::Jxl => Some(EncodeOptions::jxl()),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
#[cfg(any_standard_encode)]
use super::*;
#[test]
#[cfg(feature = "jpeg-encode")]
fn default_for_jpeg_is_jpeg() {
let opts = EncodeOptions::default_for(StandardFormat::Jpeg).unwrap();
assert_eq!(opts.format(), OutputFormat::Jpeg);
assert_eq!(opts.codec_id().id, "jpeg/jpeg-encoder");
}
#[test]
#[cfg(feature = "png-encode")]
fn default_for_unencodable_format_is_none() {
assert!(EncodeOptions::default_for(StandardFormat::Gif).is_none());
}
#[test]
#[cfg(feature = "avif-encode")]
fn avif_default_quality_and_common() {
let opts = EncodeOptions::avif();
assert_eq!(opts.format(), OutputFormat::Avif);
assert_eq!(opts.common().bit_depth, BitDepth::Sixteen);
}
#[test]
fn output_format_names_and_extensions() {
assert_eq!(OutputFormat::Jxl.name(), "JPEG XL");
assert_eq!(OutputFormat::Avif.extension(), "avif");
}
}