1#[cfg(feature = "dng-encode")]
16use crate::formats::dng_export::DngExportConfig;
17
18use super::standard::StandardFormat;
19use crate::core::CodecId;
20
21pub use crate::core::{BitDepth, MetadataEmbedOptions};
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
28#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
29pub enum OutputFormat {
30 Png,
32 Jpeg,
34 WebP,
36 Avif,
38 Jxl,
40 Dng,
42}
43
44impl OutputFormat {
45 pub fn name(self) -> &'static str {
47 match self {
48 OutputFormat::Png => "PNG",
49 OutputFormat::Jpeg => "JPEG",
50 OutputFormat::WebP => "WebP",
51 OutputFormat::Avif => "AVIF",
52 OutputFormat::Jxl => "JPEG XL",
53 OutputFormat::Dng => "DNG",
54 }
55 }
56
57 pub fn extension(self) -> &'static str {
59 match self {
60 OutputFormat::Png => "png",
61 OutputFormat::Jpeg => "jpg",
62 OutputFormat::WebP => "webp",
63 OutputFormat::Avif => "avif",
64 OutputFormat::Jxl => "jxl",
65 OutputFormat::Dng => "dng",
66 }
67 }
68}
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
76#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
77pub struct CommonEncodeOptions {
78 pub metadata: MetadataEmbedOptions,
80 pub bit_depth: BitDepth,
86}
87
88#[derive(Debug, Clone, Copy, PartialEq, Eq)]
90#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
91pub enum WebPMode {
92 Lossless,
94 Lossy,
96}
97
98#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
100#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
101pub enum JpegSubsampling {
102 #[default]
104 Yuv420,
105 Yuv422,
107 Yuv444,
109}
110
111#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
113#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
114pub enum AvifRateControl {
115 #[default]
117 ConstantQuality,
118 Constrained,
120}
121
122#[derive(Debug, Clone, PartialEq, Eq, Default)]
126#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
127pub struct ZunePngEncodeConfig {
128 pub common: CommonEncodeOptions,
132}
133
134#[derive(Debug, Clone, PartialEq, Eq)]
136#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
137pub struct JpegEncEncodeConfig {
138 pub common: CommonEncodeOptions,
140 pub quality: u8,
143}
144
145impl Default for JpegEncEncodeConfig {
146 fn default() -> Self {
147 Self {
148 common: CommonEncodeOptions::default(),
149 quality: 90,
150 }
151 }
152}
153
154#[derive(Debug, Clone, PartialEq)]
156#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
157pub struct LibwebpEncodeConfig {
158 pub common: CommonEncodeOptions,
160 pub mode: WebPMode,
162 pub quality: f32,
165 pub method: u32,
167 pub near_lossless: u32,
170}
171
172impl LibwebpEncodeConfig {
173 pub fn lossy() -> Self {
175 Self {
176 common: CommonEncodeOptions::default(),
177 mode: WebPMode::Lossy,
178 quality: 75.0,
179 method: 4,
180 near_lossless: 100,
181 }
182 }
183
184 pub fn lossless() -> Self {
186 Self {
187 mode: WebPMode::Lossless,
188 ..Self::lossy()
189 }
190 }
191}
192
193impl Default for LibwebpEncodeConfig {
194 fn default() -> Self {
195 Self::lossy()
196 }
197}
198
199#[derive(Debug, Clone, PartialEq, Eq)]
201#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
202pub struct RavifEncodeConfig {
203 pub common: CommonEncodeOptions,
205 pub quality: u8,
208 pub speed: u8,
210}
211
212impl Default for RavifEncodeConfig {
213 fn default() -> Self {
214 Self {
215 common: CommonEncodeOptions::default(),
216 quality: 80,
217 speed: 6,
218 }
219 }
220}
221
222#[derive(Debug, Clone, PartialEq)]
224#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
225pub struct ZuneJxlEncodeConfig {
226 pub common: CommonEncodeOptions,
228 pub quality: f32,
230 pub effort: u8,
233}
234
235impl Default for ZuneJxlEncodeConfig {
236 fn default() -> Self {
237 Self {
238 common: CommonEncodeOptions::default(),
239 quality: 0.0,
240 effort: 7,
241 }
242 }
243}
244
245#[derive(Debug, Clone, PartialEq, Eq)]
253#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
254pub struct LibjpegTurboEncodeConfig {
255 pub common: CommonEncodeOptions,
257 pub quality: u8,
259 pub progressive: bool,
261 pub subsampling: JpegSubsampling,
263}
264
265impl Default for LibjpegTurboEncodeConfig {
266 fn default() -> Self {
267 Self {
268 common: CommonEncodeOptions::default(),
269 quality: 90,
270 progressive: false,
271 subsampling: JpegSubsampling::Yuv420,
272 }
273 }
274}
275
276#[derive(Debug, Clone, PartialEq, Eq)]
278#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
279pub struct MozjpegEncodeConfig {
280 pub common: CommonEncodeOptions,
282 pub quality: u8,
284 pub progressive: bool,
286 pub trellis: bool,
288 pub subsampling: JpegSubsampling,
290}
291
292impl Default for MozjpegEncodeConfig {
293 fn default() -> Self {
294 Self {
295 common: CommonEncodeOptions::default(),
296 quality: 85,
297 progressive: true,
298 trellis: true,
299 subsampling: JpegSubsampling::Yuv420,
300 }
301 }
302}
303
304#[derive(Debug, Clone, PartialEq)]
306#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
307pub struct JpegliEncodeConfig {
308 pub common: CommonEncodeOptions,
310 pub distance: f32,
313 pub quality: Option<u8>,
315 pub progressive: bool,
317 pub xyb: bool,
319 pub subsampling: JpegSubsampling,
321}
322
323impl Default for JpegliEncodeConfig {
324 fn default() -> Self {
325 Self {
326 common: CommonEncodeOptions::default(),
327 distance: 1.0,
328 quality: None,
329 progressive: true,
330 xyb: false,
331 subsampling: JpegSubsampling::Yuv420,
332 }
333 }
334}
335
336#[derive(Debug, Clone, PartialEq, Eq)]
338#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
339pub struct LibaomEncodeConfig {
340 pub common: CommonEncodeOptions,
342 pub cq_level: u8,
345 pub min_quantizer: u8,
347 pub max_quantizer: u8,
349 pub cpu_used: u8,
351 pub rate_control: AvifRateControl,
353}
354
355impl Default for LibaomEncodeConfig {
356 fn default() -> Self {
357 Self {
358 common: CommonEncodeOptions::default(),
359 cq_level: 30,
360 min_quantizer: 0,
361 max_quantizer: 63,
362 cpu_used: 6,
363 rate_control: AvifRateControl::ConstantQuality,
364 }
365 }
366}
367
368#[derive(Debug, Clone, PartialEq, Eq)]
370#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
371pub struct SvtAv1EncodeConfig {
372 pub common: CommonEncodeOptions,
374 pub crf: u8,
377 pub preset: u8,
379}
380
381impl Default for SvtAv1EncodeConfig {
382 fn default() -> Self {
383 Self {
384 common: CommonEncodeOptions::default(),
385 crf: 35,
386 preset: 8,
387 }
388 }
389}
390
391#[derive(Debug, Clone, PartialEq)]
393#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
394pub struct LibjxlEncodeConfig {
395 pub common: CommonEncodeOptions,
397 pub distance: f32,
400 pub effort: u8,
402 pub progressive: bool,
404}
405
406impl Default for LibjxlEncodeConfig {
407 fn default() -> Self {
408 Self {
409 common: CommonEncodeOptions::default(),
410 distance: 1.0,
411 effort: 7,
412 progressive: false,
413 }
414 }
415}
416
417#[derive(Debug, Clone, PartialEq)]
425#[non_exhaustive]
426#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
427pub enum EncodeOptions {
428 #[cfg(feature = "png-encode")]
430 PngZune(ZunePngEncodeConfig),
431 #[cfg(feature = "jpeg-encode")]
433 JpegJpegEnc(JpegEncEncodeConfig),
434 #[cfg(feature = "webp-encode")]
436 WebpLibwebp(LibwebpEncodeConfig),
437 #[cfg(feature = "avif-encode")]
439 AvifRavif(RavifEncodeConfig),
440 #[cfg(feature = "jxl-encode")]
442 JxlZune(ZuneJxlEncodeConfig),
443 #[cfg(feature = "dng-encode")]
445 Dng(DngExportConfig),
446}
447
448#[cfg(feature = "png-encode")]
449impl Default for EncodeOptions {
450 fn default() -> Self {
451 Self::png()
452 }
453}
454
455impl EncodeOptions {
456 #[cfg(feature = "png-encode")]
458 pub fn png() -> Self {
459 Self::PngZune(ZunePngEncodeConfig::default())
460 }
461
462 #[cfg(feature = "jpeg-encode")]
464 pub fn jpeg() -> Self {
465 Self::JpegJpegEnc(JpegEncEncodeConfig::default())
466 }
467
468 #[cfg(feature = "webp-encode")]
470 pub fn webp_lossy() -> Self {
471 Self::WebpLibwebp(LibwebpEncodeConfig::lossy())
472 }
473
474 #[cfg(feature = "webp-encode")]
476 pub fn webp_lossless() -> Self {
477 Self::WebpLibwebp(LibwebpEncodeConfig::lossless())
478 }
479
480 #[cfg(feature = "avif-encode")]
482 pub fn avif() -> Self {
483 Self::AvifRavif(RavifEncodeConfig::default())
484 }
485
486 #[cfg(feature = "jxl-encode")]
488 pub fn jxl() -> Self {
489 Self::JxlZune(ZuneJxlEncodeConfig::default())
490 }
491
492 #[cfg(feature = "dng-encode")]
494 pub fn dng() -> Self {
495 Self::Dng(DngExportConfig::default())
496 }
497
498 pub fn format(&self) -> OutputFormat {
500 match self {
501 #[cfg(feature = "png-encode")]
502 EncodeOptions::PngZune(_) => OutputFormat::Png,
503 #[cfg(feature = "jpeg-encode")]
504 EncodeOptions::JpegJpegEnc(_) => OutputFormat::Jpeg,
505 #[cfg(feature = "webp-encode")]
506 EncodeOptions::WebpLibwebp(_) => OutputFormat::WebP,
507 #[cfg(feature = "avif-encode")]
508 EncodeOptions::AvifRavif(_) => OutputFormat::Avif,
509 #[cfg(feature = "jxl-encode")]
510 EncodeOptions::JxlZune(_) => OutputFormat::Jxl,
511 #[cfg(feature = "dng-encode")]
512 EncodeOptions::Dng(_) => OutputFormat::Dng,
513 #[allow(unreachable_patterns)]
516 _ => unreachable!(),
517 }
518 }
519
520 pub fn codec_id(&self) -> CodecId {
522 match self {
523 #[cfg(feature = "png-encode")]
524 EncodeOptions::PngZune(_) => CodecId::new("png/zune"),
525 #[cfg(feature = "jpeg-encode")]
526 EncodeOptions::JpegJpegEnc(_) => CodecId::new("jpeg/jpeg-encoder"),
527 #[cfg(feature = "webp-encode")]
528 EncodeOptions::WebpLibwebp(_) => CodecId::new("webp/libwebp"),
529 #[cfg(feature = "avif-encode")]
530 EncodeOptions::AvifRavif(_) => CodecId::new("avif/ravif"),
531 #[cfg(feature = "jxl-encode")]
532 EncodeOptions::JxlZune(_) => CodecId::new("jxl/zune"),
533 #[cfg(feature = "dng-encode")]
534 EncodeOptions::Dng(_) => CodecId::new("dng/rawshift"),
535 #[allow(unreachable_patterns)]
536 _ => unreachable!(),
537 }
538 }
539
540 pub fn common(&self) -> CommonEncodeOptions {
545 match self {
546 #[cfg(feature = "png-encode")]
547 EncodeOptions::PngZune(c) => c.common,
548 #[cfg(feature = "jpeg-encode")]
549 EncodeOptions::JpegJpegEnc(c) => c.common,
550 #[cfg(feature = "webp-encode")]
551 EncodeOptions::WebpLibwebp(c) => c.common,
552 #[cfg(feature = "avif-encode")]
553 EncodeOptions::AvifRavif(c) => c.common,
554 #[cfg(feature = "jxl-encode")]
555 EncodeOptions::JxlZune(c) => c.common,
556 #[cfg(feature = "dng-encode")]
557 EncodeOptions::Dng(c) => CommonEncodeOptions {
558 metadata: MetadataEmbedOptions {
559 embed_exif: c.embed_exif,
560 embed_icc: false,
561 embed_xmp: false,
562 },
563 bit_depth: BitDepth::Sixteen,
564 },
565 #[allow(unreachable_patterns)]
566 _ => unreachable!(),
567 }
568 }
569
570 pub fn default_for(format: StandardFormat) -> Option<EncodeOptions> {
576 match format {
577 #[cfg(feature = "png-encode")]
578 StandardFormat::Png => Some(EncodeOptions::png()),
579 #[cfg(feature = "jpeg-encode")]
580 StandardFormat::Jpeg => Some(EncodeOptions::jpeg()),
581 #[cfg(feature = "webp-encode")]
582 StandardFormat::WebP => Some(EncodeOptions::webp_lossy()),
583 #[cfg(feature = "avif-encode")]
584 StandardFormat::Avif => Some(EncodeOptions::avif()),
585 #[cfg(feature = "jxl-encode")]
586 StandardFormat::Jxl => Some(EncodeOptions::jxl()),
587 _ => None,
588 }
589 }
590}
591
592#[cfg(test)]
593mod tests {
594 #[cfg(any_standard_encode)]
595 use super::*;
596
597 #[test]
598 #[cfg(feature = "jpeg-encode")]
599 fn default_for_jpeg_is_jpeg() {
600 let opts = EncodeOptions::default_for(StandardFormat::Jpeg).unwrap();
601 assert_eq!(opts.format(), OutputFormat::Jpeg);
602 assert_eq!(opts.codec_id().id, "jpeg/jpeg-encoder");
603 }
604
605 #[test]
606 #[cfg(feature = "png-encode")]
607 fn default_for_unencodable_format_is_none() {
608 assert!(EncodeOptions::default_for(StandardFormat::Gif).is_none());
609 }
610
611 #[test]
612 #[cfg(feature = "avif-encode")]
613 fn avif_default_quality_and_common() {
614 let opts = EncodeOptions::avif();
615 assert_eq!(opts.format(), OutputFormat::Avif);
616 assert_eq!(opts.common().bit_depth, BitDepth::Sixteen);
617 }
618
619 #[test]
620 fn output_format_names_and_extensions() {
621 assert_eq!(OutputFormat::Jxl.name(), "JPEG XL");
622 assert_eq!(OutputFormat::Avif.extension(), "avif");
623 }
624}