use dssim::Dssim;
use jpegli::encoder::{ChromaSubsampling, EncoderConfig, PixelLayout};
use rgb::RGBA8;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
fn find_corpus_path() -> Option<PathBuf> {
let home = std::env::var("HOME").unwrap_or_default();
let candidates = [
format!(
"{}/work/codec-eval/codec-corpus/CID22/CID22-512/training",
home
),
format!("{}/work/codec-eval/codec-corpus/kodak", home),
"../corpus/CID22-512".to_string(),
];
for p in candidates {
let path = PathBuf::from(&p);
if path.exists() {
return Some(path);
}
}
None
}
fn cjpegli_path() -> Option<PathBuf> {
let candidates = [
"internal/jpegli-cpp/build/tools/cjpegli",
"../internal/jpegli-cpp/build/tools/cjpegli",
];
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".to_string());
for path in &candidates {
let p = PathBuf::from(path);
if p.exists() {
return Some(p);
}
let p = PathBuf::from(&manifest_dir).join(path);
if p.exists() {
return Some(p);
}
}
None
}
fn load_png(path: &Path) -> Option<(Vec<u8>, u32, u32)> {
let file = fs::File::open(path).ok()?;
let decoder = png::Decoder::new(file);
let mut reader = decoder.read_info().ok()?;
let mut buf = vec![0; reader.output_buffer_size()];
let info = reader.next_frame(&mut buf).ok()?;
let width = info.width;
let height = info.height;
let rgb = match info.color_type {
png::ColorType::Rgb => buf[..info.buffer_size()].to_vec(),
png::ColorType::Rgba => buf[..info.buffer_size()]
.chunks(4)
.flat_map(|c| [c[0], c[1], c[2]])
.collect(),
png::ColorType::Grayscale => buf[..info.buffer_size()]
.iter()
.flat_map(|&g| [g, g, g])
.collect(),
_ => return None,
};
Some((rgb, width, height))
}
fn crop_image(rgb: &[u8], orig_width: usize, new_width: usize, new_height: usize) -> Vec<u8> {
let mut cropped = Vec::with_capacity(new_width * new_height * 3);
for y in 0..new_height {
let row_start = y * orig_width * 3;
cropped.extend_from_slice(&rgb[row_start..row_start + new_width * 3]);
}
cropped
}
fn encode_rust(
rgb: &[u8],
width: u32,
height: u32,
subsampling: ChromaSubsampling,
quality: f32,
) -> Vec<u8> {
let config = EncoderConfig::ycbcr(quality, subsampling)
.optimize_huffman(true)
.progressive(true);
let mut enc = config
.encode_from_bytes(width, height, PixelLayout::Rgb8Srgb)
.expect("encoder setup");
enc.push_packed(rgb, enough::Unstoppable).expect("push");
enc.finish().expect("Rust encode failed")
}
fn encode_cpp(
rgb: &[u8],
width: usize,
height: usize,
subsampling: ChromaSubsampling,
quality: u8,
cjpegli: &Path,
) -> Option<Vec<u8>> {
let ppm_path = format!(
"/tmp/edge_test_{}x{}_{}.ppm",
width,
height,
std::process::id()
);
let jpg_path = format!(
"/tmp/edge_test_{}x{}_{}_cpp.jpg",
width,
height,
std::process::id()
);
let mut ppm = format!("P6\n{} {}\n255\n", width, height).into_bytes();
ppm.extend_from_slice(rgb);
fs::write(&ppm_path, &ppm).ok()?;
let sample_arg = match subsampling {
ChromaSubsampling::None => "444",
ChromaSubsampling::HalfHorizontal => "422",
ChromaSubsampling::Quarter => "420",
ChromaSubsampling::HalfVertical => "440",
_ => return None, };
let output = Command::new(cjpegli)
.args([
&ppm_path,
&jpg_path,
"-q",
&quality.to_string(),
"--chroma_subsampling",
sample_arg,
"--progressive_level=2",
])
.output()
.ok()?;
if !output.status.success() {
eprintln!(
"cjpegli failed: {}",
String::from_utf8_lossy(&output.stderr)
);
return None;
}
let result = fs::read(&jpg_path).ok();
let _ = fs::remove_file(&ppm_path);
let _ = fs::remove_file(&jpg_path);
result
}
fn decode_jpeg(data: &[u8]) -> Vec<u8> {
use zune_jpeg::zune_core::bytestream::ZCursor;
use zune_jpeg::JpegDecoder;
let cursor = ZCursor::new(data);
let mut decoder = JpegDecoder::new(cursor);
decoder.decode().expect("JPEG decode failed")
}
fn compute_dssim(orig: &[u8], decoded: &[u8], width: usize, height: usize) -> f64 {
let attr = Dssim::new();
let orig_rgba: Vec<RGBA8> = orig
.chunks(3)
.map(|c| RGBA8::new(c[0], c[1], c[2], 255))
.collect();
let dec_rgba: Vec<RGBA8> = decoded
.chunks(3)
.map(|c| RGBA8::new(c[0], c[1], c[2], 255))
.collect();
let orig_img = attr.create_image_rgba(&orig_rgba, width, height).unwrap();
let dec_img = attr.create_image_rgba(&dec_rgba, width, height).unwrap();
let (dssim, _) = attr.compare(&orig_img, dec_img);
dssim.into()
}
#[allow(dead_code)] struct TestResult {
image_name: String,
width: usize,
height: usize,
rust_size: usize,
cpp_size: usize,
size_diff_pct: f64,
rust_dssim: f64,
cpp_dssim: f64,
dssim_diff_pct: f64,
}
fn test_cropped_image(
rgb: &[u8],
orig_width: usize,
image_name: &str,
target_width: usize,
target_height: usize,
subsampling: ChromaSubsampling,
quality: u8,
cjpegli: &Path,
) -> Option<TestResult> {
let cropped = crop_image(rgb, orig_width, target_width, target_height);
let rust_jpeg = encode_rust(
&cropped,
target_width as u32,
target_height as u32,
subsampling,
quality as f32,
);
let cpp_jpeg = encode_cpp(
&cropped,
target_width,
target_height,
subsampling,
quality,
cjpegli,
)?;
let rust_decoded = decode_jpeg(&rust_jpeg);
let cpp_decoded = decode_jpeg(&cpp_jpeg);
let rust_dssim = compute_dssim(&cropped, &rust_decoded, target_width, target_height);
let cpp_dssim = compute_dssim(&cropped, &cpp_decoded, target_width, target_height);
let size_diff_pct =
(rust_jpeg.len() as f64 - cpp_jpeg.len() as f64) / cpp_jpeg.len() as f64 * 100.0;
let dssim_diff_pct = if cpp_dssim > 0.0 {
(rust_dssim - cpp_dssim) / cpp_dssim * 100.0
} else {
0.0
};
Some(TestResult {
image_name: image_name.to_string(),
width: target_width,
height: target_height,
rust_size: rust_jpeg.len(),
cpp_size: cpp_jpeg.len(),
size_diff_pct,
rust_dssim,
cpp_dssim,
dssim_diff_pct,
})
}
#[test]
#[ignore = "requires cjpegli binary and corpus"]
fn test_strip_edge_real_images() {
let cjpegli = match cjpegli_path() {
Some(p) => p,
None => {
eprintln!("Skipping: cjpegli not found");
return;
}
};
let corpus_path = match find_corpus_path() {
Some(p) => p,
None => {
eprintln!("Skipping: corpus not found");
return;
}
};
let images: Vec<_> = fs::read_dir(&corpus_path)
.unwrap()
.filter_map(|e| e.ok())
.filter(|e| e.path().extension().is_some_and(|ext| ext == "png"))
.take(5)
.collect();
if images.is_empty() {
eprintln!("Skipping: no PNG images in corpus");
return;
}
println!("\n=== REAL IMAGE EDGE HANDLING TEST ===");
println!("Corpus: {:?}", corpus_path);
println!("Images: {}", images.len());
println!("Settings: Progressive, Q85, S444, optimized Huffman\n");
let quality = 85u8;
let subsampling = ChromaSubsampling::None;
let remainders = [1, 2, 3, 4, 5, 6, 7];
let mut all_results = Vec::new();
let mut max_size_diff = 0.0f64;
let mut max_dssim_diff = 0.0f64;
let mut failures = Vec::new();
println!(
"{:>20} {:>10} {:>8} {:>8} {:>8} {:>10} {:>10} {:>8}",
"Image", "WxH", "Rust", "C++", "Size%", "RustDSSIM", "C++DSSIM", "DSSIM%"
);
println!("{}", "-".repeat(95));
for entry in &images {
let path = entry.path();
let name = path.file_stem().unwrap().to_string_lossy();
let (rgb, orig_width, orig_height) = match load_png(&path) {
Some(d) => d,
None => continue,
};
let base_height = ((orig_height as usize / 8) * 8).min(256);
for &w_rem in &remainders {
let target_width = 256 + w_rem; if target_width > orig_width as usize {
continue;
}
if let Some(result) = test_cropped_image(
&rgb,
orig_width as usize,
&name,
target_width,
base_height,
subsampling,
quality,
&cjpegli,
) {
max_size_diff = max_size_diff.max(result.size_diff_pct.abs());
max_dssim_diff = max_dssim_diff.max(result.dssim_diff_pct.abs());
let status = if result.size_diff_pct.abs() > 1.0 {
"!"
} else {
""
};
if result.size_diff_pct.abs() > 1.0 {
failures.push(format!(
"{}@{}x{}: {:.2}%",
name, result.width, result.height, result.size_diff_pct
));
}
println!(
"{:>20} {:>10} {:>8} {:>8} {:>+7.2}% {:>10.6} {:>10.6} {:>+7.2}%{}",
format!("{}(W)", name.chars().take(15).collect::<String>()),
format!("{}x{}", result.width, result.height),
result.rust_size,
result.cpp_size,
result.size_diff_pct,
result.rust_dssim,
result.cpp_dssim,
result.dssim_diff_pct,
status
);
all_results.push(result);
}
}
let base_width = ((orig_width as usize / 8) * 8).min(256);
for &h_rem in &remainders {
let target_height = 128 + h_rem; if target_height > orig_height as usize {
continue;
}
if let Some(result) = test_cropped_image(
&rgb,
orig_width as usize,
&name,
base_width,
target_height,
subsampling,
quality,
&cjpegli,
) {
max_size_diff = max_size_diff.max(result.size_diff_pct.abs());
max_dssim_diff = max_dssim_diff.max(result.dssim_diff_pct.abs());
let status = if result.size_diff_pct.abs() > 1.0 {
"!"
} else {
""
};
if result.size_diff_pct.abs() > 1.0 {
failures.push(format!(
"{}@{}x{}: {:.2}%",
name, result.width, result.height, result.size_diff_pct
));
}
println!(
"{:>20} {:>10} {:>8} {:>8} {:>+7.2}% {:>10.6} {:>10.6} {:>+7.2}%{}",
format!("{}(H)", name.chars().take(15).collect::<String>()),
format!("{}x{}", result.width, result.height),
result.rust_size,
result.cpp_size,
result.size_diff_pct,
result.rust_dssim,
result.cpp_dssim,
result.dssim_diff_pct,
status
);
all_results.push(result);
}
}
for &w_rem in &[1, 4, 7] {
for &h_rem in &[1, 4, 7] {
let target_width = 256 + w_rem;
let target_height = 128 + h_rem;
if target_width > orig_width as usize || target_height > orig_height as usize {
continue;
}
if let Some(result) = test_cropped_image(
&rgb,
orig_width as usize,
&name,
target_width,
target_height,
subsampling,
quality,
&cjpegli,
) {
max_size_diff = max_size_diff.max(result.size_diff_pct.abs());
max_dssim_diff = max_dssim_diff.max(result.dssim_diff_pct.abs());
let status = if result.size_diff_pct.abs() > 1.0 {
"!"
} else {
""
};
if result.size_diff_pct.abs() > 1.0 {
failures.push(format!(
"{}@{}x{}: {:.2}%",
name, result.width, result.height, result.size_diff_pct
));
}
println!(
"{:>20} {:>10} {:>8} {:>8} {:>+7.2}% {:>10.6} {:>10.6} {:>+7.2}%{}",
format!("{}(WH)", name.chars().take(14).collect::<String>()),
format!("{}x{}", result.width, result.height),
result.rust_size,
result.cpp_size,
result.size_diff_pct,
result.rust_dssim,
result.cpp_dssim,
result.dssim_diff_pct,
status
);
all_results.push(result);
}
}
}
}
println!("\n=== SUMMARY ===");
println!("Total tests: {}", all_results.len());
println!("Max size diff: {:.2}%", max_size_diff);
println!("Max DSSIM diff: {:.2}%", max_dssim_diff);
if !failures.is_empty() {
println!("\nFAILURES (size diff > 1%):");
for f in &failures {
println!(" {}", f);
}
}
if !all_results.is_empty() {
let avg_size_diff: f64 =
all_results.iter().map(|r| r.size_diff_pct).sum::<f64>() / all_results.len() as f64;
let avg_dssim_diff: f64 =
all_results.iter().map(|r| r.dssim_diff_pct).sum::<f64>() / all_results.len() as f64;
println!("\nAverage size diff: {:+.2}%", avg_size_diff);
println!("Average DSSIM diff: {:+.2}%", avg_dssim_diff);
}
assert!(
max_size_diff <= 1.5,
"Size difference too large: {:.2}% (max allowed: 1.5%)\nFailures: {:?}",
max_size_diff,
failures
);
assert!(
max_dssim_diff <= 5.0,
"DSSIM difference too large: {:.2}% (max allowed: 5%)",
max_dssim_diff
);
println!("\nPASS: All edge cases within tolerance");
}
#[test]
#[ignore = "requires cjpegli binary"]
fn test_strip_edge_synthetic_quick() {
let cjpegli = match cjpegli_path() {
Some(p) => p,
None => {
eprintln!("Skipping: cjpegli not found");
return;
}
};
println!("\n=== SYNTHETIC EDGE TEST (Quick) ===\n");
let quality = 85u8;
let subsampling = ChromaSubsampling::None;
let base_width = 256usize;
let height = 128usize;
let create_gradient = |w: usize, h: usize| -> Vec<u8> {
let mut rgb = vec![0u8; w * h * 3];
for y in 0..h {
for x in 0..w {
let idx = (y * w + x) * 3;
rgb[idx] = (64.0 + (x as f32 / w as f32) * 128.0) as u8;
rgb[idx + 1] = (64.0 + (y as f32 / h as f32) * 128.0) as u8;
rgb[idx + 2] = (64.0 + ((x + y) as f32 / (w + h) as f32) * 128.0) as u8;
}
}
rgb
};
println!("{:>8} {:>8} {:>8} {:>8}", "Width", "Rust", "C++", "Diff%");
println!("{}", "-".repeat(40));
for remainder in 1..=7 {
let width = base_width + remainder;
let rgb = create_gradient(width, height);
let rust_jpeg = encode_rust(
&rgb,
width as u32,
height as u32,
subsampling,
quality as f32,
);
let cpp_jpeg = match encode_cpp(&rgb, width, height, subsampling, quality, &cjpegli) {
Some(j) => j,
None => continue,
};
let size_diff =
(rust_jpeg.len() as f64 - cpp_jpeg.len() as f64) / cpp_jpeg.len() as f64 * 100.0;
println!(
"{:>8} {:>8} {:>8} {:>+7.2}%",
width,
rust_jpeg.len(),
cpp_jpeg.len(),
size_diff
);
}
}