1use std::mem;
2
3use crate::{ColorMode, Error};
4
5#[allow(unused_imports)]
6use crate::Encoder; use libwebp_sys as webp;
9
10#[derive(Clone)]
15pub struct EncoderOptions {
16 pub anim_params: AnimParams,
18
19 pub minimize_size: bool,
22
23 pub kmin: isize,
32 pub kmax: isize,
33
34 pub allow_mixed: bool,
37
38 pub verbose: bool,
40
41 pub color_mode: ColorMode,
43
44 pub encoding_config: Option<EncodingConfig>,
47}
48
49impl Default for EncoderOptions {
50 fn default() -> Self {
51 Self {
52 anim_params: AnimParams::default(),
53 minimize_size: false,
54 kmin: 0,
55 kmax: 0,
56 allow_mixed: false,
57 verbose: false,
58 color_mode: ColorMode::Rgba,
59 encoding_config: None,
60 }
61 }
62}
63
64#[derive(Clone, Default)]
66pub struct AnimParams {
67 pub loop_count: i32,
69}
70
71#[derive(Debug, Clone)]
73pub enum EncodingType {
74 Lossy(LossyEncodingConfig),
76
77 Lossless,
79}
80
81impl EncodingType {
82 pub fn new_lossy() -> Self {
83 EncodingType::Lossy(LossyEncodingConfig::default())
84 }
85}
86
87#[derive(Debug, Clone)]
92pub struct EncodingConfig {
93 pub encoding_type: EncodingType,
95
96 pub quality: f32,
102
103 pub method: usize,
105 }
107
108impl EncodingConfig {
109 pub fn new_lossy(quality: f32) -> Self {
110 Self {
111 encoding_type: EncodingType::new_lossy(),
112 quality,
113 ..Default::default()
114 }
115 }
116
117 pub(crate) fn to_config_container(&self) -> Result<ConfigContainer, Error> {
118 ConfigContainer::new(self)
119 }
120
121 pub(crate) fn apply_to(&self, webp_config: &mut webp::WebPConfig) {
122 webp_config.lossless = match &self.encoding_type {
123 EncodingType::Lossy(lossless_config) => {
124 lossless_config.apply_to(webp_config);
125 0
126 }
127 EncodingType::Lossless => 1,
128 };
129 webp_config.quality = self.quality;
130 }
131}
132
133impl Default for EncodingConfig {
134 fn default() -> Self {
135 Self {
137 encoding_type: EncodingType::Lossless,
138 quality: 1.,
139 method: 4,
140 }
141 }
142}
143
144#[derive(Debug, Clone)]
146pub struct LossyEncodingConfig {
147 pub target_size: usize,
150
151 pub target_psnr: f32,
154
155 pub segments: usize,
157
158 pub sns_strength: usize,
160
161 pub filter_strength: usize,
163
164 pub filter_sharpness: usize,
166
167 pub filter_type: usize,
170
171 pub autofilter: bool,
173
174 pub alpha_compression: bool,
177
178 pub alpha_filtering: usize,
181
182 pub alpha_quality: usize,
185
186 pub pass: usize,
188
189 pub show_compressed: bool,
192
193 pub preprocessing: bool,
195
196 pub partitions: usize,
199
200 pub partition_limit: isize,
204
205 pub use_sharp_yuv: bool,
207}
208
209impl Default for LossyEncodingConfig {
210 fn default() -> Self {
211 Self {
212 target_size: 0,
214 target_psnr: 0.,
215 segments: 1,
216 sns_strength: 50,
217 filter_strength: 60,
218 filter_sharpness: 0,
219 filter_type: 1,
220 partitions: 0,
221 pass: 1,
222 show_compressed: false,
223 autofilter: false,
224 alpha_compression: true,
225 alpha_filtering: 1,
226 alpha_quality: 100,
227 preprocessing: false,
228 partition_limit: 0,
229 use_sharp_yuv: false,
230 }
231 }
232}
233
234impl LossyEncodingConfig {
235 pub fn new_from_default_preset() -> Self {
236 Self {
237 ..Default::default()
238 }
239 }
240
241 pub fn new_from_picture_preset() -> Self {
242 Self {
243 sns_strength: 80,
244 filter_sharpness: 4,
245 filter_strength: 35,
246 preprocessing: false,
247 ..Default::default()
248 }
249 }
250
251 pub fn new_from_photo_preset() -> Self {
252 Self {
253 sns_strength: 80,
254 filter_sharpness: 3,
255 filter_strength: 30,
256 preprocessing: false,
257 ..Default::default()
258 }
259 }
260
261 pub fn new_from_drawing_preset() -> Self {
262 Self {
263 sns_strength: 25,
264 filter_sharpness: 6,
265 filter_strength: 10,
266 ..Default::default()
267 }
268 }
269
270 pub fn new_from_icon_preset() -> Self {
271 Self {
272 sns_strength: 0,
273 filter_strength: 0,
274 preprocessing: false,
275 ..Default::default()
276 }
277 }
278
279 pub fn new_from_text_preset() -> Self {
280 Self {
281 sns_strength: 0,
282 filter_strength: 0,
283 preprocessing: false,
284 segments: 2,
285 ..Default::default()
286 }
287 }
288
289 fn apply_to(&self, webp_config: &mut webp::WebPConfig) {
290 webp_config.target_size = self.target_size as i32;
291 webp_config.target_PSNR = self.target_psnr;
292 webp_config.segments = self.segments as i32;
293 webp_config.sns_strength = self.sns_strength as i32;
294 webp_config.filter_strength = self.filter_strength as i32;
295 webp_config.filter_sharpness = self.filter_sharpness as i32;
296 webp_config.filter_type = self.filter_type as i32;
297 webp_config.autofilter = self.autofilter as i32;
298 webp_config.alpha_compression = self.alpha_compression as i32;
299 webp_config.alpha_filtering = self.alpha_filtering as i32;
300 webp_config.alpha_quality = self.alpha_quality as i32;
301 webp_config.pass = self.pass as i32;
302 webp_config.show_compressed = self.show_compressed as i32;
303 webp_config.preprocessing = self.preprocessing as i32;
304 webp_config.partitions = self.partitions as i32;
305 webp_config.partition_limit = self.partition_limit as i32;
306 webp_config.use_sharp_yuv = self.use_sharp_yuv as i32;
307 }
308}
309
310pub(crate) struct ConfigContainer {
311 config: webp::WebPConfig,
312}
313
314impl ConfigContainer {
315 pub fn new(config: &EncodingConfig) -> Result<Self, Error> {
316 let mut webp_config = unsafe {
317 let mut config = mem::zeroed();
318 webp::WebPConfigInit(&mut config);
319 config
320 };
321
322 config.apply_to(&mut webp_config);
323
324 if unsafe { webp::WebPValidateConfig(&webp_config) } == 0 {
325 return Err(Error::InvalidEncodingConfig);
326 }
327
328 Ok(Self {
329 config: webp_config,
330 })
331 }
332
333 pub fn as_ptr(&self) -> &webp::WebPConfig {
334 &self.config
335 }
336}
337
338#[cfg(test)]
339mod tests {
340 use super::*;
341
342 #[test]
343 fn test_config_defaults() {
344 let default_webp_config = unsafe {
345 let mut config = mem::zeroed();
346 webp::WebPConfigInit(&mut config);
347 config
348 };
349
350 let config = ConfigContainer::new(&EncodingConfig::default()).unwrap();
351
352 let left = config.as_ptr();
353 let def = &default_webp_config;
354
355 assert_eq!(left.lossless, 1);
357 assert_eq!(left.quality, 1.0);
358
359 assert_eq!(left.method, def.method, "c.method");
361 assert_eq!(left.image_hint, def.image_hint, "c.image_hint");
362 assert_eq!(left.target_size, def.target_size, "c.target_size");
363 assert_eq!(left.target_PSNR, def.target_PSNR, "c.target_PSNR");
364 assert_eq!(left.segments, def.segments, "c.segments");
365 assert_eq!(left.sns_strength, def.sns_strength, "c.sns_strength");
366 assert_eq!(
367 left.filter_strength, def.filter_strength,
368 "c.filter_strength"
369 );
370 assert_eq!(
371 left.filter_sharpness, def.filter_sharpness,
372 "c.filter_sharpness"
373 );
374 assert_eq!(left.filter_type, def.filter_type, "c.filter_type");
375 assert_eq!(left.autofilter, def.autofilter, "c.autofilter");
376 assert_eq!(
377 left.alpha_compression, def.alpha_compression,
378 "c.alpha_compression"
379 );
380 assert_eq!(
381 left.alpha_filtering, def.alpha_filtering,
382 "c.alpha_filtering"
383 );
384 assert_eq!(left.alpha_quality, def.alpha_quality, "c.alpha_quality");
385 assert_eq!(left.pass, def.pass, "c.pass");
386 assert_eq!(
387 left.show_compressed, def.show_compressed,
388 "c.show_compressed"
389 );
390 assert_eq!(left.preprocessing, def.preprocessing, "c.preprocessing");
391 assert_eq!(left.partitions, def.partitions, "c.partitions");
392 assert_eq!(
393 left.partition_limit, def.partition_limit,
394 "c.partition_limit"
395 );
396 assert_eq!(
397 left.emulate_jpeg_size, def.emulate_jpeg_size,
398 "c.emulate_jpeg_size"
399 );
400 assert_eq!(left.thread_level, def.thread_level, "c.thread_level");
401 assert_eq!(left.low_memory, def.low_memory, "c.low_memory");
402
403 assert_eq!(left.near_lossless, def.near_lossless, "c.near_lossless");
404 assert_eq!(left.exact, def.exact, "c.exact");
405 assert_eq!(
406 left.use_delta_palette, def.use_delta_palette,
407 "c.use_delta_palette"
408 );
409 assert_eq!(left.use_sharp_yuv, def.use_sharp_yuv, "c.use_sharp_yuv");
410 assert_eq!(left.qmin, def.qmin, "c.qmin");
411 assert_eq!(left.qmax, def.qmax, "c.qmax");
412 }
413}