Skip to main content

zencodec/
detect.rs

1//! Source encoding detection traits.
2//!
3//! [`SourceEncodingDetails`] provides a codec-agnostic interface for
4//! querying properties of the encoder that produced an image file.
5//! Each codec's probe type (e.g. `WebPProbe`, `JpegProbe`) implements
6//! this trait, providing both the generic quality number and full
7//! codec-specific details via downcasting.
8//!
9//! # Usage
10//!
11//! ```rust,ignore
12//! use zencodec::decode::{DecodeOutput, SourceEncodingDetails};
13//!
14//! let output: DecodeOutput = /* decode an image */;
15//!
16//! if let Some(details) = output.source_encoding_details() {
17//!     // Generic quality (0-100, same scale as EncoderConfig::with_generic_quality)
18//!     if let Some(q) = details.source_generic_quality() {
19//!         println!("Source quality: {:.0}", q);
20//!     }
21//!
22//!     // Codec-specific details via downcast
23//!     if let Some(webp) = details.codec_details::<zenwebp::detect::WebPProbe>() {
24//!         println!("VP8 quantizer: {:?}", webp.bitstream);
25//!     }
26//! }
27//! ```
28
29use core::any::Any;
30
31/// Codec-agnostic interface for source encoding properties.
32///
33/// Implemented by each codec's probe/detect type to provide both a
34/// generic quality number and codec-specific details. The generic
35/// quality uses the same 0.0–100.0 scale as
36/// [`EncoderConfig::with_generic_quality()`](crate::encode::EncoderConfig::with_generic_quality).
37///
38/// # Downcasting
39///
40/// Use `codec_details::<T>()` to access the concrete probe type for
41/// codec-specific fields:
42///
43/// ```rust,ignore
44/// if let Some(jpeg) = details.codec_details::<zenjpeg::detect::JpegProbe>() {
45///     println!("Encoder: {:?}", jpeg.encoder);
46/// }
47/// ```
48pub trait SourceEncodingDetails: Any + Send + Sync {
49    /// Estimated source quality on the generic 0.0–100.0 scale.
50    ///
51    /// Returns `None` for lossless formats (PNG, lossless WebP) or when
52    /// quality cannot be determined from the bitstream headers.
53    ///
54    /// The value is approximate (±5) — different encoders map quality
55    /// parameters differently, so the reverse-engineered value may not
56    /// exactly match the original setting.
57    fn source_generic_quality(&self) -> Option<f32>;
58
59    /// Whether the source encoding is lossless.
60    ///
61    /// True for PNG, lossless WebP, lossless JPEG 2000, etc.
62    /// When true, `source_generic_quality()` typically returns `None`.
63    fn is_lossless(&self) -> bool {
64        false
65    }
66}
67
68// ── Design note ────────────────────────────────────────────────────────
69//
70// This trait intentionally has very few methods. Only properties that are
71// meaningful across ALL image formats belong here (quality, lossless).
72//
73// Codec-specific details — color type, bit depth, palette size, chroma
74// subsampling, encoder family, quantizer tables, etc. — belong as fields
75// or methods on the concrete probe struct (e.g. `PngProbe`, `JpegProbe`).
76// Callers access them via downcast:
77//
78//     if let Some(png) = details.codec_details::<PngProbe>() {
79//         println!("{}bpp, palette: {:?}", png.bits_per_pixel(), png.palette_size);
80//     }
81//
82// This keeps the trait stable and avoids a combinatorial explosion of
83// optional methods that only apply to a subset of codecs.
84// ───────────────────────────────────────────────────────────────────────
85
86// Downcast helper — avoids requiring callers to import `core::any::Any`.
87impl dyn SourceEncodingDetails {
88    /// Downcast to a concrete codec probe type.
89    ///
90    /// Returns `Some(&T)` if the underlying type matches, `None` otherwise.
91    ///
92    /// ```rust,ignore
93    /// use zenwebp::detect::WebPProbe;
94    ///
95    /// if let Some(webp) = details.codec_details::<WebPProbe>() {
96    ///     println!("Lossy: {:?}", webp.bitstream);
97    /// }
98    /// ```
99    pub fn codec_details<T: SourceEncodingDetails + 'static>(&self) -> Option<&T> {
100        (self as &dyn Any).downcast_ref()
101    }
102}
103
104impl dyn SourceEncodingDetails + Send {
105    /// Downcast to a concrete codec probe type.
106    pub fn codec_details<T: SourceEncodingDetails + 'static>(&self) -> Option<&T> {
107        (self as &dyn Any).downcast_ref()
108    }
109}
110
111impl dyn SourceEncodingDetails + Send + Sync {
112    /// Downcast to a concrete codec probe type.
113    pub fn codec_details<T: SourceEncodingDetails + 'static>(&self) -> Option<&T> {
114        (self as &dyn Any).downcast_ref()
115    }
116}