use crate::{Limits, Preset};
#[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 preset: Option<Preset>,
pub sharp_yuv: bool,
pub sns_strength: Option<u8>,
pub filter_strength: Option<u8>,
pub filter_sharpness: Option<u8>,
pub segments: Option<u8>,
pub limits: Limits,
}
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,
preset: None,
sharp_yuv: false,
sns_strength: None,
filter_strength: None,
filter_sharpness: None,
segments: None,
limits: Limits::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_preset_value(mut self, preset: Preset) -> Self {
self.preset = Some(preset);
self
}
#[must_use]
pub fn with_sharp_yuv(mut self, enable: bool) -> Self {
self.sharp_yuv = enable;
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
}
#[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
}
}
#[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
}
#[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
}
#[must_use]
pub fn with_sharp_yuv(mut self, enable: bool) -> Self {
if let Self::Lossy(cfg) = &mut self {
cfg.sharp_yuv = enable;
}
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("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,
use_sharp_yuv: self.sharp_yuv,
alpha_quality: self.alpha_quality,
}
}
}
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,
use_sharp_yuv: false,
alpha_quality: self.alpha_quality,
}
}
}
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,
}
}
}