use std::mem;
use crate::{ColorMode, Error};
#[allow(unused_imports)]
use crate::Encoder;
use libwebp_sys as webp;
#[derive(Clone)]
pub struct EncoderOptions {
pub minimize_size: bool,
pub kmin: isize,
pub kmax: isize,
pub allow_mixed: bool,
pub verbose: bool,
pub color_mode: ColorMode,
pub encoding_config: Option<EncodingConfig>,
}
impl Default for EncoderOptions {
fn default() -> Self {
Self {
minimize_size: false,
kmin: 0,
kmax: 0,
allow_mixed: false,
verbose: false,
color_mode: ColorMode::Rgba,
encoding_config: None,
}
}
}
#[derive(Debug, Clone)]
pub enum EncodingType {
Lossy(LossyEncodingConfig),
Lossless,
}
impl EncodingType {
pub fn new_lossy() -> Self {
EncodingType::Lossy(LossyEncodingConfig::default())
}
}
#[derive(Debug, Clone)]
pub struct EncodingConfig {
pub encoding_type: EncodingType,
pub quality: f32,
pub method: usize,
}
impl EncodingConfig {
pub fn new_lossy(quality: f32) -> Self {
Self {
encoding_type: EncodingType::new_lossy(),
quality,
..Default::default()
}
}
pub(crate) fn to_config_container(&self) -> Result<ConfigContainer, Error> {
ConfigContainer::new(self)
}
pub(crate) fn apply_to(&self, webp_config: &mut webp::WebPConfig) {
webp_config.lossless = match &self.encoding_type {
EncodingType::Lossy(lossless_config) => {
lossless_config.apply_to(webp_config);
0
}
EncodingType::Lossless => 1,
};
webp_config.quality = self.quality;
}
}
impl Default for EncodingConfig {
fn default() -> Self {
Self {
encoding_type: EncodingType::Lossless,
quality: 1.,
method: 4,
}
}
}
#[derive(Debug, Clone)]
pub struct LossyEncodingConfig {
pub target_size: usize,
pub target_psnr: f32,
pub segments: usize,
pub sns_strength: usize,
pub filter_strength: usize,
pub filter_sharpness: usize,
pub filter_type: usize,
pub autofilter: bool,
pub alpha_compression: bool,
pub alpha_filtering: usize,
pub alpha_quality: usize,
pub pass: usize,
pub show_compressed: bool,
pub preprocessing: bool,
pub partitions: usize,
pub partition_limit: isize,
pub use_sharp_yuv: bool,
}
impl Default for LossyEncodingConfig {
fn default() -> Self {
Self {
target_size: 0,
target_psnr: 0.,
segments: 1,
sns_strength: 50,
filter_strength: 60,
filter_sharpness: 0,
filter_type: 1,
partitions: 0,
pass: 1,
show_compressed: false,
autofilter: false,
alpha_compression: true,
alpha_filtering: 1,
alpha_quality: 100,
preprocessing: false,
partition_limit: 0,
use_sharp_yuv: false,
}
}
}
impl LossyEncodingConfig {
pub fn new_from_default_preset() -> Self {
Self {
..Default::default()
}
}
pub fn new_from_picture_preset() -> Self {
Self {
sns_strength: 80,
filter_sharpness: 4,
filter_strength: 35,
preprocessing: false,
..Default::default()
}
}
pub fn new_from_photo_preset() -> Self {
Self {
sns_strength: 80,
filter_sharpness: 3,
filter_strength: 30,
preprocessing: false,
..Default::default()
}
}
pub fn new_from_drawing_preset() -> Self {
Self {
sns_strength: 25,
filter_sharpness: 6,
filter_strength: 10,
..Default::default()
}
}
pub fn new_from_icon_preset() -> Self {
Self {
sns_strength: 0,
filter_strength: 0,
preprocessing: false,
..Default::default()
}
}
pub fn new_from_text_preset() -> Self {
Self {
sns_strength: 0,
filter_strength: 0,
preprocessing: false,
segments: 2,
..Default::default()
}
}
fn apply_to(&self, webp_config: &mut webp::WebPConfig) {
webp_config.target_size = self.target_size as i32;
webp_config.target_PSNR = self.target_psnr;
webp_config.segments = self.segments as i32;
webp_config.sns_strength = self.sns_strength as i32;
webp_config.filter_strength = self.filter_strength as i32;
webp_config.filter_sharpness = self.filter_sharpness as i32;
webp_config.filter_type = self.filter_type as i32;
webp_config.autofilter = self.autofilter as i32;
webp_config.alpha_compression = self.alpha_compression as i32;
webp_config.alpha_filtering = self.alpha_filtering as i32;
webp_config.alpha_quality = self.alpha_quality as i32;
webp_config.pass = self.pass as i32;
webp_config.show_compressed = self.show_compressed as i32;
webp_config.preprocessing = self.preprocessing as i32;
webp_config.partitions = self.partitions as i32;
webp_config.partition_limit = self.partition_limit as i32;
webp_config.use_sharp_yuv = self.use_sharp_yuv as i32;
}
}
pub(crate) struct ConfigContainer {
config: webp::WebPConfig,
}
impl ConfigContainer {
pub fn new(config: &EncodingConfig) -> Result<Self, Error> {
let mut webp_config = unsafe {
let mut config = mem::zeroed();
webp::WebPConfigInit(&mut config);
config
};
config.apply_to(&mut webp_config);
if unsafe { webp::WebPValidateConfig(&webp_config) } == 0 {
return Err(Error::InvalidEncodingConfig);
}
Ok(Self {
config: webp_config,
})
}
pub fn as_ptr(&self) -> &webp::WebPConfig {
&self.config
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_config_defaults() {
let default_webp_config = unsafe {
let mut config = mem::zeroed();
webp::WebPConfigInit(&mut config);
config
};
let config = ConfigContainer::new(&EncodingConfig::default()).unwrap();
let left = config.as_ptr();
let def = &default_webp_config;
assert_eq!(left.lossless, 1);
assert_eq!(left.quality, 1.0);
assert_eq!(left.method, def.method, "c.method");
assert_eq!(left.image_hint, def.image_hint, "c.image_hint");
assert_eq!(left.target_size, def.target_size, "c.target_size");
assert_eq!(left.target_PSNR, def.target_PSNR, "c.target_PSNR");
assert_eq!(left.segments, def.segments, "c.segments");
assert_eq!(left.sns_strength, def.sns_strength, "c.sns_strength");
assert_eq!(
left.filter_strength, def.filter_strength,
"c.filter_strength"
);
assert_eq!(
left.filter_sharpness, def.filter_sharpness,
"c.filter_sharpness"
);
assert_eq!(left.filter_type, def.filter_type, "c.filter_type");
assert_eq!(left.autofilter, def.autofilter, "c.autofilter");
assert_eq!(
left.alpha_compression, def.alpha_compression,
"c.alpha_compression"
);
assert_eq!(
left.alpha_filtering, def.alpha_filtering,
"c.alpha_filtering"
);
assert_eq!(left.alpha_quality, def.alpha_quality, "c.alpha_quality");
assert_eq!(left.pass, def.pass, "c.pass");
assert_eq!(
left.show_compressed, def.show_compressed,
"c.show_compressed"
);
assert_eq!(left.preprocessing, def.preprocessing, "c.preprocessing");
assert_eq!(left.partitions, def.partitions, "c.partitions");
assert_eq!(
left.partition_limit, def.partition_limit,
"c.partition_limit"
);
assert_eq!(
left.emulate_jpeg_size, def.emulate_jpeg_size,
"c.emulate_jpeg_size"
);
assert_eq!(left.thread_level, def.thread_level, "c.thread_level");
assert_eq!(left.low_memory, def.low_memory, "c.low_memory");
assert_eq!(left.near_lossless, def.near_lossless, "c.near_lossless");
assert_eq!(left.exact, def.exact, "c.exact");
assert_eq!(
left.use_delta_palette, def.use_delta_palette,
"c.use_delta_palette"
);
assert_eq!(left.use_sharp_yuv, def.use_sharp_yuv, "c.use_sharp_yuv");
assert_eq!(left.qmin, def.qmin, "c.qmin");
assert_eq!(left.qmax, def.qmax, "c.qmax");
}
}