2 use super::kernel::Kernel;
4 const DEFAULT_YUV: usize = 4;
6 const YUV_PARAMS: &[[f32; 2]] = &[
7 [ 0.333, 0.333 ], // RGB
8 [ 0.2126, 0.0722 ], // ITU-R BT709
9 [ 0.333, 0.333 ], // unspecified
10 [ 0.333, 0.333 ], // reserved
11 [ 0.299, 0.114 ], // ITU-R BT601
12 [ 0.299, 0.114 ], // ITU-R BT470
13 [ 0.299, 0.114 ], // SMPTE 170M
14 [ 0.212, 0.087 ], // SMPTE 240M
15 [ 0.333, 0.333 ], // YCoCg
16 [ 0.2627, 0.0593 ], // ITU-R BT2020
17 [ 0.2627, 0.0593 ], // ITU-R BT2020
20 fn parse_yuv_mat(name: &str) -> usize {
34 /*fn get_yuv_mat(id: usize) -> &'static str {
47 const BT_PAL_COEFFS: [f32; 2] = [ 0.493, 0.877 ];
49 const SMPTE_NTSC_COEFFS: &[f32; 4] = &[ -0.268, 0.7358, 0.4127, 0.4778 ];
51 /*const RGB2YCOCG: [[f32; 3]; 3] = [
53 [ -0.25, 0.5, -0.25 ],
56 const YCOCG2RGB: [[f32; 3]; 3] = [
62 const XYZ2RGB: [[f32; 3]; 3] = [
64 [ 0.17697, 0.8124, 0.01063 ],
67 const RGB2XYZ: [[f32; 3]; 3] = [
68 [ 2.364613, -0.89654, -0.46807 ],
69 [ -0.515167, 1.42641, 0.08876 ],
70 [ 0.0052, -0.01441, 1.00920 ]
73 fn make_rgb2yuv(kr: f32, kb: f32, mat: &mut [[f32; 3]; 3]) {
76 mat[0][1] = 1.0 - kr - kb;
79 mat[1][0] = -mat[0][0] * 0.5 / (1.0 - kb);
80 mat[1][1] = -mat[0][1] * 0.5 / (1.0 - kb);
84 mat[2][1] = -mat[0][1] * 0.5 / (1.0 - kr);
85 mat[2][2] = -mat[0][2] * 0.5 / (1.0 - kr);
88 fn make_yuv2rgb(kr: f32, kb: f32, mat: &mut [[f32; 3]; 3]) {
89 let kg = 1.0 - kr - kb;
94 mat[0][2] = 2.0 * (1.0 - kr);
97 mat[1][1] = -kb * 2.0 * (1.0 - kb) / kg;
98 mat[1][2] = -kr * 2.0 * (1.0 - kr) / kg;
101 mat[2][1] = 2.0 * (1.0 - kb);
105 fn apply_pal_rgb2yuv(eu: f32, ev: f32, mat: &mut [[f32; 3]; 3]) {
106 let ufac = 2.0 * (1.0 - mat[0][2]) * eu;
107 let vfac = 2.0 * (1.0 - mat[0][0]) * ev;
112 mat[1][2] = eu * (1.0 - mat[0][2]);
114 mat[2][0] = ev * (1.0 - mat[0][0]);
119 fn apply_pal_yuv2rgb(eu: f32, ev: f32, mat: &mut [[f32; 3]; 3]) {
120 let ufac = 1.0 / (mat[2][1] * eu);
121 let vfac = 1.0 / (mat[0][2] * ev);
132 fn apply_ntsc_rgb2yiq(params: &[f32; 4], mat: &mut [[f32; 3]; 3]) {
133 let ufac = 2.0 * (1.0 - mat[0][2]);
134 let vfac = 2.0 * (1.0 - mat[0][0]);
135 let mut tmp: [[f32; 3]; 2] = [[0.0; 3]; 2];
138 tmp[0][i] = mat[1][i] * ufac;
139 tmp[1][i] = mat[2][i] * vfac;
142 mat[1][i] = params[0] * tmp[0][i] + params[1] * tmp[1][i];
143 mat[2][i] = params[2] * tmp[0][i] + params[3] * tmp[1][i];
147 fn subm_det(mat: &[[f32; 3]; 3], col: usize, row: usize) -> f32 {
148 let row0 = if row == 0 { 1 } else { 0 };
149 let row1 = if (row == 1) || (row0 == 1) { 2 } else { 1 };
150 let col0 = if col == 0 { 1 } else { 0 };
151 let col1 = if (col == 1) || (col0 == 1) { 2 } else { 1 };
153 let det = mat[row0][col0] * mat[row1][col1] - mat[row0][col1] * mat[row1][col0];
154 if ((col ^ row) & 1) == 0 {
161 fn invert_matrix(mat: &mut [[f32; 3]; 3]) {
162 let d00 = subm_det(mat, 0, 0);
163 let d01 = subm_det(mat, 0, 1);
164 let d02 = subm_det(mat, 0, 2);
165 let d10 = subm_det(mat, 1, 0);
166 let d11 = subm_det(mat, 1, 1);
167 let d12 = subm_det(mat, 1, 2);
168 let d20 = subm_det(mat, 2, 0);
169 let d21 = subm_det(mat, 2, 1);
170 let d22 = subm_det(mat, 2, 2);
171 let det = 1.0 / (mat[0][0] * d00 + mat[0][1] * d10 + mat[0][2] * d20).abs();
173 mat[0][0] = det * d00;
174 mat[0][1] = det * d01;
175 mat[0][2] = det * d02;
176 mat[1][0] = det * d10;
177 mat[1][1] = det * d11;
178 mat[1][2] = det * d12;
179 mat[2][0] = det * d20;
180 mat[2][1] = det * d21;
181 mat[2][2] = det * d22;
184 fn matrix_mul(mat: &[[f32; 3]; 3], a: f32, b: f32, c: f32) -> (f32, f32, f32) {
185 (a * mat[0][0] + b * mat[0][1] + c * mat[0][2],
186 a * mat[1][0] + b * mat[1][1] + c * mat[1][2],
187 a * mat[2][0] + b * mat[2][1] + c * mat[2][2] )
192 matrix: [[f32; 3]; 3],
197 fn new() -> Self { Self::default() }
200 #[allow(clippy::many_single_char_names)]
201 impl Kernel for RgbToYuv {
202 fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo, options: &[(String, String)]) -> ScaleResult<NABufferType> {
203 let mut debug = false;
204 let mut mode = DEFAULT_YUV;
205 for (name, value) in options.iter() {
206 match (name.as_str(), value.as_str()) {
207 ("debug", "") => { debug = true; },
208 ("debug", "true") => { debug = true; },
209 ("rgb2yuv.mode", ymode) => {
210 mode = parse_yuv_mat(ymode);
217 let mut df = dest_fmt.fmt;
218 make_rgb2yuv(YUV_PARAMS[mode][0], YUV_PARAMS[mode][1], &mut self.matrix);
219 if let ColorModel::YUV(yuvsm) = df.get_model() {
221 YUVSubmodel::YCbCr => {},
222 YUVSubmodel::YIQ => { apply_ntsc_rgb2yiq(SMPTE_NTSC_COEFFS, &mut self.matrix); },
223 YUVSubmodel::YUVJ => { apply_pal_rgb2yuv(BT_PAL_COEFFS[0], BT_PAL_COEFFS[1], &mut self.matrix); },
226 return Err(ScaleError::InvalidArgument);
228 for i in 0..MAX_CHROMATONS {
229 if let Some(ref mut chr) = df.comp_info[i] {
231 chr.comp_offs = i as u8;
237 println!(" [intermediate format {}]", df);
239 let res = alloc_video_buffer(NAVideoInfo::new(in_fmt.width, in_fmt.height, false, df), 3);
240 if res.is_err() { return Err(ScaleError::AllocError); }
243 fn process(&mut self, pic_in: &NABufferType, pic_out: &mut NABufferType) {
244 if let (Some(ref sbuf), Some(ref mut dbuf)) = (pic_in.get_vbuf(), pic_out.get_vbuf()) {
245 if dbuf.get_info().get_format().get_num_comp() < 3 {
246 return self.process_grayscale(sbuf, dbuf);
248 let istrides = [sbuf.get_stride(0), sbuf.get_stride(1), sbuf.get_stride(2)];
249 let dstrides = [dbuf.get_stride(0), dbuf.get_stride(1), dbuf.get_stride(2)];
250 let (w, h) = sbuf.get_dimensions(0);
252 let mut roff = sbuf.get_offset(0);
253 let mut goff = sbuf.get_offset(1);
254 let mut boff = sbuf.get_offset(2);
255 let mut yoff = dbuf.get_offset(0);
256 let mut uoff = dbuf.get_offset(1);
257 let mut voff = dbuf.get_offset(2);
258 let src = sbuf.get_data();
259 let dst = dbuf.get_data_mut().unwrap();
262 let r = f32::from(src[roff + x]);
263 let g = f32::from(src[goff + x]);
264 let b = f32::from(src[boff + x]);
265 let (y, u, v) = matrix_mul(&self.matrix, r, g, b);
267 dst[yoff + x] = (y as i16).max(0).min(255) as u8;
268 dst[uoff + x] = ((u as i16).max(-128).min(128) + 128) as u8;
269 dst[voff + x] = ((v as i16).max(-128).min(128) + 128) as u8;
283 fn process_grayscale(&self, sbuf: &NAVideoBuffer<u8>, dbuf: &mut NAVideoBuffer<u8>) {
284 let istrides = [sbuf.get_stride(0), sbuf.get_stride(1), sbuf.get_stride(2)];
285 let ystride = dbuf.get_stride(0);
286 let (w, h) = sbuf.get_dimensions(0);
288 let mut roff = sbuf.get_offset(0);
289 let mut goff = sbuf.get_offset(1);
290 let mut boff = sbuf.get_offset(2);
291 let mut yoff = dbuf.get_offset(0);
292 let src = sbuf.get_data();
293 let dst = dbuf.get_data_mut().unwrap();
296 let r = f32::from(src[roff + x]);
297 let g = f32::from(src[goff + x]);
298 let b = f32::from(src[boff + x]);
299 let (y, _u, _v) = matrix_mul(&self.matrix, r, g, b);
301 dst[yoff + x] = (y as i16).max(0).min(255) as u8;
311 pub fn create_rgb2yuv() -> Box<dyn Kernel> {
312 Box::new(RgbToYuv::new())
317 matrix: [[f32; 3]; 3],
327 fn new() -> Self { Self::default() }
330 #[allow(clippy::many_single_char_names)]
331 impl Kernel for YuvToRgb {
332 fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo, options: &[(String, String)]) -> ScaleResult<NABufferType> {
333 let mut debug = false;
334 let mut mode = DEFAULT_YUV;
335 for (name, value) in options.iter() {
336 match (name.as_str(), value.as_str()) {
337 ("debug", "") => { debug = true; },
338 ("debug", "true") => { debug = true; },
339 ("yuv2rgb.mode", ymode) => {
340 mode = parse_yuv_mat(ymode);
347 let mut df = dest_fmt.fmt;
349 if !df.is_unpacked() || df.get_max_depth() != 8 || df.get_total_depth() != df.get_num_comp() as u8 * 8 {
350 df = NAPixelFormaton {
351 model: ColorModel::RGB(RGBSubmodel::RGB), components: 3,
353 Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: false, depth: 8, shift: 0, comp_offs: 0, next_elem: 1 }),
354 Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: false, depth: 8, shift: 0, comp_offs: 1, next_elem: 1 }),
355 Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: false, depth: 8, shift: 0, comp_offs: 2, next_elem: 1 }),
357 elem_size: 3, be: false, alpha: false, palette: false };
358 if in_fmt.fmt.alpha && dest_fmt.fmt.alpha {
361 df.comp_info[3] = Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: false, depth: 8, shift: 0, comp_offs: 3, next_elem: 1 });
364 make_yuv2rgb(YUV_PARAMS[mode][0], YUV_PARAMS[mode][1], &mut self.matrix);
365 if let ColorModel::YUV(yuvsm) = in_fmt.fmt.get_model() {
367 YUVSubmodel::YCbCr => {},
368 YUVSubmodel::YIQ => {
369 make_rgb2yuv(YUV_PARAMS[DEFAULT_YUV][0], YUV_PARAMS[DEFAULT_YUV][1], &mut self.matrix);
370 apply_ntsc_rgb2yiq(SMPTE_NTSC_COEFFS, &mut self.matrix);
371 invert_matrix(&mut self.matrix);
373 YUVSubmodel::YUVJ => {
374 apply_pal_yuv2rgb(BT_PAL_COEFFS[0], BT_PAL_COEFFS[1], &mut self.matrix);
377 if yuvsm != YUVSubmodel::YIQ {
378 self.yscale = Vec::with_capacity(256);
379 self.r_chr = Vec::with_capacity(256);
380 self.g_u = Vec::with_capacity(256);
381 self.g_v = Vec::with_capacity(256);
382 self.b_chr = Vec::with_capacity(256);
384 let yval = i as i16; // todo limited range as well
385 self.yscale.push(yval);
386 let rval = (((i as f32) - 128.0) * self.matrix[0][2]) as i16;
387 self.r_chr.push(rval);
388 let uval = (((i as f32) - 128.0) * self.matrix[1][1]) as i16;
390 let vval = (((i as f32) - 128.0) * self.matrix[1][2]) as i16;
392 let bval = (((i as f32) - 128.0) * self.matrix[2][1]) as i16;
393 self.b_chr.push(bval);
397 return Err(ScaleError::InvalidArgument);
399 for i in 0..MAX_CHROMATONS {
400 if let Some(ref mut chr) = df.comp_info[i] {
402 chr.comp_offs = i as u8;
406 println!(" [intermediate format {}]", df);
408 let res = alloc_video_buffer(NAVideoInfo::new(in_fmt.width, in_fmt.height, false, df), 3);
409 if res.is_err() { return Err(ScaleError::AllocError); }
412 fn process(&mut self, pic_in: &NABufferType, pic_out: &mut NABufferType) {
413 if let (Some(ref sbuf), Some(ref mut dbuf)) = (pic_in.get_vbuf(), pic_out.get_vbuf()) {
414 let istrides = [sbuf.get_stride(0), sbuf.get_stride(1), sbuf.get_stride(2)];
415 let dstrides = [dbuf.get_stride(0), dbuf.get_stride(1), dbuf.get_stride(2)];
416 let (w, h) = sbuf.get_dimensions(0);
417 if sbuf.get_info().get_format().get_num_comp() < 3 {
418 return self.process_grayscale(sbuf, dbuf);
420 let (sv0, sh0) = sbuf.get_info().get_format().get_chromaton(1).unwrap().get_subsampling();
421 let (sv1, sh1) = sbuf.get_info().get_format().get_chromaton(2).unwrap().get_subsampling();
423 let uhmask = (1 << sh0) - 1;
424 let vhmask = (1 << sh1) - 1;
425 let mut roff = dbuf.get_offset(0);
426 let mut goff = dbuf.get_offset(1);
427 let mut boff = dbuf.get_offset(2);
428 let mut yoff = sbuf.get_offset(0);
429 let mut uoff = sbuf.get_offset(1);
430 let mut voff = sbuf.get_offset(2);
431 let src = sbuf.get_data();
432 let dst = dbuf.get_data_mut().unwrap();
433 if !self.yscale.is_empty() {
436 let y = self.yscale[src[yoff + x] as usize];
437 let u = src[uoff + (x >> sv0)] as usize;
438 let v = src[voff + (x >> sv1)] as usize;
439 let r = y + self.r_chr[v];
440 let g = y + self.g_u[u] + self.g_v[v];
441 let b = y + self.b_chr[u];
442 dst[roff + x] = r.max(0).min(255) as u8;
443 dst[goff + x] = g.max(0).min(255) as u8;
444 dst[boff + x] = b.max(0).min(255) as u8;
450 if (y & uhmask) == uhmask {
453 if (y & vhmask) == vhmask {
461 let y = f32::from(src[yoff + x]);
462 let u = f32::from(i16::from(src[uoff + (x >> sv0)]) - 128);
463 let v = f32::from(i16::from(src[voff + (x >> sv1)]) - 128);
465 let (r, g, b) = matrix_mul(&self.matrix, y, u, v);
466 dst[roff + x] = (r as i16).max(0).min(255) as u8;
467 dst[goff + x] = (g as i16).max(0).min(255) as u8;
468 dst[boff + x] = (b as i16).max(0).min(255) as u8;
474 if (y & uhmask) == uhmask {
477 if (y & vhmask) == vhmask {
486 fn process_grayscale(&self, sbuf: &NAVideoBuffer<u8>, dbuf: &mut NAVideoBuffer<u8>) {
487 let ystride = sbuf.get_stride(0);
488 let dstrides = [dbuf.get_stride(0), dbuf.get_stride(1), dbuf.get_stride(2)];
489 let (w, h) = sbuf.get_dimensions(0);
490 let mut roff = dbuf.get_offset(0);
491 let mut goff = dbuf.get_offset(1);
492 let mut boff = dbuf.get_offset(2);
493 let mut yoff = sbuf.get_offset(0);
494 let src = sbuf.get_data();
495 let dst = dbuf.get_data_mut().unwrap();
496 if !self.yscale.is_empty() {
499 let y = self.yscale[src[yoff + x] as usize];
500 let r = y + self.r_chr[128];
501 let g = y + self.g_u[128] + self.g_v[128];
502 let b = y + self.b_chr[128];
503 dst[roff + x] = r.max(0).min(255) as u8;
504 dst[goff + x] = g.max(0).min(255) as u8;
505 dst[boff + x] = b.max(0).min(255) as u8;
515 let y = f32::from(src[yoff + x]);
516 let (r, g, b) = matrix_mul(&self.matrix, y, 0.0, 0.0);
517 dst[roff + x] = (r as i16).max(0).min(255) as u8;
518 dst[goff + x] = (g as i16).max(0).min(255) as u8;
519 dst[boff + x] = (b as i16).max(0).min(255) as u8;
530 pub fn create_yuv2rgb() -> Box<dyn Kernel> {
531 Box::new(YuvToRgb::new())