use rat_rdp_pdu::utils::SplitTo as _;
use wide::i16x8;
const MAX_SUBBAND_WIDTH: usize = 32;
#[inline]
fn vld(s: &[i16], off: usize) -> i16x8 {
i16x8::from_slice_unaligned(&s[off..][..8])
}
#[inline]
fn vst(s: &mut [i16], off: usize, v: i16x8) {
s[off..][..8].copy_from_slice(v.as_array_ref());
}
#[inline]
fn ceil_avg(a: i16x8, b: i16x8) -> i16x8 {
(a | b) - ((a ^ b) >> 1)
}
#[inline]
fn floor_avg(a: i16x8, b: i16x8) -> i16x8 {
(a & b) + ((a ^ b) >> 1)
}
pub fn encode(buffer: &mut [i16], temp_buffer: &mut [i16]) {
encode_block::<32>(&mut *buffer, temp_buffer);
encode_block::<16>(&mut buffer[3072..], temp_buffer);
encode_block::<8>(&mut buffer[3840..], temp_buffer);
}
fn encode_block<const SUBBAND_WIDTH: usize>(buffer: &mut [i16], temp_buffer: &mut [i16]) {
dwt_vertical::<SUBBAND_WIDTH>(buffer, temp_buffer);
dwt_horizontal::<SUBBAND_WIDTH>(buffer, temp_buffer);
}
fn dwt_vertical<const SUBBAND_WIDTH: usize>(buffer: &[i16], dwt: &mut [i16]) {
let total_width = SUBBAND_WIDTH * 2;
for x in 0..total_width {
for n in 0..SUBBAND_WIDTH {
let y = n * 2;
let l_index = n * total_width + x;
let h_index = l_index + SUBBAND_WIDTH * total_width;
let src_index = y * total_width + x;
dwt[h_index] = i32_to_i16_possible_truncation(
(i32::from(buffer[src_index + total_width])
- ((i32::from(buffer[src_index])
+ i32::from(buffer[src_index + if n < SUBBAND_WIDTH - 1 { 2 * total_width } else { 0 }]))
>> 1))
>> 1,
);
dwt[l_index] = i32_to_i16_possible_truncation(
i32::from(buffer[src_index])
+ if n == 0 {
i32::from(dwt[h_index])
} else {
(i32::from(dwt[h_index - total_width]) + i32::from(dwt[h_index])) >> 1
},
);
}
}
}
fn dwt_horizontal<const SUBBAND_WIDTH: usize>(mut buffer: &mut [i16], dwt: &[i16]) {
let total_width = SUBBAND_WIDTH * 2;
let squared_subband_width = SUBBAND_WIDTH.pow(2);
let mut hl = buffer.split_to(squared_subband_width);
let mut lh = buffer.split_to(squared_subband_width);
let mut hh = buffer.split_to(squared_subband_width);
let mut ll = buffer;
let (mut l_src, mut h_src) = dwt.split_at(squared_subband_width * 2);
for _ in 0..SUBBAND_WIDTH {
for n in 0..SUBBAND_WIDTH {
let x = n * 2;
hl[n] = i32_to_i16_possible_truncation(
(i32::from(l_src[x + 1])
- ((i32::from(l_src[x]) + i32::from(l_src[if n < SUBBAND_WIDTH - 1 { x + 2 } else { x }])) >> 1))
>> 1,
);
ll[n] = i32_to_i16_possible_truncation(
i32::from(l_src[x])
+ if n == 0 {
i32::from(hl[n])
} else {
(i32::from(hl[n - 1]) + i32::from(hl[n])) >> 1
},
);
}
for n in 0..SUBBAND_WIDTH {
let x = n * 2;
hh[n] = i32_to_i16_possible_truncation(
(i32::from(h_src[x + 1])
- ((i32::from(h_src[x]) + i32::from(h_src[if n < SUBBAND_WIDTH - 1 { x + 2 } else { x }])) >> 1))
>> 1,
);
lh[n] = i32_to_i16_possible_truncation(
i32::from(h_src[x])
+ if n == 0 {
i32::from(hh[n])
} else {
(i32::from(hh[n - 1]) + i32::from(hh[n])) >> 1
},
);
}
hl = &mut hl[SUBBAND_WIDTH..];
lh = &mut lh[SUBBAND_WIDTH..];
hh = &mut hh[SUBBAND_WIDTH..];
ll = &mut ll[SUBBAND_WIDTH..];
l_src = &l_src[total_width..];
h_src = &h_src[total_width..];
}
}
pub fn decode(buffer: &mut [i16], temp_buffer: &mut [i16]) {
decode_block::<8>(&mut buffer[3840..], temp_buffer);
decode_block::<16>(&mut buffer[3072..], temp_buffer);
decode_block::<32>(&mut *buffer, temp_buffer);
}
fn decode_block<const SUBBAND_WIDTH: usize>(buffer: &mut [i16], temp_buffer: &mut [i16]) {
inverse_horizontal::<SUBBAND_WIDTH>(buffer, temp_buffer);
inverse_vertical::<SUBBAND_WIDTH>(buffer, temp_buffer);
}
fn inverse_horizontal<const SUBBAND_WIDTH: usize>(buffer: &[i16], temp_buffer: &mut [i16]) {
let sw = SUBBAND_WIDTH;
let tw = sw * 2;
let ssw = sw * sw;
let (hl, rest) = buffer.split_at(ssw);
let (lh, rest) = rest.split_at(ssw);
let (hh, ll) = rest.split_at(ssw);
let (l_dst, h_dst) = temp_buffer.split_at_mut(ssw * 2);
for r in 0..sw {
let row = r * sw;
horizontal_band::<SUBBAND_WIDTH>(&ll[row..][..sw], &hl[row..][..sw], &mut l_dst[r * tw..][..tw]);
horizontal_band::<SUBBAND_WIDTH>(&lh[row..][..sw], &hh[row..][..sw], &mut h_dst[r * tw..][..tw]);
}
}
fn horizontal_band<const SUBBAND_WIDTH: usize>(low: &[i16], high: &[i16], dst: &mut [i16]) {
const {
assert!(
SUBBAND_WIDTH == 8 || SUBBAND_WIDTH == 16 || SUBBAND_WIDTH == 32,
"subband width must be one of 8, 16, or 32"
)
};
let sw = SUBBAND_WIDTH;
let mut high_pad = [0i16; MAX_SUBBAND_WIDTH + 1];
high_pad[0] = high[0];
high_pad[1..sw].copy_from_slice(&high[0..sw - 1]);
let mut ev = [0i16; MAX_SUBBAND_WIDTH + 1];
let mut od = [0i16; MAX_SUBBAND_WIDTH + 1];
let mut n = 0;
while n < sw {
vst(&mut ev, n, vld(low, n) - ceil_avg(vld(&high_pad, n), vld(high, n)));
n += 8;
}
let mut n = 0;
while n < sw {
vst(
&mut od,
n,
(vld(high, n) << 1) + floor_avg(vld(&ev, n), vld(&ev, n + 1)),
);
n += 8;
}
od[sw - 1] = i32_to_i16_possible_truncation((i32::from(high[sw - 1]) << 1) + i32::from(ev[sw - 1]));
for n in 0..sw {
dst[2 * n] = ev[n];
dst[2 * n + 1] = od[n];
}
}
fn inverse_vertical<const SUBBAND_WIDTH: usize>(buffer: &mut [i16], temp_buffer: &[i16]) {
const {
assert!(
SUBBAND_WIDTH == 8 || SUBBAND_WIDTH == 16 || SUBBAND_WIDTH == 32,
"subband width must be one of 8, 16, or 32"
)
};
let sw = SUBBAND_WIDTH;
let tw = sw * 2;
let mut cb = 0;
while cb < tw {
vst(buffer, cb, vld(temp_buffer, cb) - vld(temp_buffer, cb + sw * tw));
for k in 1..sw {
let l = vld(temp_buffer, cb + k * tw);
let h = vld(temp_buffer, cb + (sw + k) * tw);
let lh = vld(temp_buffer, cb + (sw - 1 + k) * tw);
let even = l - ceil_avg(lh, h);
vst(buffer, cb + k * 2 * tw, even);
let d0 = vld(buffer, cb + (k - 1) * 2 * tw);
vst(buffer, cb + (2 * k - 1) * tw, (lh << 1) + floor_avg(d0, even));
}
let lhn = vld(temp_buffer, cb + (2 * sw - 1) * tw);
let dl = vld(buffer, cb + (2 * sw - 2) * tw);
vst(buffer, cb + (2 * sw - 1) * tw, (lhn << 1) + dl);
cb += 8;
}
}
#[expect(clippy::as_conversions)]
#[expect(clippy::cast_possible_truncation)]
fn i32_to_i16_possible_truncation(value: i32) -> i16 {
value as i16
}