blob: 7094b5e53dcb8eba5beff77fefae2887145a4bca [file] [log] [blame]
// Copyright 2024 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/// TODO(greenjustin): This entire file should be replaced with LibYUV.
use byteorder::ByteOrder;
use byteorder::LittleEndian;
/// Copies `src` into `dst` as NV12, removing any extra padding.
pub fn nv12_copy(
src: &[u8],
dst: &mut [u8],
width: usize,
height: usize,
strides: [usize; 3],
offsets: [usize; 3],
) {
// Copy Y.
let src_y_lines = src[offsets[0]..]
.chunks(strides[0])
.map(|line| &line[..width]);
let dst_y_lines = dst.chunks_mut(width);
for (src_line, dst_line) in src_y_lines.zip(dst_y_lines).take(height) {
dst_line.copy_from_slice(src_line);
}
let dst_u_offset = width * height;
// Align width and height to 2 for UV plane.
// 1 sample per 4 pixels, but we have two components per line so width can remain as-is.
let uv_width = if width % 2 == 1 { width + 1 } else { width };
let uv_height = if height % 2 == 1 { height + 1 } else { height } / 2;
// Copy UV.
let src_uv_lines = src[offsets[1]..]
.chunks(strides[1])
.map(|line| &line[..uv_width]);
let dst_uv_lines = dst[dst_u_offset..].chunks_mut(uv_width);
for (src_line, dst_line) in src_uv_lines.zip(dst_uv_lines).take(uv_height) {
dst_line.copy_from_slice(src_line);
}
}
/// Copies `src` into `dst` as I4xx (YUV tri-planar).
///
/// This function does not change the data layout beyond removing any padding in the source, i.e.
/// both `src` and `dst` are 3-planar YUV buffers.
///
/// `strides` and `offsets` give the stride and starting position of each plane in `src`. In `dst`
/// each plane will be put sequentially one after the other.
///
/// `sub_h` and `sub_v` enable horizontal and vertical sub-sampling, respectively. E.g, if both
/// `sub_h` and `sub_v` are `true` the data will be `4:2:0`, if only `sub_v` is `true` then it will be
/// `4:2:2`, and if both are `false` then we have `4:4:4`.
pub fn i4xx_copy(
src: &[u8],
dst: &mut [u8],
width: usize,
height: usize,
strides: [usize; 3],
offsets: [usize; 3],
(sub_h, sub_v): (bool, bool),
) {
// Align width and height of UV planes to 2 if sub-sampling is used.
let uv_width = if sub_h { (width + 1) / 2 } else { width };
let uv_height = if sub_v { (height + 1) / 2 } else { height };
let dst_y_size = width * height;
let dst_u_size = uv_width * uv_height;
let (dst_y_plane, dst_uv_planes) = dst.split_at_mut(dst_y_size);
let (dst_u_plane, dst_v_plane) = dst_uv_planes.split_at_mut(dst_u_size);
// Copy Y.
let src_y_lines = src[offsets[0]..]
.chunks(strides[0])
.map(|line| &line[..width]);
let dst_y_lines = dst_y_plane.chunks_mut(width);
for (src_line, dst_line) in src_y_lines.zip(dst_y_lines).take(height) {
dst_line.copy_from_slice(src_line);
}
// Copy U.
let src_u_lines = src[offsets[1]..]
.chunks(strides[1])
.map(|line| &line[..uv_width]);
let dst_u_lines = dst_u_plane.chunks_mut(uv_width);
for (src_line, dst_line) in src_u_lines.zip(dst_u_lines).take(uv_height) {
dst_line.copy_from_slice(src_line);
}
// Copy V.
let src_v_lines = src[offsets[2]..]
.chunks(strides[2])
.map(|line| &line[..uv_width]);
let dst_v_lines = dst_v_plane.chunks_mut(uv_width);
for (src_line, dst_line) in src_v_lines.zip(dst_v_lines).take(uv_height) {
dst_line.copy_from_slice(src_line);
}
}
/// Copies `src` into `dst` as I410, removing all padding and changing the layout from packed to
/// triplanar. Also drops the alpha channel.
pub fn y410_to_i410(
src: &[u8],
dst: &mut [u8],
width: usize,
height: usize,
strides: [usize; 3],
offsets: [usize; 3],
) {
let src_lines = src[offsets[0]..]
.chunks(strides[0])
.map(|line| &line[..width * 4]);
let dst_y_size = width * 2 * height;
let dst_u_size = width * 2 * height;
let (dst_y_plane, dst_uv_planes) = dst.split_at_mut(dst_y_size);
let (dst_u_plane, dst_v_plane) = dst_uv_planes.split_at_mut(dst_u_size);
let dst_y_lines = dst_y_plane.chunks_mut(width * 2);
let dst_u_lines = dst_u_plane.chunks_mut(width * 2);
let dst_v_lines = dst_v_plane.chunks_mut(width * 2);
for (src_line, (dst_y_line, (dst_u_line, dst_v_line))) in src_lines
.zip(dst_y_lines.zip(dst_u_lines.zip(dst_v_lines)))
.take(height)
{
for (src, (dst_y, (dst_u, dst_v))) in src_line.chunks(4).zip(
dst_y_line
.chunks_mut(2)
.zip(dst_u_line.chunks_mut(2).zip(dst_v_line.chunks_mut(2))),
) {
let y = LittleEndian::read_u16(&[src[1] >> 2 | src[2] << 6, src[2] >> 2 & 0b11]);
let u = LittleEndian::read_u16(&[src[0], src[1] & 0b11]);
let v = LittleEndian::read_u16(&[src[2] >> 4 | src[3] << 4, src[3] >> 4 & 0b11]);
LittleEndian::write_u16(dst_y, y);
LittleEndian::write_u16(dst_u, u);
LittleEndian::write_u16(dst_v, v);
}
}
}