#![allow(clippy::similar_names)]
use crate::profile::Encoding;
#[inline]
#[must_use]
pub(crate) fn bt601_y(r: i32, g: i32, b: i32) -> i32 {
(77 * r + 150 * g + 29 * b) >> 8
}
#[inline]
#[must_use]
pub(crate) fn bt601_cb(r: i32, g: i32, b: i32) -> i32 {
((-43 * r - 85 * g + 128 * b) >> 8) + 128
}
#[inline]
#[must_use]
pub(crate) fn bt601_cr(r: i32, g: i32, b: i32) -> i32 {
((128 * r - 107 * g - 21 * b) >> 8) + 128
}
#[must_use]
#[allow(clippy::cast_sign_loss)]
pub(crate) fn interlace_fields(planar: &[u8], w: i32, h: i32, enc: Encoding) -> Vec<u8> {
let w = w as usize;
let h = h as usize;
if enc != Encoding::Ycbcr420 {
let row_stride = w * 2;
let half_rows = h.div_ceil(2);
let mut result = vec![0u8; planar.len()];
for y in 0..h {
let src_off = y * row_stride;
let dst_off = if y % 2 == 0 {
(y / 2) * row_stride
} else {
(half_rows + y / 2) * row_stride
};
result[dst_off..dst_off + row_stride].copy_from_slice(&planar[src_off..src_off + row_stride]);
}
return result;
}
let y_size = w * h;
let uv_w = w.div_ceil(2);
let uv_h = h.div_ceil(2);
let c_size = uv_w * uv_h;
let y_row = w;
let c_row = uv_w;
let half_h = h.div_ceil(2);
let half_h_uv = uv_h.div_ceil(2);
let mut result = vec![0u8; planar.len()];
for y in 0..h {
let src_off = y * y_row;
let dst_off = if y % 2 == 0 {
(y / 2) * y_row
} else {
(half_h + y / 2) * y_row
};
result[dst_off..dst_off + y_row].copy_from_slice(&planar[src_off..src_off + y_row]);
}
let cb_off = y_size;
for y in 0..h / 2 {
let src_off = cb_off + y * c_row;
let dst_off = cb_off
+ if y % 2 == 0 {
(y / 2) * c_row
} else {
(half_h_uv + y / 2) * c_row
};
result[dst_off..dst_off + c_row].copy_from_slice(&planar[src_off..src_off + c_row]);
}
let cr_off = y_size + c_size;
for y in 0..h / 2 {
let src_off = cr_off + y * c_row;
let dst_off = cr_off
+ if y % 2 == 0 {
(y / 2) * c_row
} else {
(half_h_uv + y / 2) * c_row
};
result[dst_off..dst_off + c_row].copy_from_slice(&planar[src_off..src_off + c_row]);
}
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bt601_y_black() {
assert_eq!(bt601_y(0, 0, 0), 0);
}
#[test]
fn bt601_y_white() {
assert_eq!(bt601_y(255, 255, 255), 255);
}
#[test]
fn bt601_y_mid_gray() {
assert_eq!(bt601_y(128, 128, 128), 128);
}
#[test]
fn bt601_y_red() {
assert_eq!(bt601_y(255, 0, 0), 76);
}
#[test]
fn bt601_y_green() {
assert_eq!(bt601_y(0, 255, 0), 149);
}
#[test]
fn bt601_y_blue() {
assert_eq!(bt601_y(0, 0, 255), 28);
}
#[test]
fn bt601_cb_black() {
assert_eq!(bt601_cb(0, 0, 0), 128);
}
#[test]
fn bt601_cb_white() {
assert_eq!(bt601_cb(255, 255, 255), 128);
}
#[test]
fn bt601_cb_mid_gray() {
assert_eq!(bt601_cb(128, 128, 128), 128);
}
#[test]
fn bt601_cb_red() {
assert_eq!(bt601_cb(255, 0, 0), 85);
}
#[test]
fn bt601_cb_blue() {
assert_eq!(bt601_cb(0, 0, 255), 255);
}
#[test]
fn bt601_cr_black() {
assert_eq!(bt601_cr(0, 0, 0), 128);
}
#[test]
fn bt601_cr_white() {
assert_eq!(bt601_cr(255, 255, 255), 128);
}
#[test]
fn bt601_cr_mid_gray() {
assert_eq!(bt601_cr(128, 128, 128), 128);
}
#[test]
fn bt601_cr_red() {
assert_eq!(bt601_cr(255, 0, 0), 255);
}
#[test]
fn bt601_cr_blue() {
assert_eq!(bt601_cr(0, 0, 255), 107);
}
#[test]
fn interlace_2bpp_4x4() {
let w = 2;
let h = 4;
let row_stride = w * 2; let mut planar = Vec::with_capacity(row_stride * h);
for row in 0..h {
for col in 0..row_stride {
planar.push(u8::try_from(row * 16 + col).unwrap());
}
}
let result = interlace_fields(
&planar,
i32::try_from(w).unwrap(),
i32::try_from(h).unwrap(),
Encoding::Rgb565,
);
assert_eq!(result.len(), planar.len());
let half = h.div_ceil(2); for y in 0..h {
let src_off = y * row_stride;
let expected_dst = if y % 2 == 0 {
(y / 2) * row_stride
} else {
(half + y / 2) * row_stride
};
assert_eq!(
&result[expected_dst..expected_dst + row_stride],
&planar[src_off..src_off + row_stride],
"row {y} mapped to wrong position"
);
}
}
#[test]
fn interlace_2bpp_single_row() {
let w = 4;
let h = 1;
let planar = vec![0xAAu8; w * 2 * h];
let result = interlace_fields(
&planar,
i32::try_from(w).unwrap(),
i32::try_from(h).unwrap(),
Encoding::Rgb565,
);
assert_eq!(result, planar);
}
#[test]
fn interlace_2bpp_single_column() {
let w = 1;
let h = 4;
let row_stride = w * 2;
let mut planar = Vec::with_capacity(row_stride * h);
for row in 0..h {
planar.push(u8::try_from(row * 10).unwrap());
planar.push(u8::try_from(row * 10 + 1).unwrap());
}
let result = interlace_fields(
&planar,
i32::try_from(w).unwrap(),
i32::try_from(h).unwrap(),
Encoding::Rgb565,
);
assert_eq!(result.len(), planar.len());
let half = h.div_ceil(2);
for y in 0..h {
let src_off = y * row_stride;
let expected_dst = if y % 2 == 0 {
(y / 2) * row_stride
} else {
(half + y / 2) * row_stride
};
assert_eq!(
&result[expected_dst..expected_dst + row_stride],
&planar[src_off..src_off + row_stride]
);
}
}
#[test]
fn interlace_ycbcr420_4x4() {
let w = 4;
let h = 4;
let y_size = w * h; let uv_w = 2;
let uv_h = 2;
let c_size = uv_w * uv_h; let total = y_size + c_size * 2;
let mut planar = vec![0u8; total];
for row in 0..h {
for col in 0..w {
planar[row * w + col] = u8::try_from(row * 16 + col).unwrap();
}
}
for row in 0..uv_h {
for col in 0..uv_w {
planar[y_size + row * uv_w + col] = u8::try_from(100 + row * 4 + col).unwrap();
}
}
for row in 0..uv_h {
for col in 0..uv_w {
planar[y_size + c_size + row * uv_w + col] = u8::try_from(200 + row * 4 + col).unwrap();
}
}
let result = interlace_fields(
&planar,
i32::try_from(w).unwrap(),
i32::try_from(h).unwrap(),
Encoding::Ycbcr420,
);
assert_eq!(result.len(), total);
let half_h = h.div_ceil(2); let half_h_uv = uv_h.div_ceil(2);
for y in 0..h {
let src_off = y * w;
let dst_off = if y % 2 == 0 { (y / 2) * w } else { (half_h + y / 2) * w };
assert_eq!(
&result[dst_off..dst_off + w],
&planar[src_off..src_off + w],
"Y row {y} mismatch"
);
}
for y in 0..h / 2 {
let src_off = y_size + y * uv_w;
let dst_off = y_size
+ if y % 2 == 0 {
(y / 2) * uv_w
} else {
(half_h_uv + y / 2) * uv_w
};
assert_eq!(
&result[dst_off..dst_off + uv_w],
&planar[src_off..src_off + uv_w],
"Cb row {y} mismatch"
);
}
let cr_base = y_size + c_size;
for y in 0..h / 2 {
let src_off = cr_base + y * uv_w;
let dst_off = cr_base
+ if y % 2 == 0 {
(y / 2) * uv_w
} else {
(half_h_uv + y / 2) * uv_w
};
assert_eq!(
&result[dst_off..dst_off + uv_w],
&planar[src_off..src_off + uv_w],
"Cr row {y} mismatch"
);
}
}
#[test]
fn interlace_ycbcr420_single_row() {
let w: usize = 4;
let h: usize = 1;
let uv_w = w.div_ceil(2);
let uv_h = h.div_ceil(2);
let y_size = w * h;
let c_size = uv_w * uv_h;
let planar = vec![0x42u8; y_size + c_size * 2];
let result = interlace_fields(
&planar,
i32::try_from(w).unwrap(),
i32::try_from(h).unwrap(),
Encoding::Ycbcr420,
);
assert_eq!(result.len(), planar.len());
assert_eq!(&result[..y_size], &planar[..y_size]);
assert_eq!(&result[y_size..y_size + c_size], &[0u8; 2]);
assert_eq!(&result[y_size + c_size..], &[0u8; 2]);
}
#[test]
fn interlace_ycbcr420_odd_dimensions() {
let w: usize = 6;
let h: usize = 5;
let uv_w = w.div_ceil(2); let uv_h = h.div_ceil(2); let y_size = w * h; let c_size = uv_w * uv_h; let total = y_size + c_size * 2;
let mut planar = vec![0u8; total];
for (i, item) in planar.iter_mut().enumerate().take(total) {
*item = u8::try_from(i % 251).unwrap();
}
let result = interlace_fields(
&planar,
i32::try_from(w).unwrap(),
i32::try_from(h).unwrap(),
Encoding::Ycbcr420,
);
assert_eq!(result.len(), total);
let half_h = h.div_ceil(2); let half_h_uv = uv_h.div_ceil(2);
for y in 0..h {
let src_off = y * w;
let dst_off = if y % 2 == 0 { (y / 2) * w } else { (half_h + y / 2) * w };
assert_eq!(&result[dst_off..dst_off + w], &planar[src_off..src_off + w]);
}
for y in 0..h / 2 {
let src_off = y_size + y * uv_w;
let dst_off = y_size
+ if y % 2 == 0 {
(y / 2) * uv_w
} else {
(half_h_uv + y / 2) * uv_w
};
assert_eq!(&result[dst_off..dst_off + uv_w], &planar[src_off..src_off + uv_w]);
}
let cr_base = y_size + c_size;
for y in 0..h / 2 {
let src_off = cr_base + y * uv_w;
let dst_off = cr_base
+ if y % 2 == 0 {
(y / 2) * uv_w
} else {
(half_h_uv + y / 2) * uv_w
};
assert_eq!(&result[dst_off..dst_off + uv_w], &planar[src_off..src_off + uv_w]);
}
}
#[test]
fn neutral_chroma_always_128() {
for v in [0i32, 16, 64, 128, 192, 235, 255] {
assert_eq!(bt601_cb(v, v, v), 128, "neutral Cb failed at gray={v}");
assert_eq!(bt601_cr(v, v, v), 128, "neutral Cr failed at gray={v}");
}
}
#[test]
fn interlace_2bpp_odd_height() {
let w = 2;
let h = 3;
let row_stride = w * 2;
let mut planar = Vec::with_capacity(row_stride * h);
for row in 0..h {
for col in 0..row_stride {
planar.push(u8::try_from(row * 16 + col).unwrap());
}
}
let result = interlace_fields(
&planar,
i32::try_from(w).unwrap(),
i32::try_from(h).unwrap(),
Encoding::Yuv422,
);
assert_eq!(result.len(), planar.len());
let half = h.div_ceil(2); for y in 0..h {
let src_off = y * row_stride;
let expected_dst = if y % 2 == 0 {
(y / 2) * row_stride
} else {
(half + y / 2) * row_stride
};
assert_eq!(
&result[expected_dst..expected_dst + row_stride],
&planar[src_off..src_off + row_stride]
);
}
}
}