use super::channel::Channel;
const SQUEEZE_XYB_QTABLE: [[f32; 16]; 3] = [
[
163.84, 81.92, 40.96, 20.48, 10.24, 5.12, 2.56, 1.28, 0.64, 0.32, 0.16, 0.08, 0.04, 0.02,
0.01, 0.005,
],
[
1024.0, 512.0, 256.0, 128.0, 64.0, 32.0, 16.0, 8.0, 4.0, 2.0, 1.0, 0.5, 0.5, 0.5, 0.5, 0.5,
],
[
2048.0, 1024.0, 512.0, 256.0, 128.0, 64.0, 32.0, 16.0, 8.0, 4.0, 2.0, 1.0, 0.5, 0.5, 0.5,
0.5,
],
];
const SQUEEZE_QUALITY_FACTOR_XYB: f32 = 4.0;
const SQUEEZE_QUALITY_FACTOR_Y: f32 = 1.5;
pub fn compute_channel_quantizer_xyb(
component: usize,
hshift: u32,
vshift: u32,
distance: f32,
) -> i32 {
debug_assert!(component < 3);
let base_quantizer = 0.25 * jxl_simd::fast_powf(distance, 1.2);
let shift = (hshift + vshift) as usize;
let shift = if shift > 0 { shift - 1 } else { 0 };
let shift = shift.min(15);
let mut q = base_quantizer * SQUEEZE_QUALITY_FACTOR_XYB * SQUEEZE_XYB_QTABLE[component][shift];
if component == 0 {
q *= SQUEEZE_QUALITY_FACTOR_Y;
}
(q as i32).max(1)
}
pub fn quantize_channel(channel: &mut Channel, q: i32) {
if q == 1 {
return;
}
let half = q / 2;
for y in 0..channel.height() {
let row = channel.row_mut(y);
for val in row.iter_mut() {
if *val < 0 {
*val = -((-*val + half) / q) * q;
} else {
*val = ((*val + half) / q) * q;
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IntersectionType {
None,
Partial,
Inside,
}
#[derive(Debug, Clone)]
pub struct ModularMultiplierInfo {
pub range: [[u32; 2]; 2],
pub multiplier: u32,
}
pub fn box_intersects(
needle: &[[u32; 2]; 2],
haystack: &[[u32; 2]; 2],
) -> (IntersectionType, usize, u32) {
let mut partial = false;
let mut partial_axis = 0usize;
let mut partial_val = 0u32;
for i in 0..2 {
if haystack[i][0] >= needle[i][1] {
return (IntersectionType::None, 0, 0);
}
if haystack[i][1] <= needle[i][0] {
return (IntersectionType::None, 0, 0);
}
if haystack[i][0] <= needle[i][0] && haystack[i][1] >= needle[i][1] {
continue;
}
partial = true;
partial_axis = i;
if haystack[i][0] > needle[i][0] && haystack[i][0] < needle[i][1] {
partial_val = haystack[i][0] - 1;
} else {
debug_assert!(haystack[i][1] > needle[i][0] && haystack[i][1] < needle[i][1]);
partial_val = haystack[i][1] - 1;
}
}
if partial {
(IntersectionType::Partial, partial_axis, partial_val)
} else {
(IntersectionType::Inside, 0, 0)
}
}
pub fn build_multiplier_info(
quants: &[i32],
nb_meta_channels: usize,
) -> Vec<ModularMultiplierInfo> {
let mut info = Vec::new();
for (i, &q) in quants.iter().enumerate() {
let ch_idx = (i + nb_meta_channels) as u32;
let stream_id = 0u32;
if let Some(last) = info.last_mut() {
let last: &mut ModularMultiplierInfo = last;
if last.range[1][0] == stream_id && last.multiplier == q as u32 {
last.range[0][1] = ch_idx + 1;
continue;
}
}
info.push(ModularMultiplierInfo {
range: [[ch_idx, ch_idx + 1], [stream_id, stream_id + 1]],
multiplier: q as u32,
});
}
info
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_quantize_channel_basic() {
let mut ch = Channel::from_vec(vec![0, 5, 10, 15, -5, -10, -15, 7, 3], 3, 3).unwrap();
quantize_channel(&mut ch, 10);
assert_eq!(ch.get(0, 0), 0);
assert_eq!(ch.get(1, 0), 10);
assert_eq!(ch.get(2, 0), 10);
assert_eq!(ch.get(0, 1), 20);
assert_eq!(ch.get(1, 1), -10);
assert_eq!(ch.get(2, 1), -10);
assert_eq!(ch.get(0, 2), -20);
assert_eq!(ch.get(1, 2), 10);
assert_eq!(ch.get(2, 2), 0);
}
#[test]
fn test_quantize_channel_q1_noop() {
let mut ch = Channel::from_vec(vec![1, 2, 3, 4], 2, 2).unwrap();
quantize_channel(&mut ch, 1);
assert_eq!(ch.get(0, 0), 1);
assert_eq!(ch.get(1, 1), 4);
}
#[test]
fn test_quantize_channel_divisibility() {
let mut ch = Channel::from_vec(vec![13, -27, 100, -99, 0, 51, -1, 200, 3], 3, 3).unwrap();
let q = 7;
quantize_channel(&mut ch, q);
for y in 0..3 {
for x in 0..3 {
assert_eq!(
ch.get(x, y) % q,
0,
"pixel ({},{}) = {} not divisible by {}",
x,
y,
ch.get(x, y),
q
);
}
}
}
#[test]
fn test_compute_quantizer_xyb() {
let q_y = compute_channel_quantizer_xyb(0, 0, 0, 1.0);
assert_eq!(q_y, 245);
let q_x = compute_channel_quantizer_xyb(1, 0, 0, 1.0);
assert!(
(q_x - 1024).unsigned_abs() <= 1,
"q_x={q_x}, expected ~1024"
);
}
#[test]
fn test_compute_quantizer_with_shift() {
let q = compute_channel_quantizer_xyb(0, 1, 1, 1.0);
assert_eq!(q, 122);
}
#[test]
fn test_box_intersects_none() {
let needle = [[0, 3], [0, 1]];
let haystack = [[5, 8], [0, 1]];
let (t, _, _) = box_intersects(&needle, &haystack);
assert_eq!(t, IntersectionType::None);
}
#[test]
fn test_box_intersects_inside() {
let needle = [[0, 3], [0, 1]];
let haystack = [[0, 3], [0, 1]];
let (t, _, _) = box_intersects(&needle, &haystack);
assert_eq!(t, IntersectionType::Inside);
}
#[test]
fn test_box_intersects_partial() {
let needle = [[0, 6], [0, 1]];
let haystack = [[0, 3], [0, 1]];
let (t, axis, val) = box_intersects(&needle, &haystack);
assert_eq!(t, IntersectionType::Partial);
assert_eq!(axis, 0); assert_eq!(val, 2); }
#[test]
fn test_build_multiplier_info_basic() {
let quants = vec![10, 20, 20];
let info = build_multiplier_info(&quants, 0);
assert_eq!(info.len(), 2);
assert_eq!(info[0].range[0], [0, 1]);
assert_eq!(info[0].multiplier, 10);
assert_eq!(info[1].range[0], [1, 3]);
assert_eq!(info[1].multiplier, 20);
}
#[test]
fn test_build_multiplier_info_all_same() {
let quants = vec![5, 5, 5];
let info = build_multiplier_info(&quants, 0);
assert_eq!(info.len(), 1);
assert_eq!(info[0].range[0], [0, 3]);
assert_eq!(info[0].multiplier, 5);
}
#[test]
fn test_build_multiplier_info_all_different() {
let quants = vec![1, 2, 3];
let info = build_multiplier_info(&quants, 0);
assert_eq!(info.len(), 3);
}
}