Exemple #1
0
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.");
	}
}
Exemple #3
0
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);
}
Exemple #4
0
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 ();
}
Exemple #9
0
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;
	}
}