use std::arch::x86_64::*;
const MATCH_GAMMA_OFFSET: f32 = 0.019;
const K_X_MUL: f32 = 23.426802998210313;
#[inline]
#[target_feature(enable = "avx2,fma")]
fn load8s(s: &[f32], i: usize) -> __m256 {
assert!(s.len() >= 8);
_mm256_setr_ps(
s[i],
s[i + 1],
s[i + 2],
s[i + 3],
s[i + 4],
s[i + 5],
s[i + 6],
s[i + 7],
)
}
#[inline]
#[target_feature(enable = "avx2,fma")]
fn store8(v: __m256, s: &mut [f32], i: usize) {
assert!(s.len() >= 8);
unsafe {
_mm256_storeu_ps(s[i..].as_mut_ptr(), v);
}
}
#[inline]
#[target_feature(enable = "avx2,fma")]
fn hsum(v: __m256) -> f32 {
let hi = _mm256_extractf128_ps::<1>(v);
let lo = _mm256_castps256_ps128(v);
let s = _mm_add_ps(lo, hi);
let mut shuf = _mm_movehdup_ps(s);
let mut sums = _mm_add_ps(s, shuf);
shuf = _mm_movehl_ps(shuf, sums);
sums = _mm_add_ss(sums, shuf);
_mm_cvtss_f32(sums)
}
#[inline]
#[target_feature(enable = "avx2,fma")]
fn abs_ps(v: __m256) -> __m256 {
_mm256_andnot_ps(_mm256_set1_ps(-0.0f32), v)
}
#[inline]
#[target_feature(enable = "avx2,fma")]
fn _mm256_mlaf_ps(a: __m256, b: __m256, c: __m256) -> __m256 {
_mm256_fmadd_ps(a, b, c)
}
#[inline]
#[target_feature(enable = "avx2,fma")]
#[allow(dead_code)]
fn _mm256_neg_epi32(n: __m256i) -> __m256i {
_mm256_sub_epi32(_mm256_setzero_si256(), n)
}
#[inline]
#[target_feature(enable = "avx2,fma")]
fn ratio_cubic_x8(v: __m256, invert: bool) -> __m256 {
const K_SG_MUL: f32 = 226.0480446705883;
const K_SG_MUL2: f32 = 1.0 / 73.377132366608819;
const K_LOG2: f32 = 0.693147181;
const K_SG_RET_MUL: f32 = K_SG_MUL2 * 18.6580932135 * K_LOG2;
const K_SG_V_OFFSET: f32 = 7.14672470003;
let k_epsilon = 1e-2f32;
let k_num_mul = K_SG_RET_MUL * 3.0 * K_SG_MUL;
let k_v_offset = K_SG_V_OFFSET * K_LOG2 + k_epsilon;
let k_den_mul = K_LOG2 * K_SG_MUL;
let v = _mm256_max_ps(v, _mm256_setzero_ps());
let v2 = _mm256_mul_ps(v, v);
let num = _mm256_mlaf_ps(_mm256_set1_ps(k_num_mul), v2, _mm256_set1_ps(k_epsilon));
let den = _mm256_mlaf_ps(
_mm256_mul_ps(_mm256_set1_ps(k_den_mul), v),
v2,
_mm256_set1_ps(k_v_offset),
);
if invert {
_mm256_div_ps(num, den)
} else {
_mm256_div_ps(den, num)
}
}
#[inline]
#[target_feature(enable = "avx2,fma")]
fn masking_sqrt_x8(v: __m256) -> __m256 {
let k_log_offset = 26.481471032459346f32;
let k_mul = 211.50759899638012f32;
let mul_v = (k_mul * 1e8f32).sqrt();
let inner = _mm256_mlaf_ps(v, _mm256_set1_ps(mul_v), _mm256_set1_ps(k_log_offset));
_mm256_mul_ps(_mm256_set1_ps(0.25), _mm256_sqrt_ps(inner))
}
#[target_feature(enable = "avx2,fma")]
#[allow(clippy::too_many_arguments)]
fn stage1_diff_row(
set_mode: bool,
row_y: &[f32],
row_y1: &[f32],
row_y2: &[f32],
row_x: &[f32],
row_x1: &[f32],
row_x2: &[f32],
x0: usize,
img_xsize: usize,
region_px_w: usize,
row_acc: &mut [f32],
) {
let offset = _mm256_set1_ps(MATCH_GAMMA_OFFSET);
let quarter = _mm256_set1_ps(0.25);
let kxmul = _mm256_set1_ps(K_X_MUL);
let lo_gx = 1isize;
let hi_gx = img_xsize as isize - 9;
let rx_lo = (lo_gx - x0 as isize).max(0) as usize;
let rx_hi = if hi_gx >= x0 as isize {
((hi_gx - x0 as isize) as usize + 1).min(region_px_w)
} else {
0
};
let head_end = rx_lo.min(region_px_w);
for rx in 0..head_end {
scalar_pixel(
set_mode, row_y, row_y1, row_y2, row_x, row_x1, row_x2, x0, img_xsize, rx, row_acc,
);
}
let mut rx = rx_lo;
while rx + 8 <= rx_hi {
let gx = x0 + rx;
let cy = load8s(row_y, gx);
let ly = load8s(row_y, gx - 1);
let ry_ = load8s(row_y, gx + 1);
let uy = load8s(row_y1, gx);
let dy = load8s(row_y2, gx);
let base_y = _mm256_mul_ps(
quarter,
_mm256_add_ps(_mm256_add_ps(_mm256_add_ps(dy, uy), ly), ry_),
);
let gammac = ratio_cubic_x8(_mm256_add_ps(cy, offset), false);
let dyv = _mm256_mul_ps(gammac, _mm256_sub_ps(cy, base_y));
let mut diff = _mm256_mul_ps(dyv, dyv);
let cx = load8s(row_x, gx);
let lx = load8s(row_x, gx - 1);
let rx_ = load8s(row_x, gx + 1);
let ux = load8s(row_x1, gx);
let dx = load8s(row_x2, gx);
let base_x = _mm256_mul_ps(
quarter,
_mm256_add_ps(_mm256_add_ps(_mm256_add_ps(dx, ux), lx), rx_),
);
let dxv = _mm256_mul_ps(gammac, _mm256_sub_ps(cx, base_x));
let diff_x = _mm256_mul_ps(dxv, dxv);
diff = _mm256_mlaf_ps(kxmul, diff_x, diff);
diff = masking_sqrt_x8(diff);
if set_mode {
store8(diff, row_acc, rx);
} else {
store8(_mm256_add_ps(load8s(row_acc, rx), diff), row_acc, rx);
}
rx += 8;
}
for rx in rx..region_px_w {
scalar_pixel(
set_mode, row_y, row_y1, row_y2, row_x, row_x1, row_x2, x0, img_xsize, rx, row_acc,
);
}
}
#[inline]
#[allow(clippy::too_many_arguments)]
fn scalar_pixel(
set_mode: bool,
row_y: &[f32],
row_y1: &[f32],
row_y2: &[f32],
row_x: &[f32],
row_x1: &[f32],
row_x2: &[f32],
x0: usize,
img_xsize: usize,
rx: usize,
row_acc: &mut [f32],
) {
let clampx = |x: isize| -> usize { x.max(0).min(img_xsize as isize - 1) as usize };
let gx = x0 + rx;
let gx_c = clampx(gx as isize);
let gx1 = clampx(gx as isize - 1);
let gx2 = clampx(gx as isize + 1);
let in_y = row_y[gx_c];
let base = 0.25 * (row_y2[gx_c] + row_y1[gx_c] + row_y[gx1] + row_y[gx2]);
let gammac =
crate::adaptive_quant::ratio_cubic_to_simple_gamma(in_y + MATCH_GAMMA_OFFSET, false);
let mut diff = gammac * (in_y - base);
diff *= diff;
let in_x = row_x[gx_c];
let base_x = 0.25 * (row_x2[gx_c] + row_x1[gx_c] + row_x[gx1] + row_x[gx2]);
let mut diff_x = gammac * (in_x - base_x);
diff_x *= diff_x;
diff += K_X_MUL * diff_x;
diff = crate::adaptive_quant::masking_sqrt(diff);
if !set_mode {
row_acc[rx] += diff;
} else {
row_acc[rx] = diff;
}
}
#[target_feature(enable = "avx2,fma")]
fn hf_modulation_avx2(x: usize, y: usize, xyb_y: &crate::image::Image3F, out_val: f32) -> f32 {
let shidx = _mm256_setr_epi32(1, 2, 3, 4, 5, 6, 7, 7);
let mut acc = _mm256_setzero_ps();
for dy in 0..8 {
let row = xyb_y.plane_row(1, y + dy);
let row_next: &[f32] = if dy == 7 {
row
} else {
xyb_y.plane_row(1, y + dy + 1)
};
let rv = load8s(row, x);
let below = abs_ps(_mm256_sub_ps(rv, load8s(row_next, x)));
let shifted = _mm256_permutevar8x32_ps(rv, shidx);
let right = abs_ps(_mm256_sub_ps(rv, shifted));
acc = _mm256_add_ps(acc, _mm256_add_ps(below, right));
}
let sum = hsum(acc);
f32::mul_add(sum, -2.0052193233688884f32 / 112.0, out_val)
}
#[target_feature(enable = "avx2,fma")]
fn color_modulation_avx2(
x: usize,
y: usize,
xyb: &crate::image::Image3F,
butteraugli_target: f32,
out_val: f32,
) -> f32 {
let k_strength_mul = 2.177823400325309f32;
let k_red_ramp_start = 0.0073200141118951231f32;
let k_red_ramp_length = 0.019421555948474039f32;
let k_blue_ramp_length = 0.086890611400405895f32;
let k_blue_ramp_start = 0.26973418507870539f32;
let strength = k_strength_mul * (1.0 - 0.25 * butteraugli_target);
if strength < 0.0 {
return out_val;
}
let red_strength = strength * 5.992297772961519f32;
let blue_strength = strength;
let offset = strength * -0.009174542291185913f32;
let out_val0 = out_val + offset;
let v_red_start = _mm256_set1_ps(k_red_ramp_start);
let v_red_len = _mm256_set1_ps(k_red_ramp_length);
let v_blue_start = _mm256_set1_ps(k_blue_ramp_start);
let v_blue_len = _mm256_set1_ps(k_blue_ramp_length);
let zero = _mm256_setzero_ps();
let mut red_acc = _mm256_setzero_ps();
let mut blue_acc = _mm256_setzero_ps();
for dy in 0..8 {
let ry = y + dy;
let in_x = load8s(xyb.plane_row(0, ry), x);
let pixel_y = load8s(xyb.plane_row(1, ry), x);
let in_b = load8s(xyb.plane_row(2, ry), x);
let px = _mm256_max_ps(_mm256_sub_ps(in_x, v_red_start), zero);
red_acc = _mm256_add_ps(red_acc, _mm256_min_ps(px, v_red_len));
let pb = _mm256_max_ps(
_mm256_sub_ps(in_b, _mm256_add_ps(pixel_y, v_blue_start)),
zero,
);
blue_acc = _mm256_add_ps(blue_acc, _mm256_min_ps(pb, v_blue_len));
}
let red_coverage = hsum(red_acc);
let blue_coverage = hsum(blue_acc);
let ratio = 30.610615782142737f32;
let mut overall_red = red_coverage.min(ratio * k_red_ramp_length);
overall_red *= red_strength / ratio;
let mut overall_blue = blue_coverage.min(ratio * k_blue_ramp_length);
overall_blue *= blue_strength / ratio;
out_val0 + overall_red + overall_blue
}
#[target_feature(enable = "avx2,fma")]
fn gamma_modulation_avx2(x: usize, y: usize, xyb: &crate::image::Image3F, out_val: f32) -> f32 {
let k_bias = _mm256_set1_ps(0.16f32);
let half = _mm256_set1_ps(0.5f32);
let mut acc = _mm256_setzero_ps();
for dy in 0..8 {
let ry = y + dy;
let inx = load8s(xyb.plane_row(0, ry), x);
let iny = _mm256_add_ps(load8s(xyb.plane_row(1, ry), x), k_bias);
let r = _mm256_sub_ps(iny, inx);
let g = _mm256_add_ps(iny, inx);
let ratio_r = ratio_cubic_x8(r, true);
let ratio_g = ratio_cubic_x8(g, true);
acc = _mm256_add_ps(acc, _mm256_mul_ps(half, _mm256_add_ps(ratio_r, ratio_g)));
}
let overall_ratio = hsum(acc) * (1.0 / 64.0);
let k_gam = -0.15526878023684174f32 * 0.693147180559945f32;
f32::mul_add(
k_gam,
crate::adaptive_quant::dirty_log2f(overall_ratio),
out_val,
)
}
#[inline]
#[target_feature(enable = "avx2,fma")]
fn insert8(m: &mut [__m256; 4], v: __m256) {
let mut t = v;
let n0 = _mm256_min_ps(m[0], t);
t = _mm256_max_ps(m[0], t);
m[0] = n0;
let n1 = _mm256_min_ps(m[1], t);
t = _mm256_max_ps(m[1], t);
m[1] = n1;
let n2 = _mm256_min_ps(m[2], t);
t = _mm256_max_ps(m[2], t);
m[2] = n2;
m[3] = _mm256_min_ps(m[3], t);
}
#[inline]
fn scalar_px(rowt: &[f32], row: &[f32], rowb: &[f32], pre_w: usize, fx: usize) -> f32 {
let xm1 = if fx >= 1 { fx - 1 } else { fx };
let xp1 = if fx + 1 < pre_w { fx + 1 } else { fx };
let mut mins = [row[fx], row[xm1], row[xp1], rowt[xm1]];
crate::adaptive_quant::sort4(&mut mins);
crate::adaptive_quant::store_min4(rowt[fx], &mut mins);
crate::adaptive_quant::store_min4(rowt[xp1], &mut mins);
crate::adaptive_quant::store_min4(rowb[xm1], &mut mins);
crate::adaptive_quant::store_min4(rowb[fx], &mut mins);
crate::adaptive_quant::store_min4(rowb[xp1], &mut mins);
0.05 * row[fx] + 0.05 * mins[0] + 0.05 * mins[1] + 0.05 * mins[2] + 0.05 * mins[3]
}
#[target_feature(enable = "avx2,fma")]
fn fuzzy_erosion_row(rowt: &[f32], row: &[f32], rowb: &[f32], pre_w: usize, vrow: &mut [f32]) {
let k = _mm256_set1_ps(0.05f32);
let inf = _mm256_set1_ps(f32::INFINITY);
let mut fx = 0usize;
if pre_w > 0 {
vrow[0] = scalar_px(rowt, row, rowb, pre_w, 0);
fx = 1;
}
while fx + 9 <= pre_w {
let mut m = [inf, inf, inf, inf];
insert8(&mut m, load8s(rowt, fx - 1));
insert8(&mut m, load8s(rowt, fx));
insert8(&mut m, load8s(rowt, fx + 1));
insert8(&mut m, load8s(row, fx - 1));
let mc = load8s(row, fx);
insert8(&mut m, mc);
insert8(&mut m, load8s(row, fx + 1));
insert8(&mut m, load8s(rowb, fx - 1));
insert8(&mut m, load8s(rowb, fx));
insert8(&mut m, load8s(rowb, fx + 1));
let mut v = _mm256_mul_ps(k, mc);
v = _mm256_add_ps(v, _mm256_mul_ps(k, m[0]));
v = _mm256_add_ps(v, _mm256_mul_ps(k, m[1]));
v = _mm256_add_ps(v, _mm256_mul_ps(k, m[2]));
v = _mm256_add_ps(v, _mm256_mul_ps(k, m[3]));
store8(v, vrow, fx);
fx += 8;
}
while fx < pre_w {
vrow[fx] = scalar_px(rowt, row, rowb, pre_w, fx);
fx += 1;
}
}
#[target_feature(enable = "avx2,fma")]
pub(crate) fn fill_quant_field(
opsin: &crate::image::Image3F,
raw_quant_field: &mut crate::image::ImageB,
x0: usize,
y0: usize,
distance: f32,
inv_scale: f32,
) {
let xsize_blocks = raw_quant_field.xsize();
let ysize_blocks = raw_quant_field.ysize();
let img_xsize = opsin.xsize();
let img_ysize = opsin.ysize();
let scale = crate::adaptive_quant::K_AC_QUANT / distance.powf(0.7934);
let region_px_w = xsize_blocks * 8;
let region_px_h = ysize_blocks * 8;
let pre_w = region_px_w / 4;
let pre_h = region_px_h / 4;
let mut pre = vec![0.0f32; pre_w * pre_h];
let mut row_acc = vec![0.0f32; region_px_w];
let clampy = |y: isize| -> usize { y.max(0).min(img_ysize as isize - 1) as usize };
for ry in 0..region_px_h {
let gy = y0 + ry;
let gy_c = clampy(gy as isize);
let gy1 = clampy(gy as isize - 1);
let gy2 = clampy(gy as isize + 1);
let row_y = opsin.plane_row(1, gy_c);
let row_y1 = opsin.plane_row(1, gy1);
let row_y2 = opsin.plane_row(1, gy2);
let row_x = opsin.plane_row(0, gy_c);
let row_x1 = opsin.plane_row(0, gy1);
let row_x2 = opsin.plane_row(0, gy2);
let set_mode = (ry & 3) == 0;
stage1_diff_row(
set_mode,
row_y,
row_y1,
row_y2,
row_x,
row_x1,
row_x2,
x0,
img_xsize,
region_px_w,
&mut row_acc,
);
if ry % 4 == 3 {
let out_y = ry / 4;
let prow = &mut pre[out_y * pre_w..out_y * pre_w + pre_w];
for px in 0..pre_w {
prow[px] = (row_acc[px * 4]
+ row_acc[px * 4 + 1]
+ row_acc[px * 4 + 2]
+ row_acc[px * 4 + 3])
* 0.25;
}
}
}
let mut aq_map = vec![0.0f32; xsize_blocks * ysize_blocks];
let mut vrow = vec![0.0f32; pre_w];
for fy in 0..pre_h {
let ym1 = if fy >= 1 { fy - 1 } else { fy };
let yp1 = if fy + 1 < pre_h { fy + 1 } else { fy };
let rowt = &pre[ym1 * pre_w..ym1 * pre_w + pre_w];
let row = &pre[fy * pre_w..fy * pre_w + pre_w];
let rowb = &pre[yp1 * pre_w..yp1 * pre_w + pre_w];
let out_y = fy / 2;
fuzzy_erosion_row(rowt, row, rowb, pre_w, &mut vrow);
for fx in 0..pre_w {
let out_x = fx / 2;
let idx = out_y * xsize_blocks + out_x;
if fx % 2 == 0 && fy % 2 == 0 {
aq_map[idx] = vrow[fx];
} else {
aq_map[idx] += vrow[fx];
}
}
}
let base_level = 0.5 * scale;
let k_dampen_ramp_start = 7.0f32;
let k_dampen_ramp_end = 14.0f32;
let mut dampen = 1.0f32;
if distance >= k_dampen_ramp_start {
dampen =
1.0 - ((distance - k_dampen_ramp_start) / (k_dampen_ramp_end - k_dampen_ramp_start));
if dampen < 0.0 {
dampen = 0.0;
}
}
let mul = scale * dampen;
let add = (1.0 - dampen) * base_level;
for by in 0..ysize_blocks {
let py = y0 + by * 8;
let aq_row = &aq_map[by * xsize_blocks..by * xsize_blocks + xsize_blocks];
let qf_row = raw_quant_field.row_mut(by);
for (bx, (qf_out, &aq)) in qf_row.iter_mut().zip(aq_row.iter()).enumerate() {
let px = x0 + bx * 8;
if px >= img_xsize || py >= img_ysize {
*qf_out = 1;
continue;
}
let bx_px = px.min(img_xsize.saturating_sub(8));
let by_px = py.min(img_ysize.saturating_sub(8));
let interior = bx_px + 8 <= img_xsize && by_px + 8 <= img_ysize;
let mut out_val = crate::adaptive_quant::compute_mask(aq);
if interior {
out_val = hf_modulation_avx2(bx_px, by_px, opsin, out_val);
out_val = color_modulation_avx2(bx_px, by_px, opsin, distance, out_val);
out_val = gamma_modulation_avx2(bx_px, by_px, opsin, out_val);
} else {
out_val = crate::adaptive_quant::hf_modulation(bx_px, by_px, opsin, out_val);
out_val =
crate::adaptive_quant::color_modulation(bx_px, by_px, opsin, distance, out_val);
out_val = crate::adaptive_quant::gamma_modulation(bx_px, by_px, opsin, out_val);
}
let qf = crate::adaptive_quant::fast_exp2(out_val * 1.442695041) * mul + add;
let qi = f32::mul_add(qf, inv_scale, 0.5) as i32;
*qf_out = qi.clamp(1, 255) as u8;
}
}
}