pub mod refs;
pub mod register;
pub mod detect;
+pub mod scale;
#[cfg(feature="dsp")]
pub mod dsp;
--- /dev/null
+use super::*;
+use super::kernel::Kernel;
+
+const YUV_PARAMS: &[[f32; 2]] = &[
+ [ 0.333, 0.333 ], // RGB
+ [ 0.2126, 0.0722 ], // ITU-R BT709
+ [ 0.333, 0.333 ], // unspecified
+ [ 0.333, 0.333 ], // reserved
+ [ 0.299, 0.114 ], // ITU-R BT601
+ [ 0.299, 0.114 ], // ITU-R BT470
+ [ 0.299, 0.114 ], // SMPTE 170M
+ [ 0.212, 0.087 ], // SMPTE 240M
+ [ 0.333, 0.333 ], // YCoCg
+ [ 0.2627, 0.0593 ], // ITU-R BT2020
+ [ 0.2627, 0.0593 ], // ITU-R BT2020
+];
+
+const BT_PAL_COEFFS: [f32; 2] = [ 0.493, 0.877 ];
+
+const SMPTE_NTSC_COEFFS: &[f32; 4] = &[ -0.268, 0.7358, 0.4127, 0.4778 ];
+
+/*const RGB2YCOCG: [[f32; 3]; 3] = [
+ [ 0.25, 0.5, 0.25 ],
+ [ -0.25, 0.5, -0.25 ],
+ [ 0.5, 0.0, -0.5 ]
+];
+const YCOCG2RGB: [[f32; 3]; 3] = [
+ [ 1.0, -1.0, 1.0 ],
+ [ 1.0, 1.0, 0.0 ],
+ [ 1.0, -1.0, -1.0 ]
+];
+
+const XYZ2RGB: [[f32; 3]; 3] = [
+ [ 0.49, 0.31, 0.2 ],
+ [ 0.17697, 0.8124, 0.01063 ],
+ [ 0.0, 0.01, 0.99 ]
+];
+const RGB2XYZ: [[f32; 3]; 3] = [
+ [ 2.364613, -0.89654, -0.46807 ],
+ [ -0.515167, 1.42641, 0.08876 ],
+ [ 0.0052, -0.01441, 1.00920 ]
+];*/
+
+fn make_rgb2yuv(kr: f32, kb: f32, mat: &mut [[f32; 3]; 3]) {
+ // Y
+ mat[0][0] = kr;
+ mat[0][1] = 1.0 - kr - kb;
+ mat[0][2] = kb;
+ // Cb
+ mat[1][0] = -mat[0][0] * 0.5 / (1.0 - kb);
+ mat[1][1] = -mat[0][1] * 0.5 / (1.0 - kb);
+ mat[1][2] = 0.5;
+ // Cr
+ mat[2][0] = 0.5;
+ mat[2][1] = -mat[0][1] * 0.5 / (1.0 - kr);
+ mat[2][2] = -mat[0][2] * 0.5 / (1.0 - kr);
+}
+
+fn make_yuv2rgb(kr: f32, kb: f32, mat: &mut [[f32; 3]; 3]) {
+ let kg = 1.0 - kr - kb;
+
+ // R
+ mat[0][0] = 1.0;
+ mat[0][1] = 0.0;
+ mat[0][2] = 2.0 * (1.0 - kr);
+ // G
+ mat[1][0] = 1.0;
+ mat[1][1] = -kb * 2.0 * (1.0 - kb) / kg;
+ mat[1][2] = -kr * 2.0 * (1.0 - kr) / kg;
+ // B
+ mat[2][0] = 1.0;
+ mat[2][1] = 2.0 * (1.0 - kb);
+ mat[2][2] = 0.0;
+}
+
+fn apply_pal_rgb2yuv(eu: f32, ev: f32, mat: &mut [[f32; 3]; 3]) {
+ let ufac = 2.0 * (1.0 - mat[0][2]) * eu;
+ let vfac = 2.0 * (1.0 - mat[0][0]) * ev;
+
+ // U
+ mat[1][0] *= ufac;
+ mat[1][1] *= ufac;
+ mat[1][2] = eu * (1.0 - mat[0][2]);
+ // V
+ mat[2][0] = ev * (1.0 - mat[0][0]);
+ mat[2][1] *= vfac;
+ mat[2][2] *= vfac;
+}
+
+fn apply_pal_yuv2rgb(eu: f32, ev: f32, mat: &mut [[f32; 3]; 3]) {
+ let ufac = 1.0 / (mat[2][1] * eu);
+ let vfac = 1.0 / (mat[0][2] * ev);
+
+ // R
+ mat[0][2] *= vfac;
+ // G
+ mat[1][1] *= ufac;
+ mat[1][2] *= vfac;
+ // B
+ mat[2][1] *= ufac;
+}
+
+fn apply_ntsc_rgb2yiq(params: &[f32; 4], mat: &mut [[f32; 3]; 3]) {
+ let ufac = 2.0 * (1.0 - mat[0][2]);
+ let vfac = 2.0 * (1.0 - mat[0][0]);
+ let mut tmp: [[f32; 3]; 2] = [[0.0; 3]; 2];
+
+ for i in 0..3 {
+ tmp[0][i] = mat[1][i] * ufac;
+ tmp[1][i] = mat[2][i] * vfac;
+ }
+ for i in 0..3 {
+ mat[1][i] = params[0] * tmp[0][i] + params[1] * tmp[1][i];
+ mat[2][i] = params[2] * tmp[0][i] + params[3] * tmp[1][i];
+ }
+}
+
+fn subm_det(mat: &[[f32; 3]; 3], col: usize, row: usize) -> f32 {
+ let row0 = if row == 0 { 1 } else { 0 };
+ let row1 = if (row == 1) || (row0 == 1) { 2 } else { 1 };
+ let col0 = if col == 0 { 1 } else { 0 };
+ let col1 = if (col == 1) || (col0 == 1) { 2 } else { 1 };
+
+ let det = mat[row0][col0] * mat[row1][col1] - mat[row0][col1] * mat[row1][col0];
+ if ((col ^ row) & 1) == 0 {
+ det
+ } else {
+ -det
+ }
+}
+
+fn invert_matrix(mat: &mut [[f32; 3]; 3]) {
+ let d00 = subm_det(mat, 0, 0);
+ let d01 = subm_det(mat, 0, 1);
+ let d02 = subm_det(mat, 0, 2);
+ let d10 = subm_det(mat, 1, 0);
+ let d11 = subm_det(mat, 1, 1);
+ let d12 = subm_det(mat, 1, 2);
+ let d20 = subm_det(mat, 2, 0);
+ let d21 = subm_det(mat, 2, 1);
+ let d22 = subm_det(mat, 2, 2);
+ let det = 1.0 / (mat[0][0] * d00 + mat[0][1] * d10 + mat[0][2] * d20).abs();
+
+ mat[0][0] = det * d00;
+ mat[0][1] = det * d01;
+ mat[0][2] = det * d02;
+ mat[1][0] = det * d10;
+ mat[1][1] = det * d11;
+ mat[1][2] = det * d12;
+ mat[2][0] = det * d20;
+ mat[2][1] = det * d21;
+ mat[2][2] = det * d22;
+}
+
+fn matrix_mul(mat: &[[f32; 3]; 3], a: f32, b: f32, c: f32) -> (f32, f32, f32) {
+ (a * mat[0][0] + b * mat[0][1] + c * mat[0][2],
+ a * mat[1][0] + b * mat[1][1] + c * mat[1][2],
+ a * mat[2][0] + b * mat[2][1] + c * mat[2][2] )
+}
+
+#[derive(Default)]
+struct RgbToYuv {
+ matrix: [[f32; 3]; 3],
+}
+
+impl RgbToYuv {
+ fn new() -> Self { Self::default() }
+}
+
+impl Kernel for RgbToYuv {
+ fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo) -> ScaleResult<NABufferType> {
+ let mut df = dest_fmt.fmt;
+//todo coeff selection
+ make_rgb2yuv(YUV_PARAMS[2][0], YUV_PARAMS[2][1], &mut self.matrix);
+ if let ColorModel::YUV(yuvsm) = df.get_model() {
+ match yuvsm {
+ YUVSubmodel::YCbCr => {},
+ YUVSubmodel::YIQ => { apply_ntsc_rgb2yiq(SMPTE_NTSC_COEFFS, &mut self.matrix); },
+ YUVSubmodel::YUVJ => { apply_pal_rgb2yuv(BT_PAL_COEFFS[0], BT_PAL_COEFFS[1], &mut self.matrix); },
+ };
+ } else {
+ return Err(ScaleError::InvalidArgument);
+ }
+ for i in 0..MAX_CHROMATONS {
+ if let Some(ref mut chr) = df.comp_info[i] {
+ chr.packed = false;
+ chr.comp_offs = i as u8;
+ chr.h_ss = 0;
+ chr.v_ss = 0;
+ }
+ }
+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()) {
+ 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 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 mut uoff = dbuf.get_offset(1);
+ let mut voff = dbuf.get_offset(2);
+ let src = sbuf.get_data();
+ 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 (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;
+ }
+ roff += istrides[0];
+ goff += istrides[1];
+ boff += istrides[2];
+ yoff += dstrides[0];
+ uoff += dstrides[1];
+ voff += dstrides[2];
+ }
+ }
+ }
+}
+
+pub fn create_rgb2yuv() -> Box<Kernel> {
+ Box::new(RgbToYuv::new())
+}
+
+#[derive(Default)]
+struct YuvToRgb {
+ matrix: [[f32; 3]; 3],
+}
+
+impl YuvToRgb {
+ fn new() -> Self { Self::default() }
+}
+
+impl Kernel for YuvToRgb {
+ fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo) -> ScaleResult<NABufferType> {
+ let mut df = dest_fmt.fmt;
+//todo coeff selection
+ make_yuv2rgb(YUV_PARAMS[2][0], YUV_PARAMS[2][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);
+ apply_ntsc_rgb2yiq(SMPTE_NTSC_COEFFS, &mut self.matrix);
+ invert_matrix(&mut self.matrix);
+ },
+ YUVSubmodel::YUVJ => {
+ apply_pal_yuv2rgb(BT_PAL_COEFFS[0], BT_PAL_COEFFS[1], &mut self.matrix);
+ },
+ };
+ } else {
+ return Err(ScaleError::InvalidArgument);
+ }
+ for i in 0..MAX_CHROMATONS {
+ if let Some(ref mut chr) = df.comp_info[i] {
+ chr.packed = false;
+ chr.comp_offs = i as u8;
+ }
+ }
+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()) {
+ 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 (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 uhmask = (1 << sh0) - 1;
+ let vhmask = (1 << sh1) - 1;
+ 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 mut uoff = sbuf.get_offset(1);
+ let mut voff = sbuf.get_offset(2);
+ let src = sbuf.get_data();
+ let dst = dbuf.get_data_mut().unwrap();
+ 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 (r, g, b) = matrix_mul(&self.matrix, y, u, v);
+ 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 += istrides[0];
+ if (y & uhmask) == uhmask {
+ uoff += istrides[1];
+ }
+ if (y & vhmask) == vhmask {
+ voff += istrides[2];
+ }
+ }
+ }
+ }
+}
+
+pub fn create_yuv2rgb() -> Box<Kernel> {
+ Box::new(YuvToRgb::new())
+}
--- /dev/null
+use crate::frame::*;
+use super::{ScaleInfo, ScaleResult};
+
+pub trait Kernel {
+ fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo) -> ScaleResult<NABufferType>;
+ fn process(&mut self, pic_in: &NABufferType, pic_out: &mut NABufferType);
+}
--- /dev/null
+use crate::frame::*;
+
+mod kernel;
+
+mod colorcvt;
+mod repack;
+mod scale;
+
+#[derive(Clone,Copy,PartialEq)]
+pub struct ScaleInfo {
+ pub fmt: NAPixelFormaton,
+ pub width: usize,
+ pub height: usize,
+}
+
+impl std::fmt::Display for ScaleInfo {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(f, "({}x{}, {})", self.width, self.height, self.fmt)
+ }
+}
+
+#[derive(Debug,Clone,Copy,PartialEq)]
+#[allow(dead_code)]
+pub enum ScaleError {
+ NoFrame,
+ AllocError,
+ InvalidArgument,
+ NotImplemented,
+ Bug,
+}
+
+pub type ScaleResult<T> = Result<T, ScaleError>;
+
+/*trait Kernel {
+ fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo) -> ScaleResult<NABufferType>;
+ fn process(&mut self, pic_in: &NABufferType, pic_out: &mut NABufferType);
+}*/
+
+struct KernelDesc {
+ name: &'static str,
+ create: fn () -> Box<kernel::Kernel>,
+}
+
+impl KernelDesc {
+ fn find(name: &str) -> ScaleResult<Box<kernel::Kernel>> {
+ for kern in KERNELS.iter() {
+ if kern.name == name {
+ return Ok((kern.create)());
+ }
+ }
+ Err(ScaleError::InvalidArgument)
+ }
+}
+
+const KERNELS: &[KernelDesc] = &[
+ KernelDesc { name: "pack", create: repack::create_pack },
+ KernelDesc { name: "unpack", create: repack::create_unpack },
+ KernelDesc { name: "depal", create: repack::create_depal },
+ KernelDesc { name: "scale", create: scale::create_scale },
+ KernelDesc { name: "rgb_to_yuv", create: colorcvt::create_rgb2yuv },
+ KernelDesc { name: "yuv_to_rgb", create: colorcvt::create_yuv2rgb },
+];
+
+struct Stage {
+ fmt_out: ScaleInfo,
+ tmp_pic: NABufferType,
+ next: Option<Box<Stage>>,
+ worker: Box<kernel::Kernel>,
+}
+
+pub fn get_scale_fmt_from_pic(pic: &NABufferType) -> ScaleInfo {
+ let info = pic.get_video_info().unwrap();
+ ScaleInfo { fmt: info.get_format(), width: info.get_width(), height: info.get_height() }
+}
+
+impl Stage {
+ fn new(name: &str, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo) -> ScaleResult<Self> {
+ let mut worker = KernelDesc::find(name)?;
+ let tmp_pic = worker.init(in_fmt, dest_fmt)?;
+ let fmt_out = get_scale_fmt_from_pic(&tmp_pic);
+ Ok(Self { fmt_out, tmp_pic, next: None, worker })
+ }
+ fn add(&mut self, new: Stage) {
+ if let Some(ref mut next) = self.next {
+ next.add(new);
+ } else {
+ self.next = Some(Box::new(new));
+ }
+ }
+ fn process(&mut self, pic_in: &NABufferType, pic_out: &mut NABufferType) -> ScaleResult<()> {
+ if let Some(ref mut nextstage) = self.next {
+ self.worker.process(pic_in, &mut self.tmp_pic);
+ nextstage.process(&self.tmp_pic, pic_out)?;
+ } else {
+ self.worker.process(pic_in, pic_out);
+ }
+ Ok(())
+ }
+ fn drop_last_tmp(&mut self) {
+ if let Some(ref mut nextstage) = self.next {
+ nextstage.drop_last_tmp();
+ } else {
+ self.tmp_pic = NABufferType::None;
+ }
+ }
+}
+
+pub struct NAScale {
+ fmt_in: ScaleInfo,
+ fmt_out: ScaleInfo,
+ just_convert: bool,
+ pipeline: Option<Stage>,
+}
+
+fn check_format(in_fmt: NAVideoInfo, ref_fmt: &ScaleInfo, just_convert: bool) -> ScaleResult<()> {
+ if in_fmt.get_format() != ref_fmt.fmt { return Err(ScaleError::InvalidArgument); }
+ if !just_convert && (in_fmt.get_width() != ref_fmt.width || in_fmt.get_height() != ref_fmt.height) {
+ return Err(ScaleError::InvalidArgument);
+ }
+ Ok(())
+}
+
+fn copy(pic_in: &NABufferType, pic_out: &mut NABufferType)
+{
+ if let (Some(ref sbuf), Some(ref mut dbuf)) = (pic_in.get_vbuf(), pic_out.get_vbuf()) {
+ let sdata = sbuf.get_data();
+ let ddata = dbuf.get_data_mut().unwrap();
+ ddata.copy_from_slice(&sdata[0..]);
+ } else {
+ unimplemented!();
+ }
+}
+
+macro_rules! add_stage {
+ ($head:expr, $new:expr) => {
+ if let Some(ref mut h) = $head {
+ h.add($new);
+ } else {
+ $head = Some($new);
+ }
+ };
+}
+fn is_better_fmt(a: &ScaleInfo, b: &ScaleInfo) -> bool {
+ if (a.width >= b.width) && (a.height >= b.height) {
+ return true;
+ }
+ if a.fmt.get_max_depth() > b.fmt.get_max_depth() {
+ return true;
+ }
+ if a.fmt.get_max_subsampling() < b.fmt.get_max_subsampling() {
+ return true;
+ }
+ false
+}
+fn build_pipeline(ifmt: &ScaleInfo, ofmt: &ScaleInfo, just_convert: bool) -> ScaleResult<Option<Stage>> {
+ let inname = ifmt.fmt.get_model().get_short_name();
+ let outname = ofmt.fmt.get_model().get_short_name();
+
+println!("convert {} -> {}", ifmt, ofmt);
+ let mut needs_scale = !just_convert;
+ if (ofmt.fmt.get_max_subsampling() > 0) &&
+ (ofmt.fmt.get_max_subsampling() != ifmt.fmt.get_max_subsampling()) {
+ needs_scale = true;
+ }
+ let needs_unpack = needs_scale || !ifmt.fmt.is_unpacked();
+ let needs_pack = !ofmt.fmt.is_unpacked();
+ let mut needs_convert = false;
+ if inname != outname {
+ needs_convert = true;
+ }
+ let scale_before_cvt = is_better_fmt(&ifmt, &ofmt) && needs_convert
+ && (ofmt.fmt.get_max_subsampling() == 0);
+//todo stages for model and gamma conversion
+
+ let mut stages: Option<Stage> = None;
+ let mut cur_fmt = *ifmt;
+
+ if needs_unpack {
+println!("[adding unpack]");
+ let new_stage;
+ if !cur_fmt.fmt.is_paletted() {
+ new_stage = Stage::new("unpack", &cur_fmt, &ofmt)?;
+ } else {
+ new_stage = Stage::new("depal", &cur_fmt, &ofmt)?;
+ }
+ cur_fmt = new_stage.fmt_out;
+ add_stage!(stages, new_stage);
+ }
+ if needs_scale && scale_before_cvt {
+println!("[adding scale]");
+ let new_stage = Stage::new("scale", &cur_fmt, &ofmt)?;
+ cur_fmt = new_stage.fmt_out;
+ add_stage!(stages, new_stage);
+ }
+ if needs_convert {
+println!("[adding convert]");
+ let cvtname = format!("{}_to_{}", inname, outname);
+println!("[{}]", cvtname);
+ let new_stage = Stage::new(&cvtname, &cur_fmt, &ofmt)?;
+//todo if fails try converting via RGB or YUV
+ cur_fmt = new_stage.fmt_out;
+ add_stage!(stages, new_stage);
+//todo alpha plane copy/add
+ }
+ if needs_scale && !scale_before_cvt {
+println!("[adding scale]");
+ let new_stage = Stage::new("scale", &cur_fmt, &ofmt)?;
+ cur_fmt = new_stage.fmt_out;
+ add_stage!(stages, new_stage);
+ }
+//todo flip if needed
+ if needs_pack {
+println!("[adding pack]");
+ let new_stage = Stage::new("pack", &cur_fmt, &ofmt)?;
+ //cur_fmt = new_stage.fmt_out;
+ add_stage!(stages, new_stage);
+ }
+
+ if let Some(ref mut head) = stages {
+ head.drop_last_tmp();
+ }
+
+ Ok(stages)
+}
+
+impl NAScale {
+ pub fn new(fmt_in: ScaleInfo, fmt_out: ScaleInfo) -> ScaleResult<Self> {
+ let pipeline;
+ let just_convert = (fmt_in.width == fmt_out.width) && (fmt_in.height == fmt_out.height);
+ if fmt_in != fmt_out {
+ pipeline = build_pipeline(&fmt_in, &fmt_out, just_convert)?;
+ } else {
+ pipeline = None;
+ }
+ Ok(Self { fmt_in, fmt_out, just_convert, pipeline })
+ }
+ pub fn needs_processing(&self) -> bool { self.pipeline.is_some() }
+ pub fn get_in_fmt(&self) -> ScaleInfo { self.fmt_in }
+ pub fn get_out_fmt(&self) -> ScaleInfo { self.fmt_out }
+ pub fn convert(&mut self, pic_in: &NABufferType, pic_out: &mut NABufferType) -> ScaleResult<()> {
+ let in_info = pic_in.get_video_info();
+ let out_info = pic_out.get_video_info();
+ if in_info.is_none() || out_info.is_none() { return Err(ScaleError::InvalidArgument); }
+ let in_info = in_info.unwrap();
+ let out_info = out_info.unwrap();
+ if self.just_convert &&
+ (in_info.get_width() != out_info.get_width() || in_info.get_height() != out_info.get_height()) {
+ return Err(ScaleError::InvalidArgument);
+ }
+ check_format(in_info, &self.fmt_in, self.just_convert)?;
+ check_format(out_info, &self.fmt_out, self.just_convert)?;
+ if let Some(ref mut pipe) = self.pipeline {
+ pipe.process(pic_in, pic_out)
+ } else {
+ copy(pic_in, pic_out);
+ Ok(())
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ fn fill_pic(pic: &mut NABufferType, val: u8) {
+ if let Some(ref mut buf) = pic.get_vbuf() {
+ let data = buf.get_data_mut().unwrap();
+ for el in data.iter_mut() { *el = val; }
+ } else if let Some(ref mut buf) = pic.get_vbuf16() {
+ let data = buf.get_data_mut().unwrap();
+ for el in data.iter_mut() { *el = val as u16; }
+ } else if let Some(ref mut buf) = pic.get_vbuf32() {
+ let data = buf.get_data_mut().unwrap();
+ for el in data.iter_mut() { *el = (val as u32) * 0x01010101; }
+ }
+ }
+ #[test]
+ fn test_convert() {
+ let mut in_pic = alloc_video_buffer(NAVideoInfo::new(1, 1, false, RGB565_FORMAT), 3).unwrap();
+ fill_pic(&mut in_pic, 42);
+ let mut out_pic = alloc_video_buffer(NAVideoInfo::new(1, 1, false, RGB24_FORMAT), 3).unwrap();
+ fill_pic(&mut out_pic, 0);
+ let ifmt = get_scale_fmt_from_pic(&in_pic);
+ let ofmt = get_scale_fmt_from_pic(&out_pic);
+ let mut scaler = NAScale::new(ifmt, ofmt).unwrap();
+ scaler.convert(&in_pic, &mut out_pic).unwrap();
+ let obuf = out_pic.get_vbuf().unwrap();
+ let odata = obuf.get_data();
+ assert_eq!(odata[0], 0x0);
+ assert_eq!(odata[1], 0x4);
+ assert_eq!(odata[2], 0x52);
+
+ let mut in_pic = alloc_video_buffer(NAVideoInfo::new(4, 4, false, RGB24_FORMAT), 3).unwrap();
+ fill_pic(&mut in_pic, 42);
+ let mut out_pic = alloc_video_buffer(NAVideoInfo::new(4, 4, false, YUV420_FORMAT), 3).unwrap();
+ fill_pic(&mut out_pic, 0);
+ let ifmt = get_scale_fmt_from_pic(&in_pic);
+ let ofmt = get_scale_fmt_from_pic(&out_pic);
+ let mut scaler = NAScale::new(ifmt, ofmt).unwrap();
+ scaler.convert(&in_pic, &mut out_pic).unwrap();
+ let obuf = out_pic.get_vbuf().unwrap();
+ let yoff = obuf.get_offset(0);
+ let uoff = obuf.get_offset(1);
+ let voff = obuf.get_offset(2);
+ let odata = obuf.get_data();
+ assert_eq!(odata[yoff], 42);
+ assert!(((odata[uoff] ^ 0x80) as i8).abs() <= 1);
+ assert!(((odata[voff] ^ 0x80) as i8).abs() <= 1);
+ let mut scaler = NAScale::new(ofmt, ifmt).unwrap();
+ scaler.convert(&out_pic, &mut in_pic).unwrap();
+ let obuf = in_pic.get_vbuf().unwrap();
+ let odata = obuf.get_data();
+ assert_eq!(odata[0], 42);
+ }
+ #[test]
+ fn test_scale() {
+ let mut in_pic = alloc_video_buffer(NAVideoInfo::new(2, 2, false, RGB565_FORMAT), 3).unwrap();
+ fill_pic(&mut in_pic, 42);
+ let mut out_pic = alloc_video_buffer(NAVideoInfo::new(3, 3, false, RGB565_FORMAT), 3).unwrap();
+ fill_pic(&mut out_pic, 0);
+ let ifmt = get_scale_fmt_from_pic(&in_pic);
+ let ofmt = get_scale_fmt_from_pic(&out_pic);
+ let mut scaler = NAScale::new(ifmt, ofmt).unwrap();
+ scaler.convert(&in_pic, &mut out_pic).unwrap();
+ let obuf = out_pic.get_vbuf16().unwrap();
+ let odata = obuf.get_data();
+ assert_eq!(odata[0], 42);
+ }
+ #[test]
+ fn test_scale_and_convert() {
+ let mut in_pic = alloc_video_buffer(NAVideoInfo::new(7, 3, false, RGB565_FORMAT), 3).unwrap();
+ fill_pic(&mut in_pic, 42);
+ let mut out_pic = alloc_video_buffer(NAVideoInfo::new(4, 4, false, YUV420_FORMAT), 3).unwrap();
+ fill_pic(&mut out_pic, 0);
+ let ifmt = get_scale_fmt_from_pic(&in_pic);
+ let ofmt = get_scale_fmt_from_pic(&out_pic);
+ let mut scaler = NAScale::new(ifmt, ofmt).unwrap();
+ scaler.convert(&in_pic, &mut out_pic).unwrap();
+ let obuf = out_pic.get_vbuf().unwrap();
+ let yoff = obuf.get_offset(0);
+ let uoff = obuf.get_offset(1);
+ let voff = obuf.get_offset(2);
+ let odata = obuf.get_data();
+ assert_eq!(odata[yoff], 28);
+ assert_eq!(odata[uoff], 154);
+ assert_eq!(odata[voff], 103);
+ }
+}
--- /dev/null
+use crate::formats::*;
+use super::*;
+use super::kernel::Kernel;
+
+fn convert_depth(val: u32, indepth: u8, outdepth: u8) -> u32 {
+ if indepth >= outdepth {
+ val >> (indepth - outdepth)
+ } else {
+ let mut val2 = val << (outdepth - indepth);
+ let mut shift = outdepth - indepth;
+ while shift >= indepth {
+ shift -= indepth;
+ val2 |= val << shift;
+ }
+ if shift > 0 {
+ val2 |= val >> (indepth - shift);
+ }
+ val2
+ }
+}
+
+#[derive(Default)]
+struct PackKernel {
+ shifts: [u8; MAX_CHROMATONS],
+ depths: [u8; MAX_CHROMATONS],
+ ncomps: usize,
+ osize: [u8; MAX_CHROMATONS],
+ ooff: [usize; MAX_CHROMATONS],
+}
+
+impl PackKernel {
+ fn new() -> Self { Self::default() }
+}
+
+impl Kernel for PackKernel {
+ fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo) -> ScaleResult<NABufferType> {
+ self.ncomps = in_fmt.fmt.components as usize;
+ for i in 0..self.ncomps {
+ let ichr = in_fmt.fmt.comp_info[i].unwrap();
+ let ochr = dest_fmt.fmt.comp_info[i].unwrap();
+ self.shifts[i] = ochr.shift;
+ self.osize[i] = ochr.depth;
+ self.depths[i] = ichr.depth;
+ self.ooff[i] = ochr.comp_offs as usize;
+ }
+ let res = alloc_video_buffer(NAVideoInfo::new(in_fmt.width, in_fmt.height, false, dest_fmt.fmt), 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 buf) = pic_in.get_vbuf() {
+ if let Some(ref mut dbuf) = pic_out.get_vbuf() {
+ let dstride = dbuf.get_stride(0);
+ for comp in 0..self.ncomps {
+ let ioff = buf.get_offset(comp);
+ let istride = buf.get_stride(comp);
+ let step = dbuf.get_info().get_format().get_chromaton(comp).unwrap().get_step() as usize;
+ let (w, h) = dbuf.get_dimensions(comp);
+ let sdata = buf.get_data();
+ let sdata = &sdata[ioff..];
+ let ddata = dbuf.get_data_mut().unwrap();
+ for (src, dst) in sdata.chunks(istride).zip(ddata.chunks_mut(dstride)).take(h) {
+ for x in 0..w {
+ dst[x * step + self.ooff[comp]] = convert_depth(src[x] as u32, self.depths[comp], self.osize[comp]) as u8;
+ }
+ }
+ }
+ } else if let Some(ref mut dbuf) = pic_out.get_vbuf16() {
+ let (w, h) = dbuf.get_dimensions(0);
+ let dstride = dbuf.get_stride(0);
+ let ddata = dbuf.get_data_mut().unwrap();
+ let src = buf.get_data();
+ let mut ioff: [usize; MAX_CHROMATONS] = [0; MAX_CHROMATONS];
+ let mut istride: [usize; MAX_CHROMATONS] = [0; MAX_CHROMATONS];
+ for comp in 0..self.ncomps {
+ ioff[comp] = buf.get_offset(comp);
+ istride[comp] = buf.get_stride(comp);
+ }
+ for dst in ddata.chunks_mut(dstride).take(h) {
+ for x in 0..w {
+ let mut elem: u32 = 0;
+ for comp in 0..self.ncomps {
+ let c = src[ioff[comp] + x] as u32;
+ elem |= convert_depth(c, self.depths[comp], self.osize[comp]) << self.shifts[comp];
+ }
+ dst[x] = elem as u16;
+ }
+ for comp in 0..self.ncomps {
+ ioff[comp] += istride[comp];
+ }
+ }
+ } else {
+unimplemented!();
+ }
+ } else if let Some(ref _buf) = pic_in.get_vbuf16() {
+unimplemented!();
+ } else if let Some(ref _buf) = pic_in.get_vbuf32() {
+unimplemented!();
+ } else {
+ unreachable!();
+ }
+ }
+}
+
+pub fn create_pack() -> Box<Kernel> {
+ Box::new(PackKernel::new())
+}
+
+#[derive(Default)]
+struct UnpackKernel {
+ shifts: [u8; MAX_CHROMATONS],
+ masks: [u32; MAX_CHROMATONS],
+ depths: [u8; MAX_CHROMATONS],
+ ncomps: usize,
+ osize: [u8; MAX_CHROMATONS],
+}
+
+impl UnpackKernel {
+ fn new() -> Self { Self::default() }
+}
+
+impl Kernel for UnpackKernel {
+ fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo) -> ScaleResult<NABufferType> {
+ self.ncomps = in_fmt.fmt.components as usize;
+ let mut chr: Vec<Option<NAPixelChromaton>> = Vec::with_capacity(MAX_CHROMATONS);
+ for i in 0..self.ncomps {
+ let ichr = in_fmt.fmt.comp_info[i].unwrap();
+ let ochr = dest_fmt.fmt.comp_info[i].unwrap();
+ self.shifts[i] = ichr.shift;
+ self.masks[i] = (1 << ichr.depth) - 1;
+ if ochr.depth > ichr.depth {
+ self.osize[i] = ochr.depth;
+ } else {
+ self.osize[i] = (ichr.depth + 7) & !7;
+ }
+ self.depths[i] = ichr.depth;
+ let mut dchr = ichr;
+ dchr.packed = false;
+ dchr.depth = self.osize[i];
+ dchr.shift = 0;
+ dchr.comp_offs = 0;
+ dchr.next_elem = 0;
+ chr.push(Some(dchr));
+ }
+ let mut df = in_fmt.fmt;
+ for i in 0..self.ncomps {
+ df.comp_info[i] = chr[i];
+ }
+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 buf) = pic_in.get_vbuf() {
+ let step = buf.get_info().get_format().get_chromaton(0).unwrap().get_step() as usize;
+ let mut soff: [usize; MAX_CHROMATONS] = [0; MAX_CHROMATONS];
+ for i in 0..self.ncomps {
+ soff[i] = buf.get_info().get_format().get_chromaton(i).unwrap().get_offset() as usize;
+ }
+ let (w, h) = buf.get_dimensions(0);
+ let ioff = buf.get_offset(0);
+ let istride = buf.get_stride(0);
+ let sdata1 = buf.get_data();
+ let sdata = &sdata1[ioff..];
+ if let Some(ref mut dbuf) = pic_out.get_vbuf() {
+ let mut ostride: [usize; MAX_CHROMATONS] = [0; MAX_CHROMATONS];
+ let mut offs: [usize; MAX_CHROMATONS] = [0; MAX_CHROMATONS];
+ for i in 0..self.ncomps {
+ ostride[i] = dbuf.get_stride(i);
+ offs[i] = dbuf.get_offset(i);
+ }
+ let dst = dbuf.get_data_mut().unwrap();
+ for src in sdata.chunks(istride).take(h) {
+ for x in 0..w {
+ for i in 0..self.ncomps {
+ dst[offs[i] + x] = src[x * step + soff[i]];
+ }
+ }
+ for i in 0..self.ncomps { offs[i] += ostride[i]; }
+ }
+ } else {
+unimplemented!();
+ }
+ } else if let Some(ref buf) = pic_in.get_vbuf16() {
+ let (w, h) = buf.get_dimensions(0);
+ let ioff = buf.get_offset(0);
+ let istride = buf.get_stride(0);
+ let sdata1 = buf.get_data();
+ let sdata = &sdata1[ioff..];
+ if let Some(ref mut dbuf) = pic_out.get_vbuf() {
+ let mut ostride: [usize; MAX_CHROMATONS] = [0; MAX_CHROMATONS];
+ let mut offs: [usize; MAX_CHROMATONS] = [0; MAX_CHROMATONS];
+ for i in 0..self.ncomps {
+ ostride[i] = dbuf.get_stride(i);
+ offs[i] = dbuf.get_offset(i);
+ }
+ let dst = dbuf.get_data_mut().unwrap();
+ for src in sdata.chunks(istride).take(h) {
+ for x in 0..w {
+ let elem = src[x] as u32;
+ for i in 0..self.ncomps {
+ dst[offs[i] + x] = convert_depth((elem >> self.shifts[i]) & self.masks[i], self.depths[i], self.osize[i]) as u8;
+ }
+ }
+ for i in 0..self.ncomps { offs[i] += ostride[i]; }
+ }
+ } else {
+unimplemented!();
+ }
+ } else if let Some(ref _buf) = pic_in.get_vbuf32() {
+unimplemented!();
+ } else {
+ unreachable!();
+ }
+ }
+}
+
+pub fn create_unpack() -> Box<Kernel> {
+ Box::new(UnpackKernel::new())
+}
+
+#[derive(Default)]
+struct DepalKernel {
+ depths: [u8; MAX_CHROMATONS],
+ coffs: [usize; MAX_CHROMATONS],
+ ncomps: usize,
+ palstep: usize,
+}
+
+impl DepalKernel {
+ fn new() -> Self { Self::default() }
+}
+
+impl Kernel for DepalKernel {
+ fn init(&mut self, in_fmt: &ScaleInfo, _dest_fmt: &ScaleInfo) -> ScaleResult<NABufferType> {
+//todo select output more fitting for dest_fmt if possible
+ self.ncomps = in_fmt.fmt.components as usize;
+ let mut chr: Vec<Option<NAPixelChromaton>> = Vec::with_capacity(MAX_CHROMATONS);
+ self.palstep = in_fmt.fmt.elem_size as usize;
+ for i in 0..self.ncomps {
+ let ichr = in_fmt.fmt.comp_info[i].unwrap();
+ self.coffs[i] = ichr.comp_offs as usize;
+ self.depths[i] = ichr.depth;
+ let mut dchr = ichr;
+ dchr.packed = false;
+ dchr.depth = 8;
+ dchr.shift = 0;
+ dchr.comp_offs = i as u8;
+ dchr.next_elem = 0;
+ chr.push(Some(dchr));
+ }
+ let mut df = in_fmt.fmt;
+ df.palette = false;
+ for i in 0..self.ncomps {
+ df.comp_info[i] = chr[i];
+ }
+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()) {
+ let ioff = sbuf.get_offset(0);
+ let paloff = sbuf.get_offset(1);
+ let (w, h) = sbuf.get_dimensions(0);
+ let istride = sbuf.get_stride(0);
+ let sdata1 = sbuf.get_data();
+ let sdata = &sdata1[ioff..];
+ let pal = &sdata1[paloff..];
+
+ let mut ostride: [usize; MAX_CHROMATONS] = [0; MAX_CHROMATONS];
+ let mut offs: [usize; MAX_CHROMATONS] = [0; MAX_CHROMATONS];
+ for i in 0..self.ncomps {
+ ostride[i] = dbuf.get_stride(i);
+ offs[i] = dbuf.get_offset(i);
+ }
+ let dst = dbuf.get_data_mut().unwrap();
+ for src in sdata.chunks(istride).take(h) {
+ for x in 0..w {
+ let palidx = src[x] as usize;
+ for i in 0..self.ncomps {
+ let elem = pal[palidx * self.palstep + self.coffs[i]] as u32;
+ dst[offs[i] + x] = convert_depth(elem, self.depths[i], 8) as u8;
+ }
+ }
+ for i in 0..self.ncomps { offs[i] += ostride[i]; }
+ }
+ } else {
+ unreachable!();
+ }
+ }
+}
+
+pub fn create_depal() -> Box<Kernel> {
+ Box::new(DepalKernel::new())
+}
--- /dev/null
+use super::*;
+use super::kernel::Kernel;
+
+struct NNResampler {}
+
+impl NNResampler {
+ fn new() -> Self { Self{} }
+}
+
+macro_rules! scale_loop {
+ ($sbuf:expr, $dbuf:expr) => {
+ let fmt = $sbuf.get_info().get_format();
+ let ncomp = fmt.get_num_comp();
+ for comp in 0..ncomp {
+ let istride = $sbuf.get_stride(comp);
+ let dstride = $dbuf.get_stride(comp);
+ let (sw, sh) = $sbuf.get_dimensions(comp);
+ let (dw, dh) = $dbuf.get_dimensions(comp);
+ let ioff = $sbuf.get_offset(comp);
+ let mut doff = $dbuf.get_offset(comp);
+ let src = $sbuf.get_data();
+ let dst = $dbuf.get_data_mut().unwrap();
+ for y in 0..dh {
+ let sy = y * sh / dh;
+ let soff = ioff + sy * istride;
+ for x in 0..dw {
+ let sx = x * sw / dw;
+ dst[doff + x] = src[soff + sx];
+ }
+ doff += dstride;
+ }
+ }
+ };
+}
+
+impl Kernel for NNResampler {
+ fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo) -> ScaleResult<NABufferType> {
+ let res = alloc_video_buffer(NAVideoInfo::new(dest_fmt.width, dest_fmt.height, false, in_fmt.fmt), 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()) {
+ scale_loop!(sbuf, dbuf);
+ } else if let (Some(ref sbuf), Some(ref mut dbuf)) = (pic_in.get_vbuf16(), pic_out.get_vbuf16()) {
+ scale_loop!(sbuf, dbuf);
+ } else if let (Some(ref sbuf), Some(ref mut dbuf)) = (pic_in.get_vbuf32(), pic_out.get_vbuf32()) {
+ scale_loop!(sbuf, dbuf);
+ } else {
+ unreachable!();
+ }
+ }
+}
+
+pub fn create_scale() -> Box<Kernel> {
+ Box::new(NNResampler::new())
+}
+