1 /*****************************************************************************
2
3 Transfer.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/FmtAvs.h"
31 #include "fmtcavs/Transfer.h"
32 #include "fmtcl/TransModel.h"
33 #include "fmtcl/TransOpLogC.h"
34 #include "fmtcl/TransUtil.h"
35 #include "fstb/fnc.h"
36
37 #include <cassert>
38
39
40
41 namespace fmtcavs
42 {
43
44
45
46 /*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
47
48
49
Transfer(::IScriptEnvironment & env,const::AVSValue & args)50 Transfer::Transfer (::IScriptEnvironment &env, const ::AVSValue &args)
51 : Inherited (env, args [Param_CLIP_SRC].AsClip ())
52 , _clip_src_sptr (args [Param_CLIP_SRC].AsClip ())
53 , _vi_src (vi)
54 , _fulls_flag (args [Param_FULLS].AsBool (true))
55 , _fulld_flag (args [Param_FULLD].AsBool (true))
56 {
57 const CpuOpt cpu_opt (args [Param_CPUOPT]);
58 const bool sse2_flag = cpu_opt.has_sse2 ();
59 const bool avx2_flag = cpu_opt.has_avx2 ();
60
61 // Checks the input clip
62 if (! _vi_src.IsPlanar ())
63 {
64 env.ThrowError (fmtcavs_TRANSFER ": input must be planar.");
65 }
66 const FmtAvs fmt_src (_vi_src);
67 const auto col_fam_src = fmt_src.get_col_fam ();
68 if ( col_fam_src != fmtcl::ColorFamily_GRAY
69 && col_fam_src != fmtcl::ColorFamily_RGB)
70 {
71 env.ThrowError (fmtcavs_TRANSFER ": unsupported color family.");
72 }
73 const int res_src = fmt_src.get_bitdepth ();
74 const bool flt_src_flag = fmt_src.is_float ();
75 if ( (! flt_src_flag && (res_src < 8 || res_src > 16))
76 || ( flt_src_flag && res_src != 32 ))
77 {
78 env.ThrowError (fmtcavs_TRANSFER ": pixel bitdepth not supported.");
79 }
80
81 // Destination colorspace
82 const auto fmt_dst = get_output_colorspace (env, args, fmt_src);
83 const int res_dst = fmt_dst.get_bitdepth ();
84 const bool flt_dst_flag = fmt_dst.is_float ();
85 if ( (! flt_dst_flag && res_dst != 16)
86 || ( flt_dst_flag && res_dst != 32))
87 {
88 env.ThrowError (fmtcavs_TRANSFER ": output bitdepth not supported.");
89 }
90
91 // Output format is validated.
92 if (fmt_dst.conv_to_vi (vi) != 0)
93 {
94 env.ThrowError (fmtcavs_TRANSFER ": illegal output colorspace.");
95 }
96
97 // Alpha plane processing, if any
98 _proc_alpha_uptr = std::make_unique <fmtcavs::ProcAlpha> (
99 fmt_dst, fmt_src, vi.width, vi.height, cpu_opt
100 );
101
102 // Other parameters
103 std::string transs = args [Param_TRANSS ].AsString ("");
104 std::string transd = args [Param_TRANSD ].AsString ("");
105 const double contrast = args [Param_CONT ].AsFloat (1);
106 const double gcor = args [Param_GCOR ].AsFloat (1);
107 double lvl_black = args [Param_BLACKLVL].AsFloat (0);
108 double lb = args [Param_LB ].AsFloat (0);
109 const double lw = args [Param_LW ].AsFloat (0);
110 const double lws = args [Param_LWS ].AsDblDef (lw);
111 const double lwd = args [Param_LWD ].AsDblDef (lw);
112 const double lamb = args [Param_AMBIENT ].AsFloat (5);
113 const bool scene_flag = args [Param_SCENEREF].AsBool (false);
114 const int logc_ei_raw_s = args [Param_LOGCEIS].AsInt (800);
115 const int logc_ei_raw_d = args [Param_LOGCEID].AsInt (800);
116 const auto match = fmtcl::LumMatch (
117 args [Param_MATCH].AsInt (fmtcl::LumMatch_REF_WHITE)
118 );
119 const int dbg = args [Param_DEBUG ].AsInt (0);
120 const bool gydef_flag = args [Param_GY ].Defined ();
121 const bool gy_flag = args [Param_GY ].AsBool (false);
122 const auto gy_proc =
123 (! gydef_flag) ? fmtcl::TransModel::GyProc::UNDEF
124 : gy_flag ? fmtcl::TransModel::GyProc::ON
125 : fmtcl::TransModel::GyProc::OFF;
126
127 fstb::conv_to_lower_case (transs);
128 fstb::conv_to_lower_case (transd);
129
130 _curve_s = fmtcl::TransUtil::conv_string_to_curve (transs);
131 if (_curve_s == fmtcl::TransCurve_UNDEF)
132 {
133 env.ThrowError (fmtcavs_TRANSFER ": invalid transs value.");
134 }
135 _curve_d = fmtcl::TransUtil::conv_string_to_curve (transd);
136 if (_curve_d == fmtcl::TransCurve_UNDEF)
137 {
138 env.ThrowError (fmtcavs_TRANSFER ": invalid transd value.");
139 }
140
141 const auto logc_ei_s = fmtcl::TransOpLogC::conv_logc_ei (logc_ei_raw_s);
142 if (logc_ei_s == fmtcl::TransOpLogC::ExpIdx_INVALID)
143 {
144 env.ThrowError (fmtcavs_TRANSFER ": invalid logceis value.");
145 }
146 const auto logc_ei_d = fmtcl::TransOpLogC::conv_logc_ei (logc_ei_raw_d);
147 if (logc_ei_d == fmtcl::TransOpLogC::ExpIdx_INVALID)
148 {
149 env.ThrowError (fmtcavs_TRANSFER ": invalid logceid value.");
150 }
151
152 if (contrast <= 0)
153 {
154 env.ThrowError (fmtcavs_TRANSFER ": invalid cont value.");
155 }
156 if (gcor <= 0)
157 {
158 env.ThrowError (fmtcavs_TRANSFER ": invalid gcor value.");
159 }
160
161 if (lws > 0 && lws < fmtcl::TransModel::_min_luminance)
162 {
163 env.ThrowError (fmtcavs_TRANSFER ": lws must be 0 or >= 0.1.");
164 }
165 if (lwd > 0 && lwd < fmtcl::TransModel::_min_luminance)
166 {
167 env.ThrowError (fmtcavs_TRANSFER ": lwd must be 0 or >= 0.1.");
168 }
169
170 // Lb Lw mess
171 if (lvl_black < 0)
172 {
173 env.ThrowError (fmtcavs_TRANSFER ": invalid blacklvl value.");
174 }
175 if (lws > 0 && lb >= lws)
176 {
177 env.ThrowError (fmtcavs_TRANSFER ": invalid lb/lws combination.");
178 }
179 if ( args [Param_BLACKLVL].Defined ()
180 && args [Param_LB ].Defined ()
181 && (args [Param_LWS].Defined () || args [Param_LW].Defined ()))
182 {
183 env.ThrowError (fmtcavs_TRANSFER
184 ": you can define at most two of these parameters:\n"
185 "blacklvl, lb and (lw or lws)."
186 );
187 }
188 else if (! args [Param_LB].Defined ())
189 {
190 if (args [Param_LW].Defined () || args [Param_LWS].Defined ())
191 {
192 lb = lvl_black * lws;
193 }
194 else
195 {
196 lb = lvl_black * 100;
197 }
198 }
199
200 if (lamb < fmtcl::TransModel::_min_luminance)
201 {
202 env.ThrowError (fmtcavs_TRANSFER ": ambient luminance must be > 0.1.");
203 }
204
205 if (match < 0 || match >= fmtcl::LumMatch_NBR_ELT)
206 {
207 env.ThrowError (fmtcavs_TRANSFER ": invalid match value.");
208 }
209
210 if (dbg < 0)
211 {
212 env.ThrowError (fmtcavs_TRANSFER ": debug must be >= 0.");
213 }
214 _dbg_flag = (dbg > 0);
215 if (_dbg_flag)
216 {
217 _dbg_name = fmtcl::TransUtil::gen_degub_prop_name (dbg);
218 }
219
220
221 // Finally...
222 const fmtcl::PicFmt src_picfmt =
223 conv_fmtavs_to_picfmt (fmt_src, _fulls_flag);
224 const fmtcl::PicFmt dst_picfmt =
225 conv_fmtavs_to_picfmt (fmt_dst, _fulld_flag);
226 _model_uptr = std::make_unique <fmtcl::TransModel> (
227 dst_picfmt, _curve_d, logc_ei_d,
228 src_picfmt, _curve_s, logc_ei_s,
229 contrast, gcor, lb, lws, lwd, lamb, scene_flag, match, gy_proc,
230 sse2_flag, avx2_flag
231 );
232 }
233
234
235
GetFrame(int n,::IScriptEnvironment * env_ptr)236 ::PVideoFrame __stdcall Transfer::GetFrame (int n, ::IScriptEnvironment *env_ptr)
237 {
238 ::PVideoFrame src_sptr = _clip_src_sptr->GetFrame (n, env_ptr);
239 ::PVideoFrame dst_sptr = build_new_frame (*env_ptr, vi, &src_sptr);
240
241 const auto pa { build_mat_proc (vi, dst_sptr, _vi_src, src_sptr) };
242 _model_uptr->process_frame (pa);
243
244 // Alpha plane now
245 _proc_alpha_uptr->process_plane (dst_sptr, src_sptr);
246
247 // Frame properties
248 if (supports_props ())
249 {
250 ::AVSMap * props_ptr = env_ptr->getFramePropsRW (dst_sptr);
251
252 const int cr_val = (_fulld_flag) ? 0 : 1;
253 env_ptr->propSetInt (
254 props_ptr, "_ColorRange", cr_val, ::PROPAPPENDMODE_REPLACE
255 );
256
257 int transfer = fmtcl::TransCurve_UNSPECIFIED;
258 if (_curve_d >= 0 && _curve_d <= fmtcl::TransCurve_ISO_RANGE_LAST)
259 {
260 transfer = _curve_d;
261 }
262 env_ptr->propSetInt (
263 props_ptr, "_Transfer", transfer, ::PROPAPPENDMODE_REPLACE
264 );
265
266 if (_dbg_flag)
267 {
268 const std::string & txt = _model_uptr->get_debug_text ();
269 env_ptr->propSetData (
270 props_ptr, _dbg_name.c_str (),
271 txt.c_str (), int (txt.length () + 1), ::PROPAPPENDMODE_REPLACE
272 );
273 }
274 }
275
276 return dst_sptr;
277 }
278
279
280
281 /*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
282
283
284
285 /*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
286
287
288
get_output_colorspace(::IScriptEnvironment & env,const::AVSValue & args,const FmtAvs & fmt_src)289 FmtAvs Transfer::get_output_colorspace (::IScriptEnvironment &env, const ::AVSValue &args, const FmtAvs &fmt_src)
290 {
291 auto fmt_dst = fmt_src;
292
293 bool flt_flag = fmt_dst.is_float ();
294 int res = fmt_dst.get_bitdepth ();
295 flt_flag = args [Param_FLT ].AsBool (flt_flag);
296 res = args [Param_BITS].AsInt (res);
297 const bool flt_def_flag = args [Param_FLT ].Defined ();
298 const bool res_def_flag = args [Param_BITS].Defined ();
299
300 if (! flt_def_flag && ! res_def_flag)
301 {
302 if (! flt_flag && res < 16)
303 {
304 fmt_dst.set_bitdepth (16);
305 }
306 }
307 else if (flt_def_flag && ! res_def_flag)
308 {
309 fmt_dst.set_bitdepth (32);
310 }
311 else if (! flt_def_flag && res_def_flag)
312 {
313 fmt_dst.set_bitdepth (res);
314 }
315 else
316 {
317 assert (flt_def_flag && res_def_flag);
318 if ( ( flt_flag && res != 32)
319 || (! flt_flag && res > 16))
320 {
321 env.ThrowError (
322 fmtcavs_TRANSFER ": flt and bits combination not supported."
323 );
324 }
325 fmt_dst.set_bitdepth (res);
326 }
327
328 return fmt_dst;
329 }
330
331
332
333 } // namespace fmtcavs
334
335
336
337 /*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
338