use super::super::*;
use crate::av2::tiling::{
MultitileAssembly, TiledEncodeRequest, assemble_multitile, extract_subplane,
extract_subplane_pixels, lossless_tile_payload, pad_plane_pixels, tile_specs,
};
impl Av2Encoder {
pub(super) fn encode_422_lossless_tiled<T: Pixel>(
&self,
image: &PlanarImage<T>,
color: &Cicp,
log2c: usize,
log2r: usize,
) -> Av2Frame {
let (width, height) = (image.width, image.height);
let (pw, ph) = (sb_align(width), sb_align(height));
let pcw = pw / 2;
let planes = [
pad_plane_pixels(&image.planes[0], width, height, pw, ph),
pad_plane_pixels(&image.planes[1], width / 2, height, pcw, ph),
pad_plane_pixels(&image.planes[2], width / 2, height, pcw, ph),
];
let specs = tile_specs(pw, ph, log2c, log2r);
let config = self.config(Layout::I422);
let n = specs.len();
let nthreads = Self::resolve_threads(self.threads).min(n.max(1));
let tiles: Vec<Vec<u8>> = par_map_indexed(nthreads, n, |i| {
let (x0, y0, tw, th) = specs[i];
let tile = PlanarImage {
width: tw,
height: th,
bit_depth: image.bit_depth,
planes: [
extract_subplane_pixels(&planes[0], pw, x0, y0, tw, th),
extract_subplane_pixels(&planes[1], pcw, x0 / 2, y0, tw / 2, th),
extract_subplane_pixels(&planes[2], pcw, x0 / 2, y0, tw / 2, th),
Vec::new(),
],
};
let frame = self.encode_yuv422_lossless(&tile, color, 1).unwrap();
lossless_tile_payload(&frame, &config, tw, th)
});
assemble_multitile(
&MultitileAssembly {
config: &config,
coded_width: pw,
coded_height: ph,
display_width: width,
display_height: height,
color,
log2_cols: log2c,
log2_rows: log2r,
bit_depth: self.bit_depth,
chroma_format: ChromaFormat::Yuv422,
},
&tiles,
)
}
pub(super) fn encode_422_tiled(&self, request: &TiledEncodeRequest<'_>) -> Av2Frame {
let TiledEncodeRequest {
y: yf,
u: cbf,
v: crf,
width,
height,
config,
color,
log2_cols: log2c,
log2_rows: log2r,
threads,
} = *request;
let native_specs = tile_specs(width, height, log2c, log2r);
let exact = native_specs
.iter()
.all(|&(_, _, tw, th)| native_422_mi(tw, th).is_some());
let (pw, ph) = (sb_align(width), sb_align(height));
let (sig_w, sig_h, lstride, cstride, planes, specs) = if exact {
(
width,
height,
width,
width.div_ceil(2),
(yf.to_vec(), cbf.to_vec(), crf.to_vec()),
native_specs,
)
} else {
(
pw,
ph,
pw,
pw / 2,
(
pad_plane(yf, width, height, pw, ph),
pad_plane(cbf, width.div_ceil(2), height, pw / 2, ph),
pad_plane(crf, width.div_ceil(2), height, pw / 2, ph),
),
tile_specs(pw, ph, log2c, log2r),
)
};
let (yf, cbf, crf) = (&planes.0, &planes.1, &planes.2);
let cw = cstride; let n = specs.len();
let nthreads = Self::resolve_threads(threads).min(n.max(1));
use std::sync::atomic::Ordering::Relaxed;
let cdef_search = self.tune.cdef;
let prev_capture = self.capture_recon.load(Relaxed);
if cdef_search {
self.capture_recon.store(true, Relaxed);
}
type TileOut422 = (Vec<u8>, Vec<Vec<f32>>, crate::av2::replay::DecisionRecord);
let tiles3: Vec<TileOut422> = par_map_indexed(nthreads, n, |i| {
let _tsg = crate::av2::replay::TileSubencodeGuard::enter();
let (x0, y0, tw, th) = specs[i];
let ty = extract_subplane(yf, lstride, x0, y0, tw, th);
let tu = extract_subplane(cbf, cw, x0 / 2, y0, tw.div_ceil(2), th);
let tv = extract_subplane(crf, cw, x0 / 2, y0, tw.div_ceil(2), th);
let mut rec = crate::av2::replay::DecisionRecord::new();
let mut enc = self.encode_422_core(
super::lossy::Core422Planes {
y: &ty,
u: &tu,
v: &tv,
},
tw,
th,
None,
crate::av2::replay::DecideMode::Capture(&mut rec),
);
let recon = std::mem::take(&mut enc.recon);
(enc.finish(), recon, rec)
});
let mut records: Vec<crate::av2::replay::DecisionRecord> = Vec::with_capacity(n);
let mut tiles: Vec<(Vec<u8>, Vec<Vec<f32>>)> = Vec::with_capacity(n);
for (b, r, rec) in tiles3 {
records.push(rec);
tiles.push((b, r));
}
self.capture_recon.store(prev_capture, Relaxed);
let recon = stitch_tile_recon_422(&tiles, &specs, sig_w, sig_h, cw);
let frame_dec = if cdef_search && recon.len() >= 3 && !recon[0].is_empty() {
crate::av2::cdef_est::search_per_block(
&recon,
&[yf.clone(), cbf.clone(), crf.clone()],
lstride,
sig_h,
cw,
sig_h,
1,
0,
true,
self.base_q_idx,
self.bit_depth,
)
} else {
None
};
let cfg_owned;
let (tiles_bytes, config): (Vec<Vec<u8>>, &Config) = if let Some(dec) = &frame_dec {
let fcols = dec.sb_cols;
let bytes: Vec<Vec<u8>> = par_map_indexed(nthreads, n, |i| {
let (x0, y0, tw, th) = specs[i];
let (sbx0, sby0) = (x0 / 64, y0 / 64);
let (lcols, lrows) = (tw.div_ceil(64), th.div_ceil(64));
let mut lg = vec![0u8; lcols * lrows];
for lr in 0..lrows {
for lc in 0..lcols {
let fidx = (sby0 + lr) * fcols + (sbx0 + lc);
lg[lr * lcols + lc] = dec.grid.get(fidx).copied().unwrap_or(0);
}
}
let tdec = crate::av2::cdef_est::CdefDecision {
damping: dec.damping,
y_str: dec.y_str,
uv_str: dec.uv_str,
grid: lg,
sb_cols: lcols,
};
let ty = extract_subplane(yf, lstride, x0, y0, tw, th);
let tu = extract_subplane(cbf, cw, x0 / 2, y0, tw.div_ceil(2), th);
let tv = extract_subplane(crf, cw, x0 / 2, y0, tw.div_ceil(2), th);
let cur = crate::av2::replay::DecisionCursor::new(&records[i]);
self.encode_422_core(
super::lossy::Core422Planes {
y: &ty,
u: &tu,
v: &tv,
},
tw,
th,
Some(&tdec),
crate::av2::replay::DecideMode::Replay(cur),
)
.finish()
});
let mut c = config.clone();
c.cdef = Some((dec.y_str, dec.uv_str, dec.damping));
c.cdef_per_block = true;
cfg_owned = c;
(bytes, &cfg_owned)
} else {
(tiles.iter().map(|(b, _)| b.clone()).collect(), config)
};
assemble_multitile(
&MultitileAssembly {
config,
coded_width: sig_w,
coded_height: sig_h,
display_width: width,
display_height: height,
color,
log2_cols: log2c,
log2_rows: log2r,
bit_depth: self.bit_depth,
chroma_format: ChromaFormat::Yuv422,
},
&tiles_bytes,
)
}
}
fn stitch_tile_recon_422(
tiles: &[(Vec<u8>, Vec<Vec<f32>>)],
specs: &[(usize, usize, usize, usize)],
sig_w: usize,
sig_h: usize,
cw: usize,
) -> Vec<Vec<f32>> {
if tiles.iter().all(|(_, r)| r.len() < 3) {
return Vec::new();
}
let mut planes = vec![
vec![0f32; sig_w * sig_h],
vec![0f32; cw * sig_h],
vec![0f32; cw * sig_h],
];
for ((_, recon), &(x0, y0, tw, th)) in tiles.iter().zip(specs.iter()) {
if recon.len() < 3 {
continue;
}
let tpw = sb_align(tw); let tcw = tpw / 2; let ctw = tw.div_ceil(2); let cx0 = x0 / 2;
for r in 0..th {
let src = &recon[0][r * tpw..r * tpw + tw];
let base = (y0 + r) * sig_w + x0;
planes[0][base..base + tw].copy_from_slice(src);
for pi in 1..3 {
let cs = &recon[pi][r * tcw..r * tcw + ctw];
let cbase = (y0 + r) * cw + cx0;
planes[pi][cbase..cbase + ctw].copy_from_slice(cs);
}
}
}
planes
}