filters/conditional/conditional.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | |||
| 2 | // Avisynth v2.5. Copyright 2002 Ben Rudiak-Gould et al. | ||
| 3 | // http://avisynth.nl | ||
| 4 | |||
| 5 | // This program is free software; you can redistribute it and/or modify | ||
| 6 | // it under the terms of the GNU General Public License as published by | ||
| 7 | // the Free Software Foundation; either version 2 of the License, or | ||
| 8 | // (at your option) any later version. | ||
| 9 | // | ||
| 10 | // This program is distributed in the hope that it will be useful, | ||
| 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | // GNU General Public License for more details. | ||
| 14 | // | ||
| 15 | // You should have received a copy of the GNU General Public License | ||
| 16 | // along with this program; if not, write to the Free Software | ||
| 17 | // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit | ||
| 18 | // http://www.gnu.org/copyleft/gpl.html . | ||
| 19 | // | ||
| 20 | // Linking Avisynth statically or dynamically with other modules is making a | ||
| 21 | // combined work based on Avisynth. Thus, the terms and conditions of the GNU | ||
| 22 | // General Public License cover the whole combination. | ||
| 23 | // | ||
| 24 | // As a special exception, the copyright holders of Avisynth give you | ||
| 25 | // permission to link Avisynth with independent modules that communicate with | ||
| 26 | // Avisynth solely through the interfaces defined in avisynth.h, regardless of the license | ||
| 27 | // terms of these independent modules, and to copy and distribute the | ||
| 28 | // resulting combined work under terms of your choice, provided that | ||
| 29 | // every copy of the combined work is accompanied by a complete copy of | ||
| 30 | // the source code of Avisynth (the version of Avisynth used to produce the | ||
| 31 | // combined work), being distributed under the terms of the GNU General | ||
| 32 | // Public License plus this exception. An independent module is a module | ||
| 33 | // which is not derived from or based on Avisynth, such as 3rd-party filters, | ||
| 34 | // import and export plugins, or graphical user interfaces. | ||
| 35 | |||
| 36 | #include "conditional.h" | ||
| 37 | #include "../../core/parser/scriptparser.h" | ||
| 38 | #include "conditional_reader.h" | ||
| 39 | #include <cmath> | ||
| 40 | |||
| 41 | #ifdef AVS_WINDOWS | ||
| 42 | #include <avs/win.h> | ||
| 43 | #else | ||
| 44 | #include <avs/posix.h> | ||
| 45 | #endif | ||
| 46 | |||
| 47 | #include <avs/minmax.h> | ||
| 48 | #include "../../core/internal.h" | ||
| 49 | #include "../../core/InternalEnvironment.h" | ||
| 50 | |||
| 51 | extern const AVSFunction Conditional_filters[] = { | ||
| 52 | // 2020.03.28 bool "local" parameter like in gRunT. Default false. Note! AvsNeo works as local=true, but it's incompatible with old Avisynth | ||
| 53 | // with "function" input local default is true | ||
| 54 | { "ConditionalSelect", BUILTIN_FUNC_PREFIX, "csc+[show]b[local]b", ConditionalSelect::Create }, | ||
| 55 | { "ConditionalSelect", BUILTIN_FUNC_PREFIX, "cnc+[show]b[local]b", ConditionalSelect::Create }, // function input | ||
| 56 | { "ConditionalFilter", BUILTIN_FUNC_PREFIX, "cccsss[show]b[local]b", ConditionalFilter::Create, (void *)0 }, | ||
| 57 | // easy syntax from GConditionalFilter, args3 and 4 to "=" and "true": | ||
| 58 | { "ConditionalFilter", BUILTIN_FUNC_PREFIX, "cccs[show]b[local]b", ConditionalFilter::Create, (void *)1 }, | ||
| 59 | { "ConditionalFilter", BUILTIN_FUNC_PREFIX, "cccn[show]b[local]b", ConditionalFilter::Create, (void *)2 }, // function input | ||
| 60 | { "ScriptClip", BUILTIN_FUNC_PREFIX, "cs[show]b[after_frame]b[local]b", ScriptClip::Create }, | ||
| 61 | { "ScriptClip", BUILTIN_FUNC_PREFIX, "cn[show]b[after_frame]b[local]b", ScriptClip::Create }, // function input | ||
| 62 | { "ConditionalReader", BUILTIN_FUNC_PREFIX, "css[show]b[condvarsuffix]s[local]b", ConditionalReader::Create }, | ||
| 63 | { "FrameEvaluate", BUILTIN_FUNC_PREFIX, "cs[show]b[after_frame]b[local]b", ScriptClip::Create_eval }, | ||
| 64 | { "WriteFile", BUILTIN_FUNC_PREFIX, "c[filename]ss+[append]b[flush]b[local]b", Write::Create }, | ||
| 65 | { "WriteFileIf", BUILTIN_FUNC_PREFIX, "c[filename]ss+[append]b[flush]b[local]b", Write::Create_If }, | ||
| 66 | { "WriteFileStart", BUILTIN_FUNC_PREFIX, "c[filename]ss+[append]b[local]b", Write::Create_Start }, | ||
| 67 | { "WriteFileEnd", BUILTIN_FUNC_PREFIX, "c[filename]ss+[append]b[local]b", Write::Create_End }, | ||
| 68 | { "WriteFile", BUILTIN_FUNC_PREFIX, "c[filename]sn+[append]b[flush]b[local]b", Write::Create }, // function input | ||
| 69 | { "WriteFileIf", BUILTIN_FUNC_PREFIX, "c[filename]sn+[append]b[flush]b[local]b", Write::Create_If }, // function input | ||
| 70 | { "WriteFileStart", BUILTIN_FUNC_PREFIX, "c[filename]sn+[append]b[local]b", Write::Create_Start }, // function input | ||
| 71 | { "WriteFileEnd", BUILTIN_FUNC_PREFIX, "c[filename]sn+[append]b[local]b", Write::Create_End }, // function input | ||
| 72 | { "UseVar", BUILTIN_FUNC_PREFIX, "cs+", UseVar::Create }, | ||
| 73 | // in conditional_reader.cpp, see property getters in conditional_functions.cpp | ||
| 74 | |||
| 75 | // value from Function | ||
| 76 | { "propSet", BUILTIN_FUNC_PREFIX, "csn[mode]i", SetProperty::Create, (void *)0 }, // auto from function result type | ||
| 77 | { "propSetInt", BUILTIN_FUNC_PREFIX, "csn[mode]i", SetProperty::Create, (void*)1 }, // forced check for int | ||
| 78 | { "propSetFloat", BUILTIN_FUNC_PREFIX, "csn[mode]i", SetProperty::Create, (void*)2 }, // forced check for float | ||
| 79 | { "propSetString", BUILTIN_FUNC_PREFIX, "csn[mode]i", SetProperty::Create, (void*)3 }, // forced check for string | ||
| 80 | { "propSetArray", BUILTIN_FUNC_PREFIX, "csn", SetProperty::Create, (void*)4 }, // no mode parameter, full entry refresh | ||
| 81 | { "propSetClip", BUILTIN_FUNC_PREFIX, "csn[mode]i", SetProperty::Create, (void*)5 }, // forced check for clip | ||
| 82 | |||
| 83 | // value from direct data | ||
| 84 | { "propSet", BUILTIN_FUNC_PREFIX, "csi[mode]i", SetProperty::Create, (void*)10 }, | ||
| 85 | { "propSet", BUILTIN_FUNC_PREFIX, "csf[mode]i", SetProperty::Create, (void*)11 }, | ||
| 86 | { "propSet", BUILTIN_FUNC_PREFIX, "css[mode]i", SetProperty::Create, (void*)12 }, | ||
| 87 | { "propSet", BUILTIN_FUNC_PREFIX, "csa", SetProperty::Create, (void*)13 }, // no mode parameter, full entry refresh | ||
| 88 | { "propSet", BUILTIN_FUNC_PREFIX, "csc[mode]i", SetProperty::Create, (void*)14 }, | ||
| 89 | |||
| 90 | { "propDelete", BUILTIN_FUNC_PREFIX, "cs+", DeleteProperty::Create }, | ||
| 91 | { "propClearAll", BUILTIN_FUNC_PREFIX, "c", ClearProperties::Create }, | ||
| 92 | |||
| 93 | { "propShow", BUILTIN_FUNC_PREFIX, "c[size]i[showtype]b[font]s[text_color]i[halo_color]i[bold]b[x]f[y]f[align]i[props]s+", ShowProperties::Create }, | ||
| 94 | |||
| 95 | { "propCopy", BUILTIN_FUNC_PREFIX, "cc[merge]b[props]s+[exclude]b", CopyProperties::Create}, | ||
| 96 | |||
| 97 | { 0 } | ||
| 98 | }; | ||
| 99 | |||
| 100 | #define W_DIVISOR 5 // Width divisor for onscreen messages | ||
| 101 | |||
| 102 | |||
| 103 | /******************************** | ||
| 104 | * Conditional Select | ||
| 105 | * | ||
| 106 | * Returns each one frame from N sources | ||
| 107 | * based on an integer evaluator. | ||
| 108 | ********************************/ | ||
| 109 | |||
| 110 | ✗ | ConditionalSelect::ConditionalSelect(PClip _child, AVSValue _script, | |
| 111 | int _num_args, PClip *_child_array, | ||
| 112 | ✗ | bool _show, bool _local, IScriptEnvironment* env) : | |
| 113 | ✗ | GenericVideoFilter(_child), script(_script), | |
| 114 | ✗ | num_args(_num_args), child_array(_child_array), show(_show), local(_local) { | |
| 115 | |||
| 116 | ✗ | child_devs = DEV_TYPE_ANY; | |
| 117 | ✗ | for (int i=0; i<num_args; i++) { | |
| 118 | ✗ | const VideoInfo& vin = child_array[i]->GetVideoInfo(); | |
| 119 | |||
| 120 | ✗ | if (vi.height != vin.height) | |
| 121 | ✗ | env->ThrowError("ConditionalSelect: The sources must all have the same height!"); | |
| 122 | |||
| 123 | ✗ | if (vi.width != vin.width) | |
| 124 | ✗ | env->ThrowError("ConditionalSelect: The sources must all have the same width!"); | |
| 125 | |||
| 126 | ✗ | if (!vi.IsSameColorspace(vin)) | |
| 127 | ✗ | env->ThrowError("ConditionalSelect: The sources must all be the same colorspace!"); | |
| 128 | |||
| 129 | ✗ | if (vi.num_frames < vin.num_frames) // Max of all clips | |
| 130 | ✗ | vi.num_frames = vin.num_frames; | |
| 131 | |||
| 132 | ✗ | child_devs &= GetDeviceTypes(child_array[i]); | |
| 133 | } | ||
| 134 | |||
| 135 | ✗ | if (child_devs == 0) { | |
| 136 | ✗ | env->ThrowError("ConditionalSelect: No common device among sources!"); | |
| 137 | } | ||
| 138 | ✗ | } | |
| 139 | |||
| 140 | |||
| 141 | ✗ | ConditionalSelect::~ConditionalSelect() { | |
| 142 | ✗ | delete[] child_array; | |
| 143 | ✗ | } | |
| 144 | |||
| 145 | ✗ | int __stdcall ConditionalSelect::SetCacheHints(int cachehints, int frame_range) | |
| 146 | { | ||
| 147 | AVS_UNUSED(frame_range); | ||
| 148 | ✗ | switch (cachehints) | |
| 149 | { | ||
| 150 | ✗ | case CACHE_GET_MTMODE: | |
| 151 | ✗ | return MT_NICE_FILTER; | |
| 152 | ✗ | case CACHE_GET_DEV_TYPE: | |
| 153 | ✗ | return child_devs; | |
| 154 | ✗ | case CACHE_GET_CHILD_DEV_TYPE: | |
| 155 | ✗ | return DEV_TYPE_ANY; | |
| 156 | } | ||
| 157 | ✗ | return 0; // We do not pass cache requests upwards. | |
| 158 | } | ||
| 159 | |||
| 160 | ✗ | PVideoFrame __stdcall ConditionalSelect::GetFrame(int n, IScriptEnvironment* env_) | |
| 161 | { | ||
| 162 | ✗ | InternalEnvironment* IEnv = GetAndRevealCamouflagedEnv(env_); | |
| 163 | ✗ | IScriptEnvironment* env = static_cast<IScriptEnvironment*>(IEnv); | |
| 164 | |||
| 165 | ✗ | AVSValue prev_last; | |
| 166 | ✗ | AVSValue prev_current_frame; | |
| 167 | ✗ | std::unique_ptr<GlobalVarFrame> var_frame; | |
| 168 | |||
| 169 | ✗ | AVSValue child_val = child; | |
| 170 | |||
| 171 | ✗ | if (!local) { | |
| 172 | ✗ | prev_last = env->GetVarDef("last"); // Store previous last | |
| 173 | ✗ | prev_current_frame = env->GetVarDef("current_frame"); // Store previous current_frame | |
| 174 | ✗ | env->SetVar("last", child_val); // Set implicit last | |
| 175 | ✗ | env->SetVar("current_frame", (AVSValue)n); // Set frame to be tested by the conditional filters. | |
| 176 | } | ||
| 177 | else { | ||
| 178 | // Neo's default, correct but incompatible with previous Avisynth versions | ||
| 179 | ✗ | var_frame = std::unique_ptr<GlobalVarFrame>(new GlobalVarFrame(IEnv)); // allocate new frame | |
| 180 | ✗ | env->SetGlobalVar("last", child_val); // Set explicit last | |
| 181 | ✗ | env->SetGlobalVar("current_frame", (AVSValue)n); // Set frame to be tested by the conditional filters. | |
| 182 | } | ||
| 183 | |||
| 184 | ✗ | AVSValue result; | |
| 185 | |||
| 186 | try { | ||
| 187 | ✗ | if (script.IsString()) { | |
| 188 | ✗ | ScriptParser parser(env, script.AsString(), "[Conditional Select, Expression]"); | |
| 189 | ✗ | PExpression exp = parser.Parse(); | |
| 190 | ✗ | result = exp->Evaluate(env); | |
| 191 | ✗ | } | |
| 192 | else { | ||
| 193 | //auto& func = script.AsFunction(); // c++ strict conformance: cannot Convert PFunction to PFunction& | ||
| 194 | ✗ | const PFunction& func = script.AsFunction(); | |
| 195 | ✗ | const AVSValue empty_args_array = AVSValue(nullptr, 0); // Invoke_ parameter is const AVSValue&, don't do it inline. | |
| 196 | ✗ | if (!IEnv->Invoke_(&result, child_val, | |
| 197 | ✗ | func->GetLegacyName(), func->GetDefinition(), empty_args_array, 0)) | |
| 198 | { | ||
| 199 | ✗ | env->ThrowError( | |
| 200 | "ConditionalSelect: Invalid function parameter type '%s'(%s)\n" | ||
| 201 | "Function should have one clip argument or no argument", | ||
| 202 | ✗ | func->GetDefinition()->param_types, func->ToString(env)); | |
| 203 | } | ||
| 204 | ✗ | } | |
| 205 | |||
| 206 | ✗ | if (!result.IsInt()) | |
| 207 | ✗ | env->ThrowError("Conditional Select: Expression must return an integer!"); | |
| 208 | } | ||
| 209 | ✗ | catch (const AvisynthError &error) { | |
| 210 | ✗ | if (!local) { | |
| 211 | ✗ | env->SetVar("last", prev_last); // Restore implicit last | |
| 212 | ✗ | env->SetVar("current_frame", prev_current_frame); // Restore current_frame | |
| 213 | } | ||
| 214 | |||
| 215 | ✗ | const int num_frames = child->GetVideoInfo().num_frames; | |
| 216 | ✗ | PVideoFrame dst = child->GetFrame(min(num_frames-1, n), env); | |
| 217 | |||
| 218 | ✗ | env->MakeWritable(&dst); | |
| 219 | ✗ | env->ApplyMessageEx(&dst, vi, error.msg, vi.width/W_DIVISOR, 0xa0a0a0, 0, 0, true); | |
| 220 | |||
| 221 | ✗ | return dst; | |
| 222 | ✗ | } | |
| 223 | |||
| 224 | ✗ | if (!local) { | |
| 225 | ✗ | env->SetVar("last", prev_last); // Restore implicit last | |
| 226 | ✗ | env->SetVar("current_frame", prev_current_frame); // Restore current_frame | |
| 227 | } | ||
| 228 | |||
| 229 | ✗ | const int i = result.AsInt(); | |
| 230 | |||
| 231 | ✗ | PVideoFrame dst; | |
| 232 | |||
| 233 | ✗ | if (i < 0 || i >= num_args) { | |
| 234 | ✗ | const int num_frames = child->GetVideoInfo().num_frames; | |
| 235 | ✗ | dst = child->GetFrame(min(num_frames-1, n), env); | |
| 236 | ✗ | } | |
| 237 | else { | ||
| 238 | ✗ | const int num_frames = child_array[i]->GetVideoInfo().num_frames; | |
| 239 | ✗ | dst = child_array[i]->GetFrame(min(num_frames-1, n), env); | |
| 240 | } | ||
| 241 | |||
| 242 | ✗ | if (show) { | |
| 243 | char text[32]; | ||
| 244 | |||
| 245 | ✗ | snprintf(text, sizeof(text)-1, "Expression Result:%i\n", result.AsInt()); | |
| 246 | ✗ | text[sizeof(text)-1] = '\0'; | |
| 247 | |||
| 248 | ✗ | env->MakeWritable(&dst); | |
| 249 | ✗ | env->ApplyMessage(&dst, vi, text, vi.width/4, 0xa0a0a0, 0, 0); | |
| 250 | } | ||
| 251 | |||
| 252 | ✗ | return dst; | |
| 253 | ✗ | } | |
| 254 | |||
| 255 | |||
| 256 | ✗ | AVSValue __cdecl ConditionalSelect::Create(AVSValue args, void* , IScriptEnvironment* env) | |
| 257 | { | ||
| 258 | ✗ | int num_args = 0; | |
| 259 | ✗ | PClip* child_array = 0; | |
| 260 | |||
| 261 | ✗ | if(!args[1].IsFunction() && (!args[1].IsString() || !args[1].AsString(nullptr))) | |
| 262 | ✗ | env->ThrowError("Conditional Select: expression missing!"); | |
| 263 | |||
| 264 | ✗ | if (args[2].IsArray()) { | |
| 265 | ✗ | num_args = args[2].ArraySize(); | |
| 266 | ✗ | child_array = new PClip[num_args]; | |
| 267 | |||
| 268 | ✗ | for (int i = 0; i < num_args; ++i) // Copy clips | |
| 269 | ✗ | child_array[i] = args[2][i].AsClip(); | |
| 270 | } | ||
| 271 | ✗ | else if (args[2].IsClip()) { // Make easy to call with trivial 1 clip | |
| 272 | ✗ | num_args = 1; | |
| 273 | ✗ | child_array = new PClip[1]; | |
| 274 | |||
| 275 | ✗ | child_array[0] = args[2].AsClip(); | |
| 276 | } | ||
| 277 | else { | ||
| 278 | ✗ | env->ThrowError("Conditional Select: clip array not recognized!"); | |
| 279 | } | ||
| 280 | |||
| 281 | ✗ | const bool runtime_local_default = args[1].IsFunction() ? true : false; // Avisynth compatibility: false, Neo: true. functions are legacy Neo | |
| 282 | |||
| 283 | ✗ | return new ConditionalSelect(args[0].AsClip(), args[1], num_args, child_array, args[3].AsBool(false), args[4].AsBool(runtime_local_default), env); | |
| 284 | } | ||
| 285 | |||
| 286 | |||
| 287 | /******************************** | ||
| 288 | * Conditional filter | ||
| 289 | * | ||
| 290 | * Returns each one frame from two sources, | ||
| 291 | * based on an evaluator. | ||
| 292 | ********************************/ | ||
| 293 | |||
| 294 | 3 | ConditionalFilter::ConditionalFilter(PClip _child, PClip _source1, PClip _source2, | |
| 295 | AVSValue _condition1, AVSValue _evaluator, AVSValue _condition2, | ||
| 296 | 3 | bool _show, bool _local, IScriptEnvironment* env) : | |
| 297 |
1/2✓ Branch 6 → 7 taken 3 times.
✗ Branch 6 → 64 not taken.
|
3 | GenericVideoFilter(_child), source1(_source1), source2(_source2), |
| 298 |
5/10✓ Branch 2 → 3 taken 3 times.
✗ Branch 2 → 56 not taken.
✓ Branch 3 → 4 taken 3 times.
✗ Branch 3 → 54 not taken.
✓ Branch 5 → 6 taken 3 times.
✗ Branch 5 → 66 not taken.
✓ Branch 7 → 8 taken 3 times.
✗ Branch 7 → 62 not taken.
✓ Branch 8 → 9 taken 3 times.
✗ Branch 8 → 60 not taken.
|
6 | eval1(_condition1), eval2(_condition2), show(_show), local(_local) { |
| 299 | |||
| 300 | 3 | evaluator = NONE; | |
| 301 | |||
| 302 |
1/2✓ Branch 9 → 10 taken 3 times.
✗ Branch 9 → 57 not taken.
|
3 | if (lstrcmpi(_evaluator.AsString(), "equals") == 0 || |
| 303 |
6/8✓ Branch 10 → 11 taken 3 times.
✗ Branch 10 → 15 not taken.
✓ Branch 11 → 12 taken 3 times.
✗ Branch 11 → 57 not taken.
✓ Branch 12 → 13 taken 2 times.
✓ Branch 12 → 15 taken 1 time.
✓ Branch 17 → 18 taken 1 time.
✓ Branch 17 → 19 taken 2 times.
|
5 | lstrcmpi(_evaluator.AsString(), "=") == 0 || |
| 304 |
2/4✓ Branch 13 → 14 taken 2 times.
✗ Branch 13 → 57 not taken.
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 16 taken 2 times.
|
2 | lstrcmpi(_evaluator.AsString(), "==") == 0) |
| 305 | 1 | evaluator = EQUALS; | |
| 306 |
7/10✓ Branch 19 → 20 taken 3 times.
✗ Branch 19 → 57 not taken.
✓ Branch 20 → 21 taken 3 times.
✗ Branch 20 → 23 not taken.
✓ Branch 21 → 22 taken 3 times.
✗ Branch 21 → 57 not taken.
✓ Branch 22 → 23 taken 2 times.
✓ Branch 22 → 24 taken 1 time.
✓ Branch 25 → 26 taken 2 times.
✓ Branch 25 → 27 taken 1 time.
|
3 | if (lstrcmpi(_evaluator.AsString(), "greaterthan") == 0 || lstrcmpi(_evaluator.AsString(), ">") == 0) |
| 307 | 2 | evaluator = GREATERTHAN; | |
| 308 |
5/10✓ Branch 27 → 28 taken 3 times.
✗ Branch 27 → 57 not taken.
✓ Branch 28 → 29 taken 3 times.
✗ Branch 28 → 31 not taken.
✓ Branch 29 → 30 taken 3 times.
✗ Branch 29 → 57 not taken.
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 32 taken 3 times.
✗ Branch 33 → 34 not taken.
✓ Branch 33 → 35 taken 3 times.
|
3 | if (lstrcmpi(_evaluator.AsString(), "lessthan") == 0 || lstrcmpi(_evaluator.AsString(), "<") == 0) |
| 309 | ✗ | evaluator = LESSTHAN; | |
| 310 | |||
| 311 |
1/2✗ Branch 35 → 36 not taken.
✓ Branch 35 → 37 taken 3 times.
|
3 | if (evaluator == NONE) |
| 312 | ✗ | env->ThrowError("ConditionalFilter: Evaluator could not be recognized!"); | |
| 313 | |||
| 314 |
1/2✓ Branch 38 → 39 taken 3 times.
✗ Branch 38 → 57 not taken.
|
3 | VideoInfo vi1 = source1->GetVideoInfo(); |
| 315 |
1/2✓ Branch 40 → 41 taken 3 times.
✗ Branch 40 → 57 not taken.
|
3 | VideoInfo vi2 = source2->GetVideoInfo(); |
| 316 | |||
| 317 |
1/2✗ Branch 41 → 42 not taken.
✓ Branch 41 → 43 taken 3 times.
|
3 | if (vi1.height != vi2.height) |
| 318 | ✗ | env->ThrowError("ConditionalFilter: The two sources must have the same height!"); | |
| 319 |
1/2✗ Branch 43 → 44 not taken.
✓ Branch 43 → 45 taken 3 times.
|
3 | if (vi1.width != vi2.width) |
| 320 | ✗ | env->ThrowError("ConditionalFilter: The two sources must have the same width!"); | |
| 321 |
2/4✓ Branch 45 → 46 taken 3 times.
✗ Branch 45 → 57 not taken.
✗ Branch 46 → 47 not taken.
✓ Branch 46 → 48 taken 3 times.
|
3 | if (!vi1.IsSameColorspace(vi2)) |
| 322 | ✗ | env->ThrowError("ConditionalFilter: The two sources must be the same colorspace!"); | |
| 323 | |||
| 324 | 3 | vi.height = vi1.height; | |
| 325 | 3 | vi.width = vi1.width; | |
| 326 | 3 | vi.pixel_type = vi1.pixel_type; | |
| 327 | 3 | vi.num_frames = max(vi1.num_frames,vi2.num_frames); | |
| 328 | 3 | vi.num_audio_samples = vi1.num_audio_samples; | |
| 329 | 3 | vi.audio_samples_per_second = vi1.audio_samples_per_second; | |
| 330 | 3 | vi.image_type = vi1.image_type; | |
| 331 | 3 | vi.fps_denominator = vi1.fps_denominator; | |
| 332 | 3 | vi.fps_numerator = vi1.fps_numerator; | |
| 333 | 3 | vi.nchannels = vi1.nchannels; | |
| 334 | 3 | vi.sample_type = vi1.sample_type; | |
| 335 | |||
| 336 |
2/4✓ Branch 49 → 50 taken 3 times.
✗ Branch 49 → 57 not taken.
✓ Branch 50 → 51 taken 3 times.
✗ Branch 50 → 57 not taken.
|
3 | child_devs = (GetDeviceTypes(source1) & GetDeviceTypes(source2)); |
| 337 | |||
| 338 |
1/2✗ Branch 51 → 52 not taken.
✓ Branch 51 → 53 taken 3 times.
|
3 | if (child_devs == 0) { |
| 339 | ✗ | env->ThrowError("ConditionalFilter: The two sources must support the same device!"); | |
| 340 | } | ||
| 341 | 3 | } | |
| 342 | |||
| 343 | const char* const t_TRUE="TRUE"; | ||
| 344 | const char* const t_FALSE="FALSE"; | ||
| 345 | |||
| 346 | ✗ | int __stdcall ConditionalFilter::SetCacheHints(int cachehints, int frame_range) | |
| 347 | { | ||
| 348 | AVS_UNUSED(frame_range); | ||
| 349 | ✗ | switch (cachehints) | |
| 350 | { | ||
| 351 | ✗ | case CACHE_GET_MTMODE: | |
| 352 | ✗ | return MT_NICE_FILTER; | |
| 353 | ✗ | case CACHE_GET_DEV_TYPE: | |
| 354 | ✗ | return child_devs; | |
| 355 | ✗ | case CACHE_GET_CHILD_DEV_TYPE: | |
| 356 | ✗ | return DEV_TYPE_ANY; | |
| 357 | } | ||
| 358 | ✗ | return 0; // We do not pass cache requests upwards. | |
| 359 | } | ||
| 360 | |||
| 361 | 3 | PVideoFrame __stdcall ConditionalFilter::GetFrame(int n, IScriptEnvironment* env_) | |
| 362 | { | ||
| 363 |
1/2✓ Branch 2 → 3 taken 3 times.
✗ Branch 2 → 300 not taken.
|
3 | InternalEnvironment* IEnv = GetAndRevealCamouflagedEnv(env_); |
| 364 | 3 | IScriptEnvironment* env = static_cast<IScriptEnvironment*>(IEnv); | |
| 365 | |||
| 366 |
1/2✓ Branch 3 → 4 taken 3 times.
✗ Branch 3 → 300 not taken.
|
3 | AVSValue prev_last; |
| 367 |
1/2✓ Branch 4 → 5 taken 3 times.
✗ Branch 4 → 298 not taken.
|
3 | AVSValue prev_current_frame; |
| 368 | 3 | std::unique_ptr<GlobalVarFrame> var_frame; | |
| 369 | |||
| 370 |
1/2✓ Branch 5 → 6 taken 3 times.
✗ Branch 5 → 294 not taken.
|
3 | AVSValue child_val = child; |
| 371 | |||
| 372 |
1/2✓ Branch 6 → 7 taken 3 times.
✗ Branch 6 → 24 not taken.
|
3 | if (!local) { |
| 373 |
3/6✓ Branch 7 → 8 taken 3 times.
✗ Branch 7 → 210 not taken.
✓ Branch 8 → 9 taken 3 times.
✗ Branch 8 → 208 not taken.
✓ Branch 9 → 10 taken 3 times.
✗ Branch 9 → 206 not taken.
|
3 | prev_last = env->GetVarDef("last"); // Store previous last |
| 374 |
3/6✓ Branch 12 → 13 taken 3 times.
✗ Branch 12 → 216 not taken.
✓ Branch 13 → 14 taken 3 times.
✗ Branch 13 → 214 not taken.
✓ Branch 14 → 15 taken 3 times.
✗ Branch 14 → 212 not taken.
|
3 | prev_current_frame = env->GetVarDef("current_frame"); // Store previous current_frame |
| 375 |
2/4✓ Branch 17 → 18 taken 3 times.
✗ Branch 17 → 220 not taken.
✓ Branch 18 → 19 taken 3 times.
✗ Branch 18 → 218 not taken.
|
3 | env->SetVar("last", (AVSValue)child_val); // Set implicit last |
| 376 |
2/4✓ Branch 20 → 21 taken 3 times.
✗ Branch 20 → 223 not taken.
✓ Branch 21 → 22 taken 3 times.
✗ Branch 21 → 221 not taken.
|
3 | env->SetVar("current_frame", (AVSValue)n); // Set frame to be tested by the conditional filters. |
| 377 | } | ||
| 378 | else { | ||
| 379 | // Neo's default, correct but incompatible with previous Avisynth versions | ||
| 380 | ✗ | var_frame = std::unique_ptr<GlobalVarFrame>(new GlobalVarFrame(IEnv)); // allocate new frame | |
| 381 | ✗ | env->SetGlobalVar("last", child_val); // Set explicit last | |
| 382 | ✗ | env->SetGlobalVar("current_frame", (AVSValue)n); // Set frame to be tested by the conditional filters. | |
| 383 | } | ||
| 384 | |||
| 385 |
1/2✓ Branch 37 → 38 taken 3 times.
✗ Branch 37 → 292 not taken.
|
3 | VideoInfo vi1 = source1->GetVideoInfo(); |
| 386 |
1/2✓ Branch 39 → 40 taken 3 times.
✗ Branch 39 → 292 not taken.
|
3 | VideoInfo vi2 = source2->GetVideoInfo(); |
| 387 | |||
| 388 |
1/2✓ Branch 40 → 41 taken 3 times.
✗ Branch 40 → 292 not taken.
|
3 | AVSValue e1_result; |
| 389 |
1/2✓ Branch 41 → 42 taken 3 times.
✗ Branch 41 → 290 not taken.
|
3 | AVSValue e2_result; |
| 390 | try { | ||
| 391 |
2/4✓ Branch 42 → 43 taken 3 times.
✗ Branch 42 → 251 not taken.
✓ Branch 43 → 44 taken 3 times.
✗ Branch 43 → 62 not taken.
|
3 | if (eval1.IsString()) { |
| 392 |
2/4✓ Branch 44 → 45 taken 3 times.
✗ Branch 44 → 242 not taken.
✓ Branch 45 → 46 taken 3 times.
✗ Branch 45 → 242 not taken.
|
3 | ScriptParser parser(env, eval1.AsString(), "[Conditional Filter, Expresion 1]"); |
| 393 |
1/2✓ Branch 46 → 47 taken 3 times.
✗ Branch 46 → 242 not taken.
|
3 | PExpression exp = parser.Parse(); |
| 394 |
2/4✓ Branch 48 → 49 taken 3 times.
✗ Branch 48 → 233 not taken.
✓ Branch 49 → 50 taken 3 times.
✗ Branch 49 → 231 not taken.
|
3 | e1_result = exp->Evaluate(env); |
| 395 | |||
| 396 |
2/4✓ Branch 51 → 52 taken 3 times.
✗ Branch 51 → 240 not taken.
✓ Branch 52 → 53 taken 3 times.
✗ Branch 52 → 240 not taken.
|
3 | ScriptParser parser2(env, eval2.AsString(), "[Conditional Filter, Expression 2]"); |
| 397 |
2/4✓ Branch 53 → 54 taken 3 times.
✗ Branch 53 → 236 not taken.
✓ Branch 54 → 55 taken 3 times.
✗ Branch 54 → 234 not taken.
|
3 | exp = parser2.Parse(); |
| 398 |
2/4✓ Branch 57 → 58 taken 3 times.
✗ Branch 57 → 239 not taken.
✓ Branch 58 → 59 taken 3 times.
✗ Branch 58 → 237 not taken.
|
3 | e2_result = exp->Evaluate(env); |
| 399 | 3 | } | |
| 400 | else { | ||
| 401 | //auto& func = eval1.AsFunction(); // c++ strict conformance: cannot Convert PFunction to PFunction& | ||
| 402 | ✗ | const PFunction& func = eval1.AsFunction(); | |
| 403 | ✗ | const AVSValue empty_args_array = AVSValue(nullptr, 0); // Invoke_ parameter is const AVSValue&, don't do it inline. | |
| 404 | ✗ | if (!IEnv->Invoke_(&e1_result, child_val, | |
| 405 | ✗ | func->GetLegacyName(), func->GetDefinition(), empty_args_array, 0)) | |
| 406 | { | ||
| 407 | ✗ | env->ThrowError( | |
| 408 | "ConditionalFilter: Invalid function parameter type '%s'(%s)\n" | ||
| 409 | "Function should have one clip argument or no argument", | ||
| 410 | ✗ | func->GetDefinition()->param_types, func->ToString(env)); | |
| 411 | } | ||
| 412 | ✗ | e2_result = true; | |
| 413 | ✗ | } | |
| 414 | ✗ | } catch (const AvisynthError &error) { | |
| 415 | ✗ | const char* error_msg = error.msg; | |
| 416 | |||
| 417 | ✗ | PVideoFrame dst = source1->GetFrame(n,env); | |
| 418 | ✗ | env->MakeWritable(&dst); | |
| 419 | ✗ | env->ApplyMessageEx(&dst, vi1, error_msg, vi.width/W_DIVISOR, 0xa0a0a0, 0, 0, true); | |
| 420 | |||
| 421 | ✗ | if (!local) { | |
| 422 | ✗ | env->SetVar("last", prev_last); // Restore implicit last | |
| 423 | ✗ | env->SetVar("current_frame", prev_current_frame); // Restore current_frame | |
| 424 | } | ||
| 425 | |||
| 426 | ✗ | return dst; | |
| 427 | ✗ | } | |
| 428 | |||
| 429 |
1/2✓ Branch 81 → 82 taken 3 times.
✗ Branch 81 → 84 not taken.
|
3 | if (!local) { |
| 430 |
1/2✓ Branch 82 → 83 taken 3 times.
✗ Branch 82 → 288 not taken.
|
3 | env->SetVar("last", prev_last); // Restore implicit last |
| 431 |
1/2✓ Branch 83 → 84 taken 3 times.
✗ Branch 83 → 288 not taken.
|
3 | env->SetVar("current_frame", prev_current_frame); // Restore current_frame |
| 432 | } | ||
| 433 | |||
| 434 | 3 | bool test_int=false; | |
| 435 | 3 | bool test_string=false; | |
| 436 | |||
| 437 | 3 | int e1 = 0; | |
| 438 | 3 | int e2 = 0; | |
| 439 | 3 | double f1 = 0.0; | |
| 440 | 3 | double f2 = 0.0; | |
| 441 | try { | ||
| 442 |
2/4✓ Branch 84 → 85 taken 3 times.
✗ Branch 84 → 269 not taken.
✗ Branch 85 → 86 not taken.
✓ Branch 85 → 92 taken 3 times.
|
3 | if (e1_result.IsString()) { |
| 443 | ✗ | if (!e2_result.IsString()) | |
| 444 | ✗ | env->ThrowError("Conditional filter: Second expression did not return a string, as in first string expression."); | |
| 445 | ✗ | test_string = true; | |
| 446 | ✗ | test_int = true; | |
| 447 | ✗ | e1 = lstrcmp(e1_result.AsString(), e2_result.AsString()); | |
| 448 | ✗ | e2 = 0; | |
| 449 | |||
| 450 |
2/4✓ Branch 92 → 93 taken 3 times.
✗ Branch 92 → 269 not taken.
✗ Branch 93 → 94 not taken.
✓ Branch 93 → 109 taken 3 times.
|
3 | } else if (e1_result.IsBool()) { |
| 451 | ✗ | if (!(e2_result.IsInt() || e2_result.IsBool())) | |
| 452 | ✗ | env->ThrowError("Conditional filter: Second expression did not return an integer or bool, as in first bool expression."); | |
| 453 | ✗ | test_int = true; | |
| 454 | ✗ | e1 = e1_result.AsBool(); | |
| 455 | ✗ | e2 = e2_result.IsInt() ? e2_result.AsInt() : e2_result.AsBool(); | |
| 456 | |||
| 457 |
3/4✓ Branch 109 → 110 taken 3 times.
✗ Branch 109 → 269 not taken.
✓ Branch 110 → 111 taken 1 time.
✓ Branch 110 → 131 taken 2 times.
|
3 | } else if (e1_result.IsInt()) { |
| 458 |
3/10✓ Branch 111 → 112 taken 1 time.
✗ Branch 111 → 269 not taken.
✗ Branch 112 → 113 not taken.
✓ Branch 112 → 115 taken 1 time.
✗ Branch 113 → 114 not taken.
✗ Branch 113 → 269 not taken.
✗ Branch 114 → 115 not taken.
✗ Branch 114 → 116 not taken.
✓ Branch 117 → 118 taken 1 time.
✗ Branch 117 → 125 not taken.
|
1 | if (e2_result.IsInt() || e2_result.IsBool()) { |
| 459 | 1 | test_int = true; | |
| 460 |
1/2✓ Branch 118 → 119 taken 1 time.
✗ Branch 118 → 269 not taken.
|
1 | e1 = e1_result.AsInt(); |
| 461 |
3/8✓ Branch 119 → 120 taken 1 time.
✗ Branch 119 → 269 not taken.
✓ Branch 120 → 121 taken 1 time.
✗ Branch 120 → 123 not taken.
✓ Branch 121 → 122 taken 1 time.
✗ Branch 121 → 269 not taken.
✗ Branch 123 → 124 not taken.
✗ Branch 123 → 269 not taken.
|
1 | e2 = e2_result.IsInt() ? e2_result.AsInt() : e2_result.AsBool(); |
| 462 | ✗ | } else if (e2_result.IsFloat()) { | |
| 463 | ✗ | f1 = (float)e1_result.AsFloat(); | |
| 464 | ✗ | f2 = (float)e2_result.AsFloat(); | |
| 465 | } else | ||
| 466 | ✗ | env->ThrowError("Conditional filter: Second expression did not return a float, integer or bool, as in first integer expression."); | |
| 467 | |||
| 468 |
2/4✓ Branch 131 → 132 taken 2 times.
✗ Branch 131 → 269 not taken.
✓ Branch 132 → 133 taken 2 times.
✗ Branch 132 → 139 not taken.
|
2 | } else if (e1_result.IsFloat()) { |
| 469 |
1/2✓ Branch 133 → 134 taken 2 times.
✗ Branch 133 → 269 not taken.
|
2 | f1 = (float)e1_result.AsFloat(); |
| 470 |
3/4✓ Branch 134 → 135 taken 2 times.
✗ Branch 134 → 269 not taken.
✓ Branch 135 → 136 taken 1 time.
✓ Branch 135 → 137 taken 1 time.
|
2 | if (!e2_result.IsFloat()) |
| 471 |
1/2✗ Branch 136 → 137 not taken.
✓ Branch 136 → 269 taken 1 time.
|
1 | env->ThrowError("Conditional filter: Second expression did not return a float or an integer, as in first float expression."); |
| 472 |
1/2✓ Branch 137 → 138 taken 1 time.
✗ Branch 137 → 269 not taken.
|
1 | f2 = (float)e2_result.AsFloat(); |
| 473 | } else { | ||
| 474 | ✗ | env->ThrowError("ConditionalFilter: First expression did not return an integer, bool or float!"); | |
| 475 | } | ||
| 476 |
1/2✗ Branch 269 → 270 not taken.
✓ Branch 269 → 271 taken 1 time.
|
1 | } catch (const AvisynthError &error) { |
| 477 | 1 | const char* error_msg = error.msg; | |
| 478 | |||
| 479 |
1/2✗ Branch 273 → 274 not taken.
✓ Branch 273 → 282 taken 1 time.
|
1 | PVideoFrame dst = source1->GetFrame(n,env); |
| 480 | ✗ | env->MakeWritable(&dst); | |
| 481 | ✗ | env->ApplyMessageEx(&dst, vi1, error_msg, vi.width/W_DIVISOR, 0xa0a0a0, 0, 0, true); | |
| 482 | ✗ | return dst; | |
| 483 | 1 | } | |
| 484 | |||
| 485 | |||
| 486 | 2 | bool state = false; | |
| 487 | |||
| 488 |
2/2✓ Branch 140 → 141 taken 1 time.
✓ Branch 140 → 150 taken 1 time.
|
2 | if (test_int) { // String and Int compare |
| 489 |
1/2✗ Branch 141 → 142 not taken.
✓ Branch 141 → 144 taken 1 time.
|
1 | if (evaluator == EQUALS) |
| 490 | ✗ | if (e1 == e2) state = true; | |
| 491 | |||
| 492 |
1/2✓ Branch 144 → 145 taken 1 time.
✗ Branch 144 → 147 not taken.
|
1 | if (evaluator == GREATERTHAN) |
| 493 |
1/2✗ Branch 145 → 146 not taken.
✓ Branch 145 → 147 taken 1 time.
|
1 | if (e1 > e2) state = true; |
| 494 | |||
| 495 |
1/2✗ Branch 147 → 148 not taken.
✓ Branch 147 → 159 taken 1 time.
|
1 | if (evaluator == LESSTHAN) |
| 496 | ✗ | if (e1 < e2) state = true; | |
| 497 | |||
| 498 | } else { // Float compare | ||
| 499 |
1/2✗ Branch 150 → 151 not taken.
✓ Branch 150 → 153 taken 1 time.
|
1 | if (evaluator == EQUALS) |
| 500 | ✗ | if (fabs(f1-f2)<0.000001) state = true; // Exact equal will sometimes be rounded to wrong values. | |
| 501 | |||
| 502 |
1/2✓ Branch 153 → 154 taken 1 time.
✗ Branch 153 → 156 not taken.
|
1 | if (evaluator == GREATERTHAN) |
| 503 |
1/2✗ Branch 154 → 155 not taken.
✓ Branch 154 → 156 taken 1 time.
|
1 | if (f1 > f2) state = true; |
| 504 | |||
| 505 |
1/2✗ Branch 156 → 157 not taken.
✓ Branch 156 → 159 taken 1 time.
|
1 | if (evaluator == LESSTHAN) |
| 506 | ✗ | if (f1 < f2) state = true; | |
| 507 | } | ||
| 508 | |||
| 509 |
1/2✗ Branch 159 → 160 not taken.
✓ Branch 159 → 189 taken 2 times.
|
2 | if (show) { |
| 510 | char text[400]; | ||
| 511 | ✗ | if (test_string) { | |
| 512 | ✗ | snprintf(text, sizeof(text)-1, | |
| 513 | "Left side Conditional Result:%.40s\n" | ||
| 514 | "Right side Conditional Result:%.40s\n" | ||
| 515 | "Evaluate result: %s\n", | ||
| 516 | e1_result.AsString(), e2_result.AsString(), (state) ? t_TRUE : t_FALSE | ||
| 517 | ); | ||
| 518 | ✗ | } else if (test_int) { | |
| 519 | ✗ | snprintf(text, sizeof(text)-1, | |
| 520 | "Left side Conditional Result:%i\n" | ||
| 521 | "Right side Conditional Result:%i\n" | ||
| 522 | "Evaluate result: %s\n", | ||
| 523 | e1, e2, (state) ? t_TRUE : t_FALSE | ||
| 524 | ); | ||
| 525 | } else { | ||
| 526 | ✗ | snprintf(text, sizeof(text)-1, | |
| 527 | "Left side Conditional Result:%7.4f\n" | ||
| 528 | "Right side Conditional Result:%7.4f\n" | ||
| 529 | "Evaluate result: %s\n", | ||
| 530 | f1, f2, (state) ? t_TRUE : t_FALSE | ||
| 531 | ); | ||
| 532 | } | ||
| 533 | |||
| 534 | ✗ | PVideoFrame dst = (state) ? source1->GetFrame(min(vi1.num_frames-1,n),env) : source2->GetFrame(min(vi2.num_frames-1,n),env); | |
| 535 | ✗ | env->MakeWritable(&dst); | |
| 536 | ✗ | env->ApplyMessage(&dst, vi, text, vi.width/4, 0xa0a0a0, 0, 0); | |
| 537 | ✗ | return dst; | |
| 538 | ✗ | } | |
| 539 | |||
| 540 |
1/2✗ Branch 189 → 190 not taken.
✓ Branch 189 → 194 taken 2 times.
|
2 | if (state) |
| 541 | ✗ | return source1->GetFrame(min(vi1.num_frames-1,n),env); | |
| 542 | |||
| 543 |
2/2✓ Branch 196 → 197 taken 1 time.
✓ Branch 196 → 288 taken 1 time.
|
2 | return source2->GetFrame(min(vi1.num_frames-1,n),env); |
| 544 | 13 | } | |
| 545 | |||
| 546 | ✗ | void __stdcall ConditionalFilter::GetAudio(void* buf, int64_t start, int64_t count, IScriptEnvironment* env) { | |
| 547 | ✗ | source1->GetAudio(buf, start, count, env); | |
| 548 | ✗ | } | |
| 549 | |||
| 550 | |||
| 551 | ✗ | AVSValue __cdecl ConditionalFilter::Create(AVSValue args, void* user_data, IScriptEnvironment* env) | |
| 552 | { | ||
| 553 | ✗ | intptr_t userdata = (intptr_t)user_data; | |
| 554 | |||
| 555 | ✗ | const bool runtime_local_default = args[3].IsFunction() ? true : false; // Avisynth compatibility: false, Neo: true. functions are legacy Neo | |
| 556 | |||
| 557 | ✗ | if (userdata == 0) | |
| 558 | ✗ | return new ConditionalFilter(args[0].AsClip(), args[1].AsClip(), args[2].AsClip(), args[3], args[4], args[5], args[6].AsBool(false), args[7].AsBool(runtime_local_default), env); | |
| 559 | else // like GConditional filter shortcut: no "=" "true" needed | ||
| 560 | ✗ | return new ConditionalFilter(args[0].AsClip(), args[1].AsClip(), args[2].AsClip(), args[3], "=", "true", args[4].AsBool(false), args[5].AsBool(runtime_local_default), env); | |
| 561 | } | ||
| 562 | |||
| 563 | |||
| 564 | /************************** | ||
| 565 | * ScriptClip. | ||
| 566 | * | ||
| 567 | * Returns the value of a script evaluated at each frame. | ||
| 568 | * | ||
| 569 | * Implicit last, and current frame is set on each frame. | ||
| 570 | **************************/ | ||
| 571 | |||
| 572 | ✗ | ScriptClip::ScriptClip(PClip _child, AVSValue _script, bool _show, bool _only_eval, bool _eval_after_frame, bool _local, IScriptEnvironment* env) : | |
| 573 | ✗ | GenericVideoFilter(_child), script(_script), show(_show), only_eval(_only_eval), eval_after(_eval_after_frame), local(_local) { | |
| 574 | AVS_UNUSED(env); | ||
| 575 | ✗ | } | |
| 576 | |||
| 577 | |||
| 578 | ✗ | int __stdcall ScriptClip::SetCacheHints(int cachehints, int frame_range) | |
| 579 | { | ||
| 580 | ✗ | switch (cachehints) | |
| 581 | { | ||
| 582 | ✗ | case CACHE_GET_MTMODE: | |
| 583 | ✗ | return MT_NICE_FILTER; | |
| 584 | ✗ | case CACHE_GET_DEV_TYPE: | |
| 585 | ✗ | return (child->GetVersion() >= 5) ? child->SetCacheHints(CACHE_GET_DEV_TYPE, 0) : 0; | |
| 586 | } | ||
| 587 | ✗ | return 0; // We do not pass cache requests upwards. | |
| 588 | } | ||
| 589 | |||
| 590 | ✗ | PVideoFrame __stdcall ScriptClip::GetFrame(int n, IScriptEnvironment* env_) | |
| 591 | { | ||
| 592 | |||
| 593 | ✗ | InternalEnvironment* IEnv = GetAndRevealCamouflagedEnv(env_); | |
| 594 | ✗ | IScriptEnvironment* env = static_cast<IScriptEnvironment*>(IEnv); | |
| 595 | |||
| 596 | ✗ | AVSValue prev_last; | |
| 597 | ✗ | AVSValue prev_current_frame; | |
| 598 | ✗ | std::unique_ptr<GlobalVarFrame> var_frame; | |
| 599 | |||
| 600 | ✗ | AVSValue child_val = child; | |
| 601 | |||
| 602 | ✗ | if (!local) { | |
| 603 | ✗ | prev_last = env->GetVarDef("last"); // Store previous last | |
| 604 | ✗ | prev_current_frame = env->GetVarDef("current_frame"); // Store previous current_frame | |
| 605 | ✗ | env->SetVar("last", (AVSValue)child_val); // Set implicit last | |
| 606 | ✗ | env->SetVar("current_frame", (AVSValue)n); // Set frame to be tested by the conditional filters. | |
| 607 | } | ||
| 608 | else { | ||
| 609 | // Neo's default, correct but incompatible with previous Avisynth versions | ||
| 610 | ✗ | var_frame = std::unique_ptr<GlobalVarFrame>(new GlobalVarFrame(IEnv)); // allocate new frame | |
| 611 | ✗ | env->SetGlobalVar("last", child_val); // Set explicit last | |
| 612 | ✗ | env->SetGlobalVar("current_frame", (AVSValue)n); // Set frame to be tested by the conditional filters. | |
| 613 | } | ||
| 614 | |||
| 615 | ✗ | if (show) { | |
| 616 | ✗ | PVideoFrame dst = child->GetFrame(n,env); | |
| 617 | ✗ | const char* text = script.IsString() ? script.AsString() : script.AsFunction()->ToString(env); | |
| 618 | ✗ | env->MakeWritable(&dst); | |
| 619 | ✗ | env->ApplyMessage(&dst, vi, text, vi.width/6, 0xa0a0a0, 0, 0); | |
| 620 | ✗ | if (!local) { | |
| 621 | ✗ | env->SetVar("last", prev_last); // Restore implicit last | |
| 622 | ✗ | env->SetVar("current_frame", prev_current_frame); // Restore current_frame | |
| 623 | } | ||
| 624 | ✗ | return dst; | |
| 625 | ✗ | } | |
| 626 | |||
| 627 | ✗ | AVSValue result; | |
| 628 | ✗ | PVideoFrame eval_return; // Frame to be returned if script should be evaluated AFTER frame has been fetched. Otherwise not used. | |
| 629 | |||
| 630 | ✗ | if (eval_after) eval_return = child->GetFrame(n,env); | |
| 631 | |||
| 632 | try { | ||
| 633 | ✗ | if (script.IsString()) { | |
| 634 | ✗ | ScriptParser parser(env, script.AsString(), "[ScriptClip]"); | |
| 635 | ✗ | PExpression exp = parser.Parse(); | |
| 636 | ✗ | result = exp->Evaluate(env); | |
| 637 | ✗ | } | |
| 638 | else { | ||
| 639 | ✗ | const PFunction& func = script.AsFunction(); | |
| 640 | ✗ | const AVSValue empty_args_array = AVSValue(nullptr, 0); // Invoke_ parameter is const AVSValue&, don't do it inline. | |
| 641 | ✗ | const Function* fd = func->GetDefinition(); | |
| 642 | ✗ | if (!IEnv->Invoke_(&result, child_val, | |
| 643 | ✗ | func->GetLegacyName(),fd , empty_args_array, 0)) | |
| 644 | { | ||
| 645 | /* fd is nullptr: | ||
| 646 | ps = function propSetterFunc(Clip c) { propSetInt("frameluma_sc_func",func(AverageLuma)) } | ||
| 647 | ScriptClip(func(ps)) | ||
| 648 | */ | ||
| 649 | ✗ | if (fd == nullptr) | |
| 650 | ✗ | env->ThrowError( | |
| 651 | "ScriptClip: Invalid function parameter type '%s'(%s)\n" | ||
| 652 | "Function should have one clip argument or no argument", | ||
| 653 | ✗ | "<no definition>", func->ToString(env)); | |
| 654 | else | ||
| 655 | ✗ | env->ThrowError( | |
| 656 | "ScriptClip: Invalid function parameter type '%s'(%s)\n" | ||
| 657 | "Function should have one clip argument or no argument", | ||
| 658 | ✗ | func->GetDefinition()->param_types, func->ToString(env)); | |
| 659 | } | ||
| 660 | ✗ | } | |
| 661 | ✗ | } catch (const AvisynthError &error) { | |
| 662 | ✗ | const char* error_msg = error.msg; | |
| 663 | |||
| 664 | ✗ | PVideoFrame dst = child->GetFrame(n,env); | |
| 665 | ✗ | env->MakeWritable(&dst); | |
| 666 | ✗ | env->ApplyMessageEx(&dst, vi, error_msg, vi.width/W_DIVISOR, 0xa0a0a0, 0, 0, true); | |
| 667 | ✗ | if (!local) { | |
| 668 | ✗ | env->SetVar("last", prev_last); // Restore implicit last | |
| 669 | ✗ | env->SetVar("current_frame", prev_current_frame); // Restore current_frame | |
| 670 | } | ||
| 671 | ✗ | return dst; | |
| 672 | ✗ | } | |
| 673 | |||
| 674 | ✗ | if (!local) { | |
| 675 | ✗ | env->SetVar("last", prev_last); // Restore implicit last | |
| 676 | ✗ | env->SetVar("current_frame", prev_current_frame); // Restore current_frame | |
| 677 | } | ||
| 678 | |||
| 679 | ✗ | if (eval_after && only_eval) return eval_return; | |
| 680 | ✗ | if (only_eval) return child->GetFrame(n,env); | |
| 681 | |||
| 682 | ✗ | const char* error = NULL; | |
| 683 | ✗ | VideoInfo vi2 = vi; | |
| 684 | ✗ | if (!result.IsClip()) { | |
| 685 | ✗ | if (result.IsBool()) | |
| 686 | ✗ | error = "ScriptClip: Function did not return a video clip! (Was a bool)"; | |
| 687 | ✗ | else if (result.IsInt()) | |
| 688 | ✗ | error = "ScriptClip: Function did not return a video clip! (Was an int)"; | |
| 689 | ✗ | else if (result.IsFloat()) | |
| 690 | ✗ | error = "ScriptClip: Function did not return a video clip! (Was a float)"; | |
| 691 | ✗ | else if (result.IsString()) | |
| 692 | ✗ | error = "ScriptClip: Function did not return a video clip! (Was a string)"; | |
| 693 | ✗ | else if (result.IsArray()) | |
| 694 | ✗ | error = "ScriptClip: Function did not return a video clip! (Was an array)"; | |
| 695 | ✗ | else if (!result.Defined()) | |
| 696 | ✗ | error = "ScriptClip: Function did not return a video clip! (Was the undefined value)"; | |
| 697 | else | ||
| 698 | ✗ | error = "ScriptClip: Function did not return a video clip! (type is unknown)"; | |
| 699 | } else { | ||
| 700 | ✗ | vi2 = result.AsClip()->GetVideoInfo(); | |
| 701 | ✗ | if (!vi.IsSameColorspace(vi2)) { | |
| 702 | ✗ | error = "ScriptClip: Function did not return a video clip of the same colorspace as the source clip!"; | |
| 703 | ✗ | } else if (vi2.width != vi.width) { | |
| 704 | ✗ | error = "ScriptClip: Function did not return a video clip with the same width as the source clip!"; | |
| 705 | ✗ | } else if (vi2.height != vi.height) { | |
| 706 | ✗ | error = "ScriptClip: Function did not return a video clip with the same height as the source clip!"; | |
| 707 | } | ||
| 708 | } | ||
| 709 | |||
| 710 | ✗ | if (error != NULL) { | |
| 711 | ✗ | PVideoFrame dst = child->GetFrame(n,env); | |
| 712 | ✗ | env->MakeWritable(&dst); | |
| 713 | ✗ | env->ApplyMessage(&dst, vi, error, vi.width/W_DIVISOR, 0xa0a0a0, 0, 0); | |
| 714 | ✗ | return dst; | |
| 715 | ✗ | } | |
| 716 | |||
| 717 | ✗ | n = min(n,vi2.num_frames-1); // We ignore it if the new clip is not as long as the current one. This can allow the resulting clip to be one frame. | |
| 718 | |||
| 719 | ✗ | return result.AsClip()->GetFrame(n,env); | |
| 720 | ✗ | } | |
| 721 | |||
| 722 | |||
| 723 | ✗ | AVSValue __cdecl ScriptClip::Create(AVSValue args, void* , IScriptEnvironment* env) | |
| 724 | { | ||
| 725 | ✗ | const bool runtime_local_default = args[1].IsFunction() ? true : false; // Avisynth compatibility: false, Neo: true. functions are legacy Neo | |
| 726 | |||
| 727 | ✗ | return new ScriptClip(args[0].AsClip(), args[1], args[2].AsBool(false),false, args[3].AsBool(false), args[4].AsBool(runtime_local_default), env); | |
| 728 | } | ||
| 729 | |||
| 730 | |||
| 731 | ✗ | AVSValue __cdecl ScriptClip::Create_eval(AVSValue args, void* , IScriptEnvironment* env) | |
| 732 | { | ||
| 733 | ✗ | const bool runtime_local_default = args[1].IsFunction() ? true : false; // Avisynth compatibility: false, Neo: true. functions are legacy Neo | |
| 734 | |||
| 735 | ✗ | return new ScriptClip(args[0].AsClip(), args[1], args[2].AsBool(false),true, args[3].AsBool(false), args[4].AsBool(runtime_local_default), env);} | |
| 736 |