#![cfg(feature = "encode")]
use almost_enough::{StopToken, Unstoppable};
use imgref::Img;
use rgb::Rgb;
use zenavif::{EncoderConfig, encode_rgb8};
fn stop() -> StopToken {
StopToken::new(Unstoppable)
}
fn make_test_tmap_metadata() -> Vec<u8> {
let mut buf = Vec::new();
buf.push(0); buf.extend_from_slice(&0u16.to_be_bytes()); buf.extend_from_slice(&0u16.to_be_bytes()); buf.push(0b0100_0000);
buf.extend_from_slice(&0u32.to_be_bytes());
buf.extend_from_slice(&1u32.to_be_bytes());
buf.extend_from_slice(&1u32.to_be_bytes());
buf.extend_from_slice(&1u32.to_be_bytes());
buf.extend_from_slice(&0i32.to_be_bytes());
buf.extend_from_slice(&1u32.to_be_bytes());
buf.extend_from_slice(&1i32.to_be_bytes());
buf.extend_from_slice(&1u32.to_be_bytes());
buf.extend_from_slice(&1u32.to_be_bytes());
buf.extend_from_slice(&1u32.to_be_bytes());
buf.extend_from_slice(&0i32.to_be_bytes());
buf.extend_from_slice(&1u32.to_be_bytes());
buf.extend_from_slice(&0i32.to_be_bytes());
buf.extend_from_slice(&1u32.to_be_bytes());
buf
}
fn make_rgb8_image() -> Img<Vec<Rgb<u8>>> {
let mut pixels = Vec::with_capacity(16 * 16);
for y in 0..16u8 {
for x in 0..16u8 {
pixels.push(Rgb {
r: x * 16,
g: y * 16,
b: 128,
});
}
}
Img::new(pixels, 16, 16)
}
#[test]
fn encode_with_gain_map_produces_valid_avif() {
let img = make_rgb8_image();
let metadata = make_test_tmap_metadata();
let gain_map_av1 = vec![0x12, 0x00, 0x0A, 0x0A, 0x00, 0x00, 0x00, 0x04, 0x2C, 0xC6];
let config = EncoderConfig::new().quality(80.0).speed(10).with_gain_map(
gain_map_av1.clone(),
4,
4,
8,
metadata.clone(),
);
let encoded =
encode_rgb8(img.as_ref(), &config, stop()).expect("encode with gain map should succeed");
assert!(!encoded.avif_file.is_empty());
assert!(encoded.color_byte_size > 0);
let parser = zenavif_parse::AvifParser::from_bytes(&encoded.avif_file)
.expect("output should be valid AVIF");
let gm_meta = parser
.gain_map_metadata()
.expect("gain map metadata should be present");
assert!(!gm_meta.is_multichannel);
assert!(gm_meta.use_base_colour_space);
assert_eq!(gm_meta.base_hdr_headroom_n, 0);
assert_eq!(gm_meta.base_hdr_headroom_d, 1);
assert_eq!(gm_meta.alternate_hdr_headroom_n, 1);
assert_eq!(gm_meta.alternate_hdr_headroom_d, 1);
let gm_data = parser
.gain_map_data()
.expect("gain map data should be present")
.expect("gain map data should resolve");
assert_eq!(gm_data.as_ref(), &gain_map_av1[..]);
}
#[test]
fn encode_without_gain_map_has_none() {
let img = make_rgb8_image();
let config = EncoderConfig::new().quality(80.0).speed(10);
let encoded = encode_rgb8(img.as_ref(), &config, stop()).expect("encode should succeed");
let parser = zenavif_parse::AvifParser::from_bytes(&encoded.avif_file)
.expect("output should be valid AVIF");
assert!(
parser.gain_map_metadata().is_none(),
"image without gain map should have no tmap"
);
assert!(parser.gain_map_data().is_none());
}
#[test]
fn encode_gain_map_roundtrip_through_decode() {
let img = make_rgb8_image();
let metadata = make_test_tmap_metadata();
let gain_map_av1 = vec![0x12, 0x00, 0x0A, 0x0A, 0x00, 0x00, 0x00, 0x04, 0x2C, 0xC6];
let config = EncoderConfig::new().quality(80.0).speed(10).with_gain_map(
gain_map_av1.clone(),
4,
4,
8,
metadata.clone(),
);
let encoded = encode_rgb8(img.as_ref(), &config, stop()).expect("encode should succeed");
let decoder =
zenavif::ManagedAvifDecoder::new(&encoded.avif_file, &zenavif::DecoderConfig::default())
.expect("decoder should open encoded file");
let info = decoder.probe_info().expect("probe should succeed");
let gm = info
.gain_map
.as_ref()
.expect("gain map should be present after decode");
assert!(!gm.metadata.is_multichannel);
assert!(gm.metadata.use_base_colour_space);
assert_eq!(gm.metadata.base_hdr_headroom_n, 0);
assert_eq!(gm.metadata.base_hdr_headroom_d, 1);
assert_eq!(gm.metadata.alternate_hdr_headroom_n, 1);
assert_eq!(gm.metadata.alternate_hdr_headroom_d, 1);
assert_eq!(gm.metadata.channels.len(), 3);
let ch = &gm.metadata.channels[0];
assert_eq!(ch.gain_map_min_n, 0);
assert_eq!(ch.gain_map_min_d, 1);
assert_eq!(ch.gain_map_max_n, 1);
assert_eq!(ch.gain_map_max_d, 1);
assert_eq!(gm.gain_map_data, gain_map_av1);
}
#[test]
fn encode_gain_map_with_exif_and_icc() {
let img = make_rgb8_image();
let metadata = make_test_tmap_metadata();
let gain_map_av1 = vec![0xAA, 0xBB, 0xCC];
let config = EncoderConfig::new()
.quality(80.0)
.speed(10)
.exif(vec![0xFF, 0xD8, 0x00, 0x00])
.icc_profile(vec![0x00, 0x00, 0x02, 0x0C]) .with_gain_map(gain_map_av1.clone(), 2, 2, 8, metadata);
let encoded = encode_rgb8(img.as_ref(), &config, stop())
.expect("encode with gain map + metadata should succeed");
let parser = zenavif_parse::AvifParser::from_bytes(&encoded.avif_file)
.expect("output should be valid AVIF");
assert!(parser.gain_map_metadata().is_some());
let gm_data = parser.gain_map_data().unwrap().unwrap();
assert_eq!(gm_data.as_ref(), &gain_map_av1[..]);
assert!(parser.exif().is_some());
}