const SCALEBITS: i32 = 16;
const ONE_HALF: i32 = 1 << (SCALEBITS - 1);
const CBCR_OFFSET: i32 = 128 << SCALEBITS;
const FIX_0_299: i32 = 19595; const FIX_0_587: i32 = 38470; const FIX_0_114: i32 = 7471; const FIX_0_16874: i32 = 11059; const FIX_0_33126: i32 = 21709; const FIX_0_500: i32 = 32768; const FIX_0_41869: i32 = 27439; const FIX_0_08131: i32 = 5329;
#[inline(always)]
fn rgb_to_ycbcr(r: i32, g: i32, b: i32) -> (u8, u8, u8) {
let y_val = ((FIX_0_299 * r + FIX_0_587 * g + FIX_0_114 * b + ONE_HALF) >> SCALEBITS) as u8;
let cb_val = ((-FIX_0_16874 * r - FIX_0_33126 * g + FIX_0_500 * b + CBCR_OFFSET + ONE_HALF - 1)
>> SCALEBITS) as u8;
let cr_val = ((FIX_0_500 * r - FIX_0_41869 * g - FIX_0_08131 * b + CBCR_OFFSET + ONE_HALF - 1)
>> SCALEBITS) as u8;
(y_val, cb_val, cr_val)
}
pub fn rgb_to_ycbcr_row(rgb: &[u8], y: &mut [u8], cb: &mut [u8], cr: &mut [u8], width: usize) {
for i in 0..width {
let r = rgb[i * 3] as i32;
let g = rgb[i * 3 + 1] as i32;
let b = rgb[i * 3 + 2] as i32;
let (y_val, cb_val, cr_val) = rgb_to_ycbcr(r, g, b);
y[i] = y_val;
cb[i] = cb_val;
cr[i] = cr_val;
}
}
pub fn rgba_to_ycbcr_row(rgba: &[u8], y: &mut [u8], cb: &mut [u8], cr: &mut [u8], width: usize) {
for i in 0..width {
let r = rgba[i * 4] as i32;
let g = rgba[i * 4 + 1] as i32;
let b = rgba[i * 4 + 2] as i32;
let (y_val, cb_val, cr_val) = rgb_to_ycbcr(r, g, b);
y[i] = y_val;
cb[i] = cb_val;
cr[i] = cr_val;
}
}
#[allow(clippy::too_many_arguments)]
pub fn generic_to_ycbcr_row(
pixels: &[u8],
y: &mut [u8],
cb: &mut [u8],
cr: &mut [u8],
width: usize,
bpp: usize,
r_off: usize,
g_off: usize,
b_off: usize,
) {
for i in 0..width {
let base: usize = i * bpp;
let r = pixels[base + r_off] as i32;
let g = pixels[base + g_off] as i32;
let b = pixels[base + b_off] as i32;
let (y_val, cb_val, cr_val) = rgb_to_ycbcr(r, g, b);
y[i] = y_val;
cb[i] = cb_val;
cr[i] = cr_val;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pure_white_rgb() {
let rgb = [255u8, 255, 255];
let mut y = [0u8; 1];
let mut cb = [0u8; 1];
let mut cr = [0u8; 1];
rgb_to_ycbcr_row(&rgb, &mut y, &mut cb, &mut cr, 1);
assert_eq!(y[0], 255);
assert_eq!(cb[0], 128);
assert_eq!(cr[0], 128);
}
#[test]
fn pure_black_rgb() {
let rgb = [0u8, 0, 0];
let mut y = [0u8; 1];
let mut cb = [0u8; 1];
let mut cr = [0u8; 1];
rgb_to_ycbcr_row(&rgb, &mut y, &mut cb, &mut cr, 1);
assert_eq!(y[0], 0);
assert_eq!(cb[0], 128);
assert_eq!(cr[0], 128);
}
#[test]
fn pure_red_rgb() {
let rgb = [255u8, 0, 0];
let mut y = [0u8; 1];
let mut cb = [0u8; 1];
let mut cr = [0u8; 1];
rgb_to_ycbcr_row(&rgb, &mut y, &mut cb, &mut cr, 1);
assert_eq!(y[0], 76);
assert!(cb[0] == 84 || cb[0] == 85);
assert_eq!(cr[0], 255);
}
#[test]
fn pure_green_rgb() {
let rgb = [0u8, 255, 0];
let mut y = [0u8; 1];
let mut cb = [0u8; 1];
let mut cr = [0u8; 1];
rgb_to_ycbcr_row(&rgb, &mut y, &mut cb, &mut cr, 1);
assert!(y[0] == 149 || y[0] == 150);
}
#[test]
fn rgba_ignores_alpha() {
let rgba = [100u8, 150, 200, 255];
let mut y1 = [0u8; 1];
let mut cb1 = [0u8; 1];
let mut cr1 = [0u8; 1];
rgba_to_ycbcr_row(&rgba, &mut y1, &mut cb1, &mut cr1, 1);
let rgba2 = [100u8, 150, 200, 0];
let mut y2 = [0u8; 1];
let mut cb2 = [0u8; 1];
let mut cr2 = [0u8; 1];
rgba_to_ycbcr_row(&rgba2, &mut y2, &mut cb2, &mut cr2, 1);
assert_eq!(y1[0], y2[0]);
assert_eq!(cb1[0], cb2[0]);
assert_eq!(cr1[0], cr2[0]);
}
#[test]
fn rgb_multiple_pixels() {
let rgb = [255u8, 0, 0, 0, 255, 0, 0, 0, 255];
let mut y = [0u8; 3];
let mut cb = [0u8; 3];
let mut cr = [0u8; 3];
rgb_to_ycbcr_row(&rgb, &mut y, &mut cb, &mut cr, 3);
assert_eq!(y[0], 76); assert!(y[1] == 149 || y[1] == 150); assert_eq!(y[2], 29); }
}