1 /*****************************************************************************
2 
3         Resample.cpp
4         Author: Laurent de Soras, 2012
5 
6 --- Legal stuff ---
7 
8 This program is free software. It comes without any warranty, to
9 the extent permitted by applicable law. You can redistribute it
10 and/or modify it under the terms of the Do What The Fuck You Want
11 To Public License, Version 2, as published by Sam Hocevar. See
12 http://sam.zoy.org/wtfpl/COPYING for more details.
13 
14 *Tab=3***********************************************************************/
15 
16 
17 
18 #if defined (_MSC_VER)
19 	#pragma warning (1 : 4130 4223 4705 4706)
20 	#pragma warning (4 : 4355 4786 4800)
21 #endif
22 
23 
24 
25 /*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
26 
27 #include "fmtc/CpuOpt.h"
28 #include "fmtc/fnc.h"
29 #include "fmtc/Resample.h"
30 #include "fmtcl/ColorFamily.h"
31 #include "fstb/def.h"
32 #include "vsutl/fnc.h"
33 #include "vsutl/FrameRefSPtr.h"
34 #include "vsutl/PlaneProcMode.h"
35 
36 #include <algorithm>
37 #include <stdexcept>
38 #include <vector>
39 
40 #include <cassert>
41 #include <cctype>
42 #include <cstring>
43 
44 
45 
46 namespace fmtc
47 {
48 
49 
50 
51 /*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
52 
53 
54 
55 static constexpr bool Resample_old_fieldbased_behaviour_flag =
56 	(VAPOURSYNTH_API_VERSION < 0x30002);
57 
58 
59 
Resample(const::VSMap & in,::VSMap & out,void * user_data_ptr,::VSCore & core,const::VSAPI & vsapi)60 Resample::Resample (const ::VSMap &in, ::VSMap &out, void *user_data_ptr, ::VSCore &core, const ::VSAPI &vsapi)
61 :	vsutl::FilterBase (vsapi, "resample", ::fmParallel, 0)
62 ,	_clip_src_sptr (vsapi.propGetNode (&in, "clip", 0, 0), vsapi)
63 ,	_vi_in (*_vsapi.getVideoInfo (_clip_src_sptr.get ()))
64 ,	_vi_out (_vi_in)
65 ,	_interlaced_src (static_cast <Ru::InterlacingParam> (
66 		get_arg_int (in, out, "interlaced", Ru::InterlacingParam_AUTO)
67 	))
68 ,	_interlaced_dst (static_cast <Ru::InterlacingParam> (
69 		get_arg_int (in, out, "interlacedd", _interlaced_src)
70 	))
71 ,	_field_order_src (static_cast <Ru::FieldOrder> (
72 		get_arg_int (in, out, "tff", Ru::FieldOrder_AUTO)
73 	))
74 ,	_field_order_dst (static_cast <Ru::FieldOrder> (
75 		get_arg_int (in, out, "tffd", _field_order_src)
76 	))
77 ,	_int_flag (get_arg_int (in, out, "flt", 0) == 0)
78 ,	_norm_flag (get_arg_int (in, out, "cnorm", 1) != 0)
79 #if defined (_MSC_VER)
80 #pragma warning (push)
81 #pragma warning (disable : 4355)
82 #endif // 'this': used in base member initializer list
83 ,	_plane_processor (vsapi, *this, "resample", true)
84 #if defined (_MSC_VER)
85 #pragma warning (pop)
86 #endif
87 {
88 	fstb::unused (user_data_ptr);
89 
90 	const fmtc::CpuOpt   cpu_opt (*this, in, out);
91 	_sse2_flag = cpu_opt.has_sse2 ();
92 	_avx2_flag = cpu_opt.has_avx2 ();
93 
94 	// Checks the input clip
95 	if (! vsutl::is_constant_format (_vi_in))
96 	{
97 		throw_inval_arg ("only constant formats are supported.");
98 	}
99 
100 	const ::VSFormat &   fmt_src = *_vi_in.format;
101 
102 	{
103 		const int            st  = fmt_src.sampleType;
104 		const int            bps = fmt_src.bytesPerSample;
105 		const int            res = fmt_src.bitsPerSample;
106 		if (! (   (st == ::stInteger && bps == 1 &&     res ==  8 )
107 		       || (st == ::stInteger && bps == 2 && (   res ==  9
108 		                                             || res == 10
109 		                                             || res == 12
110 		                                             || res == 14
111 		                                             || res == 16))
112 		       || (st == ::stFloat   && bps == 4 &&     res == 32 )))
113 		{
114 			throw_inval_arg ("input pixel bitdepth not supported.");
115 		}
116 		if (fmt_src.colorFamily == ::cmCompat)
117 		{
118 			throw_inval_arg ("\"compat\"colorspace not supported.");
119 		}
120 	}
121 
122 	_src_width  = _vi_in.width;
123 	_src_height = _vi_in.height;
124 	conv_vsfmt_to_splfmt (_src_type, _src_res, *_vi_in.format);
125 
126 	// Destination colorspace
127 	::VSFormat     fmt_def = fmt_src;
128 	if (fmt_def.sampleType == ::stInteger && fmt_def.bitsPerSample < 16)
129 	{
130 		fmt_def.bitsPerSample = 16;
131 	}
132 
133 	const ::VSFormat& fmt_dst = get_output_colorspace (in, out, core, fmt_def);
134 
135 	// Checks the provided format
136 	const int      st  = fmt_dst.sampleType;
137 	const int      bps = fmt_dst.bytesPerSample;
138 	const int      res = fmt_dst.bitsPerSample;
139 	if (res < 16)
140 	{
141 		throw_inval_arg (
142 			"cannot output 8-, 9-, 12- or 12-bit data directly. "
143 			"Output to 16 bits then dither with fmtc.bitdepth."
144 		);
145 	}
146 	if (! (   (st == ::stInteger && bps == 2 && res == 16)
147 	       || (st == ::stFloat   && bps == 4 && res == 32)))
148 	{
149 		throw_inval_arg ("specified output pixel bitdepth not supported.");
150 	}
151 	if (   fmt_dst.colorFamily  != fmt_src.colorFamily
152 	    || fmt_dst.numPlanes    != fmt_src.numPlanes)
153 	{
154 		throw_inval_arg (
155 			"specified output colorspace is not compatible with input."
156 		);
157 	}
158 
159 	// Done with the format
160 	_vi_out.format = &fmt_dst;
161 
162 	conv_vsfmt_to_splfmt (_dst_type, _dst_res, *_vi_out.format);
163 
164 	if (   _interlaced_src < 0
165 	    || _interlaced_src >= Ru::InterlacingParam_NBR_ELT)
166 	{
167 		throw_inval_arg ("interlaced argument out of range.");
168 	}
169 	if (   _interlaced_dst < 0
170 	    || _interlaced_dst >= Ru::InterlacingParam_NBR_ELT)
171 	{
172 		throw_inval_arg ("interlacedd argument out of range.");
173 	}
174 	if (   _field_order_src < 0
175 	    || _field_order_src >= Ru::FieldOrder_NBR_ELT)
176 	{
177 		throw_inval_arg ("tff argument out of range.");
178 	}
179 	if (   _field_order_dst < 0
180 	    || _field_order_dst >= Ru::FieldOrder_NBR_ELT)
181 	{
182 		throw_inval_arg ("tffd argument out of range.");
183 	}
184 
185 	_full_range_in_flag  = (get_arg_int (
186 		in, out, "fulls" ,
187 		vsutl::is_full_range_default (fmt_src) ? 1 : 0,
188 		0, &_range_set_in_flag
189 	) != 0);
190 	_full_range_out_flag = (get_arg_int (
191 		in, out, "fulld",
192 		_full_range_in_flag ? 1 : 0,
193 		0, &_range_set_out_flag
194 	) != 0);
195 
196 	for (int plane_index = 0; plane_index < fmt_src.numPlanes; ++plane_index)
197 	{
198 		auto &         plane_data = _plane_data_arr [plane_index];
199 		vsutl::compute_fmt_mac_cst (
200 			plane_data._gain,
201 			plane_data._add_cst,
202 			*_vi_out.format, _full_range_out_flag,
203 			fmt_src, _full_range_in_flag,
204 			plane_index
205 		);
206 	}
207 
208 	// Target size: scale
209 	const double   scale  = get_arg_flt (in, out, "scale" ,     0);
210 	const double   scaleh = get_arg_flt (in, out, "scaleh", scale);
211 	const double   scalev = get_arg_flt (in, out, "scalev", scale);
212 	if (scaleh < 0 || scalev < 0)
213 	{
214 		throw_inval_arg ("scale parameters must be positive or 0.");
215 	}
216 	if (scaleh > 0)
217 	{
218 		const int      cssh = 1 << _vi_out.format->subSamplingW;
219 		const int      wtmp = fstb::round_int (_vi_out.width  * scaleh / cssh);
220 		_vi_out.width = std::max (wtmp, 1) * cssh;
221 	}
222 	if (scalev > 0)
223 	{
224 		const int      cssv = 1 << _vi_out.format->subSamplingH;
225 		const int      htmp = fstb::round_int (_vi_out.height * scalev / cssv);
226 		_vi_out.height = std::max (htmp, 1) * cssv;
227 	}
228 
229 	// Target size: explicit dimensions
230 	_vi_out.width = get_arg_int (in, out, "w", _vi_out.width);
231 	if (_vi_out.width < 1)
232 	{
233 		throw_inval_arg ("w must be positive.");
234 	}
235 	else if ((_vi_out.width & ((1 << _vi_out.format->subSamplingW) - 1)) != 0)
236 	{
237 		throw_inval_arg (
238 			"w is not compatible with the output chroma subsampling."
239 		);
240 	}
241 
242 	_vi_out.height = get_arg_int (in, out, "h", _vi_out.height);
243 	if (_vi_out.height < 1)
244 	{
245 		throw_inval_arg ("h must be positive.");
246 	}
247 	else if ((_vi_out.height & ((1 << _vi_out.format->subSamplingH) - 1)) != 0)
248 	{
249 		throw_inval_arg (
250 			"h is not compatible with the output chroma subsampling."
251 		);
252 	}
253 
254 	// Chroma placement
255 	const std::string cplace_str = get_arg_str (in, out, "cplace", "mpeg2");
256 	_cplace_s = conv_str_to_chroma_placement (
257 		*this, get_arg_str (in, out, "cplaces", cplace_str)
258 	);
259 	_cplace_d = conv_str_to_chroma_placement (
260 		*this, get_arg_str (in, out, "cplaced", cplace_str, 0, &_cplace_d_set_flag)
261 	);
262 
263 	// Could be per-plane, but it would be more complicated to use with the
264 	// Vapoursynth interface
265 	const std::vector <double> impulse   =
266 		get_arg_vflt (in, out, "impulse" , { });
267 	const std::vector <double> impulse_h =
268 		get_arg_vflt (in, out, "impulseh", impulse);
269 	const std::vector <double> impulse_v =
270 		get_arg_vflt (in, out, "impulsev", impulse);
271 
272 	// Per-plane parameters
273 	const int      nbr_sx = _vsapi.propNumElements (&in, "sx");
274 	const int      nbr_sy = _vsapi.propNumElements (&in, "sy");
275 	const int      nbr_sw = _vsapi.propNumElements (&in, "sw");
276 	const int      nbr_sh = _vsapi.propNumElements (&in, "sh");
277 	for (int plane_index = 0; plane_index < fmt_src.numPlanes; ++plane_index)
278 	{
279 		auto &         plane_data = _plane_data_arr [plane_index];
280 
281 		// Source window
282 		auto &         s = plane_data._win;
283 		if (plane_index > 0)
284 		{
285 			s = _plane_data_arr [plane_index - 1]._win;
286 		}
287 		else
288 		{
289 			s._x = 0;
290 			s._y = 0;
291 			s._w = 0;
292 			s._h = 0;
293 		}
294 
295 		if (plane_index < nbr_sx)
296 		{
297 			s._x = get_arg_flt (in, out, "sx", s._x, plane_index);
298 		}
299 		if (plane_index < nbr_sy)
300 		{
301 			s._y = get_arg_flt (in, out, "sy", s._y, plane_index);
302 		}
303 		if (plane_index < nbr_sw)
304 		{
305 			s._w = get_arg_flt (in, out, "sw", s._w, plane_index);
306 		}
307 		if (plane_index < nbr_sh)
308 		{
309 			s._h = get_arg_flt (in, out, "sh", s._h, plane_index);
310 		}
311 
312 		const double   eps = 1e-9;
313 
314 		if (fstb::is_null (s._w, eps))
315 		{
316 			s._w = _src_width;
317 		}
318 		else if (s._w < 0)
319 		{
320 			s._w = _src_width + s._w - s._x;
321 			if (s._w <= eps)
322 			{
323 				throw_inval_arg ("sw must be positive.");
324 			}
325 		}
326 
327 		if (fstb::is_null (s._h, eps))
328 		{
329 			s._h = _src_height;
330 		}
331 		else if (s._h < 0)
332 		{
333 			s._h = _src_height + s._h - s._y;
334 			if (s._h <= eps)
335 			{
336 				throw_inval_arg ("sh must be positive.");
337 			}
338 		}
339 
340 		// Kernel
341 		std::string    kernel_fnc     = get_arg_str (in, out, "kernel", "spline36", -plane_index);
342 		std::string    kernel_fnc_h   = get_arg_str (in, out, "kernelh", ""       , -plane_index);
343 		std::string    kernel_fnc_v   = get_arg_str (in, out, "kernelv", ""       , -plane_index);
344 		const int      kovrspl        = get_arg_int (in, out, "kovrspl", 0   , -plane_index);
345 		const int      taps           = get_arg_int (in, out, "taps"   , 4   , -plane_index);
346 		const int      taps_h         = get_arg_int (in, out, "tapsh"  , taps, -plane_index);
347 		const int      taps_v         = get_arg_int (in, out, "tapsv"  , taps, -plane_index);
348 		bool           a1_flag, a1_h_flag, a1_v_flag;
349 		bool           a2_flag, a2_h_flag, a2_v_flag;
350 		bool           a3_flag, a3_h_flag, a3_v_flag;
351 		const double   a1             = get_arg_flt (in, out, "a1", 0.0, -plane_index, &a1_flag);
352 		const double   a2             = get_arg_flt (in, out, "a2", 0.0, -plane_index, &a2_flag);
353 		const double   a3             = get_arg_flt (in, out, "a3", 0.0, -plane_index, &a3_flag);
354 		const double   a1_h           = get_arg_flt (in, out, "a1h", a1, -plane_index, &a1_h_flag);
355 		const double   a2_h           = get_arg_flt (in, out, "a2h", a2, -plane_index, &a2_h_flag);
356 		const double   a3_h           = get_arg_flt (in, out, "a3h", a3, -plane_index, &a3_h_flag);
357 		const double   a1_v           = get_arg_flt (in, out, "a1v", a1, -plane_index, &a1_v_flag);
358 		const double   a2_v           = get_arg_flt (in, out, "a2v", a2, -plane_index, &a2_v_flag);
359 		const double   a3_v           = get_arg_flt (in, out, "a3v", a3, -plane_index, &a3_v_flag);
360 		const double   total          = get_arg_flt (in, out, "total", 0.0, -plane_index);
361 		plane_data._norm_val_h        = get_arg_flt (in, out, "totalh", total, -plane_index);
362 		plane_data._norm_val_v        = get_arg_flt (in, out, "totalv", total, -plane_index);
363 		const bool     invks_flag     = (get_arg_int (in, out, "invks", 0, -plane_index) != 0);
364 		const bool     invks_h_flag   = cumulate_flag (invks_flag, in, out, "invksh", -plane_index);
365 		const bool     invks_v_flag   = cumulate_flag (invks_flag, in, out, "invksv", -plane_index);
366 		const int      invks_taps     = get_arg_int (in, out, "invkstaps" , 4         , -plane_index);
367 		const int      invks_taps_h   = get_arg_int (in, out, "invkstapsh", invks_taps, -plane_index);
368 		const int      invks_taps_v   = get_arg_int (in, out, "invkstapsv", invks_taps, -plane_index);
369 		plane_data._kernel_scale_h    = get_arg_flt (in, out, "fh", 1.0, -plane_index);
370 		plane_data._kernel_scale_v    = get_arg_flt (in, out, "fv", 1.0, -plane_index);
371 		plane_data._preserve_center_flag = (get_arg_int (in, out, "center", 1, -plane_index) != 0);
372 		if (kernel_fnc_h.empty ())
373 		{
374 			kernel_fnc_h = kernel_fnc;
375 		}
376 		if (kernel_fnc_v.empty ())
377 		{
378 			kernel_fnc_v = kernel_fnc;
379 		}
380 		if (fstb::is_null (plane_data._kernel_scale_h))
381 		{
382 			throw_inval_arg ("fh cannot be null.");
383 		}
384 		if (fstb::is_null (plane_data._kernel_scale_v))
385 		{
386 			throw_inval_arg ("fv cannot be null.");
387 		}
388 		if (   taps_h < 1 || taps_h > Ru::_max_nbr_taps
389 		    || taps_v < 1 || taps_v > Ru::_max_nbr_taps)
390 		{
391 			throw_inval_arg ("taps* must be in the 1-128 range.");
392 		}
393 		if (plane_data._norm_val_h < 0)
394 		{
395 			throw_inval_arg ("totalh must be positive or null.");
396 		}
397 		if (plane_data._norm_val_v < 0)
398 		{
399 			throw_inval_arg ("totalv must be positive or null.");
400 		}
401 		if (   invks_taps_h < 1 || invks_taps_h > Ru::_max_nbr_taps
402 		    || invks_taps_v < 1 || invks_taps_v > Ru::_max_nbr_taps)
403 		{
404 			throw_inval_arg ("invkstaps* must be in the 1-128 range.");
405 		}
406 
407 		// Serious stuff now
408 		try
409 		{
410 			plane_data._kernel_arr [fmtcl::FilterResize::Dir_H].create_kernel (
411 				kernel_fnc_h, impulse_h, taps_h,
412 				(a1_flag || a1_h_flag), a1_h,
413 				(a2_flag || a2_h_flag), a2_h,
414 				(a3_flag || a3_h_flag), a3_h,
415 				kovrspl,
416 				invks_h_flag,
417 				invks_taps_h
418 			);
419 
420 			plane_data._kernel_arr [fmtcl::FilterResize::Dir_V].create_kernel (
421 				kernel_fnc_v, impulse_v, taps_v,
422 				(a1_flag || a1_v_flag), a1_v,
423 				(a2_flag || a2_v_flag), a2_v,
424 				(a3_flag || a3_v_flag), a3_v,
425 				kovrspl,
426 				invks_v_flag,
427 				invks_taps_v
428 			);
429 		}
430 		catch (const std::exception &e)
431 		{
432 			throw_inval_arg (e.what ());
433 		}
434 		catch (...)
435 		{
436 			throw_inval_arg ("resample: failed to create kernel.");
437 		}
438 	}
439 
440 	create_all_plane_specs ();
441 }
442 
443 
444 
init_filter(::VSMap & in,::VSMap & out,::VSNode & node,::VSCore & core)445 void	Resample::init_filter (::VSMap &in, ::VSMap &out, ::VSNode &node, ::VSCore &core)
446 {
447 	fstb::unused (core);
448 
449 	_vsapi.setVideoInfo (&_vi_out, 1, &node);
450 	_plane_processor.set_filter (in, out, _vi_out);
451 }
452 
453 
454 
get_frame(int n,int activation_reason,void * & frame_data_ptr,::VSFrameContext & frame_ctx,::VSCore & core)455 const ::VSFrameRef *	Resample::get_frame (int n, int activation_reason, void * &frame_data_ptr, ::VSFrameContext &frame_ctx, ::VSCore &core)
456 {
457 	assert (n >= 0);
458 
459 	::VSFrameRef *    dst_ptr = nullptr;
460 	::VSNodeRef &     node    = *_clip_src_sptr;
461 
462 	if (activation_reason == ::arInitial)
463 	{
464 		_vsapi.requestFrameFilter (n, &node, &frame_ctx);
465 	}
466 	else if (activation_reason == ::arAllFramesReady)
467 	{
468 		vsutl::FrameRefSPtr	src_sptr (
469 			_vsapi.getFrameFilter (n, &node, &frame_ctx),
470 			_vsapi
471 		);
472 		const ::VSFrameRef & src = *src_sptr;
473 
474 		dst_ptr = _vsapi.newVideoFrame (
475 			_vi_out.format,
476 			_vi_out.width,
477 			_vi_out.height,
478 			&src,
479 			&core
480 		);
481 
482 		Ru::FieldBased prop_fieldbased = Ru::FieldBased_INVALID;
483 		Ru::Field      prop_field      = Ru::Field_INVALID;
484 		const ::VSMap* src_prop_ptr    = _vsapi.getFramePropsRO (&src);
485 		if (src_prop_ptr != nullptr)
486 		{
487 			int            err      = 0;
488 			int64_t        prop_val = -1;
489 			prop_val = _vsapi.propGetInt (src_prop_ptr, "_FieldBased", 0, &err);
490 			prop_fieldbased =
491 				  (err      != 0) ? Ru::FieldBased_INVALID
492 				: (prop_val == 0) ? Ru::FieldBased_FRAMES
493 				: (prop_val == 1) ? Ru::FieldBased_BFF
494 				: (prop_val == 2) ? Ru::FieldBased_TFF
495 				:                   Ru::FieldBased_INVALID;
496 			prop_val = _vsapi.propGetInt (src_prop_ptr, "_Field", 0, &err);
497 			prop_field =
498 				  (err      != 0) ? Ru::Field_INVALID
499 				: (prop_val == 0) ? Ru::Field_BOT
500 				: (prop_val == 1) ? Ru::Field_TOP
501 				:                   Ru::Field_INVALID;
502 		}
503 
504 		// Collects informations from the input frame properties
505 		Ru::FrameInfo  frame_info;
506 		Ru::get_interlacing_param (
507 			frame_info._itl_s_flag, frame_info._top_s_flag,
508 			n, _interlaced_src, _field_order_src, prop_fieldbased, prop_field,
509 			Resample_old_fieldbased_behaviour_flag
510 		);
511 		Ru::get_interlacing_param (
512 			frame_info._itl_d_flag, frame_info._top_d_flag,
513 			n, _interlaced_dst, _field_order_dst, prop_fieldbased, prop_field,
514 			Resample_old_fieldbased_behaviour_flag
515 		);
516 		frame_data_ptr = &frame_info;
517 
518 		const int      ret_val = _plane_processor.process_frame (
519 			*dst_ptr, n, frame_data_ptr, frame_ctx, core, _clip_src_sptr
520 		);
521 
522 		// Output frame properties
523 		if (   ret_val == 0
524 		    && (   _range_set_out_flag
525 		        || _cplace_d_set_flag
526 		        || _interlaced_dst != Ru::InterlacingParam_AUTO))
527 		{
528 			::VSMap &      dst_prop = *(_vsapi.getFramePropsRW (dst_ptr));
529 			if (_range_set_out_flag)
530 			{
531 				const int      cr_val = (_full_range_out_flag) ? 0 : 1;
532 				_vsapi.propSetInt (&dst_prop, "_ColorRange", cr_val, ::paReplace);
533 			}
534 			if (_cplace_d_set_flag)
535 			{
536 				int            cl_val = -1; // Unknown or cannot be expressed
537 				if (   _cplace_d == fmtcl::ChromaPlacement_MPEG2
538 				    || (   _cplace_d == fmtcl::ChromaPlacement_DV
539 				        && _vi_out.format->subSamplingW == 2
540 				        && _vi_out.format->subSamplingH == 0))
541 				{
542 					cl_val = 0; // Left
543 				}
544 				else if (_cplace_d == fmtcl::ChromaPlacement_MPEG1)
545 				{
546 					cl_val = 1; // Center
547 				}
548 				else if (_cplace_d == fmtcl::ChromaPlacement_T_L)
549 				{
550 					cl_val = 2; // Top-left
551 				}
552 
553 				if (cl_val >= 0)
554 				{
555 					_vsapi.propSetInt (&dst_prop, "_ChromaLocation", cl_val, ::paReplace);
556 				}
557 			}
558 			if (_interlaced_dst != Ru::InterlacingParam_AUTO)
559 			{
560 				if (frame_info._itl_d_flag)
561 				{
562 					if (Resample_old_fieldbased_behaviour_flag)
563 					{
564 						_vsapi.propSetInt (&dst_prop, "_FieldBased", 1, ::paReplace);
565 					}
566 					if (_field_order_dst != Ru::FieldOrder_AUTO)
567 					{
568 						if (! Resample_old_fieldbased_behaviour_flag)
569 						{
570 							_vsapi.propSetInt (
571 								&dst_prop, "_FieldBased",
572 								(_field_order_dst == Ru::FieldOrder_BFF) ? 1 : 2,
573 								::paReplace
574 							);
575 						}
576 						_vsapi.propSetInt (
577 							&dst_prop, "_Field",
578 							(frame_info._top_d_flag) ? 1 : 0,
579 							::paReplace
580 						);
581 					}
582 				}
583 				else
584 				{
585 					_vsapi.propSetInt (&dst_prop, "_FieldBased", 0, ::paReplace);
586 					_vsapi.propDeleteKey (&dst_prop, "_Field");
587 				}
588 			}
589 		}
590 
591 		if (ret_val != 0)
592 		{
593 			_vsapi.freeFrame (dst_ptr);
594 			dst_ptr = 0;
595 		}
596 	}
597 
598 	return dst_ptr;
599 }
600 
601 
602 
conv_str_to_chroma_placement(const vsutl::FilterBase & flt,std::string cplace)603 fmtcl::ChromaPlacement	Resample::conv_str_to_chroma_placement (const vsutl::FilterBase &flt, std::string cplace)
604 {
605 	const auto     cp_val = Ru::conv_str_to_chroma_placement (cplace);
606 	if (cp_val < 0)
607 	{
608 		flt.throw_inval_arg ("unexpected cplace string.");
609 	}
610 
611 	return cp_val;
612 }
613 
614 
615 
conv_str_to_chroma_subspl(const vsutl::FilterBase & flt,int & ssh,int & ssv,std::string css)616 void	Resample::conv_str_to_chroma_subspl (const vsutl::FilterBase &flt, int &ssh, int &ssv, std::string css)
617 {
618 	const int      ret_val = Ru::conv_str_to_chroma_subspl (ssh, ssv, css);
619 	if (ret_val != 0)
620 	{
621 		flt.throw_inval_arg ("unsupported css value.");
622 	}
623 }
624 
625 
626 
627 /*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
628 
629 
630 
do_process_plane(::VSFrameRef & dst,int n,int plane_index,void * frame_data_ptr,::VSFrameContext & frame_ctx,::VSCore & core,const vsutl::NodeRefSPtr & src_node1_sptr,const vsutl::NodeRefSPtr & src_node2_sptr,const vsutl::NodeRefSPtr & src_node3_sptr)631 int	Resample::do_process_plane (::VSFrameRef &dst, int n, int plane_index, void *frame_data_ptr, ::VSFrameContext &frame_ctx, ::VSCore &core, const vsutl::NodeRefSPtr &src_node1_sptr, const vsutl::NodeRefSPtr &src_node2_sptr, const vsutl::NodeRefSPtr &src_node3_sptr)
632 {
633 	fstb::unused (core, src_node2_sptr, src_node3_sptr);
634 	assert (src_node1_sptr.get () != nullptr);
635 	assert (frame_data_ptr != nullptr);
636 
637 	int            ret_val = 0;
638 
639 	const vsutl::PlaneProcMode proc_mode =
640 		_plane_processor.get_mode (plane_index);
641 
642 	if (proc_mode == vsutl::PlaneProcMode_PROCESS)
643 	{
644 		const Ru::FrameInfo &   frame_info =
645 			*reinterpret_cast <const Ru::FrameInfo *> (frame_data_ptr);
646 		process_plane_proc (
647 			dst, n, plane_index, frame_ctx, src_node1_sptr, frame_info
648 		);
649 	}
650 
651 	// Copy (and convert)
652 	else if (proc_mode == vsutl::PlaneProcMode_COPY1)
653 	{
654 		process_plane_copy (
655 			dst, n, plane_index, frame_ctx, src_node1_sptr
656 		);
657 	}
658 
659 	// Fill
660 	else if (proc_mode < vsutl::PlaneProcMode_COPY1)
661 	{
662 		const double   val = _plane_processor.get_mode_val (plane_index);
663 		_plane_processor.fill_plane (dst, val, plane_index);
664 	}
665 
666 	return ret_val;
667 }
668 
669 
670 
671 /*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
672 
673 
674 
675 constexpr int	Resample::_max_nbr_planes;
676 
677 
678 
get_output_colorspace(const::VSMap & in,::VSMap & out,::VSCore & core,const::VSFormat & fmt_src) const679 const ::VSFormat &	Resample::get_output_colorspace (const ::VSMap &in, ::VSMap &out, ::VSCore &core, const ::VSFormat &fmt_src) const
680 {
681 	const ::VSFormat *   fmt_dst_ptr = &fmt_src;
682 
683 	// Full colorspace
684 	int            csp_dst = get_arg_int (in, out, "csp", ::pfNone);
685 	if (csp_dst != ::pfNone)
686 	{
687 		fmt_dst_ptr = _vsapi.getFormatPreset (csp_dst, &core);
688 		if (fmt_dst_ptr == nullptr)
689 		{
690 			throw_inval_arg ("unknown output colorspace.");
691 		}
692 	}
693 
694 	int            col_fam  = fmt_dst_ptr->colorFamily;
695 	int            spl_type = fmt_dst_ptr->sampleType;
696 	int            bits     = fmt_dst_ptr->bitsPerSample;
697 	int            ssh      = fmt_dst_ptr->subSamplingW;
698 	int            ssv      = fmt_dst_ptr->subSamplingH;
699 
700 	// Chroma subsampling
701 	std::string    css (get_arg_str (in, out, "css", ""));
702 	if (! css.empty ())
703 	{
704 		conv_str_to_chroma_subspl (*this, ssh, ssv, css);
705 		if (   (ssh > 0 || ssv > 0)
706 		    && ! vsutl::has_chroma (col_fam))
707 		{
708 			throw_rt_err (
709 				"chroma subsampling not compatible with the output format."
710 			);
711 		}
712 	}
713 
714 	// Combines the modified parameters and validates the format
715 	try
716 	{
717 		fmt_dst_ptr = register_format (
718 			col_fam,
719 			spl_type,
720 			bits,
721 			ssh,
722 			ssv,
723 			core
724 		);
725 	}
726 	catch (...)
727 	{
728 		fmt_dst_ptr = nullptr;
729 	}
730 	if (fmt_dst_ptr == nullptr)
731 	{
732 		throw_rt_err (
733 			"couldn\'t get a pixel format identifier for the output clip."
734 		);
735 	}
736 
737 	return *fmt_dst_ptr;
738 }
739 
740 
741 
cumulate_flag(bool flag,const::VSMap & in,::VSMap & out,const char name_0[],int pos) const742 bool	Resample::cumulate_flag (bool flag, const ::VSMap &in, ::VSMap &out, const char name_0 [], int pos) const
743 {
744 	assert (name_0 != nullptr);
745 
746 	if (is_arg_defined (in, name_0))
747 	{
748 		const int      val = get_arg_int (in, out, name_0, 0, pos);
749 		flag = (val != 0);
750 	}
751 
752 	return flag;
753 }
754 
755 
756 
process_plane_proc(::VSFrameRef & dst,int n,int plane_index,::VSFrameContext & frame_ctx,const vsutl::NodeRefSPtr & src_node1_sptr,const Ru::FrameInfo & frame_info)757 int	Resample::process_plane_proc (::VSFrameRef &dst, int n, int plane_index, ::VSFrameContext &frame_ctx, const vsutl::NodeRefSPtr &src_node1_sptr, const Ru::FrameInfo &frame_info)
758 {
759 	int            ret_val = 0;
760 
761 	vsutl::FrameRefSPtr	src_sptr (
762 		_vsapi.getFrameFilter (n, src_node1_sptr.get (), &frame_ctx),
763 		_vsapi
764 	);
765 	const ::VSFrameRef & src = *src_sptr;
766 
767 	const uint8_t* data_src_ptr = _vsapi.getReadPtr (&src, plane_index);
768 	const int      stride_src   = _vsapi.getStride (&src, plane_index);
769 	uint8_t *      data_dst_ptr = _vsapi.getWritePtr (&dst, plane_index);
770 	const int      stride_dst   = _vsapi.getStride (&dst, plane_index);
771 
772 	const fmtcl::InterlacingType  itl_s = fmtcl::InterlacingType_get (
773 		frame_info._itl_s_flag, frame_info._top_s_flag
774 	);
775 	const fmtcl::InterlacingType  itl_d = fmtcl::InterlacingType_get (
776 		frame_info._itl_d_flag, frame_info._top_d_flag
777 	);
778 
779 	try
780 	{
781 		fmtcl::FilterResize *   filter_ptr = create_or_access_plane_filter (
782 			plane_index,
783 			itl_d,
784 			itl_s
785 		);
786 
787 		const bool     chroma_flag =
788 			vsutl::is_chroma_plane (*_vi_in.format, plane_index);
789 
790 		filter_ptr->process_plane (
791 			data_dst_ptr, data_src_ptr, stride_dst, stride_src, chroma_flag
792 		);
793 	}
794 
795 	catch (const std::exception &e)
796 	{
797 		_vsapi.setFilterError (e.what (), &frame_ctx);
798 		ret_val = -1;
799 	}
800 	catch (...)
801 	{
802 		_vsapi.setFilterError ("resample: exception.", &frame_ctx);
803 		ret_val = -1;
804 	}
805 
806 	return ret_val;
807 }
808 
809 
810 
process_plane_copy(::VSFrameRef & dst,int n,int plane_index,::VSFrameContext & frame_ctx,const vsutl::NodeRefSPtr & src_node1_sptr)811 int	Resample::process_plane_copy (::VSFrameRef &dst, int n, int plane_index, ::VSFrameContext &frame_ctx, const vsutl::NodeRefSPtr &src_node1_sptr)
812 {
813 	int            ret_val = 0;
814 
815 	vsutl::FrameRefSPtr	src_sptr (
816 		_vsapi.getFrameFilter (n, src_node1_sptr.get (), &frame_ctx),
817 		_vsapi
818 	);
819 	const ::VSFrameRef & src = *src_sptr;
820 
821 	const int      src_w = _vsapi.getFrameWidth (&src, plane_index);
822 	const int      src_h = _vsapi.getFrameHeight (&src, plane_index);
823 	const int      dst_w = _vsapi.getFrameWidth (&dst, plane_index);
824 	const int      dst_h = _vsapi.getFrameHeight (&dst, plane_index);
825 
826 	const uint8_t* data_src_ptr = _vsapi.getReadPtr (&src, plane_index);
827 	const int      stride_src   = _vsapi.getStride (&src, plane_index);
828 	uint8_t *      data_dst_ptr = _vsapi.getWritePtr (&dst, plane_index);
829 	const int      stride_dst   = _vsapi.getStride (&dst, plane_index);
830 
831 	const int      w = std::min (src_w, dst_w);
832 	const int      h = std::min (src_h, dst_h);
833 
834 	// Copied from fmtcl::FilterResize::process_plane_bypass()
835 	fmtcl::BitBltConv::ScaleInfo *   scale_info_ptr = nullptr;
836 	fmtcl::BitBltConv::ScaleInfo     scale_info;
837 	const bool     dst_flt_flag = (_dst_type == fmtcl::SplFmt_FLOAT);
838 	const bool     src_flt_flag = (_src_type == fmtcl::SplFmt_FLOAT);
839 	if (dst_flt_flag != src_flt_flag)
840 	{
841 		const auto &   plane_data = _plane_data_arr [plane_index];
842 		scale_info._gain    = plane_data._gain;
843 		scale_info._add_cst = plane_data._add_cst;
844 
845 		scale_info_ptr = &scale_info;
846 	}
847 
848 	fmtcl::BitBltConv blitter (_sse2_flag, _avx2_flag);
849 	blitter.bitblt (
850 		_dst_type, _dst_res, data_dst_ptr, stride_dst,
851 		_src_type, _src_res, data_src_ptr, stride_src,
852 		w, h, scale_info_ptr
853 	);
854 
855 	return ret_val;
856 }
857 
858 
859 
create_or_access_plane_filter(int plane_index,fmtcl::InterlacingType itl_d,fmtcl::InterlacingType itl_s)860 fmtcl::FilterResize *	Resample::create_or_access_plane_filter (int plane_index, fmtcl::InterlacingType itl_d, fmtcl::InterlacingType itl_s)
861 {
862 	assert (plane_index >= 0);
863 	assert (plane_index < _max_nbr_planes);
864 	assert (itl_d >= 0);
865 	assert (itl_d < fmtcl::InterlacingType_NBR_ELT);
866 	assert (itl_s >= 0);
867 	assert (itl_s < fmtcl::InterlacingType_NBR_ELT);
868 
869 	const auto &   plane_data = _plane_data_arr [plane_index];
870 	const fmtcl::ResampleSpecPlane & key = plane_data._spec_arr [itl_d] [itl_s];
871 
872 	std::lock_guard <std::mutex>  autolock (_filter_mutex);
873 
874 	std::unique_ptr <fmtcl::FilterResize> &   filter_uptr = _filter_uptr_map [key];
875 	if (filter_uptr.get () == nullptr)
876 	{
877 		filter_uptr = std::make_unique <fmtcl::FilterResize> (
878 			key,
879 			*(plane_data._kernel_arr [fmtcl::FilterResize::Dir_H]._k_uptr),
880 			*(plane_data._kernel_arr [fmtcl::FilterResize::Dir_V]._k_uptr),
881 			_norm_flag, plane_data._norm_val_h, plane_data._norm_val_v,
882 			plane_data._gain,
883 			_src_type, _src_res, _dst_type, _dst_res,
884 			_int_flag, _sse2_flag, _avx2_flag
885 		);
886 	}
887 
888 	return filter_uptr.get ();
889 }
890 
891 
892 
create_all_plane_specs()893 void	Resample::create_all_plane_specs ()
894 {
895 	const fmtcl::ColorFamily src_cf = fmtc::conv_vsfmt_to_colfam (*_vi_in.format);
896 	const fmtcl::ColorFamily dst_cf = fmtc::conv_vsfmt_to_colfam (*_vi_out.format);
897 	const int      src_ss_h   = _vi_in.format->subSamplingW;
898 	const int      src_ss_v   = _vi_in.format->subSamplingH;
899 	const int      dst_ss_h   = _vi_out.format->subSamplingW;
900 	const int      dst_ss_v   = _vi_out.format->subSamplingH;
901 	const int      nbr_planes = _vi_in.format->numPlanes;
902 	for (int plane_index = 0; plane_index < nbr_planes; ++plane_index)
903 	{
904 		auto &         plane_data = _plane_data_arr [plane_index];
905 		Ru::create_plane_specs (
906 			plane_data, plane_index,
907 			src_cf, _src_width   , src_ss_h, _src_height   , src_ss_v, _cplace_s,
908 			dst_cf, _vi_out.width, dst_ss_h, _vi_out.height, dst_ss_v, _cplace_d
909 		);
910 	}
911 }
912 
913 
914 
915 }	// namespace fmtc
916 
917 
918 
919 /*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
920