1 /*****************************************************************************
2 
3         Resample.cpp
4         Author: Laurent de Soras, 2021
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://www.wtfpl.net/ 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 "fmtcavs/CpuOpt.h"
28 #include "fmtcavs/fnc.h"
29 #include "fmtcavs/function_names.h"
30 #include "fmtcavs/Resample.h"
31 #include "fmtcl/BitBltConv.h"
32 #include "fmtcl/fnc.h"
33 
34 #include <stdexcept>
35 
36 #include <cassert>
37 
38 
39 
40 namespace fmtcavs
41 {
42 
43 
44 
45 /*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
46 
47 
48 
Resample(::IScriptEnvironment & env,const::AVSValue & args)49 Resample::Resample (::IScriptEnvironment &env, const ::AVSValue &args)
50 :	Inherited (env, args [Param_CLIP_SRC].AsClip ())
51 ,	_clip_src_sptr (args [Param_CLIP_SRC].AsClip ())
52 ,	_vi_src (vi)
53 ,	_interlaced_src (static_cast <Ru::InterlacingParam> (
54 		args [Param_INTERLACED].AsInt (Ru::InterlacingParam_AUTO)
55 	))
56 ,	_interlaced_dst (static_cast <Ru::InterlacingParam> (
57 		args [Param_INTERLACEDD].AsInt (_interlaced_src)
58 	))
59 ,	_field_order_src (static_cast <Ru::FieldOrder> (
60 		args [Param_TFF].AsInt (Ru::FieldOrder_AUTO)
61 	))
62 ,	_field_order_dst (static_cast <Ru::FieldOrder> (
63 		args [Param_TFFD].AsInt (_field_order_src)
64 	))
65 ,	_int_flag (! args [Param_FLT].AsBool (false))
66 ,	_norm_flag (args [Param_CNORM].AsBool (true))
67 {
68 	const CpuOpt   cpu_opt (args [Param_CPUOPT]);
69 	_sse2_flag = cpu_opt.has_sse2 ();
70 	_avx2_flag = cpu_opt.has_avx2 ();
71 
72 	// Checks the input clip
73 	if (! _vi_src.IsPlanar ())
74 	{
75 		env.ThrowError (fmtcavs_RESAMPLE ": input must be planar.");
76 	}
77 
78 	_fmt_src.conv_from_vi (_vi_src);
79 	const bool     src_flt_flag = _fmt_src.is_float ();
80 	const int      nbr_planes_src = _vi_src.NumComponents ();
81 	_src_res = _fmt_src.get_bitdepth ();
82 	if (! (   (! src_flt_flag && (   _src_res ==  8
83 	                              || _src_res == 10
84 	                              || _src_res == 12
85 	                              || _src_res == 14
86 	                              || _src_res == 16))
87 	       || (  src_flt_flag &&     _src_res == 32 )))
88 	{
89 		env.ThrowError (
90 			fmtcavs_RESAMPLE ": input pixel bitdepth not supported."
91 		);
92 	}
93 
94 	_src_width  = _vi_src.width;
95 	_src_height = _vi_src.height;
96 	_src_type   = conv_vi_to_splfmt (_vi_src);
97 
98 	// Destination colorspace
99 	_fmt_dst = _fmt_src;
100 	if (! src_flt_flag && _src_res < 16)
101 	{
102 		_fmt_dst.set_bitdepth (16);
103 	}
104 	_fmt_dst = get_output_colorspace (env, args, _fmt_dst);
105 
106 	// Checks the provided format
107 	const bool     dst_flt_flag   = _fmt_dst.is_float ();
108 	const int      nbr_planes_dst = _vi_src.NumComponents ();
109 	_dst_res = _fmt_dst.get_bitdepth ();
110 	if (_dst_res < 16)
111 	{
112 		env.ThrowError (fmtcavs_RESAMPLE
113 			": cannot output 8-, 12- or 12-bit data directly. "
114 			"Output to 16 bits then dither with " fmtcavs_BITDEPTH "."
115 		);
116 	}
117 	if (! (   (! dst_flt_flag && _dst_res == 16)
118 	       || (  dst_flt_flag && _dst_res == 32)))
119 	{
120 		env.ThrowError (
121 			fmtcavs_RESAMPLE ": specified output pixel bitdepth not supported."
122 		);
123 	}
124 	if (   _fmt_dst.get_col_fam () != _fmt_src.get_col_fam ()
125 	    || nbr_planes_dst          != nbr_planes_src)
126 	{
127 		env.ThrowError (fmtcavs_RESAMPLE
128 			": specified output colorspace is not compatible with input."
129 		);
130 	}
131 
132 	// Done with the format
133 	if (_fmt_dst.conv_to_vi (vi) != 0)
134 	{
135 		env.ThrowError (fmtcavs_RESAMPLE ": invalid output format.");
136 	}
137 	_dst_type = conv_vi_to_splfmt (vi);
138 
139 	if (   _interlaced_src < 0
140 	    || _interlaced_src >= Ru::InterlacingParam_NBR_ELT)
141 	{
142 		env.ThrowError (fmtcavs_RESAMPLE ": interlaced argument out of range.");
143 	}
144 	if (   _interlaced_dst < 0
145 	    || _interlaced_dst >= Ru::InterlacingParam_NBR_ELT)
146 	{
147 		env.ThrowError (fmtcavs_RESAMPLE ": interlacedd argument out of range.");
148 	}
149 	if (   _field_order_src < 0
150 	    || _field_order_src >= Ru::FieldOrder_NBR_ELT)
151 	{
152 		env.ThrowError (fmtcavs_RESAMPLE ": tff argument out of range.");
153 	}
154 	if (   _field_order_dst < 0
155 	    || _field_order_dst >= Ru::FieldOrder_NBR_ELT)
156 	{
157 		env.ThrowError (fmtcavs_RESAMPLE ": tffd argument out of range.");
158 	}
159 
160 	// Range
161 	_range_s_def_flag = args [Param_FULLS].Defined ();
162 	_fulls_flag       = args [Param_FULLS].AsBool (
163 		fmtcl::is_full_range_default (_fmt_src.get_col_fam ())
164 	);
165 	_range_d_def_flag = args [Param_FULLD].Defined ();
166 	_fulld_flag       = args [Param_FULLD].AsBool (
167 		fmtcl::is_full_range_default (_fmt_dst.get_col_fam ())
168 	);
169 
170 	// Configures the plane processor
171 	_plane_proc_uptr =
172 		std::make_unique <avsutl::PlaneProcessor> (vi, *this, true);
173 	_plane_proc_uptr->set_clip_info (
174 		avsutl::PlaneProcessor::ClipIdx_SRC1, _clip_src_sptr
175 	);
176 	_plane_proc_uptr->set_proc_mode (
177 		args [Param_PLANES], env, fmtcavs_RESAMPLE ", planes"
178 	);
179 
180 	const auto     src_picfmt = conv_fmtavs_to_picfmt (_fmt_src, _fulls_flag);
181 	const auto     dst_picfmt = conv_fmtavs_to_picfmt (_fmt_dst, _fulld_flag);
182 	for (int plane_index = 0; plane_index < nbr_planes_src; ++plane_index)
183 	{
184 		auto &         plane_data = _plane_data_arr [plane_index];
185 		fmtcl::compute_fmt_mac_cst (
186 			plane_data._gain, plane_data._add_cst,
187 			dst_picfmt, src_picfmt, plane_index
188 		);
189 	}
190 
191 	// Target size: scale
192 	const double   scale  = args [Param_SCALE ].AsFloat (0);
193 	const double   scaleh = args [Param_SCALEH].AsFloat (float (scale));
194 	const double   scalev = args [Param_SCALEV].AsFloat (float (scale));
195 	if (scaleh < 0 || scalev < 0)
196 	{
197 		env.ThrowError (
198 			fmtcavs_RESAMPLE ": scale parameters must be positive or 0."
199 		);
200 	}
201 	const int      cssh = 1 << _fmt_dst.get_subspl_h ();
202 	if (scaleh > 0)
203 	{
204 		const int      wtmp = fstb::round_int (vi.width  * scaleh / cssh);
205 		vi.width = std::max (wtmp, 1) * cssh;
206 	}
207 	const int      cssv = 1 << _fmt_dst.get_subspl_v ();
208 	if (scalev > 0)
209 	{
210 		const int      htmp = fstb::round_int (vi.height * scalev / cssv);
211 		vi.height = std::max (htmp, 1) * cssv;
212 	}
213 
214 	// Target size: explicit dimensions
215 	vi.width = args [Param_W].AsInt (vi.width);
216 	if (vi.width < 1)
217 	{
218 		env.ThrowError (fmtcavs_RESAMPLE ": w must be positive.");
219 	}
220 	else if ((vi.width & (cssh - 1)) != 0)
221 	{
222 		env.ThrowError (fmtcavs_RESAMPLE
223 			": w is not compatible with the output chroma subsampling."
224 		);
225 	}
226 
227 	vi.height = args [Param_H].AsInt (vi.height);
228 	if (vi.height < 1)
229 	{
230 		env.ThrowError (fmtcavs_RESAMPLE ": h must be positive.");
231 	}
232 	else if ((vi.height & (cssv - 1)) != 0)
233 	{
234 		env.ThrowError (fmtcavs_RESAMPLE
235 			": h is not compatible with the output chroma subsampling."
236 		);
237 	}
238 
239 	// Chroma placement
240 	const std::string cplace_str = args [Param_CPLACE].AsString ("mpeg2");
241 	_cplace_s = conv_str_to_chroma_placement (
242 		env, args [Param_CPLACES].AsString (cplace_str.c_str ())
243 	);
244 	_cplace_d_set_flag =  args [Param_CPLACES].Defined ();
245 	_cplace_d = conv_str_to_chroma_placement (
246 		env, args [Param_CPLACED].AsString (cplace_str.c_str ())
247 	);
248 
249 	// Per-plane parameters
250 	const auto impulse   =
251 		extract_array_f (env, args [Param_IMPULSE ], fmtcavs_RESAMPLE ", impulse");
252 	const auto impulse_h = is_array_defined (args [Param_IMPULSEH])
253 		? extract_array_f (env, args [Param_IMPULSEH], fmtcavs_RESAMPLE ", impulseh")
254 		: impulse;
255 	const auto impulse_v = is_array_defined (args [Param_IMPULSEV])
256 		? extract_array_f (env, args [Param_IMPULSEV], fmtcavs_RESAMPLE ", impulsev")
257 		: impulse;
258 	const bool a1_flag   = is_array_defined (args [Param_A1 ]);
259 	const bool a2_flag   = is_array_defined (args [Param_A2 ]);
260 	const bool a3_flag   = is_array_defined (args [Param_A3 ]);
261 	const bool a1_h_flag = is_array_defined (args [Param_A1H]);
262 	const bool a2_h_flag = is_array_defined (args [Param_A2H]);
263 	const bool a3_h_flag = is_array_defined (args [Param_A3H]);
264 	const bool a1_v_flag = is_array_defined (args [Param_A1V]);
265 	const bool a2_v_flag = is_array_defined (args [Param_A2V]);
266 	const bool a3_v_flag = is_array_defined (args [Param_A3V]);
267 	const auto sx_arr         = extract_array_f (env, args [Param_SX        ], fmtcavs_RESAMPLE ", sx");
268 	const auto sy_arr         = extract_array_f (env, args [Param_SY        ], fmtcavs_RESAMPLE ", sy");
269 	const auto sw_arr         = extract_array_f (env, args [Param_SW        ], fmtcavs_RESAMPLE ", sw");
270 	const auto sh_arr         = extract_array_f (env, args [Param_SH        ], fmtcavs_RESAMPLE ", sh");
271 	const auto kernel_arr     = extract_array_s (env, args [Param_KERNEL    ], fmtcavs_RESAMPLE ", kernel");
272 	const auto kernelh_arr    = extract_array_s (env, args [Param_KERNELH   ], fmtcavs_RESAMPLE ", kernelh");
273 	const auto kernelv_arr    = extract_array_s (env, args [Param_KERNELV   ], fmtcavs_RESAMPLE ", kernelv");
274 	const auto kovrspl_arr    = extract_array_i (env, args [Param_KOVRSPL   ], fmtcavs_RESAMPLE ", koverspl");
275 	const auto taps_arr       = extract_array_i (env, args [Param_TAPS      ], fmtcavs_RESAMPLE ", taps");
276 	const auto tapsh_arr      = extract_array_i (env, args [Param_TAPSH     ], fmtcavs_RESAMPLE ", tapsh");
277 	const auto tapsv_arr      = extract_array_i (env, args [Param_TAPSV     ], fmtcavs_RESAMPLE ", tapsv");
278 	const auto a1_arr         = extract_array_f (env, args [Param_A1        ], fmtcavs_RESAMPLE ", a1");
279 	const auto a2_arr         = extract_array_f (env, args [Param_A2        ], fmtcavs_RESAMPLE ", a2");
280 	const auto a3_arr         = extract_array_f (env, args [Param_A3        ], fmtcavs_RESAMPLE ", a3");
281 	const auto a1h_arr        = extract_array_f (env, args [Param_A1H       ], fmtcavs_RESAMPLE ", a1h");
282 	const auto a2h_arr        = extract_array_f (env, args [Param_A2H       ], fmtcavs_RESAMPLE ", a2h");
283 	const auto a3h_arr        = extract_array_f (env, args [Param_A3H       ], fmtcavs_RESAMPLE ", a3h");
284 	const auto a1v_arr        = extract_array_f (env, args [Param_A1V       ], fmtcavs_RESAMPLE ", a1v");
285 	const auto a2v_arr        = extract_array_f (env, args [Param_A2V       ], fmtcavs_RESAMPLE ", a2v");
286 	const auto a3v_arr        = extract_array_f (env, args [Param_A3V       ], fmtcavs_RESAMPLE ", a3v");
287 	const auto total_arr      = extract_array_f (env, args [Param_TOTAL     ], fmtcavs_RESAMPLE ", total");
288 	const auto totalh_arr     = extract_array_f (env, args [Param_TOTALH    ], fmtcavs_RESAMPLE ", totalh");
289 	const auto totalv_arr     = extract_array_f (env, args [Param_TOTALV    ], fmtcavs_RESAMPLE ", totalv");
290 	const auto invks_arr      = extract_array_b (env, args [Param_INVKS     ], fmtcavs_RESAMPLE ", invks");
291 	const auto invksh_arr     = extract_array_b (env, args [Param_INVKSH    ], fmtcavs_RESAMPLE ", invksh");
292 	const auto invksv_arr     = extract_array_b (env, args [Param_INVKSV    ], fmtcavs_RESAMPLE ", invksv");
293 	const auto invkstaps_arr  = extract_array_i (env, args [Param_INVKSTAPS ], fmtcavs_RESAMPLE ", invkstaps");
294 	const auto invkstapsh_arr = extract_array_i (env, args [Param_INVKSTAPSH], fmtcavs_RESAMPLE ", invkstapsh");
295 	const auto invkstapsv_arr = extract_array_i (env, args [Param_INVKSTAPSV], fmtcavs_RESAMPLE ", invkstapsv");
296 	const auto fh_arr         = extract_array_f (env, args [Param_FH        ], fmtcavs_RESAMPLE ", fh");
297 	const auto fv_arr         = extract_array_f (env, args [Param_FV        ], fmtcavs_RESAMPLE ", fv");
298 	const auto center_arr     = extract_array_b (env, args [Param_CENTER    ], fmtcavs_RESAMPLE ", center");
299 
300 	for (int plane_index = 0; plane_index < nbr_planes_src; ++plane_index)
301 	{
302 		auto &         plane_data = _plane_data_arr [plane_index];
303 
304 		// Source window
305 		auto &         s = plane_data._win;
306 		if (plane_index > 0)
307 		{
308 			s = _plane_data_arr [plane_index - 1]._win;
309 		}
310 		else
311 		{
312 			s._x = 0;
313 			s._y = 0;
314 			s._w = 0;
315 			s._h = 0;
316 		}
317 
318 		if (plane_index < int (sx_arr.size ())) { s._x = sx_arr [plane_index]; }
319 		if (plane_index < int (sy_arr.size ())) { s._y = sy_arr [plane_index]; }
320 		if (plane_index < int (sw_arr.size ())) { s._w = sw_arr [plane_index]; }
321 		if (plane_index < int (sh_arr.size ())) { s._h = sh_arr [plane_index]; }
322 
323 		const double   eps = 1e-9;
324 
325 		if (fstb::is_null (s._w, eps))
326 		{
327 			s._w = _src_width;
328 		}
329 		else if (s._w < 0)
330 		{
331 			s._w = _src_width + s._w - s._x;
332 			if (s._w <= eps)
333 			{
334 				env.ThrowError (fmtcavs_RESAMPLE ": sw must be positive.");
335 			}
336 		}
337 
338 		if (fstb::is_null (s._h, eps))
339 		{
340 			s._h = _src_height;
341 		}
342 		else if (s._h < 0)
343 		{
344 			s._h = _src_height + s._h - s._y;
345 			if (s._h <= eps)
346 			{
347 				env.ThrowError (fmtcavs_RESAMPLE ": sh must be positive.");
348 			}
349 		}
350 
351 		// Kernel
352 		std::string  kernel_fnc    = fmtcl::get_arr_elt (kernel_arr , plane_index, { "spline36" });
353 		std::string  kernel_fnc_h  = fmtcl::get_arr_elt (kernelh_arr, plane_index, kernel_fnc);
354 		std::string  kernel_fnc_v  = fmtcl::get_arr_elt (kernelv_arr, plane_index, kernel_fnc);
355 		const int    kovrspl       = fmtcl::get_arr_elt (kovrspl_arr, plane_index, 0   );
356 		const int    taps          = fmtcl::get_arr_elt (taps_arr   , plane_index, 4   );
357 		const int    taps_h        = fmtcl::get_arr_elt (tapsh_arr  , plane_index, taps);
358 		const int    taps_v        = fmtcl::get_arr_elt (tapsv_arr  , plane_index, taps);
359 		const double a1            = fmtcl::get_arr_elt (a1_arr     , plane_index, 0.0);
360 		const double a2            = fmtcl::get_arr_elt (a2_arr     , plane_index, 0.0);
361 		const double a3            = fmtcl::get_arr_elt (a3_arr     , plane_index, 0.0);
362 		const double a1_h          = fmtcl::get_arr_elt (a1h_arr    , plane_index, a1 );
363 		const double a2_h          = fmtcl::get_arr_elt (a2h_arr    , plane_index, a2 );
364 		const double a3_h          = fmtcl::get_arr_elt (a3h_arr    , plane_index, a3 );
365 		const double a1_v          = fmtcl::get_arr_elt (a1v_arr    , plane_index, a1 );
366 		const double a2_v          = fmtcl::get_arr_elt (a2v_arr    , plane_index, a2 );
367 		const double a3_v          = fmtcl::get_arr_elt (a3v_arr    , plane_index, a3 );
368 		const double total         = fmtcl::get_arr_elt (total_arr  , plane_index, 0.0);
369 		plane_data._norm_val_h     = fmtcl::get_arr_elt (totalh_arr , plane_index, total);
370 		plane_data._norm_val_v     = fmtcl::get_arr_elt (totalv_arr , plane_index, total);
371 		const bool   invks_flag    = fmtcl::get_arr_elt (invks_arr  , plane_index, false);
372 		const bool   invks_h_flag  = fmtcl::get_arr_elt (invksh_arr , plane_index, invks_flag);
373 		const bool   invks_v_flag  = fmtcl::get_arr_elt (invksv_arr , plane_index, invks_flag);
374 		const int    invks_taps    = fmtcl::get_arr_elt (invkstaps_arr , plane_index, 4         );
375 		const int    invks_taps_h  = fmtcl::get_arr_elt (invkstapsh_arr, plane_index, invks_taps);
376 		const int    invks_taps_v  = fmtcl::get_arr_elt (invkstapsv_arr, plane_index, invks_taps);
377 		plane_data._kernel_scale_h = fmtcl::get_arr_elt (fh_arr     , plane_index, 1.0);
378 		plane_data._kernel_scale_v = fmtcl::get_arr_elt (fv_arr     , plane_index, 1.0);
379 		plane_data._preserve_center_flag = fmtcl::get_arr_elt (center_arr, plane_index, true);
380 		if (fstb::is_null (plane_data._kernel_scale_h))
381 		{
382 			env.ThrowError (fmtcavs_RESAMPLE ": fh cannot be null.");
383 		}
384 		if (fstb::is_null (plane_data._kernel_scale_v))
385 		{
386 			env.ThrowError (fmtcavs_RESAMPLE ": 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 			env.ThrowError (
392 				fmtcavs_RESAMPLE ": taps* must be in the 1-128 range."
393 			);
394 		}
395 		if (plane_data._norm_val_h < 0)
396 		{
397 			env.ThrowError (
398 				fmtcavs_RESAMPLE ": totalh must be positive or null."
399 			);
400 		}
401 		if (plane_data._norm_val_v < 0)
402 		{
403 			env.ThrowError (
404 				fmtcavs_RESAMPLE ": totalv must be positive or null."
405 			);
406 		}
407 		if (   invks_taps_h < 1 || invks_taps_h > Ru::_max_nbr_taps
408 		    || invks_taps_v < 1 || invks_taps_v > Ru::_max_nbr_taps)
409 		{
410 			env.ThrowError (
411 				fmtcavs_RESAMPLE ": invkstaps* must be in the 1-128 range."
412 			);
413 		}
414 
415 		// Serious stuff now
416 		try
417 		{
418 			plane_data._kernel_arr [fmtcl::FilterResize::Dir_H].create_kernel (
419 				kernel_fnc_h, impulse_h, taps_h,
420 				(a1_flag || a1_h_flag), a1_h,
421 				(a2_flag || a2_h_flag), a2_h,
422 				(a3_flag || a3_h_flag), a3_h,
423 				kovrspl,
424 				invks_h_flag,
425 				invks_taps_h
426 			);
427 
428 			plane_data._kernel_arr [fmtcl::FilterResize::Dir_V].create_kernel (
429 				kernel_fnc_v, impulse_v, taps_v,
430 				(a1_flag || a1_v_flag), a1_v,
431 				(a2_flag || a2_v_flag), a2_v,
432 				(a3_flag || a3_v_flag), a3_v,
433 				kovrspl,
434 				invks_v_flag,
435 				invks_taps_v
436 			);
437 		}
438 		catch (const std::exception &e)
439 		{
440 			env.ThrowError (fmtcavs_RESAMPLE ": %s", e.what ());
441 		}
442 		catch (...)
443 		{
444 			env.ThrowError (fmtcavs_RESAMPLE ": failed to create kernel.");
445 		}
446 	}
447 
448 	create_all_plane_specs (_fmt_dst, _fmt_src);
449 }
450 
451 
452 
GetFrame(int n,::IScriptEnvironment * env_ptr)453 ::PVideoFrame __stdcall	Resample::GetFrame (int n, ::IScriptEnvironment *env_ptr)
454 {
455 	::PVideoFrame  src_sptr = _clip_src_sptr->GetFrame (n, env_ptr);
456 	::PVideoFrame	dst_sptr = build_new_frame (*env_ptr, vi, &src_sptr);
457 
458 	Ru::FieldBased prop_fieldbased = Ru::FieldBased_INVALID;
459 	Ru::Field      prop_field      = Ru::Field_INVALID;
460 
461 	// Default property values based on VideoInfo
462 	const bool     interlaced_flag = (vi.IsFieldBased () && vi.IsParityKnown ());
463 	if (interlaced_flag)
464 	{
465 		const bool     top_flag = vi.IsTFF (); // or _src_clip_sptr->GetParity(n)?
466 		prop_fieldbased = (top_flag) ? Ru::FieldBased_TFF : Ru::FieldBased_BFF;
467 	}
468 
469 	// Now reads the existing frame properties
470 	if (supports_props ())
471 	{
472 		const ::AVSMap *  props_ptr = env_ptr->getFramePropsRO (src_sptr);
473 		int            err      = 0;
474 		int64_t        prop_val = -1;
475 		prop_val = env_ptr->propGetInt (props_ptr, "_FieldBased", 0, &err);
476 		prop_fieldbased =
477 			  (err      != 0) ? prop_fieldbased
478 			: (prop_val == 0) ? Ru::FieldBased_FRAMES
479 			: (prop_val == 1) ? Ru::FieldBased_BFF
480 			: (prop_val == 2) ? Ru::FieldBased_TFF
481 			:                   Ru::FieldBased_INVALID;
482 		prop_val = env_ptr->propGetInt (props_ptr, "_Field", 0, &err);
483 		prop_field =
484 			  (err      != 0) ? prop_field
485 			: (prop_val == 0) ? Ru::Field_BOT
486 			: (prop_val == 1) ? Ru::Field_TOP
487 			:                   Ru::Field_INVALID;
488 	}
489 
490 	// Collects informations from the input frame properties
491 	Ru::FrameInfo  frame_info;
492 	Ru::get_interlacing_param (
493 		frame_info._itl_s_flag, frame_info._top_s_flag,
494 		n, _interlaced_src, _field_order_src, prop_fieldbased, prop_field,
495 		false
496 	);
497 	Ru::get_interlacing_param (
498 		frame_info._itl_d_flag, frame_info._top_d_flag,
499 		n, _interlaced_dst, _field_order_dst, prop_fieldbased, prop_field,
500 		false
501 	);
502 
503 	// Processing
504 	_plane_proc_uptr->process_frame (dst_sptr, n, *env_ptr, &frame_info);
505 
506 	// Output frame properties
507 	if (   supports_props ()
508 	    && (   _range_d_def_flag
509 	        || _cplace_d_set_flag
510 	        || _interlaced_dst != Ru::InterlacingParam_AUTO))
511 	{
512 		::AVSMap *     props_ptr = env_ptr->getFramePropsRW (dst_sptr);
513 		if (_range_d_def_flag)
514 		{
515 			const int      cr_val = (_fulld_flag) ? 0 : 1;
516 			env_ptr->propSetInt (
517 				props_ptr, "_ColorRange", cr_val, ::PROPAPPENDMODE_REPLACE
518 			);
519 		}
520 		if (_cplace_d_set_flag)
521 		{
522 			int            cl_val = -1; // Unknown or cannot be expressed
523 			if (   _cplace_d == fmtcl::ChromaPlacement_MPEG2
524 			    || (   _cplace_d == fmtcl::ChromaPlacement_DV
525 			        && _fmt_dst.get_subspl_h () == 2
526 			        && _fmt_dst.get_subspl_v () == 0))
527 			{
528 				cl_val = 0; // Left
529 			}
530 			else if (_cplace_d == fmtcl::ChromaPlacement_MPEG1)
531 			{
532 				cl_val = 1; // Center
533 			}
534 			else if (_cplace_d == fmtcl::ChromaPlacement_T_L)
535 			{
536 				cl_val = 2; // Top-left
537 			}
538 
539 			if (cl_val >= 0)
540 			{
541 				env_ptr->propSetInt (
542 					props_ptr, "_ChromaLocation", cl_val, ::PROPAPPENDMODE_REPLACE
543 				);
544 			}
545 		}
546 		if (_interlaced_dst != Ru::InterlacingParam_AUTO)
547 		{
548 			if (frame_info._itl_d_flag)
549 			{
550 				if (_field_order_dst != Ru::FieldOrder_AUTO)
551 				{
552 					env_ptr->propSetInt (
553 						props_ptr, "_FieldBased",
554 						(_field_order_dst == Ru::FieldOrder_BFF) ? 1 : 2,
555 						::PROPAPPENDMODE_REPLACE
556 					);
557 					env_ptr->propSetInt (
558 						props_ptr, "_Field",
559 						(frame_info._top_d_flag) ? 1 : 0,
560 						::PROPAPPENDMODE_REPLACE
561 					);
562 				}
563 			}
564 			else
565 			{
566 				env_ptr->propSetInt (
567 					props_ptr, "_FieldBased", 0, ::PROPAPPENDMODE_REPLACE
568 				);
569 				env_ptr->propDeleteKey (props_ptr, "_Field");
570 			}
571 		}
572 	}
573 
574 	return dst_sptr;
575 }
576 
577 
578 
conv_str_to_chroma_placement(::IScriptEnvironment & env,std::string cplace)579 fmtcl::ChromaPlacement	Resample::conv_str_to_chroma_placement (::IScriptEnvironment &env, std::string cplace)
580 {
581 	const auto     cp_val = Ru::conv_str_to_chroma_placement (cplace);
582 	if (cp_val < 0)
583 	{
584 		env.ThrowError (fmtcavs_RESAMPLE ": unexpected cplace string.");
585 	}
586 
587 	return cp_val;
588 }
589 
590 
591 
conv_str_to_chroma_subspl(::IScriptEnvironment & env,int & ssh,int & ssv,std::string css)592 void	Resample::conv_str_to_chroma_subspl (::IScriptEnvironment &env, int &ssh, int &ssv, std::string css)
593 {
594 	const int      ret_val = Ru::conv_str_to_chroma_subspl (ssh, ssv, css);
595 	if (ret_val != 0)
596 	{
597 		env.ThrowError (fmtcavs_RESAMPLE ": unsupported css value.");
598 	}
599 }
600 
601 
602 
603 /*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
604 
605 
606 
do_process_plane(::PVideoFrame & dst_sptr,int n,::IScriptEnvironment & env,int plane_index,int plane_id,void * ctx_ptr)607 void	Resample::do_process_plane (::PVideoFrame &dst_sptr, int n, ::IScriptEnvironment &env, int plane_index, int plane_id, void *ctx_ptr)
608 {
609 	fstb::unused (plane_id);
610 	assert (ctx_ptr != nullptr);
611 
612 	const auto     proc_mode = _plane_proc_uptr->get_mode (plane_index);
613 
614 	if (proc_mode == avsutl::PlaneProcMode_PROCESS)
615 	{
616 		const Ru::FrameInfo &   frame_info =
617 			*reinterpret_cast <const Ru::FrameInfo *> (ctx_ptr);
618 		process_plane_proc (dst_sptr, env, n, plane_index, frame_info);
619 	}
620 
621 	// Copy (and convert)
622 	else if (proc_mode == avsutl::PlaneProcMode_COPY1)
623 	{
624 		process_plane_copy (dst_sptr, env, n, plane_index);
625 	}
626 
627 	// Fill
628 	else if (proc_mode < avsutl::PlaneProcMode_FILL)
629 	{
630 		const double   val = _plane_proc_uptr->get_fill_val (plane_index);
631 		_plane_proc_uptr->fill_plane (dst_sptr, n, val, plane_index);
632 	}
633 }
634 
635 
636 
637 /*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
638 
639 
640 
get_output_colorspace(::IScriptEnvironment & env,const::AVSValue & args,const FmtAvs & fmt_src)641 FmtAvs	Resample::get_output_colorspace (::IScriptEnvironment &env, const ::AVSValue &args, const FmtAvs &fmt_src)
642 {
643 	FmtAvs         fmt_dst = fmt_src;
644 
645 	// Full colorspace
646 	if (args [Param_CSP].Defined ())
647 	{
648 		if (fmt_dst.conv_from_str (args [Param_CSP].AsString ()) != 0)
649 		{
650 			env.ThrowError (fmtcavs_RESAMPLE ": invalid output colorspace.");
651 		}
652 	}
653 
654 	if (! fmt_dst.is_planar ())
655 	{
656 		env.ThrowError (fmtcavs_RESAMPLE ": output colorspace must be planar.");
657 	}
658 
659 	// Chroma subsampling
660 	int            ssh = fmt_dst.get_subspl_h ();
661 	int            ssv = fmt_dst.get_subspl_v ();
662 	const std::string css (args [Param_CSS].AsString (""));
663 	if (! css.empty ())
664 	{
665 		conv_str_to_chroma_subspl (env, ssh, ssv, css);
666 		if (   (ssh > 0 || ssv > 0)
667 		    && ! fmtcl::has_chroma (fmt_dst.get_col_fam ()))
668 		{
669 			env.ThrowError (fmtcavs_RESAMPLE
670 				": chroma subsampling not compatible with the output format."
671 			);
672 		}
673 		fmt_dst.set_subspl_h (ssh);
674 		fmt_dst.set_subspl_v (ssv);
675 	}
676 
677 	return fmt_dst;
678 }
679 
680 
681 
process_plane_proc(::PVideoFrame & dst_sptr,::IScriptEnvironment & env,int n,int plane_index,const Ru::FrameInfo & frame_info)682 void	Resample::process_plane_proc (::PVideoFrame &dst_sptr, ::IScriptEnvironment &env, int n, int plane_index, const Ru::FrameInfo &frame_info)
683 {
684 	assert (n >= 0);
685 	assert (plane_index >= 0);
686 	assert (plane_index < _max_nbr_planes);
687 
688 	::PVideoFrame  src_sptr     = _clip_src_sptr->GetFrame (n, &env);
689 
690 	const int      plane_id_s   = _plane_proc_uptr->get_plane_id (
691 		plane_index, avsutl::PlaneProcessor::ClipIdx_SRC1
692 	);
693 	const int      plane_id_d   = _plane_proc_uptr->get_plane_id (
694 		plane_index, avsutl::PlaneProcessor::ClipIdx_DST
695 	);
696 
697 	uint8_t *      data_dst_ptr = dst_sptr->GetWritePtr (plane_id_d);
698 	const int      stride_dst   = dst_sptr->GetPitch (plane_id_d);
699 	const uint8_t* data_src_ptr = src_sptr->GetReadPtr (plane_id_s);
700 	const int      stride_src   = src_sptr->GetPitch (plane_id_s);
701 
702 	const fmtcl::InterlacingType  itl_s = fmtcl::InterlacingType_get (
703 		frame_info._itl_s_flag, frame_info._top_s_flag
704 	);
705 	const fmtcl::InterlacingType  itl_d = fmtcl::InterlacingType_get (
706 		frame_info._itl_d_flag, frame_info._top_d_flag
707 	);
708 
709 	try
710 	{
711 		fmtcl::FilterResize *   filter_ptr = create_or_access_plane_filter (
712 			plane_index,
713 			itl_d,
714 			itl_s
715 		);
716 
717 		const bool     chroma_flag =
718 			fmtcl::is_chroma_plane (_fmt_src.get_col_fam (), plane_index);
719 
720 		filter_ptr->process_plane (
721 			data_dst_ptr, data_src_ptr, stride_dst, stride_src, chroma_flag
722 		);
723 	}
724 
725 	catch (std::exception &e)
726 	{
727 		env.ThrowError (fmtcavs_RESAMPLE ": exception: %s.", e.what ());
728 	}
729 	catch (...)
730 	{
731 		env.ThrowError (fmtcavs_RESAMPLE ": exception.");
732 	}
733 }
734 
735 
736 
process_plane_copy(::PVideoFrame & dst_sptr,::IScriptEnvironment & env,int n,int plane_index)737 void	Resample::process_plane_copy (::PVideoFrame &dst_sptr, ::IScriptEnvironment &env, int n, int plane_index)
738 {
739 	assert (n >= 0);
740 	assert (plane_index >= 0);
741 	assert (plane_index < _max_nbr_planes);
742 
743 	::PVideoFrame  src_sptr     = _clip_src_sptr->GetFrame (n, &env);
744 
745 	const int      plane_id_s   = _plane_proc_uptr->get_plane_id (
746 		plane_index, avsutl::PlaneProcessor::ClipIdx_SRC1
747 	);
748 	const int      plane_id_d   = _plane_proc_uptr->get_plane_id (
749 		plane_index, avsutl::PlaneProcessor::ClipIdx_DST
750 	);
751 
752 	const int      src_w = _plane_proc_uptr->get_width (
753 		src_sptr, plane_id_s, avsutl::PlaneProcessor::ClipIdx_SRC1
754 	);
755 	const int      src_h = _plane_proc_uptr->get_height (src_sptr, plane_id_s);
756 	const int      dst_w = _plane_proc_uptr->get_width (
757 		dst_sptr, plane_id_d, avsutl::PlaneProcessor::ClipIdx_DST
758 	);
759 	const int      dst_h = _plane_proc_uptr->get_height (dst_sptr, plane_id_d);
760 
761 	uint8_t *      data_dst_ptr = dst_sptr->GetWritePtr (plane_id_d);
762 	const int      stride_dst   = dst_sptr->GetPitch (plane_id_d);
763 	const uint8_t* data_src_ptr = src_sptr->GetReadPtr (plane_id_s);
764 	const int      stride_src   = src_sptr->GetPitch (plane_id_s);
765 
766 	const int      w = std::min (src_w, dst_w);
767 	const int      h = std::min (src_h, dst_h);
768 
769 	// Copied from fmtcl::FilterResize::process_plane_bypass()
770 	fmtcl::BitBltConv::ScaleInfo *   scale_info_ptr = nullptr;
771 	fmtcl::BitBltConv::ScaleInfo     scale_info;
772 	const bool     dst_flt_flag = (_dst_type == fmtcl::SplFmt_FLOAT);
773 	const bool     src_flt_flag = (_src_type == fmtcl::SplFmt_FLOAT);
774 	if (dst_flt_flag != src_flt_flag)
775 	{
776 		const auto &   plane_data = _plane_data_arr [plane_index];
777 		scale_info._gain    = plane_data._gain;
778 		scale_info._add_cst = plane_data._add_cst;
779 
780 		scale_info_ptr = &scale_info;
781 	}
782 
783 	fmtcl::BitBltConv blitter (_sse2_flag, _avx2_flag);
784 	blitter.bitblt (
785 		_dst_type, _dst_res, data_dst_ptr, stride_dst,
786 		_src_type, _src_res, data_src_ptr, stride_src,
787 		w, h, scale_info_ptr
788 	);
789 }
790 
791 
792 
create_or_access_plane_filter(int plane_index,fmtcl::InterlacingType itl_d,fmtcl::InterlacingType itl_s)793 fmtcl::FilterResize *	Resample::create_or_access_plane_filter (int plane_index, fmtcl::InterlacingType itl_d, fmtcl::InterlacingType itl_s)
794 {
795 	assert (plane_index >= 0);
796 	assert (plane_index < _max_nbr_planes);
797 	assert (itl_d >= 0);
798 	assert (itl_d < fmtcl::InterlacingType_NBR_ELT);
799 	assert (itl_s >= 0);
800 	assert (itl_s < fmtcl::InterlacingType_NBR_ELT);
801 
802 	const auto &   plane_data = _plane_data_arr [plane_index];
803 	const fmtcl::ResampleSpecPlane & key = plane_data._spec_arr [itl_d] [itl_s];
804 
805 	std::lock_guard <std::mutex>  autolock (_filter_mutex);
806 
807 	std::unique_ptr <fmtcl::FilterResize> &   filter_uptr = _filter_uptr_map [key];
808 	if (filter_uptr.get () == nullptr)
809 	{
810 		filter_uptr = std::make_unique <fmtcl::FilterResize> (
811 			key,
812 			*(plane_data._kernel_arr [fmtcl::FilterResize::Dir_H]._k_uptr),
813 			*(plane_data._kernel_arr [fmtcl::FilterResize::Dir_V]._k_uptr),
814 			_norm_flag, plane_data._norm_val_h, plane_data._norm_val_v,
815 			plane_data._gain,
816 			_src_type, _src_res, _dst_type, _dst_res,
817 			_int_flag, _sse2_flag, _avx2_flag
818 		);
819 	}
820 
821 	return filter_uptr.get ();
822 }
823 
824 
825 
create_all_plane_specs(const FmtAvs & fmt_dst,const FmtAvs & fmt_src)826 void	Resample::create_all_plane_specs (const FmtAvs &fmt_dst, const FmtAvs &fmt_src)
827 {
828 	const fmtcl::ColorFamily src_cf = fmt_src.get_col_fam ();
829 	const fmtcl::ColorFamily dst_cf = fmt_dst.get_col_fam ();
830 	const int      src_ss_h   = fmt_src.get_subspl_h ();
831 	const int      src_ss_v   = fmt_src.get_subspl_v ();
832 	const int      dst_ss_h   = fmt_dst.get_subspl_h ();
833 	const int      dst_ss_v   = fmt_dst.get_subspl_v ();
834 	const int      nbr_planes = _vi_src.NumComponents ();
835 	for (int plane_index = 0; plane_index < nbr_planes; ++plane_index)
836 	{
837 		auto &         plane_data = _plane_data_arr [plane_index];
838 		Ru::create_plane_specs (
839 			plane_data, plane_index,
840 			src_cf, _src_width, src_ss_h, _src_height, src_ss_v, _cplace_s,
841 			dst_cf, vi.width  , dst_ss_h, vi.height  , dst_ss_v, _cplace_d
842 		);
843 	}
844 }
845 
846 
847 
848 }  // namespace fmtcavs
849 
850 
851 
852 /*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
853