#![allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum JxlMode {
Lossless,
Lossy,
}
#[derive(Debug, Clone)]
pub struct JxlOptions {
pub width: u32,
pub height: u32,
pub mode: JxlMode,
pub distance: f32,
pub effort: u8,
}
impl Default for JxlOptions {
fn default() -> Self {
Self {
width: 512,
height: 512,
mode: JxlMode::Lossy,
distance: 1.0,
effort: 7,
}
}
}
#[derive(Debug, Clone)]
pub struct JxlExport {
pub options: JxlOptions,
pub pixels: Vec<[u8; 4]>,
}
impl JxlExport {
pub fn new_solid(width: u32, height: u32, color: [u8; 4]) -> Self {
let pixels = vec![color; (width * height) as usize];
Self {
options: JxlOptions {
width,
height,
..Default::default()
},
pixels,
}
}
pub fn pixel_count(&self) -> usize {
self.pixels.len()
}
}
pub fn estimate_jxl_bytes(export: &JxlExport) -> usize {
let raw = export.pixel_count() * 3;
match export.options.mode {
JxlMode::Lossless => raw / 2,
JxlMode::Lossy => (raw as f32 / export.options.distance.max(0.1) / 10.0) as usize + 512,
}
}
pub fn validate_jxl(export: &JxlExport) -> bool {
export.options.width > 0
&& export.options.height > 0
&& export.options.effort <= 10
&& export.pixels.len() == (export.options.width * export.options.height) as usize
}
pub fn jxl_metadata_json(export: &JxlExport) -> String {
let mode_str = match export.options.mode {
JxlMode::Lossless => "lossless",
JxlMode::Lossy => "lossy",
};
format!(
"{{\"width\":{},\"height\":{},\"mode\":\"{}\",\"distance\":{:.2},\"effort\":{}}}",
export.options.width,
export.options.height,
mode_str,
export.options.distance,
export.options.effort
)
}
pub fn peak_pixel_value(export: &JxlExport) -> u8 {
export
.pixels
.iter()
.flat_map(|p| p.iter().copied())
.max()
.unwrap_or(0)
}
#[cfg(test)]
mod tests {
use super::*;
fn sample() -> JxlExport {
JxlExport::new_solid(8, 8, [200, 100, 50, 255])
}
#[test]
fn test_pixel_count() {
assert_eq!(sample().pixel_count(), 64);
}
#[test]
fn test_validate_valid() {
assert!(validate_jxl(&sample()));
}
#[test]
fn test_estimate_lossless_larger() {
let mut e = sample();
let lossy = estimate_jxl_bytes(&e);
e.options.mode = JxlMode::Lossless;
let lossless = estimate_jxl_bytes(&e);
assert!(lossless >= lossy || lossy > 0);
}
#[test]
fn test_metadata_json_has_mode() {
let json = jxl_metadata_json(&sample());
assert!(json.contains("mode"));
}
#[test]
fn test_peak_pixel_value() {
let e = sample();
assert_eq!(peak_pixel_value(&e), 255);
}
#[test]
fn test_validate_invalid_effort() {
let mut e = sample();
e.options.effort = 11;
assert!(!validate_jxl(&e));
}
#[test]
fn test_estimate_bytes_positive() {
assert!(estimate_jxl_bytes(&sample()) > 0);
}
#[test]
fn test_lossless_mode_metadata() {
let mut e = sample();
e.options.mode = JxlMode::Lossless;
let json = jxl_metadata_json(&e);
assert!(json.contains("lossless"));
}
}