void Primaries::check_colorspace (const ::VSFormat &fmt, const char *inout_0) const { assert (inout_0 != 0); if (fmt.subSamplingW != 0 || fmt.subSamplingH != 0) { fstb::snprintf4all ( _filter_error_msg_0, _max_error_buf_len, "%s must be 4:4:4.", inout_0 ); throw_inval_arg (_filter_error_msg_0); } if (fmt.colorFamily != ::cmRGB) { fstb::snprintf4all ( _filter_error_msg_0, _max_error_buf_len, "%s colorspace must be RGB (assumed linear).", inout_0 ); throw_inval_arg (_filter_error_msg_0); } if ( (fmt.sampleType == ::stInteger && fmt.bitsPerSample != 16) || (fmt.sampleType == ::stFloat && fmt.bitsPerSample != 32)) { fstb::snprintf4all ( _filter_error_msg_0, _max_error_buf_len, "pixel bitdepth not supported, " "%s must be 16-bit integer or 32-bit float.", inout_0 ); throw_inval_arg (_filter_error_msg_0); } assert (fmt.numPlanes == NBR_PLANES); }
void Matrix::make_mat_from_str (fmtcl::Mat4 &m, const std::string &mat, bool to_rgb_flag) const { assert (&m != 0); assert (&mat != 0); if (mat.empty () || mat == "rgb") { m[0][0] = 1; m[0][1] = 0; m[0][2] = 0; m[1][0] = 0; m[1][1] = 1; m[1][2] = 0; m[2][0] = 0; m[2][1] = 0; m[2][2] = 1; complete_mat3 (m); } else if (mat == "601") { make_mat_yuv (m, 0.299, 0.587, 0.114, to_rgb_flag); } else if (mat == "709") { make_mat_yuv (m, 0.2126, 0.7152, 0.0722, to_rgb_flag); } else if (mat == "240") { make_mat_yuv (m, 0.212, 0.701, 0.087, to_rgb_flag); } else if (mat == "fcc") { make_mat_yuv (m, 0.30, 0.59, 0.11, to_rgb_flag); } else if (mat == "ycgco" || mat == "ycocg") { make_mat_ycgco (m, to_rgb_flag); } else if (mat == "2020") { make_mat_yuv (m, 0.2627, 0.678, 0.0593, to_rgb_flag); } else { throw_inval_arg ("unknown matrix identifier."); } }
fmtcl::TransCurve Convert::retrieve_tcurve (const ::VSFormat &fmt, const ::VSMap &in, ::VSMap &out, const char arg_0 [], const char def_0 []) { fmtcl::TransCurve tcurve = fmtcl::TransCurve_UNDEF; bool curve_flag = false; std::string curve_str = get_arg_str (in, out, arg_0, def_0, 0, &curve_flag); fstb::conv_to_lower_case (curve_str); if (! curve_flag || curve_str.empty ()) { tcurve = fmtcl::TransCurve_UNDEF; } else if (curve_str == "linear") { tcurve = fmtcl::TransCurve_LINEAR; } else if (curve_str == "srgb" || curve_str == "6196621") { tcurve = fmtcl::TransCurve_SRGB; } else if (curve_str == "709") { tcurve = fmtcl::TransCurve_709; } else if (curve_str == "601" || curve_str == "170") { tcurve = fmtcl::TransCurve_601; } else if (curve_str == "470m") { tcurve = fmtcl::TransCurve_470M; } else if (curve_str == "470bg") { tcurve = fmtcl::TransCurve_470BG; } else if (curve_str == "240") { tcurve = fmtcl::TransCurve_240; } else if (curve_str == "2020") { tcurve = fmtcl::TransCurve_2020_12; } else if (curve_str == "log100") { tcurve = fmtcl::TransCurve_LOG100; } else if (curve_str == "log316") { tcurve = fmtcl::TransCurve_LOG316; } else if (curve_str == "6196624") { tcurve = fmtcl::TransCurve_61966_2_4; } else if (curve_str == "1361") { tcurve = fmtcl::TransCurve_1361; } else if (curve_str == "1886") { tcurve = fmtcl::TransCurve_1886; } else if (curve_str == "1886a") { tcurve = fmtcl::TransCurve_1886A; } else { throw_inval_arg ("unexpected string for the transfer curve."); } return (tcurve); }
void Convert::retrieve_output_colorspace (const ::VSMap &in, ::VSMap &out, ::VSCore &core, const ::VSFormat &fmt_src) { const ::VSFormat * fmt_dst_ptr = &fmt_src; // Full colorspace int csp_dst = get_arg_int (in, out, "csp", ::pfNone); if (csp_dst != ::pfNone) { fmt_dst_ptr = _vsapi.getFormatPreset (csp_dst, &core); if (fmt_dst_ptr == 0) { throw_inval_arg ("unknown output colorspace."); } } int col_fam = fmt_dst_ptr->colorFamily; int spl_type = fmt_dst_ptr->sampleType; int bits = fmt_dst_ptr->bitsPerSample; int ssh = fmt_dst_ptr->subSamplingW; int ssv = fmt_dst_ptr->subSamplingH; // Color family _col_fam = get_arg_int (in, out, "col_fam", col_fam); // Chroma subsampling std::string css (get_arg_str (in, out, "css", "")); if (! css.empty ()) { const int ret_val = vsutl::conv_str_to_chroma_subspl (ssh, ssv, css); if (ret_val != 0) { throw_inval_arg ("unsupported css value."); } } // Destination bit depth and sample type bool bits_def_flag = false; bool flt_def_flag = false; int flt = (spl_type != ::stInteger) ? 1 : 0; bits = get_arg_int (in, out, "bits", bits, 0, &bits_def_flag); flt = get_arg_int (in, out, "flt" , flt, 0, &flt_def_flag ); spl_type = (flt != 0) ? ::stFloat : ::stInteger; if (flt_def_flag && ! bits_def_flag) { if (spl_type == ::stFloat) { bits = 32; } else { if (bits > 16) { throw_inval_arg ( "Cannot deduce the output bitdepth. Please specify it." ); } } } else if (bits_def_flag && ! flt_def_flag) { if (bits >= 32) { spl_type = ::stFloat; } else { spl_type = ::stInteger; } } // Combines the modified parameters and validates the format try { fmt_dst_ptr = register_format ( _col_fam, spl_type, bits, ssh, ssv, core ); } catch (std::exception &) { throw; } catch (...) { fmt_dst_ptr = 0; } if (fmt_dst_ptr == 0) { throw_rt_err ( "couldn\'t get a pixel format identifier for the output clip." ); } _vi_out.format = fmt_dst_ptr; }
Matrix::Matrix (const ::VSMap &in, ::VSMap &out, void * /*user_data_ptr*/, ::VSCore &core, const ::VSAPI &vsapi) : vsutl::FilterBase (vsapi, "matrix", ::fmParallel, 0) , _clip_src_sptr (vsapi.propGetNode (&in, "clip", 0, 0), vsapi) , _vi_in (*_vsapi.getVideoInfo (_clip_src_sptr.get ())) , _vi_out (_vi_in) , _sse_flag (false) , _sse2_flag (false) , _avx_flag (false) , _avx2_flag (false) , _range_set_src_flag (false) , _range_set_dst_flag (false) , _full_range_src_flag (false) , _full_range_dst_flag (false) /*, _mat_main ()*/ , _csp_out (fmtcl::ColorSpaceH265_UNSPECIFIED) , _plane_out (get_arg_int (in, out, "singleout", -1)) , _proc_uptr () { assert (&in != 0); assert (&out != 0); assert (&core != 0); assert (&vsapi != 0); vsutl::CpuOpt cpu_opt (*this, in, out); _sse_flag = cpu_opt.has_sse (); _sse2_flag = cpu_opt.has_sse2 (); _avx_flag = cpu_opt.has_avx (); _avx2_flag = cpu_opt.has_avx2 (); _proc_uptr = std::unique_ptr <fmtcl::MatrixProc> (new fmtcl::MatrixProc ( _sse_flag, _sse2_flag, _avx_flag, _avx2_flag )); // Checks the input clip if (_vi_in.format == 0) { throw_inval_arg ("only constant pixel formats are supported."); } const ::VSFormat & fmt_src = *_vi_in.format; if (fmt_src.subSamplingW != 0 || fmt_src.subSamplingH != 0) { throw_inval_arg ("input must be 4:4:4."); } if (fmt_src.numPlanes != NBR_PLANES) { throw_inval_arg ("greyscale format not supported as input."); } if ( ( fmt_src.sampleType == ::stInteger && ( fmt_src.bitsPerSample < 8 || fmt_src.bitsPerSample > 12) && fmt_src.bitsPerSample != 16) || ( fmt_src.sampleType == ::stFloat && fmt_src.bitsPerSample != 32)) { throw_inval_arg ("pixel bitdepth not supported."); } if (_plane_out >= NBR_PLANES) { throw_inval_arg ( "singleout is a plane index and must be -1 or ranging from 0 to 3." ); } // Destination colorspace bool force_col_fam_flag; const ::VSFormat * fmt_dst_ptr = get_output_colorspace ( in, out, core, fmt_src, _plane_out, force_col_fam_flag ); if ( fmt_dst_ptr->colorFamily != ::cmGray && fmt_dst_ptr->colorFamily != ::cmRGB && fmt_dst_ptr->colorFamily != ::cmYUV && fmt_dst_ptr->colorFamily != ::cmYCoCg) { throw_inval_arg ("unsupported color family for output."); } if ( ( fmt_dst_ptr->sampleType == ::stInteger && ( fmt_dst_ptr->bitsPerSample < 8 || fmt_dst_ptr->bitsPerSample > 12) && fmt_dst_ptr->bitsPerSample != 16) || ( fmt_dst_ptr->sampleType == ::stFloat && fmt_dst_ptr->bitsPerSample != 32)) { throw_inval_arg ("output bitdepth not supported."); } if ( fmt_dst_ptr->sampleType != fmt_src.sampleType || fmt_dst_ptr->bitsPerSample < fmt_src.bitsPerSample || fmt_dst_ptr->subSamplingW != fmt_src.subSamplingW || fmt_dst_ptr->subSamplingH != fmt_src.subSamplingH) { throw_inval_arg ( "specified output colorspace is not compatible with the input." ); } // Preliminary matrix test: deduce the target color family if unspecified if ( ! force_col_fam_flag && fmt_dst_ptr->colorFamily != ::cmGray) { int def_count = 0; def_count += is_arg_defined (in, "mat" ) ? 1 : 0; def_count += is_arg_defined (in, "mats") ? 1 : 0; def_count += is_arg_defined (in, "matd") ? 1 : 0; if (def_count == 1) { std::string tmp_mat (get_arg_str (in, out, "mat", "")); tmp_mat = get_arg_str (in, out, "mats", tmp_mat); tmp_mat = get_arg_str (in, out, "matd", tmp_mat); fmtcl::ColorSpaceH265 tmp_csp = find_cs_from_mat_str (*this, tmp_mat, false); fmt_dst_ptr = find_dst_col_fam (tmp_csp, fmt_dst_ptr, fmt_src, core); } } // Output format is validated. _vi_out.format = fmt_dst_ptr; const ::VSFormat &fmt_dst = *fmt_dst_ptr; const int nbr_expected_coef = NBR_PLANES * (NBR_PLANES + 1); bool mat_init_flag = false; // Matrix presets std::string mat (get_arg_str (in, out, "mat", "")); std::string mats (( fmt_src.colorFamily == ::cmYUV ) ? mat : ""); std::string matd (( fmt_dst.colorFamily == ::cmYUV || fmt_dst.colorFamily == ::cmGray) ? mat : ""); mats = get_arg_str (in, out, "mats", mats); matd = get_arg_str (in, out, "matd", matd); if (! mats.empty () || ! matd.empty ()) { fstb::conv_to_lower_case (mats); fstb::conv_to_lower_case (matd); select_def_mat (mats, fmt_src); select_def_mat (matd, fmt_dst); fmtcl::Mat4 m2s; fmtcl::Mat4 m2d; make_mat_from_str (m2s, mats, true); make_mat_from_str (m2d, matd, false); _csp_out = find_cs_from_mat_str (*this, matd, false); _mat_main = m2d * m2s; mat_init_flag = true; } // Range _full_range_src_flag = (get_arg_int ( in, out, "fulls" , vsutl::is_full_range_default (fmt_src) ? 1 : 0, 0, &_range_set_src_flag ) != 0); _full_range_dst_flag = (get_arg_int ( in, out, "fulld", vsutl::is_full_range_default (fmt_dst) ? 1 : 0, 0, &_range_set_dst_flag ) != 0); // Custom coefficients const int nbr_coef = _vsapi.propNumElements (&in, "coef"); const bool custom_mat_flag = (nbr_coef > 0); if (custom_mat_flag) { if (nbr_coef != nbr_expected_coef) { throw_inval_arg ("coef has a wrong number of elements."); } for (int y = 0; y < NBR_PLANES + 1; ++y) { for (int x = 0; x < NBR_PLANES + 1; ++x) { _mat_main [y] [x] = (x == y) ? 1 : 0; if ( (x < fmt_src.numPlanes || x == NBR_PLANES) && y < fmt_dst.numPlanes) { int err = 0; const int index = y * (fmt_src.numPlanes + 1) + x; const double c = _vsapi.propGetFloat (&in, "coef", index, &err); if (err != 0) { throw_rt_err ("error while reading the matrix coefficients."); } _mat_main [y] [x] = c; } } } mat_init_flag = true; } if (! mat_init_flag) { throw_inval_arg ( "you must specify a matrix preset or a custom coefficient list." ); } prepare_coef (fmt_dst, fmt_src); if (_vsapi.getError (&out) != 0) { throw -1; } }
void Matrix::prepare_coef (const ::VSFormat &fmt_dst, const ::VSFormat &fmt_src) { const bool int_proc_flag = ( fmt_src.sampleType == ::stInteger && fmt_dst.sampleType == ::stInteger); fmtcl::Mat4 m (1, fmtcl::Mat4::Preset_DIAGONAL); ::VSFormat fmt_dst2 = fmt_dst; if (int_proc_flag) { // For the coefficient calculation, use the same output bitdepth // as the input. The bitdepth change will be done separately with // a simple bitshift. fmt_dst2.bitsPerSample = fmt_src.bitsPerSample; } override_fmt_with_csp (fmt_dst2); fmtcl::Mat4 m1s; fmtcl::Mat4 m1d; make_mat_flt_int (m1s, true , fmt_src , _full_range_src_flag); make_mat_flt_int (m1d, false, fmt_dst2, _full_range_dst_flag); m *= m1d; if (! int_proc_flag) { if (_plane_out > 0 && vsutl::is_chroma_plane (fmt_dst2, _plane_out)) { // When we extract a single plane, it's a conversion to R or // to Y, so the outout range is always [0; 1]. Therefore we // need to offset the chroma planes. m [_plane_out] [NBR_PLANES] += 0.5; } } m *= _mat_main; m *= m1s; const fmtcl::SplFmt splfmt_src = conv_vsfmt_to_splfmt (fmt_src); const fmtcl::SplFmt splfmt_dst = conv_vsfmt_to_splfmt (fmt_dst); const fmtcl::MatrixProc::Err ret_val = _proc_uptr->configure ( m, int_proc_flag, splfmt_src, fmt_src.bitsPerSample, splfmt_dst, fmt_dst.bitsPerSample, _plane_out ); if (ret_val != fmtcl::MatrixProc::Err_OK) { if (ret_val == fmtcl::MatrixProc::Err_POSSIBLE_OVERFLOW) { throw_inval_arg ("one of the coefficients could cause an overflow."); } else if (ret_val == fmtcl::MatrixProc::Err_TOO_BIG_COEF) { throw_inval_arg ("too big matrix coefficient."); } else if (ret_val == fmtcl::MatrixProc::Err_INVALID_FORMAT_COMBINATION) { throw_inval_arg ("invalid frame format combination."); } else { assert (false); throw_inval_arg ("unidentified error while building the matrix."); } } }
const ::VSFormat * Matrix::get_output_colorspace (const ::VSMap &in, ::VSMap &out, ::VSCore &core, const ::VSFormat &fmt_src, int &plane_out, bool &force_col_fam_flag) const { assert (&in != 0); assert (&out != 0); assert (&core != 0); assert (&fmt_src != 0); assert (&plane_out != 0); force_col_fam_flag = false; const ::VSFormat * fmt_dst_ptr = &fmt_src; // Full colorspace int csp_dst = get_arg_int (in, out, "csp", ::pfNone); if (csp_dst != ::pfNone) { fmt_dst_ptr = _vsapi.getFormatPreset (csp_dst, &core); if (fmt_dst_ptr == 0) { throw_inval_arg ("unknown output colorspace."); } else { force_col_fam_flag = true; } } int col_fam = fmt_dst_ptr->colorFamily; int spl_type = fmt_dst_ptr->sampleType; int bits = fmt_dst_ptr->bitsPerSample; int ssh = fmt_dst_ptr->subSamplingW; int ssv = fmt_dst_ptr->subSamplingH; // Color family if (is_arg_defined (in, "col_fam")) { force_col_fam_flag = true; col_fam = get_arg_int (in, out, "col_fam", col_fam); } if (plane_out >= 0) { col_fam = ::cmGray; } else if (col_fam == ::cmGray) { plane_out = 0; } // Destination bit depth bits = get_arg_int (in, out, "bits", bits); // Combines the modified parameters and validates the format try { fmt_dst_ptr = register_format ( col_fam, spl_type, bits, ssh, ssv, core ); } catch (std::exception &) { throw; } catch (...) { fmt_dst_ptr = 0; } if (fmt_dst_ptr == 0) { throw_rt_err ( "couldn\'t get a pixel format identifier for the output clip." ); } return (fmt_dst_ptr); }
Transfer::Transfer (const ::VSMap &in, ::VSMap &out, void * /*user_data_ptr*/, ::VSCore &core, const ::VSAPI &vsapi) : vsutl::FilterBase (vsapi, "transfer", ::fmParallel, 0) , _clip_src_sptr (vsapi.propGetNode (&in, "clip", 0, 0), vsapi) , _vi_in (*_vsapi.getVideoInfo (_clip_src_sptr.get ())) , _vi_out (_vi_in) , _sse2_flag (false) , _avx2_flag (false) , _transs (get_arg_str (in, out, "transs", "")) , _transd (get_arg_str (in, out, "transd", "")) , _contrast (get_arg_flt (in, out, "cont", 1)) , _gcor (get_arg_flt (in, out, "gcor", 1)) , _lvl_black (get_arg_flt (in, out, "blacklvl", 0)) , _full_range_src_flag (get_arg_int (in, out, "fulls", 1) != 0) , _full_range_dst_flag (get_arg_int (in, out, "fulld", 1) != 0) , _curve_s (fmtcl::TransCurve_UNDEF) , _curve_d (fmtcl::TransCurve_UNDEF) , _loglut_flag (false) , _plane_processor (vsapi, *this, "transfer", true) , _lut_uptr () { assert (&in != 0); assert (&out != 0); assert (&core != 0); assert (&vsapi != 0); fstb::conv_to_lower_case (_transs); fstb::conv_to_lower_case (_transd); vsutl::CpuOpt cpu_opt (*this, in, out); _sse2_flag = cpu_opt.has_sse2 (); _avx2_flag = cpu_opt.has_avx2 (); // Checks the input clip if (_vi_in.format == 0) { throw_inval_arg ("only constant pixel formats are supported."); } const ::VSFormat & fmt_src = *_vi_in.format; if ( fmt_src.colorFamily != ::cmGray && fmt_src.colorFamily != ::cmRGB) { throw_inval_arg ("unsupported color family."); } if ( ( fmt_src.sampleType == ::stInteger && ( fmt_src.bitsPerSample < 8 || fmt_src.bitsPerSample > 16)) || ( fmt_src.sampleType == ::stFloat && fmt_src.bitsPerSample != 32)) { throw_inval_arg ("pixel bitdepth not supported."); } // Destination colorspace const ::VSFormat& fmt_dst = get_output_colorspace (in, out, core, fmt_src); if ( ( fmt_dst.sampleType == ::stInteger && fmt_dst.bitsPerSample != 16) || ( fmt_dst.sampleType == ::stFloat && fmt_dst.bitsPerSample != 32)) { throw_inval_arg ("output bitdepth not supported."); } // Output format is validated. _vi_out.format = &fmt_dst; init_table (); }
Primaries::Primaries (const ::VSMap &in, ::VSMap &out, void *user_data_ptr, ::VSCore &core, const ::VSAPI &vsapi) : vsutl::FilterBase (vsapi, "primaries", ::fmParallel, 0) , _clip_src_sptr (vsapi.propGetNode (&in, "clip", 0, 0), vsapi) , _vi_in (*_vsapi.getVideoInfo (_clip_src_sptr.get ())) , _vi_out (_vi_in) , _sse_flag (false) , _sse2_flag (false) , _avx_flag (false) , _avx2_flag (false) , _prim_s () , _prim_d () , _mat_main () , _proc_uptr () { vsutl::CpuOpt cpu_opt (*this, in, out); _sse_flag = cpu_opt.has_sse (); _sse2_flag = cpu_opt.has_sse2 (); _avx_flag = cpu_opt.has_avx (); _avx2_flag = cpu_opt.has_avx2 (); _proc_uptr = std::unique_ptr <fmtcl::MatrixProc> (new fmtcl::MatrixProc ( _sse_flag, _sse2_flag, _avx_flag, _avx2_flag )); // Checks the input clip if (_vi_in.format == 0) { throw_inval_arg ("only constant pixel formats are supported."); } // Source colorspace const ::VSFormat & fmt_src = *_vi_in.format; check_colorspace (fmt_src, "input"); // Destination colorspace (currently the same as the source) const ::VSFormat & fmt_dst = fmt_src; check_colorspace (fmt_dst, "output"); // Output format is validated. _vi_out.format = &fmt_dst; // Primaries _prim_s.init (*this, in, out, "prims"); _prim_s.init (*this, in, out, "rs", "gs", "bs", "ws"); if (! _prim_s.is_ready ()) { throw_inval_arg ("input primaries not set."); } _prim_d = _prim_s; _prim_d.init (*this, in, out, "primd"); _prim_d.init (*this, in, out, "rd", "gd", "bd", "wd"); assert (_prim_d.is_ready ()); const fmtcl::Mat3 mat_conv = compute_conversion_matrix (); _mat_main.insert3 (mat_conv); _mat_main.clean3 (1); prepare_matrix_coef ( *this, *_proc_uptr, _mat_main, fmt_dst, true, fmt_src, true ); if (_vsapi.getError (&out) != 0) { throw -1; } }