#![allow(dead_code)]
#[derive(Debug, Clone)]
pub struct WebpOptions {
pub width: u32,
pub height: u32,
pub quality: f32,
pub lossless: bool,
}
impl Default for WebpOptions {
fn default() -> Self {
Self {
width: 512,
height: 512,
quality: 80.0,
lossless: false,
}
}
}
#[derive(Debug, Clone)]
pub struct WebpExport {
pub options: WebpOptions,
pub pixels: Vec<[u8; 4]>,
}
impl WebpExport {
pub fn new_solid(width: u32, height: u32, color: [u8; 4]) -> Self {
let pixels = vec![color; (width * height) as usize];
Self {
options: WebpOptions {
width,
height,
..Default::default()
},
pixels,
}
}
pub fn pixel_count(&self) -> usize {
self.pixels.len()
}
}
pub fn estimate_webp_bytes(export: &WebpExport) -> usize {
let raw = export.pixel_count() * 4;
if export.options.lossless {
raw / 2
} else {
((raw as f32 * export.options.quality / 100.0) as usize).max(1)
}
}
pub fn validate_webp(export: &WebpExport) -> bool {
export.options.width > 0
&& export.options.height > 0
&& (0.0..=100.0).contains(&export.options.quality)
&& export.pixels.len() == (export.options.width * export.options.height) as usize
}
pub fn webp_metadata_json(export: &WebpExport) -> String {
format!(
"{{\"width\":{},\"height\":{},\"quality\":{:.1},\"lossless\":{}}}",
export.options.width,
export.options.height,
export.options.quality,
export.options.lossless
)
}
pub fn average_luminance(export: &WebpExport) -> f32 {
if export.pixels.is_empty() {
return 0.0;
}
let sum: f32 = export
.pixels
.iter()
.map(|p| 0.2126 * p[0] as f32 + 0.7152 * p[1] as f32 + 0.0722 * p[2] as f32)
.sum();
sum / export.pixels.len() as f32
}
#[cfg(test)]
mod tests {
use super::*;
fn white_export() -> WebpExport {
WebpExport::new_solid(8, 8, [255, 255, 255, 255])
}
#[test]
fn test_pixel_count() {
let e = white_export();
assert_eq!(e.pixel_count(), 64);
}
#[test]
fn test_validate_valid() {
assert!(validate_webp(&white_export()));
}
#[test]
fn test_validate_zero_dim() {
let mut e = white_export();
e.options.width = 0;
assert!(!validate_webp(&e));
}
#[test]
fn test_estimate_webp_bytes_positive() {
assert!(estimate_webp_bytes(&white_export()) > 0);
}
#[test]
fn test_lossless_larger_estimate() {
let mut e = white_export();
let lossy = estimate_webp_bytes(&e);
e.options.lossless = true;
let lossless = estimate_webp_bytes(&e);
assert!(lossless > 0 && lossy > 0);
}
#[test]
fn test_average_luminance_white() {
let e = white_export();
assert!(average_luminance(&e) > 200.0);
}
#[test]
fn test_metadata_json_contains_quality() {
let e = white_export();
assert!(webp_metadata_json(&e).contains("quality"));
}
#[test]
fn test_black_luminance() {
let e = WebpExport::new_solid(4, 4, [0, 0, 0, 255]);
assert!(average_luminance(&e) < 1.0);
}
}