use crate::bit_writer::BitWriter;
use crate::error::Result;
use crate::f16::f16_roundtrip;
use crate::headers::frame_header::FrameHeader;
use crate::modular::channel::{Channel, ModularImage};
#[cfg(test)]
const _K_MIN_BUTTERAUGLI_DISTANCE: f32 = 0.05;
#[derive(Debug, Clone, Copy)]
pub(crate) struct DcQuantFactors {
pub dc_quant: [f32; 3],
pub inv_dc_quant: [f32; 3],
}
impl DcQuantFactors {
#[allow(dead_code)]
pub fn compute(main_distance: f32) -> Self {
const K_MIN_BUTTERAUGLI_DISTANCE: f32 = 0.05;
let dc_distance = (main_distance * 0.02).max(K_MIN_BUTTERAUGLI_DISTANCE * 0.02);
let mut enc_factors = [65536.0f32, 4096.0, 4096.0]; enc_factors[0] /= 1.0 + 23.0 * dc_distance;
enc_factors[1] /= 1.0 + 14.0 * dc_distance;
enc_factors[2] /= 1.0 + 14.0 * dc_distance;
Self::from_enc_factors(enc_factors)
}
pub fn full_precision() -> Self {
Self::from_enc_factors([65536.0, 4096.0, 4096.0])
}
#[allow(dead_code)]
pub fn jxl_default() -> Self {
Self::from_enc_factors([4096.0, 512.0, 256.0])
}
fn from_enc_factors(enc_factors: [f32; 3]) -> Self {
let dc_quant: [f32; 3] = [
f16_roundtrip(128.0 / enc_factors[0]).unwrap() / 128.0,
f16_roundtrip(128.0 / enc_factors[1]).unwrap() / 128.0,
f16_roundtrip(128.0 / enc_factors[2]).unwrap() / 128.0,
];
let inv_dc_quant: [f32; 3] = dc_quant.map(|q| 1.0 / q);
Self {
dc_quant,
inv_dc_quant,
}
}
}
fn round_hafz(val: f32) -> i32 {
(val + if val < 0.0 { -0.5 } else { 0.5 }) as i32
}
pub(crate) fn encode_lf_frame(
float_dc: &[Vec<f32>; 3],
main_distance: f32,
xsize_blocks: usize,
ysize_blocks: usize,
use_ans: bool,
effort: u8,
writer: &mut BitWriter,
) -> Result<([Vec<f32>; 3], [f32; 3])> {
let factors = DcQuantFactors::full_precision();
#[cfg(feature = "trace-bitstream")]
{
eprintln!("LFRAME: dc_quant = {:?}", factors.dc_quant);
eprintln!("LFRAME: inv_dc_quant = {:?}", factors.inv_dc_quant);
eprintln!("LFRAME: dim = {}x{}", xsize_blocks, ysize_blocks);
}
let n = xsize_blocks * ysize_blocks;
let mut ch_y_data = Vec::with_capacity(n);
let mut ch_x_data = Vec::with_capacity(n);
let mut ch_by_data = Vec::with_capacity(n);
for ((&dc_x, &dc_y), &dc_b) in float_dc[0]
.iter()
.zip(float_dc[1].iter())
.zip(float_dc[2].iter())
{
let y_int = round_hafz(dc_y * factors.inv_dc_quant[1]); let x_int = round_hafz(dc_x * factors.inv_dc_quant[0]); let b_quant = round_hafz(dc_b * factors.inv_dc_quant[2]); let b_int = b_quant - y_int;
ch_y_data.push(y_int);
ch_x_data.push(x_int);
ch_by_data.push(b_int);
}
let decoded_dc = {
let mut dc_x = Vec::with_capacity(n);
let mut dc_y = Vec::with_capacity(n);
let mut dc_b = Vec::with_capacity(n);
for i in 0..n {
dc_y.push(ch_y_data[i] as f32 * factors.dc_quant[1]);
dc_x.push(ch_x_data[i] as f32 * factors.dc_quant[0]);
dc_b.push((ch_by_data[i] + ch_y_data[i]) as f32 * factors.dc_quant[2]);
}
[dc_x, dc_y, dc_b]
};
#[cfg(feature = "trace-bitstream")]
{
let y_min = ch_y_data.iter().copied().min().unwrap_or(0);
let y_max = ch_y_data.iter().copied().max().unwrap_or(0);
let x_min = ch_x_data.iter().copied().min().unwrap_or(0);
let x_max = ch_x_data.iter().copied().max().unwrap_or(0);
let by_min = ch_by_data.iter().copied().min().unwrap_or(0);
let by_max = ch_by_data.iter().copied().max().unwrap_or(0);
eprintln!("LFRAME: Y int range [{y_min}, {y_max}]");
eprintln!("LFRAME: X int range [{x_min}, {x_max}]");
eprintln!("LFRAME: B-Y int range [{by_min}, {by_max}]");
}
let fh = FrameHeader::lf_frame(xsize_blocks as u32, ysize_blocks as u32, 1);
fh.write(writer)?;
let mut ch_y = Channel::from_vec(ch_y_data, xsize_blocks, ysize_blocks)?;
let mut ch_x = Channel::from_vec(ch_x_data, xsize_blocks, ysize_blocks)?;
let mut ch_by = Channel::from_vec(ch_by_data, xsize_blocks, ysize_blocks)?;
ch_y.component = 0; ch_x.component = 1; ch_by.component = 2; ch_y.hshift = 3;
ch_y.vshift = 3;
ch_x.hshift = 3;
ch_x.vshift = 3;
ch_by.hshift = 3;
ch_by.vshift = 3;
let mod_channels = vec![ch_y, ch_x, ch_by];
let image = ModularImage {
channels: mod_channels,
bit_depth: 16, is_grayscale: false,
has_alpha: false,
};
let lf_effort = (effort + 1).min(10);
let mut profile =
crate::effort::EffortProfile::lossless(lf_effort, crate::api::EncoderMode::Reference);
profile.patches = false;
profile.lz77 = false;
let num_groups_x = xsize_blocks.div_ceil(crate::GROUP_DIM);
let num_groups_y = ysize_blocks.div_ceil(crate::GROUP_DIM);
let num_groups = num_groups_x * num_groups_y;
if num_groups == 1 {
let mut section_writer = BitWriter::new();
let lossy_opts = crate::modular::encode::LossyModularOptions {
distance: main_distance,
};
crate::modular::encode::write_modular_stream_with_tree_dc_quant(
&image,
&mut section_writer,
&profile,
false, profile.lz77,
profile.lz77_method,
Some(factors.dc_quant),
Some(lossy_opts),
false, )?;
let section_data = section_writer.finish();
writer.write(1, 0)?; writer.zero_pad_to_byte();
write_toc_entry(writer, section_data.len() as u32)?;
writer.zero_pad_to_byte();
writer.append_bytes(§ion_data)?;
} else {
encode_lf_frame_multi_group(
&image,
&factors,
&profile,
xsize_blocks,
ysize_blocks,
use_ans,
writer,
)?;
}
Ok((decoded_dc, factors.dc_quant))
}
fn encode_lf_frame_multi_group(
image: &ModularImage,
factors: &DcQuantFactors,
profile: &crate::effort::EffortProfile,
xsize_blocks: usize,
ysize_blocks: usize,
_use_ans: bool,
writer: &mut BitWriter,
) -> Result<()> {
use crate::modular::encode::write_group_modular_section_idx;
use crate::modular::section::{
GlobalTransforms, GroupTransforms, write_global_modular_section_with_tree_dc_quant,
};
let num_groups_x = xsize_blocks.div_ceil(crate::GROUP_DIM);
let num_groups_y = ysize_blocks.div_ceil(crate::GROUP_DIM);
let num_groups = num_groups_x * num_groups_y;
let lf_group_dim = crate::GROUP_DIM * 8;
let num_lf_groups_x = xsize_blocks.div_ceil(lf_group_dim);
let num_lf_groups_y = ysize_blocks.div_ceil(lf_group_dim);
let num_lf_groups = num_lf_groups_x * num_lf_groups_y;
let num_passes = 1;
let mut group_images: Vec<ModularImage> = Vec::with_capacity(num_groups);
for group_idx in 0..num_groups {
let gx = group_idx % num_groups_x;
let gy = group_idx / num_groups_x;
let x_start = gx * crate::GROUP_DIM;
let y_start = gy * crate::GROUP_DIM;
let x_end = (x_start + crate::GROUP_DIM).min(xsize_blocks);
let y_end = (y_start + crate::GROUP_DIM).min(ysize_blocks);
let group_image = image.extract_region(x_start, y_start, x_end, y_end)?;
group_images.push(group_image);
}
let mut lf_global_writer = BitWriter::new();
let global_state = write_global_modular_section_with_tree_dc_quant(
&group_images,
&mut lf_global_writer,
profile,
GlobalTransforms::rct_only(None), false,
profile.lz77_method,
Some(factors.dc_quant),
None, )?;
let lf_global_data = lf_global_writer.finish();
let lf_group_data: Vec<Vec<u8>> = (0..num_lf_groups).map(|_| Vec::new()).collect();
let hf_global_data: Vec<u8> = Vec::new();
let mut pass_group_data: Vec<Vec<u8>> = Vec::with_capacity(num_groups * num_passes);
for (group_idx, group_image) in group_images.iter().enumerate() {
for _pass in 0..num_passes {
let mut group_writer = BitWriter::new();
write_group_modular_section_idx(
group_image,
&global_state,
group_idx as u32,
&GroupTransforms::none(),
&mut group_writer,
)?;
pass_group_data.push(group_writer.finish());
}
}
let mut section_sizes = Vec::with_capacity(2 + num_lf_groups + num_groups * num_passes);
section_sizes.push(lf_global_data.len());
for data in &lf_group_data {
section_sizes.push(data.len());
}
section_sizes.push(hf_global_data.len());
for data in &pass_group_data {
section_sizes.push(data.len());
}
writer.write(1, 0)?; writer.zero_pad_to_byte();
for &size in §ion_sizes {
write_toc_entry(writer, size as u32)?;
}
writer.zero_pad_to_byte();
for byte in &lf_global_data {
writer.write_u8(*byte)?;
}
for data in &lf_group_data {
for byte in data {
writer.write_u8(*byte)?;
}
}
for byte in &hf_global_data {
writer.write_u8(*byte)?;
}
for data in &pass_group_data {
for byte in data {
writer.write_u8(*byte)?;
}
}
Ok(())
}
fn write_toc_entry(writer: &mut BitWriter, size: u32) -> Result<()> {
if size < 1024 {
writer.write(2, 0)?;
writer.write(10, size as u64)?;
} else if size < 17408 {
writer.write(2, 1)?;
writer.write(14, (size - 1024) as u64)?;
} else if size < 4211712 {
writer.write(2, 2)?;
writer.write(22, (size - 17408) as u64)?;
} else {
writer.write(2, 3)?;
writer.write(30, (size - 4211712) as u64)?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dc_quant_factors_d1() {
let f = DcQuantFactors::compute(1.0);
assert!(f.inv_dc_quant[0] > 40000.0 && f.inv_dc_quant[0] < 50000.0);
assert!(f.inv_dc_quant[1] > 3000.0 && f.inv_dc_quant[1] < 3500.0);
assert_eq!(f.inv_dc_quant[1], f.inv_dc_quant[2]); }
#[test]
fn test_dc_quant_factors_d0_5() {
let f = DcQuantFactors::compute(0.5);
let f1 = DcQuantFactors::compute(1.0);
assert!(f.inv_dc_quant[0] > f1.inv_dc_quant[0]);
assert!(f.inv_dc_quant[1] > f1.inv_dc_quant[1]);
}
#[test]
fn test_dc_quant_f16_roundtrip() {
let f = DcQuantFactors::compute(1.0);
for c in 0..3 {
let rt = f16_roundtrip(f.dc_quant[c] * 128.0).unwrap() / 128.0;
assert_eq!(rt, f.dc_quant[c], "channel {c}");
}
}
#[test]
fn test_round_hafz() {
assert_eq!(round_hafz(0.5), 1);
assert_eq!(round_hafz(-0.5), -1);
assert_eq!(round_hafz(0.4), 0);
assert_eq!(round_hafz(-0.4), 0);
assert_eq!(round_hafz(1.5), 2);
assert_eq!(round_hafz(-1.5), -2);
}
}