const FIX: i32 = 16;
const HALF: i32 = 1 << (FIX - 1);
const CR_TO_R: i32 = 91881; const CB_TO_G: i32 = -22554; const CR_TO_G: i32 = -46802; const CB_TO_B: i32 = 116130;
#[inline]
fn clamp_u8(v: i32) -> u8 {
v.clamp(0, 255) as u8
}
#[inline]
fn ycbcr_to_rgb_pixel(y: u8, cb: u8, cr: u8) -> (u8, u8, u8) {
let y = y as i32;
let cb = cb as i32 - 128;
let cr = cr as i32 - 128;
let r = y + ((cr * CR_TO_R + HALF) >> FIX);
let g = y + ((cb * CB_TO_G + cr * CR_TO_G + HALF) >> FIX);
let b = y + ((cb * CB_TO_B + HALF) >> FIX);
(clamp_u8(r), clamp_u8(g), clamp_u8(b))
}
pub fn ycbcr_to_rgb(y_row: &[u8], cb_row: &[u8], cr_row: &[u8], output: &mut [u8], width: usize) {
for i in 0..width {
let (r, g, b) = ycbcr_to_rgb_pixel(y_row[i], cb_row[i], cr_row[i]);
output[i * 3] = r;
output[i * 3 + 1] = g;
output[i * 3 + 2] = b;
}
}
pub fn ycbcr_to_rgba(y_row: &[u8], cb_row: &[u8], cr_row: &[u8], output: &mut [u8], width: usize) {
for i in 0..width {
let (r, g, b) = ycbcr_to_rgb_pixel(y_row[i], cb_row[i], cr_row[i]);
output[i * 4] = r;
output[i * 4 + 1] = g;
output[i * 4 + 2] = b;
output[i * 4 + 3] = 255;
}
}
pub fn ycbcr_to_bgra(y_row: &[u8], cb_row: &[u8], cr_row: &[u8], output: &mut [u8], width: usize) {
for i in 0..width {
let (r, g, b) = ycbcr_to_rgb_pixel(y_row[i], cb_row[i], cr_row[i]);
output[i * 4] = b;
output[i * 4 + 1] = g;
output[i * 4 + 2] = r;
output[i * 4 + 3] = 255;
}
}
pub fn grey_copy(y_row: &[u8], output: &mut [u8], width: usize) {
output[..width].copy_from_slice(&y_row[..width]);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn white_pixel() {
let (r, g, b) = ycbcr_to_rgb_pixel(255, 128, 128);
assert_eq!((r, g, b), (255, 255, 255));
}
#[test]
fn black_pixel() {
let (r, g, b) = ycbcr_to_rgb_pixel(0, 128, 128);
assert_eq!((r, g, b), (0, 0, 0));
}
#[test]
fn red_pixel() {
let (r, g, b) = ycbcr_to_rgb_pixel(76, 85, 255);
assert!((r as i32 - 255).abs() <= 2, "r={r}");
assert!((g as i32).abs() <= 2, "g={g}");
assert!((b as i32).abs() <= 2, "b={b}");
}
#[test]
fn clamping() {
let (r, g, b) = ycbcr_to_rgb_pixel(255, 0, 255);
let _ = (r, g, b);
}
#[test]
fn grey_copy_works() {
let y = [10, 20, 30, 40];
let mut out = [0u8; 4];
grey_copy(&y, &mut out, 4);
assert_eq!(out, [10, 20, 30, 40]);
}
#[test]
fn rgb_row_conversion() {
let y = [128, 128];
let cb = [128, 128];
let cr = [128, 128];
let mut out = [0u8; 6];
ycbcr_to_rgb(&y, &cb, &cr, &mut out, 2);
assert_eq!(out, [128, 128, 128, 128, 128, 128]);
}
}