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}