[ 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 {
#[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[DEFAULT_YUV][0], YUV_PARAMS[DEFAULT_YUV][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())
#[derive(Default)]
struct YuvToRgb {
matrix: [[f32; 3]; 3],
+ mode: usize,
yscale: Vec<i16>,
r_chr: Vec<i16>,
g_u: Vec<i16>,
#[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;
df.palette = false;
if !df.is_unpacked() || df.get_max_depth() != 8 || df.get_total_depth() != df.get_num_comp() as u8 * 8 {
df.comp_info[3] = Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: false, depth: 8, shift: 0, comp_offs: 3, next_elem: 1 });
}
}
-//todo coeff selection
- make_yuv2rgb(YUV_PARAMS[DEFAULT_YUV][0], YUV_PARAMS[DEFAULT_YUV][1], &mut self.matrix);
+ 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 => {},
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())
use super::{ScaleInfo, ScaleResult};
pub trait Kernel {
- 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>;
fn process(&mut self, pic_in: &NABufferType, pic_out: &mut NABufferType);
}
}
impl Stage {
- fn new(name: &str, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo) -> ScaleResult<Self> {
+ fn new(name: &str, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo, options: &[(String, String)]) -> ScaleResult<Self> {
let mut worker = KernelDesc::find(name)?;
- let tmp_pic = worker.init(in_fmt, dest_fmt)?;
+ let tmp_pic = worker.init(in_fmt, dest_fmt, options)?;
let fmt_out = get_scale_fmt_from_pic(&tmp_pic);
Ok(Self { fmt_out, tmp_pic, next: None, worker })
}
}
false
}
-fn build_pipeline(ifmt: &ScaleInfo, ofmt: &ScaleInfo, just_convert: bool) -> ScaleResult<Option<Stage>> {
+fn build_pipeline(ifmt: &ScaleInfo, ofmt: &ScaleInfo, just_convert: bool, options: &[(String, String)]) -> ScaleResult<Option<Stage>> {
+ let mut debug = false;
+ for (name, value) in options.iter() {
+ if name == "debug" && (value == "" || value == "true") {
+ debug = true;
+ break;
+ }
+ }
+
let inname = ifmt.fmt.get_model().get_short_name();
let outname = ofmt.fmt.get_model().get_short_name();
-println!("convert {} -> {}", ifmt, ofmt);
+ if debug {
+ println!("convert {} -> {}", ifmt, ofmt);
+ }
let needs_scale = if fmt_needs_scale(&ifmt.fmt, &ofmt.fmt) {
true
} else {
let mut cur_fmt = *ifmt;
if needs_unpack {
-println!("[adding unpack]");
+ if debug {
+ println!("[adding unpack]");
+ }
let new_stage = if !cur_fmt.fmt.is_paletted() {
- Stage::new("unpack", &cur_fmt, &ofmt)?
+ Stage::new("unpack", &cur_fmt, &ofmt, options)?
} else {
- Stage::new("depal", &cur_fmt, &ofmt)?
+ Stage::new("depal", &cur_fmt, &ofmt, options)?
};
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)?;
+ if debug {
+ println!("[adding scale]");
+ }
+ let new_stage = Stage::new("scale", &cur_fmt, &ofmt, options)?;
cur_fmt = new_stage.fmt_out;
add_stage!(stages, new_stage);
}
if needs_convert {
-println!("[adding convert]");
+ if debug {
+ println!("[adding convert]");
+ }
let cvtname = format!("{}_to_{}", inname, outname);
-println!("[{}]", cvtname);
- let new_stage = Stage::new(&cvtname, &cur_fmt, &ofmt)?;
+ if debug {
+ println!("[{}]", cvtname);
+ }
+ let new_stage = Stage::new(&cvtname, &cur_fmt, &ofmt, options)?;
//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)?;
+ if debug {
+ println!("[adding scale]");
+ }
+ let new_stage = Stage::new("scale", &cur_fmt, &ofmt, options)?;
cur_fmt = new_stage.fmt_out;
add_stage!(stages, new_stage);
}
if needs_pack && !needs_palettise {
-println!("[adding pack]");
- let new_stage = Stage::new("pack", &cur_fmt, &ofmt)?;
+ if debug {
+ println!("[adding pack]");
+ }
+ let new_stage = Stage::new("pack", &cur_fmt, &ofmt, options)?;
//cur_fmt = new_stage.fmt_out;
add_stage!(stages, new_stage);
}
if needs_palettise {
-println!("[adding palettise]");
- let new_stage = Stage::new("palette", &cur_fmt, &ofmt)?;
+ if debug {
+ println!("[adding palettise]");
+ }
+ let new_stage = Stage::new("palette", &cur_fmt, &ofmt, options)?;
//cur_fmt = new_stage.fmt_out;
add_stage!(stages, new_stage);
}
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)?;
+ pipeline = build_pipeline(&fmt_in, &fmt_out, just_convert, &[])?;
+ } else {
+ pipeline = None;
+ }
+ Ok(Self { fmt_in, fmt_out, just_convert, pipeline })
+ }
+ /// Constructs a new `NAScale` instance taking into account provided options.
+ pub fn new_with_options(fmt_in: ScaleInfo, fmt_out: ScaleInfo, options: &[(String, String)]) -> 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, options)?;
} else {
pipeline = None;
}
}
impl Kernel for PalettiseKernel {
- 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> {
+ for (name, value) in options.iter() {
+ match name.as_str() {
+ "pal.quant" => {
+ self.qmode = match value.as_str() {
+ "mediancut" => QuantisationMode::MedianCut,
+ "elbg" => QuantisationMode::ELBG,
+ "neuquant" => QuantisationMode::NeuQuant(3),
+ _ => QuantisationMode::default(),
+ };
+ },
+ "pal.search" => {
+ self.palmode = match value.as_str() {
+ "full" => PaletteSearchMode::Full,
+ "local" => PaletteSearchMode::Local,
+ "kdtree" => PaletteSearchMode::KDTree,
+ _ => PaletteSearchMode::default(),
+ };
+ },
+ _ => {},
+ };
+ }
+
self.pixels = Vec::with_capacity(in_fmt.width * in_fmt.height);
let res = alloc_video_buffer(NAVideoInfo::new(in_fmt.width, in_fmt.height, false, PAL8_FORMAT), 0);
if res.is_err() { return Err(ScaleError::AllocError); }
}
impl Kernel for PackKernel {
- 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> {
self.ncomps = in_fmt.fmt.components.min(dest_fmt.fmt.components) as usize;
for i in 0..self.ncomps {
let ichr = in_fmt.fmt.comp_info[i].unwrap();
}
impl Kernel for UnpackKernel {
- 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;
+ for (name, value) in options.iter() {
+ match (name.as_str(), value.as_str()) {
+ ("debug", "") => { debug = true; },
+ ("debug", "true") => { debug = true; },
+ _ => {},
+ }
+ }
+
self.ncomps = in_fmt.fmt.components.min(dest_fmt.fmt.components) as usize;
let mut chr: Vec<Option<NAPixelChromaton>> = Vec::with_capacity(MAX_CHROMATONS);
for i in 0..self.ncomps {
df.comp_info[..self.ncomps].clone_from_slice(&chr[..self.ncomps]);
df.components = self.ncomps as u8;
df.palette = false;
-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())
}
impl Kernel for DepalKernel {
- 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;
+ for (name, value) in options.iter() {
+ match (name.as_str(), value.as_str()) {
+ ("debug", "") => { debug = true; },
+ ("debug", "true") => { debug = true; },
+ _ => {},
+ }
+ }
+
//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);
let mut df = in_fmt.fmt;
df.palette = false;
df.comp_info[..self.ncomps].clone_from_slice(&chr[..self.ncomps]);
-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())
}
impl Kernel for NNResampler {
- 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 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())