use super::*;
use super::kernel::Kernel;
+const DEFAULT_YUV: usize = 4;
+
const YUV_PARAMS: &[[f32; 2]] = &[
[ 0.333, 0.333 ], // RGB
[ 0.2126, 0.0722 ], // ITU-R BT709
[ 0.2627, 0.0593 ], // ITU-R BT2020
];
+fn parse_yuv_mat(name: &str) -> usize {
+ match name {
+ "rgb" => 0,
+ "bt709" => 1,
+ "bt601" => 4,
+ "bt470" => 5,
+ "smpte170m" => 6,
+ "smpte240m" => 7,
+ "ycocg" => 8,
+ "bt2020" => 9,
+ _ => 2,
+ }
+}
+
+/*fn get_yuv_mat(id: usize) -> &'static str {
+ match id {
+ 1 => "bt709",
+ 4 => "bt601",
+ 5 => "bt470",
+ 6 => "smpte170m",
+ 7 => "smpte240m",
+ 8 => "ycocg",
+ 9 => "bt2020",
+ _ => "rgb",
+ }
+}*/
+
const BT_PAL_COEFFS: [f32; 2] = [ 0.493, 0.877 ];
const SMPTE_NTSC_COEFFS: &[f32; 4] = &[ -0.268, 0.7358, 0.4127, 0.4778 ];
#[derive(Default)]
struct RgbToYuv {
matrix: [[f32; 3]; 3],
+ mode: usize,
}
impl RgbToYuv {
fn new() -> Self { Self::default() }
}
+#[allow(clippy::many_single_char_names)]
impl Kernel for RgbToYuv {
- fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo) -> ScaleResult<NABufferType> {
+ fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo, options: &[(String, String)]) -> ScaleResult<NABufferType> {
+ let mut debug = false;
+ let mut mode = DEFAULT_YUV;
+ for (name, value) in options.iter() {
+ match (name.as_str(), value.as_str()) {
+ ("debug", "") => { debug = true; },
+ ("debug", "true") => { debug = true; },
+ ("rgb2yuv.mode", ymode) => {
+ mode = parse_yuv_mat(ymode);
+ },
+ _ => {},
+ }
+ }
+ self.mode = mode;
+
let mut df = dest_fmt.fmt;
-//todo coeff selection
- make_rgb2yuv(YUV_PARAMS[2][0], YUV_PARAMS[2][1], &mut self.matrix);
+ make_rgb2yuv(YUV_PARAMS[mode][0], YUV_PARAMS[mode][1], &mut self.matrix);
if let ColorModel::YUV(yuvsm) = df.get_model() {
match yuvsm {
YUVSubmodel::YCbCr => {},
chr.v_ss = 0;
}
}
-println!(" [intermediate format {}]", df);
+ if debug {
+ println!(" [intermediate format {}]", df);
+ }
let res = alloc_video_buffer(NAVideoInfo::new(in_fmt.width, in_fmt.height, false, df), 3);
if res.is_err() { return Err(ScaleError::AllocError); }
Ok(res.unwrap())
}
fn process(&mut self, pic_in: &NABufferType, pic_out: &mut NABufferType) {
if let (Some(ref sbuf), Some(ref mut dbuf)) = (pic_in.get_vbuf(), pic_out.get_vbuf()) {
+ if dbuf.get_info().get_format().get_num_comp() < 3 {
+ return self.process_grayscale(sbuf, dbuf);
+ }
let istrides = [sbuf.get_stride(0), sbuf.get_stride(1), sbuf.get_stride(2)];
let dstrides = [dbuf.get_stride(0), dbuf.get_stride(1), dbuf.get_stride(2)];
let (w, h) = sbuf.get_dimensions(0);
let dst = dbuf.get_data_mut().unwrap();
for _y in 0..h {
for x in 0..w {
- let r = src[roff + x] as f32;
- let g = src[goff + x] as f32;
- let b = src[boff + x] as f32;
+ let r = f32::from(src[roff + x]);
+ let g = f32::from(src[goff + x]);
+ let b = f32::from(src[boff + x]);
let (y, u, v) = matrix_mul(&self.matrix, r, g, b);
dst[yoff + x] = (y as i16).max(0).min(255) as u8;
- dst[uoff + x] = ((u as i16).max(-128).min(128) + 128) as u8;
- dst[voff + x] = ((v as i16).max(-128).min(128) + 128) as u8;
+ dst[uoff + x] = ((u as i16).max(-128).min(127) + 128) as u8;
+ dst[voff + x] = ((v as i16).max(-128).min(127) + 128) as u8;
}
roff += istrides[0];
goff += istrides[1];
}
}
-pub fn create_rgb2yuv() -> Box<Kernel> {
+impl RgbToYuv {
+ fn process_grayscale(&self, sbuf: &NAVideoBuffer<u8>, dbuf: &mut NAVideoBuffer<u8>) {
+ let istrides = [sbuf.get_stride(0), sbuf.get_stride(1), sbuf.get_stride(2)];
+ let ystride = dbuf.get_stride(0);
+ let (w, h) = sbuf.get_dimensions(0);
+
+ let mut roff = sbuf.get_offset(0);
+ let mut goff = sbuf.get_offset(1);
+ let mut boff = sbuf.get_offset(2);
+ let mut yoff = dbuf.get_offset(0);
+ let src = sbuf.get_data();
+ let dst = dbuf.get_data_mut().unwrap();
+ for _y in 0..h {
+ for x in 0..w {
+ let r = f32::from(src[roff + x]);
+ let g = f32::from(src[goff + x]);
+ let b = f32::from(src[boff + x]);
+ let (y, _u, _v) = matrix_mul(&self.matrix, r, g, b);
+
+ dst[yoff + x] = (y as i16).max(0).min(255) as u8;
+ }
+ roff += istrides[0];
+ goff += istrides[1];
+ boff += istrides[2];
+ yoff += ystride;
+ }
+ }
+}
+
+pub fn create_rgb2yuv() -> Box<dyn Kernel> {
Box::new(RgbToYuv::new())
}
#[derive(Default)]
struct YuvToRgb {
matrix: [[f32; 3]; 3],
+ mode: usize,
+ yscale: Vec<i16>,
+ r_chr: Vec<i16>,
+ g_u: Vec<i16>,
+ g_v: Vec<i16>,
+ b_chr: Vec<i16>,
}
impl YuvToRgb {
fn new() -> Self { Self::default() }
}
+#[allow(clippy::many_single_char_names)]
impl Kernel for YuvToRgb {
- fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo) -> ScaleResult<NABufferType> {
+ fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo, options: &[(String, String)]) -> ScaleResult<NABufferType> {
+ let mut debug = false;
+ let mut mode = DEFAULT_YUV;
+ for (name, value) in options.iter() {
+ match (name.as_str(), value.as_str()) {
+ ("debug", "") => { debug = true; },
+ ("debug", "true") => { debug = true; },
+ ("yuv2rgb.mode", ymode) => {
+ mode = parse_yuv_mat(ymode);
+ },
+ _ => {},
+ }
+ }
+ self.mode = mode;
+
let mut df = dest_fmt.fmt;
-//todo coeff selection
- make_yuv2rgb(YUV_PARAMS[2][0], YUV_PARAMS[2][1], &mut self.matrix);
+ df.palette = false;
+ if !df.is_unpacked() || df.get_max_depth() != 8 || df.get_total_depth() != df.get_num_comp() as u8 * 8 {
+ df = NAPixelFormaton {
+ model: ColorModel::RGB(RGBSubmodel::RGB), components: 3,
+ comp_info: [
+ Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: false, depth: 8, shift: 0, comp_offs: 0, next_elem: 1 }),
+ Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: false, depth: 8, shift: 0, comp_offs: 1, next_elem: 1 }),
+ Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: false, depth: 8, shift: 0, comp_offs: 2, next_elem: 1 }),
+ None, None],
+ elem_size: 3, be: false, alpha: false, palette: false };
+ if in_fmt.fmt.alpha && dest_fmt.fmt.alpha {
+ df.alpha = true;
+ df.components = 4;
+ df.comp_info[3] = Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: false, depth: 8, shift: 0, comp_offs: 3, next_elem: 1 });
+ }
+ }
+ make_yuv2rgb(YUV_PARAMS[mode][0], YUV_PARAMS[mode][1], &mut self.matrix);
if let ColorModel::YUV(yuvsm) = in_fmt.fmt.get_model() {
match yuvsm {
YUVSubmodel::YCbCr => {},
YUVSubmodel::YIQ => {
- make_rgb2yuv(YUV_PARAMS[2][0], YUV_PARAMS[2][1], &mut self.matrix);
+ make_rgb2yuv(YUV_PARAMS[DEFAULT_YUV][0], YUV_PARAMS[DEFAULT_YUV][1], &mut self.matrix);
apply_ntsc_rgb2yiq(SMPTE_NTSC_COEFFS, &mut self.matrix);
invert_matrix(&mut self.matrix);
},
apply_pal_yuv2rgb(BT_PAL_COEFFS[0], BT_PAL_COEFFS[1], &mut self.matrix);
},
};
+ if yuvsm != YUVSubmodel::YIQ {
+ self.yscale = Vec::with_capacity(256);
+ self.r_chr = Vec::with_capacity(256);
+ self.g_u = Vec::with_capacity(256);
+ self.g_v = Vec::with_capacity(256);
+ self.b_chr = Vec::with_capacity(256);
+ for i in 0..256 {
+ let yval = i as i16; // todo limited range as well
+ self.yscale.push(yval);
+ let rval = (((i as f32) - 128.0) * self.matrix[0][2]) as i16;
+ self.r_chr.push(rval);
+ let uval = (((i as f32) - 128.0) * self.matrix[1][1]) as i16;
+ self.g_u.push(uval);
+ let vval = (((i as f32) - 128.0) * self.matrix[1][2]) as i16;
+ self.g_v.push(vval);
+ let bval = (((i as f32) - 128.0) * self.matrix[2][1]) as i16;
+ self.b_chr.push(bval);
+ }
+ }
} else {
return Err(ScaleError::InvalidArgument);
}
chr.comp_offs = i as u8;
}
}
-println!(" [intermediate format {}]", df);
+ if debug {
+ println!(" [intermediate format {}]", df);
+ }
let res = alloc_video_buffer(NAVideoInfo::new(in_fmt.width, in_fmt.height, false, df), 3);
if res.is_err() { return Err(ScaleError::AllocError); }
Ok(res.unwrap())
let istrides = [sbuf.get_stride(0), sbuf.get_stride(1), sbuf.get_stride(2)];
let dstrides = [dbuf.get_stride(0), dbuf.get_stride(1), dbuf.get_stride(2)];
let (w, h) = sbuf.get_dimensions(0);
+ if sbuf.get_info().get_format().get_num_comp() < 3 {
+ return self.process_grayscale(sbuf, dbuf);
+ }
let (sv0, sh0) = sbuf.get_info().get_format().get_chromaton(1).unwrap().get_subsampling();
let (sv1, sh1) = sbuf.get_info().get_format().get_chromaton(2).unwrap().get_subsampling();
let mut voff = sbuf.get_offset(2);
let src = sbuf.get_data();
let dst = dbuf.get_data_mut().unwrap();
+ if !self.yscale.is_empty() {
+ for y in 0..h {
+ for x in 0..w {
+ let y = self.yscale[src[yoff + x] as usize];
+ let u = src[uoff + (x >> sv0)] as usize;
+ let v = src[voff + (x >> sv1)] as usize;
+ let r = y + self.r_chr[v];
+ let g = y + self.g_u[u] + self.g_v[v];
+ let b = y + self.b_chr[u];
+ dst[roff + x] = r.max(0).min(255) as u8;
+ dst[goff + x] = g.max(0).min(255) as u8;
+ dst[boff + x] = b.max(0).min(255) as u8;
+ }
+ roff += dstrides[0];
+ goff += dstrides[1];
+ boff += dstrides[2];
+ yoff += istrides[0];
+ if (y & uhmask) == uhmask {
+ uoff += istrides[1];
+ }
+ if (y & vhmask) == vhmask {
+ voff += istrides[2];
+ }
+ }
+ return;
+ }
for y in 0..h {
for x in 0..w {
- let y = src[yoff + x] as f32;
- let u = ((src[uoff + (x >> sv0)] as i16) - 128) as f32;
- let v = ((src[voff + (x >> sv1)] as i16) - 128) as f32;
+ let y = f32::from(src[yoff + x]);
+ let u = f32::from(i16::from(src[uoff + (x >> sv0)]) - 128);
+ let v = f32::from(i16::from(src[voff + (x >> sv1)]) - 128);
let (r, g, b) = matrix_mul(&self.matrix, y, u, v);
dst[roff + x] = (r as i16).max(0).min(255) as u8;
}
}
-pub fn create_yuv2rgb() -> Box<Kernel> {
+impl YuvToRgb {
+ fn process_grayscale(&self, sbuf: &NAVideoBuffer<u8>, dbuf: &mut NAVideoBuffer<u8>) {
+ let ystride = sbuf.get_stride(0);
+ let dstrides = [dbuf.get_stride(0), dbuf.get_stride(1), dbuf.get_stride(2)];
+ let (w, h) = sbuf.get_dimensions(0);
+ let mut roff = dbuf.get_offset(0);
+ let mut goff = dbuf.get_offset(1);
+ let mut boff = dbuf.get_offset(2);
+ let mut yoff = sbuf.get_offset(0);
+ let src = sbuf.get_data();
+ let dst = dbuf.get_data_mut().unwrap();
+ if !self.yscale.is_empty() {
+ for _y in 0..h {
+ for x in 0..w {
+ let y = self.yscale[src[yoff + x] as usize];
+ let r = y + self.r_chr[128];
+ let g = y + self.g_u[128] + self.g_v[128];
+ let b = y + self.b_chr[128];
+ dst[roff + x] = r.max(0).min(255) as u8;
+ dst[goff + x] = g.max(0).min(255) as u8;
+ dst[boff + x] = b.max(0).min(255) as u8;
+ }
+ roff += dstrides[0];
+ goff += dstrides[1];
+ boff += dstrides[2];
+ yoff += ystride;
+ }
+ } else {
+ for _y in 0..h {
+ for x in 0..w {
+ let y = f32::from(src[yoff + x]);
+ let (r, g, b) = matrix_mul(&self.matrix, y, 0.0, 0.0);
+ dst[roff + x] = (r as i16).max(0).min(255) as u8;
+ dst[goff + x] = (g as i16).max(0).min(255) as u8;
+ dst[boff + x] = (b as i16).max(0).min(255) as u8;
+ }
+ roff += dstrides[0];
+ goff += dstrides[1];
+ boff += dstrides[2];
+ yoff += ystride;
+ }
+ }
+ }
+}
+
+pub fn create_yuv2rgb() -> Box<dyn Kernel> {
Box::new(YuvToRgb::new())
}