use alloc::vec::Vec;
use pith_digest::{Error, Result};
use crate::parser::{Component, Frame};
pub(crate) fn to_full_size(
comp: &Component,
plane: &[u8],
stride: usize,
frame: &Frame,
) -> Result<Vec<u8>> {
let out_w = frame.width;
let out_h = frame.height;
let (dw, dh) = (comp.down_w, comp.down_h);
let h_in = comp.h;
let v_in = comp.v;
let h_out = frame.hmax;
let v_out = frame.vmax;
if h_in == h_out && v_in == v_out {
return Ok(crop(plane, stride, dw, out_w, out_h));
}
if h_in * 2 == h_out && v_in == v_out {
if dw > 2 {
return Ok(h2v1_fancy(plane, stride, dw, out_w, out_h));
}
return Ok(box_x2(plane, stride, dw, dh, out_w, out_h));
}
if h_in == h_out && v_in * 2 == v_out {
return Ok(h1v2_fancy(plane, stride, dh, out_w, out_h));
}
if h_in * 2 == h_out && v_in * 2 == v_out {
if dw > 2 {
return Ok(h2v2_fancy(plane, stride, dw, dh, out_w, out_h));
}
return Ok(box_x2y2(plane, stride, dw, dh, out_w, out_h));
}
if h_out % h_in == 0 && v_out % v_in == 0 {
return Ok(replicate(
plane,
stride,
(dw, dh),
(out_w, out_h),
(h_out / h_in, v_out / v_in),
));
}
Err(Error::Unsupported(
"fractional sampling ratio (non-integral upsample)",
))
}
fn crop(plane: &[u8], stride: usize, _w_in: usize, w: usize, h: usize) -> Vec<u8> {
let mut out = alloc::vec![0u8; w * h];
for y in 0..h {
out[y * w..y * w + w].copy_from_slice(&plane[y * stride..y * stride + w]);
}
out
}
#[inline]
fn px(plane: &[u8], stride: usize, x: usize, y: usize) -> i32 {
i32::from(plane[y * stride + x])
}
fn h2v1_fancy(plane: &[u8], stride: usize, dw: usize, out_w: usize, out_h: usize) -> Vec<u8> {
let pitch = 2 * dw;
let mut wide = alloc::vec![0u8; pitch * out_h];
for r in 0..out_h {
let mut o = r * pitch;
let first = px(plane, stride, 0, r);
wide[o] = first as u8;
wide[o + 1] = ((first * 3 + px(plane, stride, 1, r) + 2) >> 2) as u8;
o += 2;
for c in 1..dw - 1 {
let v = px(plane, stride, c, r) * 3;
wide[o] = ((v + px(plane, stride, c - 1, r) + 1) >> 2) as u8;
wide[o + 1] = ((v + px(plane, stride, c + 1, r) + 2) >> 2) as u8;
o += 2;
}
let last = px(plane, stride, dw - 1, r);
wide[o] = ((last * 3 + px(plane, stride, dw - 2, r) + 1) >> 2) as u8;
wide[o + 1] = last as u8;
}
crop(&wide, pitch, 0, out_w, out_h)
}
fn h1v2_fancy(plane: &[u8], stride: usize, dh: usize, out_w: usize, out_h: usize) -> Vec<u8> {
let mut out = alloc::vec![0u8; out_w * out_h];
let mut inrow = 0usize;
let mut outrow = 0usize;
while outrow < out_h {
for v in 0..2 {
if outrow >= out_h {
break;
}
let (near, far, bias) = if v == 0 {
(inrow, inrow.saturating_sub(1), 1i32)
} else {
(inrow, (inrow + 1).min(dh.saturating_sub(1)), 2i32)
};
for x in 0..out_w {
let colsum = px(plane, stride, x, near) * 3 + px(plane, stride, x, far);
out[outrow * out_w + x] = ((colsum + bias) >> 2) as u8;
}
outrow += 1;
}
inrow += 1;
}
out
}
fn h2v2_fancy(
plane: &[u8],
stride: usize,
dw: usize,
dh: usize,
out_w: usize,
out_h: usize,
) -> Vec<u8> {
let pitch = 2 * dw;
let mut wide = alloc::vec![0u8; pitch * out_h];
let mut inrow = 0usize;
let mut outrow = 0usize;
while outrow < out_h {
for v in 0..2 {
if outrow >= out_h {
break;
}
let near = inrow;
let far = if v == 0 {
inrow.saturating_sub(1)
} else {
(inrow + 1).min(dh - 1)
};
let row = outrow * pitch;
let colsum =
|c: usize| -> i32 { px(plane, stride, c, near) * 3 + px(plane, stride, c, far) };
let mut thiscol = colsum(0);
let mut next = colsum(1);
wide[row] = ((thiscol * 4 + 8) >> 4) as u8;
wide[row + 1] = ((thiscol * 3 + next + 7) >> 4) as u8;
let mut last = thiscol;
thiscol = next;
let mut o = row + 2;
for c in 1..dw - 1 {
next = colsum(c + 1);
wide[o] = ((thiscol * 3 + last + 8) >> 4) as u8;
wide[o + 1] = ((thiscol * 3 + next + 7) >> 4) as u8;
last = thiscol;
thiscol = next;
o += 2;
}
wide[o] = ((thiscol * 3 + last + 8) >> 4) as u8;
wide[o + 1] = ((thiscol * 4 + 7) >> 4) as u8;
outrow += 1;
}
inrow += 1;
}
crop(&wide, pitch, 0, out_w, out_h)
}
fn box_x2(
plane: &[u8],
stride: usize,
dw: usize,
_dh: usize,
out_w: usize,
out_h: usize,
) -> Vec<u8> {
let mut out = alloc::vec![0u8; out_w * out_h];
for y in 0..out_h {
for x in 0..out_w {
out[y * out_w + x] = plane[y * stride + (x / 2).min(dw - 1)];
}
}
out
}
fn box_x2y2(
plane: &[u8],
stride: usize,
dw: usize,
dh: usize,
out_w: usize,
out_h: usize,
) -> Vec<u8> {
let mut out = alloc::vec![0u8; out_w * out_h];
for y in 0..out_h {
for x in 0..out_w {
out[y * out_w + x] = plane[(y / 2).min(dh - 1) * stride + (x / 2).min(dw - 1)];
}
}
out
}
fn replicate(
plane: &[u8],
stride: usize,
dims: (usize, usize),
out: (usize, usize),
expand: (usize, usize),
) -> Vec<u8> {
let (dw, dh) = dims;
let (out_w, out_h) = out;
let (hx, vx) = expand;
let mut out = alloc::vec![0u8; out_w * out_h];
for y in 0..out_h {
let sy = (y / vx).min(dh - 1);
for x in 0..out_w {
out[y * out_w + x] = plane[sy * stride + (x / hx).min(dw - 1)];
}
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn h2v1_known_answer() {
let plane = [10u8, 20, 30, 40];
let out = h2v1_fancy(&plane, 4, 4, 8, 1);
assert_eq!(out, vec![10, 13, 17, 23, 27, 33, 37, 40]);
}
#[test]
fn h2v2_known_answer() {
let plane = [0u8, 100, 200, 255];
let out = h2v2_fancy(&plane, 2, 2, 2, 4, 4);
assert_eq!(&out[0..4], &[0, 25, 75, 100]);
assert_eq!(&out[4..8], &[50, 72, 117, 139]);
}
#[test]
fn replicate_4x() {
let plane = [9u8, 8, 7, 6];
let out = replicate(&plane, 4, (4, 1), (8, 2), (4, 2));
assert_eq!(&out[0..8], &[9, 9, 9, 9, 8, 8, 8, 8]);
assert_eq!(&out[8..16], &[9, 9, 9, 9, 8, 8, 8, 8]);
}
#[test]
fn box_x2_known_answer() {
let plane = [3u8, 7];
let out = box_x2(&plane, 2, 2, 1, 4, 1);
assert_eq!(out, vec![3, 3, 7, 7]);
}
#[test]
fn box_x2y2_known_answer() {
let plane = [1u8, 2, 3, 4];
let out = box_x2y2(&plane, 2, 2, 2, 4, 4);
assert_eq!(out, vec![1, 1, 2, 2, 1, 1, 2, 2, 3, 3, 4, 4, 3, 3, 4, 4]);
}
#[test]
fn h1v2_known_answer_odd_height() {
let plane = [10u8, 30];
let out = h1v2_fancy(&plane, 1, 2, 1, 3);
assert_eq!(out, vec![10, 15, 25]);
}
fn comp(h: usize, v: usize, down_w: usize, down_h: usize) -> crate::parser::Component {
crate::parser::Component {
id: 1,
h,
v,
tq: 0,
td: 0,
ta: 0,
blocks_w: down_w.div_ceil(8),
blocks_h: down_h.div_ceil(8),
down_w,
down_h,
coefs: alloc::vec::Vec::new(),
}
}
fn frame(
comp: crate::parser::Component,
w: usize,
h: usize,
hmax: usize,
vmax: usize,
) -> crate::parser::Frame {
crate::parser::Frame {
progressive: false,
width: w,
height: h,
hmax,
vmax,
mcus_x: 1,
mcus_y: 1,
comps: alloc::vec![comp],
}
}
#[test]
fn to_full_size_dispatch() {
let c = comp(1, 1, 16, 16);
let f = frame(c, 15, 15, 1, 1);
let plane = vec![7u8; 16 * 16];
let out = to_full_size(&f.comps[0], &plane, 16, &f).expect("fullsize");
assert_eq!(out.len(), 15 * 15);
let c = comp(1, 1, 2, 8);
let f = frame(c, 4, 8, 2, 1);
let plane = vec![9u8; 2 * 8];
let out = to_full_size(&f.comps[0], &plane, 2, &f).expect("box_x2");
assert_eq!(out.len(), 4 * 8);
let c = comp(1, 1, 2, 2);
let f = frame(c, 4, 4, 2, 2);
let plane = vec![5u8; 4];
let out = to_full_size(&f.comps[0], &plane, 2, &f).expect("box_x2y2");
assert_eq!(out.len(), 4 * 4);
let c = comp(3, 1, 24, 8);
let f = frame(c, 32, 8, 4, 1);
let plane = vec![0u8; 24 * 8];
let err = to_full_size(&f.comps[0], &plane, 24, &f).expect_err("fractional");
assert!(err.to_string().contains("fractional sampling"));
}
}