use mediaway_common::Bytes;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) struct CropRect {
pub(super) left: u32,
pub(super) top: u32,
pub(super) right: u32,
pub(super) bottom: u32,
}
impl CropRect {
pub(super) const fn width(self) -> u32 {
self.right.saturating_sub(self.left)
}
pub(super) const fn height(self) -> u32 {
self.bottom.saturating_sub(self.top)
}
}
pub(super) fn strip_and_crop_nv12(
data: &[u8],
stride: u32,
slice_height: u32,
crop: CropRect,
) -> Bytes {
let out_width = crop.width() as usize;
let out_height = crop.height() as usize;
if out_width == 0 || out_height == 0 {
return Bytes::new();
}
let stride = stride as usize;
let left = crop.left as usize;
let top = crop.top as usize;
let uv_plane_offset = stride * slice_height as usize;
let uv_rows = out_height / 2;
let mut out = vec![0u8; out_width * out_height + out_width * uv_rows];
for row in 0..out_height {
let Some(src_start) = top
.checked_add(row)
.and_then(|r| r.checked_mul(stride))
.and_then(|base| base.checked_add(left))
else {
break;
};
let src_end = src_start + out_width;
if src_end > data.len() {
break;
}
let dst_start = row * out_width;
out[dst_start..dst_start + out_width].copy_from_slice(&data[src_start..src_end]);
}
let y_plane_bytes = out_width * out_height;
let chroma_top = top / 2;
for row in 0..uv_rows {
let Some(src_start) = chroma_top
.checked_add(row)
.and_then(|r| r.checked_mul(stride))
.and_then(|base| base.checked_add(uv_plane_offset))
.and_then(|base| base.checked_add(left))
else {
break;
};
let src_end = src_start + out_width;
if src_end > data.len() {
break;
}
let dst_start = y_plane_bytes + row * out_width;
out[dst_start..dst_start + out_width].copy_from_slice(&data[src_start..src_end]);
}
Bytes::from(out)
}
#[cfg(test)]
#[path = "nv12_tests.rs"]
mod tests;