use super::super::*;
use crate::av2::tiling::TiledEncodeRequest;
use crate::av2::tiling::tile_grid_for;
impl Av2Encoder {
pub fn encode_yuv422<T: Pixel>(
&self,
planar_image: &PlanarImage<T>,
color: &Cicp,
) -> Result<Av2Frame, EncodeError> {
let width = planar_image.width;
let height = planar_image.height;
planar_image.validate_422()?;
if self.base_q_idx == 0 {
if let Some((log2c, log2r)) =
tile_grid_for(self.tune.tile_cols, self.tune.tile_rows, width, height)
{
return Ok(self.encode_422_lossless_tiled(planar_image, color, log2c, log2r));
}
return self.encode_yuv422_lossless(planar_image, color, self.threads);
}
let to_plane = |s: &[T]| s.iter().map(|p| p.to_f32()).collect::<Vec<f32>>();
let yf = to_plane(&planar_image.planes[0]);
let cbf = to_plane(&planar_image.planes[1]);
let crf = to_plane(&planar_image.planes[2]);
let core_planes = super::lossy::Core422Planes {
y: &yf,
u: &cbf,
v: &crf,
};
let (pw, ph) = (sb_align(width), sb_align(height));
let config = self.config(Layout::I422);
let multithreaded = Self::resolve_threads(self.threads) > 1;
let video = self.video_mode.load(std::sync::atomic::Ordering::Relaxed);
let _ = (multithreaded, video);
if let Some((log2c, log2r)) =
tile_grid_for(self.tune.tile_cols, self.tune.tile_rows, width, height)
{
return Ok(self.encode_422_tiled(&TiledEncodeRequest {
y: &yf,
u: &cbf,
v: &crf,
width,
height,
config: &config,
color,
log2_cols: log2c,
log2_rows: log2r,
threads: self.threads,
}));
}
let mut rec = crate::av2::replay::DecisionRecord::new();
let mut enc = self.encode_422_core(
core_planes,
width,
height,
None,
replay::DecideMode::Capture(&mut rec),
);
if self.tune.cdef
&& self.base_q_idx != 0
&& let Some(dec) = enc.cdef_decided.clone()
{
let cur = replay::DecisionCursor::new(&rec);
enc = self.encode_422_core(
core_planes,
width,
height,
Some(&dec),
replay::DecideMode::Replay(cur),
);
} else if multithreaded && !video {
let cur = replay::DecisionCursor::new(&rec);
enc = self.encode_422_core(
core_planes,
width,
height,
None,
replay::DecideMode::Replay(cur),
);
}
Ok(self.finish(enc, &config, pw, ph, width, height, color))
}
pub fn encode_image_422<T: Pixel>(
&self,
img: &PlanarImage<T>,
color: &Cicp,
) -> Result<Av2Frame, EncodeError> {
img.validate_444()?;
validate_dims(img.width as u32, img.height as u32)?;
let (w, h) = (img.width, img.height);
let bd = img.bit_depth.bits();
let maxv = (1i32 << bd) - 1;
let off_q = (1i32 << (bd - 1)) << Q;
let mx_i = maxv;
let cw = w.div_ceil(2);
let mut y = vec![0i32; w * h];
let mut fcb_q = vec![0i32; w * h];
let mut fcr_q = vec![0i32; w * h];
for (((((yv, fcbv), fcrv), &rr), &gg), &bb) in y
.iter_mut()
.zip(fcb_q.iter_mut())
.zip(fcr_q.iter_mut())
.zip(img.planes[2].iter())
.zip(img.planes[0].iter())
.zip(img.planes[1].iter())
{
let (ri, gi, bi) = (rr.to_i32(), gg.to_i32(), bb.to_i32());
*yv = ((Y_R * ri + Y_G * gi + Y_B * bi + HALF) >> Q).clamp(0, mx_i);
*fcbv = CB_R * ri + CB_G * gi + CB_B * bi + off_q;
*fcrv = CR_R * ri + CR_G * gi + CR_B * bi + off_q;
}
const HALF_AVG: i32 = 1 << Q;
let (mut cb, mut cr) = (vec![0i32; cw * h], vec![0i32; cw * h]);
let pair_sum = |pair: &[i32]| {
let first = *pair.first().expect("chroma pair is non-empty");
first + pair.get(1).copied().unwrap_or(first)
};
let dst_rows = cb.chunks_exact_mut(cw).zip(cr.chunks_exact_mut(cw));
let src_rows = fcb_q.chunks_exact(w).zip(fcr_q.chunks_exact(w));
for ((cb_row, cr_row), (fcb_row, fcr_row)) in dst_rows.zip(src_rows) {
let dst = cb_row.iter_mut().zip(cr_row);
let src = fcb_row.chunks(2).zip(fcr_row.chunks(2));
for ((cb, cr), (fcb, fcr)) in dst.zip(src) {
*cb = ((pair_sum(fcb) + HALF_AVG) >> (Q + 1)).clamp(0, mx_i);
*cr = ((pair_sum(fcr) + HALF_AVG) >> (Q + 1)).clamp(0, mx_i);
}
}
self.encode_yuv422(
&PlanarImage {
width: img.width,
height: img.height,
bit_depth: img.bit_depth,
planes: [y, cb, cr, Vec::new()],
},
color,
)
}
}