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