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_444_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 planes = [
pad_plane_pixels(&image.planes[0], width, height, pw, ph),
pad_plane_pixels(&image.planes[1], width, height, pw, ph),
pad_plane_pixels(&image.planes[2], width, height, pw, ph),
];
let specs = tile_specs(pw, ph, log2c, log2r);
let config = self.config(Layout::I444);
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], pw, x0, y0, tw, th),
extract_subplane_pixels(&planes[2], pw, x0, y0, tw, th),
Vec::new(),
],
};
let frame = self.encode_yuv444_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::Yuv444,
},
&tiles,
)
}
pub(super) fn encode_444_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)| lossy_native_mi(tw, th).is_some());
let (pw, ph) = (sb_align(width), sb_align(height));
let (sig_w, sig_h, stride, planes, specs) = if exact {
(
width,
height,
width,
(yf.to_vec(), cbf.to_vec(), crf.to_vec()),
native_specs,
)
} else {
(
pw,
ph,
pw,
(
pad_plane(yf, width, height, pw, ph),
pad_plane(cbf, width, height, pw, ph),
pad_plane(crf, width, height, pw, ph),
),
tile_specs(pw, ph, log2c, log2r),
)
};
let (yf, cbf, crf) = (&planes.0, &planes.1, &planes.2);
let n = specs.len();
let inter = self.inter_tile.load(Relaxed);
let full_ref: std::sync::Arc<Vec<Vec<f32>>> = if inter {
std::sync::Arc::clone(&self.last_ref.lock().unwrap())
} else {
std::sync::Arc::new(Vec::new())
};
let has_ref = full_ref.len() >= 3 && !full_ref[0].is_empty();
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);
}
let nthreads = Self::resolve_threads(threads).min(n.max(1));
type TileOut = (Vec<u8>, Vec<Vec<f32>>, replay::DecisionRecord);
let tiles3: Vec<TileOut> = par_map_indexed(nthreads, n, |i| {
let _tsg = replay::TileSubencodeGuard::enter();
let (x0, y0, tw, th) = specs[i];
let ty = extract_subplane(yf, stride, x0, y0, tw, th);
let tu = extract_subplane(cbf, stride, x0, y0, tw, th);
let tv = extract_subplane(crf, stride, x0, y0, tw, th);
let tile_ref = if has_ref {
Some(tiling::TileRefCtx {
planes: std::sync::Arc::clone(&full_ref),
luma_stride: stride,
chroma_stride: stride, x0,
y0,
})
} else {
None
};
let core_planes = super::lossy::Core444Planes {
y: &ty,
u: &tu,
v: &tv,
};
let mut rec = replay::DecisionRecord::new();
let mut enc = self.encode_444_core(
core_planes,
tw,
th,
tile_ref.as_ref(),
None,
replay::DecideMode::Capture(&mut rec),
);
let recon = std::mem::take(&mut enc.recon);
(enc.finish(), recon, rec)
});
let mut records: Vec<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));
}
let recon = stitch_tile_recon_444(&tiles, &specs, sig_w, sig_h);
let frame_dec = if cdef_search && recon.len() >= 3 && !recon[0].is_empty() {
cdef_est::search_per_block(
&recon,
&[yf.clone(), cbf.clone(), crf.clone()],
stride,
sig_h,
stride,
sig_h,
0,
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 = 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, stride, x0, y0, tw, th);
let tu = extract_subplane(cbf, stride, x0, y0, tw, th);
let tv = extract_subplane(crf, stride, x0, y0, tw, th);
let tile_ref = if has_ref {
Some(tiling::TileRefCtx {
planes: std::sync::Arc::clone(&full_ref),
luma_stride: stride,
chroma_stride: stride,
x0,
y0,
})
} else {
None
};
let core_planes = super::lossy::Core444Planes {
y: &ty,
u: &tu,
v: &tv,
};
let cur = replay::DecisionCursor::new(&records[i]);
self.encode_444_core(
core_planes,
tw,
th,
tile_ref.as_ref(),
Some(&tdec),
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)
};
self.capture_recon.store(prev_capture, Relaxed);
let mut frame = 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::Yuv444,
},
&tiles_bytes,
);
frame.recon = recon;
frame
}
}
fn stitch_tile_recon_444(
tiles: &[(Vec<u8>, Vec<Vec<f32>>)],
specs: &[(usize, usize, usize, usize)],
sig_w: usize,
sig_h: 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]; 3];
for ((_, recon), &(x0, y0, tw, th)) in tiles.iter().zip(specs.iter()) {
if recon.len() < 3 {
continue;
}
let tpw = sb_align(tw);
for (pi, out) in planes.iter_mut().enumerate() {
for r in 0..th {
let src = &recon[pi][r * tpw..r * tpw + tw];
out[(y0 + r) * sig_w + x0..(y0 + r) * sig_w + x0 + tw].copy_from_slice(src);
}
}
}
planes
}