use crate::{Limits, Preset};
#[cfg(feature = "__expert")]
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum SharpYuvSetting {
Off,
On,
Custom(zenyuv::SharpYuvConfig),
}
#[cfg(feature = "__expert")]
#[derive(Clone, Debug, Default)]
#[non_exhaustive]
pub struct InternalParams {
pub partition_limit: Option<u8>,
pub multi_pass_stats: Option<bool>,
pub smooth_segment_map: Option<bool>,
pub sharp_yuv: Option<SharpYuvSetting>,
pub cost_model: Option<super::api::CostModel>,
}
#[derive(Clone)]
#[non_exhaustive]
pub struct LossyConfig {
pub quality: f32,
pub method: u8,
pub alpha_quality: u8,
pub target_size: u32,
pub target_psnr: f32,
pub target_zensim: Option<super::zensim_target::ZensimTarget>,
pub preset: Option<Preset>,
pub sharp_yuv: Option<zenyuv::SharpYuvConfig>,
pub sns_strength: Option<u8>,
pub filter_strength: Option<u8>,
pub filter_sharpness: Option<u8>,
pub segments: Option<u8>,
pub partition_limit: Option<u8>,
pub smooth_segment_map: bool,
pub cost_model: super::api::CostModel,
pub multi_pass_stats: bool,
pub limits: Limits,
#[doc(hidden)]
pub(crate) segment_quant_overrides: Option<[i8; 4]>,
}
impl Default for LossyConfig {
fn default() -> Self {
Self::new()
}
}
impl LossyConfig {
#[must_use]
pub fn new() -> Self {
Self {
quality: 75.0,
method: 4,
alpha_quality: 100,
target_size: 0,
target_psnr: 0.0,
target_zensim: None,
preset: None,
sharp_yuv: None,
sns_strength: None,
filter_strength: None,
filter_sharpness: None,
segments: None,
partition_limit: None,
smooth_segment_map: false,
cost_model: super::api::CostModel::ZenwebpDefault,
multi_pass_stats: false,
limits: Limits::none(),
segment_quant_overrides: None,
}
}
#[must_use]
pub fn with_preset(preset: Preset, quality: f32) -> Self {
Self {
quality,
preset: Some(preset),
..Self::new()
}
}
#[must_use]
pub fn with_quality(mut self, quality: f32) -> Self {
self.quality = quality.clamp(0.0, 100.0);
self
}
#[must_use]
pub fn with_method(mut self, method: u8) -> Self {
self.method = method.min(6);
self
}
#[must_use]
pub fn with_alpha_quality(mut self, quality: u8) -> Self {
self.alpha_quality = quality.min(100);
self
}
#[must_use]
pub fn with_target_size(mut self, size: u32) -> Self {
self.target_size = size;
self
}
#[must_use]
pub fn with_target_psnr(mut self, psnr: f32) -> Self {
self.target_psnr = psnr;
self
}
#[must_use]
pub fn with_target_zensim<T: Into<super::zensim_target::ZensimTarget>>(
mut self,
target: T,
) -> Self {
self.target_zensim = Some(target.into());
self
}
#[must_use]
pub fn with_preset_value(mut self, preset: Preset) -> Self {
self.preset = Some(preset);
self
}
#[cfg(feature = "__expert")]
#[must_use]
pub fn with_sharp_yuv(mut self, enable: bool) -> Self {
self.sharp_yuv = if enable {
Some(zenyuv::SharpYuvConfig::default())
} else {
None
};
self
}
#[cfg(feature = "__expert")]
#[must_use]
pub fn with_sharp_yuv_config(mut self, config: zenyuv::SharpYuvConfig) -> Self {
self.sharp_yuv = Some(config);
self
}
#[must_use]
pub fn with_sns_strength(mut self, strength: u8) -> Self {
self.sns_strength = Some(strength.min(100));
self
}
#[must_use]
pub fn with_filter_strength(mut self, strength: u8) -> Self {
self.filter_strength = Some(strength.min(100));
self
}
#[must_use]
pub fn with_filter_sharpness(mut self, sharpness: u8) -> Self {
self.filter_sharpness = Some(sharpness.min(7));
self
}
#[must_use]
pub fn with_segments(mut self, segments: u8) -> Self {
self.segments = Some(segments.clamp(1, 4));
self
}
#[cfg(feature = "__expert")]
#[must_use]
pub fn with_partition_limit(mut self, limit: u8) -> Self {
self.partition_limit = Some(limit.min(100));
self
}
#[cfg(feature = "__expert")]
#[must_use]
pub fn with_cost_model(mut self, model: super::api::CostModel) -> Self {
self.cost_model = model;
self
}
#[cfg(feature = "__expert")]
#[must_use]
pub fn with_smooth_segment_map(mut self, on: bool) -> Self {
self.smooth_segment_map = on;
self
}
#[cfg(feature = "__expert")]
#[must_use]
pub fn with_multi_pass_stats(mut self, on: bool) -> Self {
self.multi_pass_stats = on;
self
}
#[cfg(feature = "__expert")]
#[must_use]
pub fn with_internal_params(mut self, knobs: InternalParams) -> Self {
if let Some(pl) = knobs.partition_limit {
self = self.with_partition_limit(pl);
}
if let Some(b) = knobs.multi_pass_stats {
self = self.with_multi_pass_stats(b);
}
if let Some(b) = knobs.smooth_segment_map {
self = self.with_smooth_segment_map(b);
}
match knobs.sharp_yuv {
Some(SharpYuvSetting::Off) => {
self = self.with_sharp_yuv(false);
}
Some(SharpYuvSetting::On) => {
self = self.with_sharp_yuv(true);
}
Some(SharpYuvSetting::Custom(cfg)) => {
self = self.with_sharp_yuv_config(cfg);
}
None => {}
}
if let Some(cm) = knobs.cost_model {
self = self.with_cost_model(cm);
}
self
}
#[must_use]
pub fn with_limits(mut self, limits: Limits) -> Self {
self.limits = limits;
self
}
#[must_use]
pub fn with_max_dimensions(mut self, width: u32, height: u32) -> Self {
self.limits = self.limits.max_dimensions(width, height);
self
}
#[must_use]
pub fn with_max_memory(mut self, bytes: u64) -> Self {
self.limits = self.limits.max_memory(bytes);
self
}
#[must_use]
pub fn estimate_memory(&self, width: u32, height: u32, bpp: u8) -> u64 {
crate::heuristics::estimate_encode(
width,
height,
bpp,
&crate::EncoderConfig::Lossy(self.clone()),
)
.peak_memory_bytes
}
#[must_use]
pub fn estimate_memory_ceiling(&self, width: u32, height: u32, bpp: u8) -> u64 {
crate::heuristics::estimate_encode(
width,
height,
bpp,
&crate::EncoderConfig::Lossy(self.clone()),
)
.peak_memory_bytes_max
}
pub(crate) fn encode_pixels_with_metrics(
&self,
pixels: &[u8],
layout: super::api::PixelLayout,
width: u32,
height: u32,
) -> Result<
(
alloc::vec::Vec<u8>,
super::zensim_target::ZensimEncodeMetrics,
),
super::api::EncodeError,
> {
encode_pixels_with_metrics_impl(self, pixels, layout, width, height)
}
}
#[derive(Clone)]
#[non_exhaustive]
pub struct LosslessConfig {
pub quality: f32,
pub method: u8,
pub alpha_quality: u8,
pub target_size: u32,
pub near_lossless: u8,
pub exact: bool,
pub limits: Limits,
}
impl Default for LosslessConfig {
fn default() -> Self {
Self::new()
}
}
impl LosslessConfig {
#[must_use]
pub fn new() -> Self {
Self {
quality: 75.0,
method: 4,
alpha_quality: 100,
target_size: 0,
near_lossless: 100,
exact: false,
limits: Limits::none(),
}
}
#[must_use]
pub fn with_quality(mut self, quality: f32) -> Self {
self.quality = quality.clamp(0.0, 100.0);
self
}
#[must_use]
pub fn with_method(mut self, method: u8) -> Self {
self.method = method.min(6);
self
}
#[must_use]
pub fn with_alpha_quality(mut self, quality: u8) -> Self {
self.alpha_quality = quality.min(100);
self
}
#[must_use]
pub fn with_target_size(mut self, size: u32) -> Self {
self.target_size = size;
self
}
#[must_use]
pub fn with_near_lossless(mut self, value: u8) -> Self {
self.near_lossless = value.min(100);
self
}
#[must_use]
pub fn with_exact(mut self, exact: bool) -> Self {
self.exact = exact;
self
}
#[must_use]
pub fn with_limits(mut self, limits: Limits) -> Self {
self.limits = limits;
self
}
#[must_use]
pub fn with_max_dimensions(mut self, width: u32, height: u32) -> Self {
self.limits = self.limits.max_dimensions(width, height);
self
}
#[must_use]
pub fn with_max_memory(mut self, bytes: u64) -> Self {
self.limits = self.limits.max_memory(bytes);
self
}
#[must_use]
pub fn estimate_memory(&self, width: u32, height: u32, bpp: u8) -> u64 {
crate::heuristics::estimate_encode(
width,
height,
bpp,
&crate::EncoderConfig::Lossless(self.clone()),
)
.peak_memory_bytes
}
#[must_use]
pub fn estimate_memory_ceiling(&self, width: u32, height: u32, bpp: u8) -> u64 {
crate::heuristics::estimate_encode(
width,
height,
bpp,
&crate::EncoderConfig::Lossless(self.clone()),
)
.peak_memory_bytes_max
}
}
#[derive(Clone)]
pub enum EncoderConfig {
Lossy(LossyConfig),
Lossless(LosslessConfig),
}
impl EncoderConfig {
#[must_use]
pub fn new_lossy() -> Self {
Self::Lossy(LossyConfig::new())
}
#[must_use]
pub fn new_lossless() -> Self {
Self::Lossless(LosslessConfig::new())
}
#[must_use]
pub fn with_preset(preset: Preset, quality: f32) -> Self {
Self::Lossy(LossyConfig::with_preset(preset, quality))
}
#[must_use]
pub fn with_quality(mut self, quality: f32) -> Self {
match &mut self {
Self::Lossy(cfg) => cfg.quality = quality,
Self::Lossless(cfg) => cfg.quality = quality,
}
self
}
#[must_use]
pub fn with_method(mut self, method: u8) -> Self {
match &mut self {
Self::Lossy(cfg) => cfg.method = method,
Self::Lossless(cfg) => cfg.method = method,
}
self
}
#[must_use]
pub fn with_sns_strength(mut self, strength: u8) -> Self {
if let Self::Lossy(cfg) = &mut self {
cfg.sns_strength = Some(strength);
}
self
}
#[must_use]
pub fn with_filter_strength(mut self, strength: u8) -> Self {
if let Self::Lossy(cfg) = &mut self {
cfg.filter_strength = Some(strength);
}
self
}
#[must_use]
pub fn with_filter_sharpness(mut self, sharpness: u8) -> Self {
if let Self::Lossy(cfg) = &mut self {
cfg.filter_sharpness = Some(sharpness);
}
self
}
#[must_use]
pub fn with_segments(mut self, segments: u8) -> Self {
if let Self::Lossy(cfg) = &mut self {
cfg.segments = Some(segments);
}
self
}
#[cfg(feature = "__expert")]
#[must_use]
pub fn with_partition_limit(mut self, limit: u8) -> Self {
if let Self::Lossy(cfg) = &mut self {
cfg.partition_limit = Some(limit.min(100));
}
self
}
#[must_use]
pub fn with_near_lossless(mut self, value: u8) -> Self {
if let Self::Lossless(cfg) = &mut self {
cfg.near_lossless = value;
}
self
}
#[must_use]
pub fn with_target_size(mut self, bytes: u32) -> Self {
match &mut self {
Self::Lossy(cfg) => cfg.target_size = bytes,
Self::Lossless(cfg) => cfg.target_size = bytes,
}
self
}
#[must_use]
pub fn with_target_psnr(mut self, psnr: f32) -> Self {
if let Self::Lossy(cfg) = &mut self {
cfg.target_psnr = psnr;
}
self
}
#[must_use]
pub fn limits(mut self, limits: crate::Limits) -> Self {
match &mut self {
Self::Lossy(cfg) => cfg.limits = limits,
Self::Lossless(cfg) => cfg.limits = limits,
}
self
}
#[cfg(feature = "__expert")]
#[must_use]
pub fn with_sharp_yuv(mut self, enable: bool) -> Self {
if let Self::Lossy(cfg) = &mut self {
cfg.sharp_yuv = if enable {
Some(zenyuv::SharpYuvConfig::default())
} else {
None
};
}
self
}
#[must_use]
pub fn with_lossless(self, enable: bool) -> Self {
match (&self, enable) {
(Self::Lossy(_), true) => {
let q = self.get_quality();
let m = self.get_method();
let l = self.get_limits().clone();
Self::Lossless(LosslessConfig {
quality: q,
method: m,
limits: l,
..LosslessConfig::new()
})
}
(Self::Lossless(_), false) => {
let q = self.get_quality();
let m = self.get_method();
let l = self.get_limits().clone();
Self::Lossy(LossyConfig {
quality: q,
method: m,
limits: l,
..LossyConfig::new()
})
}
_ => self, }
}
#[must_use]
pub fn estimate(&self, width: u32, height: u32, bpp: u8) -> crate::heuristics::EncodeEstimate {
crate::heuristics::estimate_encode(width, height, bpp, self)
}
#[must_use]
pub fn estimate_memory(&self, width: u32, height: u32, bpp: u8) -> u64 {
crate::heuristics::estimate_encode(width, height, bpp, self).peak_memory_bytes
}
#[must_use]
pub fn estimate_memory_ceiling(&self, width: u32, height: u32, bpp: u8) -> u64 {
crate::heuristics::estimate_encode(width, height, bpp, self).peak_memory_bytes_max
}
#[must_use]
pub fn is_lossless(&self) -> bool {
matches!(self, Self::Lossless(_))
}
#[must_use]
pub fn as_lossy(&self) -> Option<&LossyConfig> {
match self {
Self::Lossy(cfg) => Some(cfg),
Self::Lossless(_) => None,
}
}
#[must_use]
pub fn as_lossless(&self) -> Option<&LosslessConfig> {
match self {
Self::Lossy(_) => None,
Self::Lossless(cfg) => Some(cfg),
}
}
}
impl core::fmt::Debug for LossyConfig {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("LossyConfig")
.field("quality", &self.quality)
.field("method", &self.method)
.field("alpha_quality", &self.alpha_quality)
.field("target_size", &self.target_size)
.field("target_psnr", &self.target_psnr)
.field("preset", &self.preset)
.field("sharp_yuv", &self.sharp_yuv)
.field("sns_strength", &self.sns_strength)
.field("filter_strength", &self.filter_strength)
.field("filter_sharpness", &self.filter_sharpness)
.field("segments", &self.segments)
.field("partition_limit", &self.partition_limit)
.field("limits", &self.limits)
.finish()
}
}
impl core::fmt::Debug for LosslessConfig {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("LosslessConfig")
.field("quality", &self.quality)
.field("method", &self.method)
.field("alpha_quality", &self.alpha_quality)
.field("target_size", &self.target_size)
.field("near_lossless", &self.near_lossless)
.field("exact", &self.exact)
.field("limits", &self.limits)
.finish()
}
}
impl core::fmt::Debug for EncoderConfig {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Lossy(cfg) => f.debug_tuple("Lossy").field(cfg).finish(),
Self::Lossless(cfg) => f.debug_tuple("Lossless").field(cfg).finish(),
}
}
}
use super::api::EncoderParams;
use super::fast_math;
impl LossyConfig {
pub(crate) fn to_params(&self) -> EncoderParams {
let (sns, filter, sharp, segs) = match self.preset {
Some(Preset::Default) => (50, 60, 0, 4),
Some(Preset::Photo) => (80, 30, 3, 4),
Some(Preset::Drawing) => (25, 10, 6, 4),
Some(Preset::Icon) => (0, 0, 0, 4),
Some(Preset::Text) => (0, 0, 0, 2),
Some(Preset::Picture) => (80, 35, 4, 4),
Some(Preset::Auto) | None => (50, 60, 0, 4),
};
EncoderParams {
use_predictor_transform: true,
use_lossy: true,
lossy_quality: fast_math::roundf(self.quality) as u8,
method: self.method,
sns_strength: self.sns_strength.unwrap_or(sns),
filter_strength: self.filter_strength.unwrap_or(filter),
filter_sharpness: self.filter_sharpness.unwrap_or(sharp),
num_segments: self.segments.unwrap_or(segs),
preset: self.preset.unwrap_or(Preset::Default),
target_size: self.target_size,
target_psnr: self.target_psnr,
sharp_yuv: self.sharp_yuv,
alpha_quality: self.alpha_quality,
partition_limit: self.partition_limit,
exact: false, smooth_segment_map: self.smooth_segment_map,
cost_model: self.cost_model,
multi_pass_stats: self.multi_pass_stats,
segment_quant_overrides: self.segment_quant_overrides,
}
}
}
impl LosslessConfig {
pub(crate) fn to_params(&self) -> EncoderParams {
EncoderParams {
use_predictor_transform: true,
use_lossy: false,
lossy_quality: fast_math::roundf(self.quality) as u8,
method: self.method,
sns_strength: 0,
filter_strength: 0,
filter_sharpness: 0,
num_segments: 1,
preset: Preset::Default, target_size: self.target_size,
target_psnr: 0.0,
sharp_yuv: None,
alpha_quality: self.alpha_quality,
partition_limit: None, exact: self.exact,
smooth_segment_map: false, cost_model: super::api::CostModel::ZenwebpDefault,
multi_pass_stats: false, segment_quant_overrides: None,
}
}
}
impl EncoderConfig {
pub(crate) fn to_params(&self) -> EncoderParams {
match self {
Self::Lossy(cfg) => cfg.to_params(),
Self::Lossless(cfg) => cfg.to_params(),
}
}
pub(crate) fn get_quality(&self) -> f32 {
match self {
Self::Lossy(cfg) => cfg.quality,
Self::Lossless(cfg) => cfg.quality,
}
}
pub(crate) fn get_method(&self) -> u8 {
match self {
Self::Lossy(cfg) => cfg.method,
Self::Lossless(cfg) => cfg.method,
}
}
pub(crate) fn get_limits(&self) -> &Limits {
match self {
Self::Lossy(cfg) => &cfg.limits,
Self::Lossless(cfg) => &cfg.limits,
}
}
}
#[cfg(feature = "target-zensim")]
fn encode_pixels_with_metrics_impl(
cfg: &LossyConfig,
pixels: &[u8],
layout: super::api::PixelLayout,
width: u32,
height: u32,
) -> Result<
(
alloc::vec::Vec<u8>,
super::zensim_target::ZensimEncodeMetrics,
),
super::api::EncodeError,
> {
if let Some(t) = cfg.target_zensim {
return super::zensim_target::iteration::run(cfg, t, pixels, layout, width, height);
}
encode_single_pass(cfg, pixels, layout, width, height)
}
#[cfg(not(feature = "target-zensim"))]
fn encode_pixels_with_metrics_impl(
cfg: &LossyConfig,
pixels: &[u8],
layout: super::api::PixelLayout,
width: u32,
height: u32,
) -> Result<
(
alloc::vec::Vec<u8>,
super::zensim_target::ZensimEncodeMetrics,
),
super::api::EncodeError,
> {
if let Some(t) = cfg.target_zensim {
let mut probe = cfg.clone();
probe.target_zensim = None;
probe.quality = super::zensim_target::zensim_to_starting_q_for_bucket(
t.target,
super::analysis::ImageContentType::Photo,
)
.clamp(0.0, 100.0);
return encode_single_pass(&probe, pixels, layout, width, height);
}
encode_single_pass(cfg, pixels, layout, width, height)
}
fn encode_single_pass(
cfg: &LossyConfig,
pixels: &[u8],
layout: super::api::PixelLayout,
width: u32,
height: u32,
) -> Result<
(
alloc::vec::Vec<u8>,
super::zensim_target::ZensimEncodeMetrics,
),
super::api::EncodeError,
> {
let req = super::api::EncodeRequest::lossy(cfg, pixels, layout, width, height);
match req.encode() {
Ok(bytes) => {
let len = bytes.len();
Ok((
bytes,
super::zensim_target::ZensimEncodeMetrics::no_target(len),
))
}
Err(at_err) => Err(at_err.decompose().0),
}
}
#[cfg(feature = "target-zensim")]
use super::validation::TARGET_ZENSIM_RANGE;
use super::validation::{
self as v, ALPHA_QUALITY_RANGE, FILTER_SHARPNESS_RANGE, FILTER_STRENGTH_RANGE, METHOD_RANGE,
NEAR_LOSSLESS_RANGE, PARTITION_LIMIT_RANGE, SEGMENTS_RANGE, SNS_STRENGTH_RANGE,
ValidationError,
};
impl LossyConfig {
pub fn validate(&self) -> Result<(), ValidationError> {
v::check_quality(self.quality)?;
v::check_method(self.method)?;
v::check_alpha_quality(self.alpha_quality)?;
v::check_target_psnr(self.target_psnr)?;
if let Some(s) = self.sns_strength
&& !SNS_STRENGTH_RANGE.contains(&s)
{
return Err(ValidationError::SnsStrengthOutOfRange {
value: s,
valid: SNS_STRENGTH_RANGE,
});
}
if let Some(s) = self.filter_strength
&& !FILTER_STRENGTH_RANGE.contains(&s)
{
return Err(ValidationError::FilterStrengthOutOfRange {
value: s,
valid: FILTER_STRENGTH_RANGE,
});
}
if let Some(s) = self.filter_sharpness
&& !FILTER_SHARPNESS_RANGE.contains(&s)
{
return Err(ValidationError::FilterSharpnessOutOfRange {
value: s,
valid: FILTER_SHARPNESS_RANGE,
});
}
if let Some(s) = self.segments
&& !SEGMENTS_RANGE.contains(&s)
{
return Err(ValidationError::SegmentsOutOfRange {
value: s,
valid: SEGMENTS_RANGE,
});
}
if let Some(p) = self.partition_limit
&& !PARTITION_LIMIT_RANGE.contains(&p)
{
return Err(ValidationError::PartitionLimitOutOfRange {
value: p,
valid: PARTITION_LIMIT_RANGE,
});
}
#[cfg(feature = "target-zensim")]
if let Some(t) = self.target_zensim {
if !t.target.is_finite() || !TARGET_ZENSIM_RANGE.contains(&t.target) {
return Err(ValidationError::TargetZensimOutOfRange {
value: t.target,
valid: TARGET_ZENSIM_RANGE,
});
}
if t.max_passes == 0 {
return Err(ValidationError::TargetZensimMaxPassesZero {
value: t.max_passes,
});
}
for (field, opt) in [
("max_overshoot", t.max_overshoot),
("max_undershoot", t.max_undershoot),
("max_undershoot_ship", t.max_undershoot_ship),
] {
if let Some(val) = opt
&& (!val.is_finite() || val < 0.0)
{
return Err(ValidationError::TargetZensimToleranceInvalid {
field,
value: val,
});
}
}
}
if let Some(cfg) = self.sharp_yuv
&& (!cfg.convergence_threshold.is_finite() || cfg.convergence_threshold < 0.0)
{
return Err(ValidationError::SharpYuvConvergenceThresholdInvalid {
value: cfg.convergence_threshold,
});
}
let size_set = self.target_size != 0;
let psnr_set = self.target_psnr != 0.0;
if size_set && psnr_set {
return Err(ValidationError::TargetMutuallyExclusive {
first: "target_size",
second: "target_psnr",
});
}
#[cfg(feature = "target-zensim")]
{
let zensim_set = self.target_zensim.is_some();
if size_set && zensim_set {
return Err(ValidationError::TargetMutuallyExclusive {
first: "target_size",
second: "target_zensim",
});
}
if psnr_set && zensim_set {
return Err(ValidationError::TargetMutuallyExclusive {
first: "target_psnr",
second: "target_zensim",
});
}
}
Ok(())
}
}
impl LosslessConfig {
pub fn validate(&self) -> Result<(), ValidationError> {
v::check_quality(self.quality)?;
v::check_method(self.method)?;
v::check_alpha_quality(self.alpha_quality)?;
if !NEAR_LOSSLESS_RANGE.contains(&self.near_lossless) {
return Err(ValidationError::NearLosslessOutOfRange {
value: self.near_lossless,
valid: NEAR_LOSSLESS_RANGE,
});
}
let _ = ALPHA_QUALITY_RANGE; let _ = METHOD_RANGE;
Ok(())
}
}
impl EncoderConfig {
pub fn validate(&self) -> Result<(), ValidationError> {
match self {
Self::Lossy(c) => c.validate(),
Self::Lossless(c) => c.validate(),
}
}
}
#[cfg(feature = "__expert")]
impl InternalParams {
pub fn validate(&self) -> Result<(), ValidationError> {
if let Some(p) = self.partition_limit
&& !PARTITION_LIMIT_RANGE.contains(&p)
{
return Err(ValidationError::PartitionLimitOutOfRange {
value: p,
valid: PARTITION_LIMIT_RANGE,
});
}
if let Some(SharpYuvSetting::Custom(cfg)) = &self.sharp_yuv
&& (!cfg.convergence_threshold.is_finite() || cfg.convergence_threshold < 0.0)
{
return Err(ValidationError::SharpYuvConvergenceThresholdInvalid {
value: cfg.convergence_threshold,
});
}
Ok(())
}
}