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