X-Git-Url: https://git.nihav.org/?p=nihav.git;a=blobdiff_plain;f=nihav-core%2Fsrc%2Fscale%2Fcolorcvt.rs;h=00ae2a56203b38e2c9ac315be0e1696edd341226;hp=2e2f299fbadcf8e67c7af0f4d38e30374175a57b;hb=25e0bf9a974b1bb5a8f8b38e23b4a7e91db3eab0;hpb=03accf760cf79bc56d7c6dc6e82cd885fb7e1e13 diff --git a/nihav-core/src/scale/colorcvt.rs b/nihav-core/src/scale/colorcvt.rs index 2e2f299..00ae2a5 100644 --- a/nihav-core/src/scale/colorcvt.rs +++ b/nihav-core/src/scale/colorcvt.rs @@ -1,6 +1,8 @@ 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 @@ -15,6 +17,33 @@ const YUV_PARAMS: &[[f32; 2]] = &[ [ 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 ]; @@ -161,17 +190,32 @@ fn matrix_mul(mat: &[[f32; 3]; 3], a: f32, b: f32, c: f32) -> (f32, f32, f32) { #[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 { + fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo, options: &[(String, String)]) -> ScaleResult { + 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 => {}, @@ -189,13 +233,18 @@ impl Kernel for RgbToYuv { 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); @@ -210,9 +259,9 @@ println!(" [intermediate format {}]", df); 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; @@ -230,29 +279,94 @@ println!(" [intermediate format {}]", df); } } -pub fn create_rgb2yuv() -> Box { +impl RgbToYuv { + fn process_grayscale(&self, sbuf: &NAVideoBuffer, dbuf: &mut NAVideoBuffer) { + 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 { Box::new(RgbToYuv::new()) } #[derive(Default)] struct YuvToRgb { matrix: [[f32; 3]; 3], + mode: usize, + yscale: Vec, + r_chr: Vec, + g_u: Vec, + g_v: Vec, + b_chr: Vec, } 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 { + fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo, options: &[(String, String)]) -> ScaleResult { + 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); }, @@ -260,6 +374,25 @@ impl Kernel for YuvToRgb { 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); } @@ -269,7 +402,9 @@ impl Kernel for YuvToRgb { 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()) @@ -279,6 +414,9 @@ println!(" [intermediate format {}]", df); 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(); @@ -292,11 +430,37 @@ println!(" [intermediate format {}]", df); 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; @@ -318,6 +482,51 @@ println!(" [intermediate format {}]", df); } } -pub fn create_yuv2rgb() -> Box { +impl YuvToRgb { + fn process_grayscale(&self, sbuf: &NAVideoBuffer, dbuf: &mut NAVideoBuffer) { + 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 { Box::new(YuvToRgb::new()) }