use jpegli::decoder::Decoder;
use jpegli::decoder::PixelFormat;
use jpegli::encoder::{ChromaSubsampling, EncoderConfig, PixelLayout};
#[test]
fn test_progressive_subsampling_external_decoder_compat() {
let width = 64u32;
let height = 64u32;
let mut rgb = Vec::with_capacity((width * height * 3) as usize);
for y in 0..height {
for x in 0..width {
rgb.push((x * 4) as u8);
rgb.push((y * 4) as u8);
rgb.push(128u8);
}
}
let configs = [
(ChromaSubsampling::None, "S444"),
(ChromaSubsampling::HalfHorizontal, "S422"),
(ChromaSubsampling::Quarter, "S420"),
(ChromaSubsampling::HalfVertical, "S440"),
];
for (subsampling, name) in configs {
let config = EncoderConfig::ycbcr(85.0, subsampling)
.progressive(true)
.optimize_huffman(true);
let mut enc = config
.encode_from_bytes(width, height, PixelLayout::Rgb8Srgb)
.unwrap_or_else(|e| panic!("Progressive {} encoder setup failed: {:?}", name, e));
enc.push_packed(&rgb, enough::Unstoppable)
.unwrap_or_else(|e| panic!("Progressive {} push failed: {:?}", name, e));
let jpeg = enc
.finish()
.unwrap_or_else(|e| panic!("Progressive {} encode failed: {:?}", name, e));
use zune_jpeg::zune_core::bytestream::ZCursor;
use zune_jpeg::JpegDecoder;
let zune_result = JpegDecoder::new(ZCursor::new(&jpeg)).decode();
assert!(
zune_result.is_ok(),
"Progressive {} failed zune-jpeg decode: {:?}",
name,
zune_result.err()
);
let our_result = Decoder::new().output_format(PixelFormat::Rgb).decode(&jpeg);
assert!(
our_result.is_ok(),
"Progressive {} failed jpegli-rs decode: {:?}",
name,
our_result.err()
);
}
}
#[test]
fn test_progressive_subsampling_file_sizes() {
let width = 128u32;
let height = 128u32;
let mut rgb = Vec::with_capacity((width * height * 3) as usize);
for y in 0..height {
for x in 0..width {
rgb.push(((x * 2 + y) % 256) as u8);
rgb.push(((y * 2 + x) % 256) as u8);
rgb.push(((x + y) % 256) as u8);
}
}
let encode = |sub: ChromaSubsampling| -> usize {
let config = EncoderConfig::ycbcr(85.0, sub)
.progressive(true)
.optimize_huffman(true);
let mut enc = config
.encode_from_bytes(width, height, PixelLayout::Rgb8Srgb)
.expect("encoder setup");
enc.push_packed(&rgb, enough::Unstoppable)
.expect("push data");
enc.finish().expect("encode failed").len()
};
let size_444 = encode(ChromaSubsampling::None);
let size_422 = encode(ChromaSubsampling::HalfHorizontal);
let size_420 = encode(ChromaSubsampling::Quarter);
let size_440 = encode(ChromaSubsampling::HalfVertical);
eprintln!("Progressive file sizes:");
eprintln!(" S444: {} bytes", size_444);
eprintln!(" S422: {} bytes", size_422);
eprintln!(" S420: {} bytes", size_420);
eprintln!(" S440: {} bytes", size_440);
assert!(
size_422 < size_444,
"Progressive S422 ({}) should be smaller than S444 ({})",
size_422,
size_444
);
assert!(
size_420 < size_444,
"Progressive S420 ({}) should be smaller than S444 ({})",
size_420,
size_444
);
assert!(
size_440 < size_444,
"Progressive S440 ({}) should be smaller than S444 ({})",
size_440,
size_444
);
assert!(
size_420 < size_422,
"Progressive S420 ({}) should be smaller than S422 ({})",
size_420,
size_422
);
assert!(
size_420 < size_440,
"Progressive S420 ({}) should be smaller than S440 ({})",
size_420,
size_440
);
}
#[test]
fn test_baseline_subsampling_works() {
let width = 128u32;
let height = 128u32;
let mut rgb = Vec::with_capacity((width * height * 3) as usize);
for y in 0..height {
for x in 0..width {
rgb.push(((x * 2 + y) % 256) as u8);
rgb.push(((y * 2 + x) % 256) as u8);
rgb.push(((x + y) % 256) as u8);
}
}
let encode = |sub: ChromaSubsampling| -> usize {
let config = EncoderConfig::ycbcr(85.0, sub).optimize_huffman(true);
let mut enc = config
.encode_from_bytes(width, height, PixelLayout::Rgb8Srgb)
.expect("encoder setup");
enc.push_packed(&rgb, enough::Unstoppable)
.expect("push data");
enc.finish().expect("encode failed").len()
};
let size_444 = encode(ChromaSubsampling::None);
let size_420 = encode(ChromaSubsampling::Quarter);
eprintln!("Baseline file sizes: S444={}, S420={}", size_444, size_420);
assert!(
size_420 < size_444,
"Baseline S420 ({}) should be smaller than S444 ({})",
size_420,
size_444
);
}