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