GCC Code Coverage Report


Directory: avs_core/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 19.4% 193 / 0 / 994
Functions: 31.7% 19 / 0 / 60
Branches: 6.9% 136 / 0 / 1980

filters/conditional/conditional_reader.cpp
Line Branch Exec Source
1 /*
2 ConditionalReader (c) 2004 by Klaus Post
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16
17 The author can be contacted at:
18 sh0dan[at]stofanet.dk
19 */
20
21 #include "conditional_reader.h"
22 #include "../core/internal.h"
23 #include "../core/parser/scriptparser.h"
24 #include <cstdlib>
25
26 #ifdef AVS_WINDOWS
27 #include <avs/win.h>
28 #else
29 #include <avs/posix.h>
30 #endif
31
32 #include <avs/minmax.h>
33 #include "../core/parser/scriptparser.h"
34 #include "../core/AVSMap.h"
35 #include <iostream>
36 #include <sstream>
37 #include <iomanip>
38 #include <string>
39 #include <cstring>
40 #include "../convert/convert_helper.h"
41 #include <regex>
42
43
44 /*****************************************************************************
45 * Helper code from XviD (http://www.xvid.org)
46 *
47 * Copyright (C) 2002 Foxer <email?>
48 * 2002 Dirk Knop <dknop@gwdg.de>
49 * 2002-2003 Edouard Gomez <ed.gomez@free.fr>
50 * 2003 Pete Ross <pross@xvid.org>
51 *****************************************************************************/
52
53 /* Default buffer size for reading lines */
54 #define BUF_SZ 1024
55
56
57 /* This function returns an allocated string containing a complete line read
58 * from the file starting at the current position */
59 static char *
60 3 readline(FILE *f)
61 {
62 3 char *buffer = NULL;
63 3 int buffer_size = 0;
64 3 int pos = 0;
65
66 for (;;) {
67 int c;
68
69 /* Read a character from the stream */
70 32 c = fgetc(f);
71
72 /* Is that EOF or new line ? */
73
4/4
✓ Branch 4 → 5 taken 31 times.
✓ Branch 4 → 11 taken 1 time.
✓ Branch 5 → 6 taken 29 times.
✓ Branch 5 → 11 taken 2 times.
32 if(c == EOF || c == '\n')
74 break;
75
76 /* Do we have to update buffer ? */
77
2/2
✓ Branch 6 → 7 taken 2 times.
✓ Branch 6 → 10 taken 27 times.
29 if(pos >= buffer_size - 1) {
78 2 buffer_size += BUF_SZ;
79 2 char *tmpbuffer = (char*)realloc(buffer, buffer_size);
80
1/2
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 2 times.
2 if (tmpbuffer == NULL) {
81 free(buffer);
82 return(NULL);
83 }
84 2 buffer = tmpbuffer;
85 }
86
87 29 buffer[pos] = (char)c;
88 29 pos++;
89 29 }
90
91 /* Read \n or EOF */
92
2/2
✓ Branch 11 → 12 taken 1 time.
✓ Branch 11 → 17 taken 2 times.
3 if (buffer == NULL) {
93 /* EOF, so we reached the end of the file, return NULL */
94
1/2
✓ Branch 13 → 14 taken 1 time.
✗ Branch 13 → 15 not taken.
1 if(feof(f))
95 1 return(NULL);
96
97 /* Just an empty line with just a newline, allocate a 1 byte buffer to
98 * store a zero length string */
99 buffer = (char*)malloc(1);
100 if(buffer == NULL)
101 return(NULL);
102 }
103
104 /* Zero terminated string */
105
2/4
✓ Branch 17 → 18 taken 2 times.
✗ Branch 17 → 20 not taken.
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 20 taken 2 times.
2 if (pos && buffer[pos-1] == '\r')
106 buffer[pos-1] = '\0';
107 else
108 2 buffer[pos] = '\0';
109
110 2 return(buffer);
111 }
112
113 /* This function returns a pointer to the first non space char in the given
114 * string or the end of the string */
115
116 static char *
117 6 skipspaces(char *string)
118 {
119
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 6 times.
6 if (string == NULL) return(NULL);
120
121
1/2
✓ Branch 8 → 5 taken 6 times.
✗ Branch 8 → 9 not taken.
6 while (*string != '\0') {
122 /* Test against space chars */
123
1/2
✓ Branch 5 → 6 taken 6 times.
✗ Branch 5 → 7 not taken.
6 if (!isspace(*string)) return(string);
124 string++;
125 }
126 return(string);
127 }
128
129 /* This function returns a pointer to the first space char in the given
130 * string or the end of the string */
131
132 static char *
133 3 findspace(char *string)
134 {
135
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 3 times.
3 if (string == NULL) return(NULL);
136
137
2/2
✓ Branch 8 → 5 taken 20 times.
✓ Branch 8 → 9 taken 1 time.
21 while (*string != '\0') {
138 /* Test against space chars */
139
2/2
✓ Branch 5 → 6 taken 2 times.
✓ Branch 5 → 7 taken 18 times.
20 if (isspace(*string)) return(string);
140 18 string++;
141 }
142 1 return(string);
143 }
144
145 /* This function returns a boolean that tells if the string is only a
146 * comment */
147 static int
148 2 iscomment(char *string)
149 {
150 2 const char comments[] =
151 {
152 '#',';', '%', '\0'
153 };
154 2 const char *cmtchar = comments;
155 2 int iscomment = 0;
156
157
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 2 times.
2 if (string == NULL) return(1);
158
159 2 string = skipspaces(string);
160
161
2/2
✓ Branch 9 → 6 taken 6 times.
✓ Branch 9 → 10 taken 2 times.
8 while(*cmtchar != '\0') {
162
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 6 times.
6 if(*string == *cmtchar) {
163 iscomment = 1;
164 break;
165 }
166 6 cmtchar++;
167 }
168
169 2 return(iscomment);
170 }
171
172
173 // Reader ------------------------------------------------
174
175
176 1 ConditionalReader::ConditionalReader(PClip _child, const char* filename, const char _varname[],
177 1 bool _show, const char *_condVarSuffix, bool _local, IScriptEnvironment* env)
178
2/4
✓ Branch 2 → 3 taken 1 time.
✗ Branch 2 → 133 not taken.
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 131 not taken.
1 : GenericVideoFilter(_child), show(_show), mode(MODE_UNKNOWN), offset(0), local(_local), stringcache(0)
179 {
180 FILE * f;
181 1 char *line = 0;
182 int lines;
183
184
1/2
✓ Branch 6 → 7 taken 1 time.
✗ Branch 6 → 171 not taken.
1 variableName = _varname; // std::string
185
1/2
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 1 time.
1 if (_condVarSuffix[0])
186 variableName += _condVarSuffix; // append if parameter exists
187
1/2
✓ Branch 10 → 11 taken 1 time.
✗ Branch 10 → 171 not taken.
1 variableNameFixed = env->SaveString(variableName.c_str());
188
189
2/4
✓ Branch 11 → 12 taken 1 time.
✗ Branch 11 → 171 not taken.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 1 time.
1 if ((f = fopen(filename, "rb")) == NULL)
190 env->ThrowError("ConditionalReader: Could not open file '%s'.", filename);
191
192 1 lines = 0;
193
194 try {
195
3/4
✓ Branch 125 → 126 taken 3 times.
✗ Branch 125 → 164 not taken.
✓ Branch 126 → 15 taken 2 times.
✓ Branch 126 → 127 taken 1 time.
3 while ((line = readline(f)) != NULL) {
196 char *ptr;
197 int fields;
198
199 2 lines++;
200
201 /* We skip spaces */
202 2 ptr = skipspaces(line);
203
204 /* Skip coment lines or empty lines */
205
3/6
✓ Branch 17 → 18 taken 2 times.
✗ Branch 17 → 19 not taken.
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 20 taken 2 times.
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 23 taken 2 times.
2 if(iscomment(ptr) || *ptr == '\0') {
206 free(line);
207 line = 0;
208 continue;
209 }
210
211
2/2
✓ Branch 23 → 24 taken 1 time.
✓ Branch 23 → 55 taken 1 time.
2 if (mode == MODE_UNKNOWN) {
212 // We have not recieved a mode - We expect type.
213 1 char* keyword = ptr;
214
215 1 ptr = findspace(ptr);
216
1/2
✓ Branch 25 → 26 taken 1 time.
✗ Branch 25 → 123 not taken.
1 if (*ptr) {
217 1 *ptr++ = '\0';
218
1/2
✓ Branch 26 → 27 taken 1 time.
✗ Branch 26 → 123 not taken.
1 if (!lstrcmpi(keyword, "type")) {
219 /* We skip spaces */
220 1 char* type = skipspaces(ptr);
221
222 1 ptr = findspace(type);
223 1 *ptr = '\0';
224
225
1/2
✓ Branch 29 → 30 taken 1 time.
✗ Branch 29 → 35 not taken.
1 if (!lstrcmpi(type, "int")) {
226 1 mode = MODE_INT;
227
2/4
✓ Branch 30 → 31 taken 1 time.
✗ Branch 30 → 32 not taken.
✓ Branch 33 → 34 taken 1 time.
✗ Branch 33 → 164 not taken.
1 intVal = new int[vi.num_frames];
228 } else if (!lstrcmpi(type, "float")) {
229 mode = MODE_FLOAT;
230 floatVal = new float[vi.num_frames];
231 } else if (!lstrcmpi(type, "bool")) {
232 mode = MODE_BOOL;
233 boolVal = new bool[vi.num_frames];
234 } else if (!lstrcmpi(type, "string")) {
235 mode = MODE_STRING;
236 stringVal = new const char*[vi.num_frames];
237 } else {
238 ThrowLine("ConditionalReader: Unknown 'Type' specified in line %d", lines, env);
239 }// end if compare type
240
2/4
✓ Branch 51 → 52 taken 1 time.
✗ Branch 51 → 136 not taken.
✓ Branch 52 → 53 taken 1 time.
✗ Branch 52 → 134 not taken.
1 SetRange(0, vi.num_frames-1, AVSValue());
241 }// end if compare keyword
242 }// end if fields
243
244 } else { // We have a defined mode and allocated the values.
245
246 1 char* keyword = ptr;
247 1 char* type = findspace(keyword);
248
249
1/2
✓ Branch 56 → 57 taken 1 time.
✗ Branch 56 → 58 not taken.
1 if (*type) *type++ = '\0';
250
251
1/2
✗ Branch 58 → 59 not taken.
✓ Branch 58 → 65 taken 1 time.
1 if (!lstrcmpi(keyword, "default")) {
252 AVSValue def = ConvertType(type, lines, env);
253 SetRange(0, vi.num_frames-1, def);
254
255
1/2
✗ Branch 65 → 66 not taken.
✓ Branch 65 → 68 taken 1 time.
1 } else if (!lstrcmpi(keyword, "offset")) {
256 fields = sscanf(type, "%d", &offset);
257 if (fields != 1)
258 ThrowLine("ConditionalReader: Could not read Offset in line %d", lines, env);
259
260
2/4
✓ Branch 68 → 69 taken 1 time.
✗ Branch 68 → 70 not taken.
✗ Branch 69 → 70 not taken.
✓ Branch 69 → 85 taken 1 time.
1 } else if (keyword[0] == 'R' || keyword[0] == 'r') { // Range
261 int start;
262 int stop;
263
264 type = skipspaces(type);
265 fields = sscanf(type, "%d", &start);
266
267 type = findspace(type);
268 type = skipspaces(type);
269 fields += sscanf(type, "%d", &stop);
270
271 type = findspace(type);
272 if (!*type || fields != 2)
273 ThrowLine("ConditionalReader: Could not read Range in line %d", lines, env);
274
275 if (start > stop)
276 ThrowLine("ConditionalReader: The Range start frame is after the end frame in line %d", lines, env);
277
278 AVSValue set = ConvertType(type+1, lines, env);
279 SetRange(start, stop, set);
280
281
1/4
✗ Branch 85 → 86 not taken.
✓ Branch 85 → 87 taken 1 time.
✗ Branch 86 → 87 not taken.
✗ Branch 86 → 112 not taken.
1 } else if (keyword[0] == 'I' || keyword[0] == 'i') { // Interpolate
282
1/2
✗ Branch 87 → 88 not taken.
✓ Branch 87 → 89 taken 1 time.
1 if (mode == MODE_BOOL)
283 ThrowLine("ConditionalReader: Cannot Interpolate booleans in line %d", lines, env);
284
285
1/2
✗ Branch 89 → 90 not taken.
✓ Branch 89 → 91 taken 1 time.
1 if (mode == MODE_STRING)
286 ThrowLine("ConditionalReader: Cannot Interpolate strings in line %d", lines, env);
287
288 1 type = skipspaces(type);
289 int start;
290 int stop;
291 char start_value[64];
292 char stop_value[64];
293 1 fields = sscanf(type, "%d %d %63s %63s", &start, &stop, start_value, stop_value);
294
295
1/2
✗ Branch 92 → 93 not taken.
✓ Branch 92 → 94 taken 1 time.
1 if (fields != 4)
296 ThrowLine("ConditionalReader: Could not read Interpolation range in line %d", lines, env);
297
1/2
✗ Branch 94 → 95 not taken.
✓ Branch 94 → 96 taken 1 time.
1 if (start > stop)
298 ThrowLine("ConditionalReader: The Interpolation start frame is after the end frame in line %d", lines, env);
299
300 1 start_value[63] = '\0';
301
1/2
✓ Branch 96 → 97 taken 1 time.
✗ Branch 96 → 156 not taken.
1 AVSValue set_start = ConvertType(start_value, lines, env);
302
303 1 stop_value[63] = '\0';
304
1/2
✓ Branch 97 → 98 taken 1 time.
✗ Branch 97 → 154 not taken.
1 AVSValue set_stop = ConvertType(stop_value, lines, env);
305
306 1 const int range = stop-start;
307
2/4
✓ Branch 98 → 99 taken 1 time.
✗ Branch 98 → 152 not taken.
✓ Branch 99 → 100 taken 1 time.
✗ Branch 99 → 152 not taken.
1 const double diff = (set_stop.AsFloat() - set_start.AsFloat()) / range;
308
2/2
✓ Branch 108 → 101 taken 1 time.
✓ Branch 108 → 109 taken 1 time.
2 for (int i = 0; i<=range; i++) {
309
1/2
✓ Branch 101 → 102 taken 1 time.
✗ Branch 101 → 152 not taken.
1 const double n = i * diff + set_start.AsFloat();
310
1/2
✓ Branch 105 → 106 taken 1 time.
✗ Branch 105 → 149 not taken.
1 SetFrame(i+start, (mode == MODE_FLOAT)
311
1/4
✗ Branch 102 → 103 not taken.
✓ Branch 102 → 104 taken 1 time.
✗ Branch 103 → 105 not taken.
✗ Branch 103 → 151 not taken.
2 ? AVSValue(n)
312
1/2
✓ Branch 104 → 105 taken 1 time.
✗ Branch 104 → 151 not taken.
1 : AVSValue((int)(n+0.5)));
313 }
314 1 } else {
315 int cframe;
316 fields = sscanf(keyword, "%d", &cframe);
317 if ((*type || mode == MODE_STRING) && fields == 1) { // allow empty string
318 AVSValue set = ConvertType(type, lines, env);
319 SetFrame(cframe, set);
320 } else {
321 ThrowLine("ConditionalReader: Do not understand line %d", lines, env);
322 }
323 }
324
325 } // End we have defined type
326 2 free(line);
327 2 line = 0;
328 }// end while still some file left to read.
329 }
330 catch (...) {
331 free(line);
332 fclose(f);
333 CleanUp();
334 throw;
335 }
336
337 /* We are done with the file */
338
1/2
✓ Branch 127 → 128 taken 1 time.
✗ Branch 127 → 171 not taken.
1 fclose(f);
339
340
1/2
✗ Branch 128 → 129 not taken.
✓ Branch 128 → 130 taken 1 time.
1 if (mode == MODE_UNKNOWN)
341 env->ThrowError("ConditionalReader: Type was not defined!");
342
343 1 }
344
345
346
347 // Converts from the char array given to the type specified.
348
349 2 AVSValue ConditionalReader::ConvertType(const char* content, int line, IScriptEnvironment* env)
350 {
351
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 2 times.
2 if (mode == MODE_UNKNOWN)
352 ThrowLine("ConditionalReader: Type has not been defined. Line %d", line, env);
353
354 int fields;
355
1/5
✓ Branch 4 → 5 taken 2 times.
✗ Branch 4 → 9 not taken.
✗ Branch 4 → 13 not taken.
✗ Branch 4 → 38 not taken.
✗ Branch 4 → 50 not taken.
2 switch (mode) {
356 2 case MODE_INT:
357 int ival;
358 2 fields = sscanf(content, "%d", &ival);
359
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 2 times.
2 if (fields != 1)
360 ThrowLine("ConditionalReader: Could not find an expected integer at line %d!", line, env);
361
362
1/2
✓ Branch 7 → 8 taken 2 times.
✗ Branch 7 → 52 not taken.
2 return AVSValue(ival);
363
364 case MODE_FLOAT:
365 float fval;
366 fields = sscanf(content, "%e", &fval);
367 if (fields != 1)
368 ThrowLine("ConditionalReader: Could not find an expected float at line %d!", line, env);
369
370 return AVSValue(fval);
371
372 case MODE_BOOL:
373 char bval[8];
374 bval[0] = '\0';
375 fields = sscanf(content, "%7s", bval);
376 bval[7] = '\0';
377 if (!lstrcmpi(bval, "true")) {
378 return AVSValue(true);
379 }
380 else if (!lstrcmpi(bval, "t")) {
381 return AVSValue(true);
382 }
383 else if (!lstrcmpi(bval, "yes")) {
384 return AVSValue(true);
385 }
386 else if (!lstrcmp(bval, "1")) {
387 return AVSValue(true);
388 }
389 else if (!lstrcmpi(bval, "false")) {
390 return AVSValue(false);
391 }
392 else if (!lstrcmpi(bval, "f")) {
393 return AVSValue(false);
394 }
395 else if (!lstrcmpi(bval, "no")) {
396 return AVSValue(false);
397 }
398 else if (!lstrcmp(bval, "0")) {
399 return AVSValue(false);
400 }
401 ThrowLine("ConditionalReader: Boolean value was not true or false in line %d", line, env);
402
403 case MODE_STRING:
404 StringCache *str;
405
406 // Look for an existing duplicate
407 for (str = stringcache; str; str = str->next ) {
408 if (!lstrcmp(str->string, content)) break;
409 }
410 // Could not find one, add it
411 if (!str) {
412 str = new StringCache;
413 str->string = _strdup(content);
414 str->next = stringcache;
415 stringcache = str;
416 }
417 return AVSValue(str->string);
418 }
419 return AVSValue();
420 }
421
422
423 // Sets range with both start and stopframe inclusive.
424
425 1 void ConditionalReader::SetRange(int start_frame, int stop_frame, AVSValue v) {
426 int i;
427 1 start_frame = max(start_frame+offset, 0);
428 1 stop_frame = min(stop_frame+offset, vi.num_frames-1);
429 int p;
430 float q;
431 bool r;
432 const char* s;
433
434
1/5
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 14 not taken.
✗ Branch 4 → 23 not taken.
✗ Branch 4 → 32 not taken.
✗ Branch 4 → 37 not taken.
1 switch (mode) {
435 1 case MODE_INT:
436
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 9 taken 1 time.
1 p = v.Defined() ? v.AsInt() : 0;
437
2/2
✓ Branch 12 → 11 taken 3 times.
✓ Branch 12 → 13 taken 1 time.
4 for (i = start_frame; i <= stop_frame; i++) {
438 3 intVal[i] = p;
439 }
440 1 break;
441 case MODE_FLOAT:
442 q = v.Defined() ? v.AsFloatf() : 0.0f;
443 for (i = start_frame; i <= stop_frame; i++) {
444 floatVal[i] = q;
445 }
446 break;
447 case MODE_BOOL:
448 r = v.Defined() ? v.AsBool() : false;
449 for (i = start_frame; i <= stop_frame; i++) {
450 boolVal[i] = r;
451 }
452 break;
453 case MODE_STRING:
454 s = v.AsString("");
455 for (i = start_frame; i <= stop_frame; i++) {
456 stringVal[i] = s;
457 }
458 break;
459 }
460 1 }
461
462 // Sets the value of one frame.
463
464 1 void ConditionalReader::SetFrame(int framenumber, AVSValue v) {
465
466
2/4
✓ Branch 2 → 3 taken 1 time.
✗ Branch 2 → 4 not taken.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 1 time.
1 if ((framenumber+offset) < 0 || (framenumber+offset) > vi.num_frames-1 )
467 return;
468
469
1/5
✓ Branch 5 → 6 taken 1 time.
✗ Branch 5 → 8 not taken.
✗ Branch 5 → 10 not taken.
✗ Branch 5 → 12 not taken.
✗ Branch 5 → 14 not taken.
1 switch (mode) {
470 1 case MODE_INT:
471 1 intVal[framenumber+offset] = v.AsInt();
472 1 break;
473 case MODE_FLOAT:
474 floatVal[framenumber+offset] = v.AsFloatf();
475 break;
476 case MODE_BOOL:
477 boolVal[framenumber+offset] = v.AsBool();
478 break;
479 case MODE_STRING:
480 stringVal[framenumber+offset] = v.AsString("");
481 break;
482 }
483 }
484
485 // Get the value of a frame.
486 1 AVSValue ConditionalReader::GetFrameValue(int framenumber) {
487 1 framenumber = clamp(framenumber, 0, vi.num_frames-1);
488
489
1/5
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 5 not taken.
✗ Branch 3 → 6 not taken.
✗ Branch 3 → 7 not taken.
✗ Branch 3 → 8 not taken.
1 switch (mode) {
490 1 case MODE_INT:
491 1 return AVSValue(intVal[framenumber]);
492
493 case MODE_FLOAT:
494 return AVSValue(floatVal[framenumber]);
495
496 case MODE_BOOL:
497 return AVSValue(boolVal[framenumber]);
498
499 case MODE_STRING:
500 return AVSValue(stringVal[framenumber]);
501
502 }
503 return AVSValue(0);
504 }
505
506 // Destructor
507 1 ConditionalReader::~ConditionalReader(void)
508 {
509 1 CleanUp();
510 1 }
511
512
513 1 void ConditionalReader::CleanUp(void)
514 {
515
1/5
✓ Branch 2 → 3 taken 1 time.
✗ Branch 2 → 6 not taken.
✗ Branch 2 → 9 not taken.
✗ Branch 2 → 12 not taken.
✗ Branch 2 → 20 not taken.
1 switch (mode) {
516 1 case MODE_INT:
517
1/2
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 5 not taken.
1 delete[] intVal;
518 1 break;
519 case MODE_FLOAT:
520 delete[] floatVal;
521 break;
522 case MODE_BOOL:
523 delete[] boolVal;
524 break;
525 case MODE_STRING:
526 delete[] stringVal;
527
528 //free the cached strings
529 for (StringCache* str = stringcache; str; ) {
530 StringCache* curr = str;
531 free(str->string);
532 str = str->next;
533 delete curr;
534 }
535 stringcache = 0;
536
537 break;
538 }
539 1 mode = MODE_UNKNOWN;
540 1 }
541
542
543 void ConditionalReader::ThrowLine(const char* err, int line, IScriptEnvironment* env) {
544 env->ThrowError(err, line);
545 }
546
547
548 1 PVideoFrame __stdcall ConditionalReader::GetFrame(int n, IScriptEnvironment* env_)
549 {
550
1/2
✓ Branch 2 → 3 taken 1 time.
✗ Branch 2 → 49 not taken.
1 AVSValue v = GetFrameValue(n);
551
552
1/2
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 47 not taken.
1 InternalEnvironment* IEnv = GetAndRevealCamouflagedEnv(env_);
553 1 IScriptEnvironment* env = static_cast<IScriptEnvironment*>(IEnv);
554
555 1 std::unique_ptr<GlobalVarFrame> var_frame;
556
557
1/2
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 45 not taken.
1 AVSValue child_val = child;
558
559
1/2
✓ Branch 5 → 6 taken 1 time.
✗ Branch 5 → 7 not taken.
1 if (!local) {
560
1/2
✓ Branch 6 → 15 taken 1 time.
✗ Branch 6 → 43 not taken.
1 env->SetGlobalVar(variableNameFixed, v);
561 }
562 else {
563 // Neo's default, correct but incompatible with previous Avisynth versions
564 var_frame = std::unique_ptr<GlobalVarFrame>(new GlobalVarFrame(IEnv)); // allocate new frame
565 env->SetGlobalVar(variableNameFixed, v);
566 }
567
568
569
1/2
✓ Branch 16 → 17 taken 1 time.
✗ Branch 16 → 43 not taken.
1 PVideoFrame src = child->GetFrame(n,env);
570
571
1/2
✗ Branch 17 → 18 not taken.
✓ Branch 17 → 26 taken 1 time.
1 if (show) {
572 AVSValue v2 = env->Invoke("String", v);
573 env->MakeWritable(&src);
574 env->ApplyMessage(&src, vi, v2.AsString(""), vi.width/2, 0xa0a0a0, 0, 0);
575 }
576 1 return src;
577 1 }
578
579 int __stdcall ConditionalReader::SetCacheHints(int cachehints, int frame_range)
580 {
581 switch (cachehints)
582 {
583 case CACHE_GET_MTMODE:
584 return MT_NICE_FILTER;
585 case CACHE_GET_DEV_TYPE:
586 return (child->GetVersion() >= 5) ? child->SetCacheHints(CACHE_GET_DEV_TYPE, 0) : 0;
587 }
588 return 0; // We do not pass cache requests upwards.
589 }
590
591
592
593 AVSValue __cdecl ConditionalReader::Create(AVSValue args, void*, IScriptEnvironment* env)
594 {
595 const bool runtime_local_default = false; // Avisynth compatibility: false, Neo: true.
596
597 return new ConditionalReader(args[0].AsClip(), args[1].AsString(""), args[2].AsString("Conditional") , args[3].AsBool(false), args[4].AsString(""), args[5].AsBool(runtime_local_default), env);
598 }
599
600
601 // Write ------------------------------------------------
602
603
604 static const char EMPTY[] = "";
605 static const char AplusT[] = "a+t";
606 static const char WplusT[] = "w+t";
607
608
609 Write::Write(PClip _child, const char* _filename, AVSValue args, int _linecheck, bool _append, bool _flush, bool _local, IScriptEnvironment* env_) :
610 GenericVideoFilter(_child), linecheck(_linecheck), flush(_flush), append(_append), local(_local), arglist(0)
611 {
612 InternalEnvironment* IEnv = GetAndRevealCamouflagedEnv(env_);
613 IScriptEnvironment* env = static_cast<IScriptEnvironment*>(IEnv);
614
615 #ifdef AVS_WINDOWS
616 _fullpath(filename, _filename, _MAX_PATH);
617 #else
618 // Use realpath and handle possible failure to avoid unused-result warning.
619 if (realpath(_filename, filename) == NULL) {
620 // Fallback: copy original input path into filename safely.
621 size_t len = strlen(_filename);
622 if (len >= sizeof(filename)) len = sizeof(filename) - 1;
623 memcpy(filename, _filename, len);
624 filename[len] = '\0';
625 }
626 #endif
627
628 fout = fopen(filename, append ? AplusT : WplusT); //append or purge file
629 if (!fout) env->ThrowError("Write: File '%s' cannot be opened.", filename);
630
631 if (flush) fclose(fout); //will be reopened in FileOut
632
633 arrsize = args.ArraySize();
634
635 arglist = new exp_res[arrsize];
636
637 for (int i = 0; i < arrsize; i++) {
638 arglist[i].expression = args[i];
639 arglist[i].string = EMPTY;
640 }
641
642 if (linecheck != -1 && linecheck != -2)
643 return;
644
645 AVSValue prev_last;
646 AVSValue prev_current_frame;
647 std::unique_ptr<GlobalVarFrame> var_frame;
648
649 AVSValue child_val = child;
650
651 if (!local) {
652 prev_last = env->GetVarDef("last"); // Store previous last
653 prev_current_frame = env->GetVarDef("current_frame"); // Store previous current_frame
654 env->SetVar("last", child_val); // Set implicit last
655 env->SetVar("current_frame", (AVSValue)linecheck); // special -1 or -2
656 }
657 else {
658 // Neo's default, correct but incompatible with previous Avisynth versions
659 var_frame = std::unique_ptr<GlobalVarFrame>(new GlobalVarFrame(IEnv)); // allocate new frame
660 env->SetGlobalVar("last", child_val); // Set explicit last
661 env->SetGlobalVar("current_frame", (AVSValue)linecheck); // special -1 or -2
662 }
663
664 Write::DoEval(env); // at both write at start and write at end
665
666 if (linecheck == -1) { //write at start
667 Write::FileOut(env, AplusT);
668 }
669
670 if (!local) {
671 env->SetVar("last", prev_last); // Restore implicit last
672 env->SetVar("current_frame", prev_current_frame); // Restore current_frame
673 }
674 }
675
676 PVideoFrame __stdcall Write::GetFrame(int n, IScriptEnvironment* env_) {
677
678 //changed to call write AFTER the child->GetFrame
679
680 InternalEnvironment* IEnv = GetAndRevealCamouflagedEnv(env_);
681 IScriptEnvironment* env = static_cast<IScriptEnvironment*>(IEnv);
682
683 PVideoFrame tmpframe = child->GetFrame(n, env);
684
685 if (linecheck < 0) return tmpframe; //do nothing here when writing only start or end
686
687 AVSValue prev_last;
688 AVSValue prev_current_frame;
689 std::unique_ptr<GlobalVarFrame> var_frame;
690
691 AVSValue child_val = child;
692
693 if (!local) {
694 prev_last = env->GetVarDef("last"); // Store previous last
695 prev_current_frame = env->GetVarDef("current_frame"); // Store previous current_frame
696 env->SetVar("last", (AVSValue)child_val); // Set implicit last
697 env->SetVar("current_frame", (AVSValue)n); // Set frame to be tested by the conditional filters.
698 }
699 else {
700 // Neo's default, correct but incompatible with previous Avisynth versions
701 var_frame = std::unique_ptr<GlobalVarFrame>(new GlobalVarFrame(IEnv)); // allocate new frame
702 env->SetGlobalVar("last", child_val); // Set implicit last (to avoid recursive stack calls?)
703 env->SetGlobalVar("current_frame", (AVSValue)n); // Set frame to be tested by the conditional filters.
704 }
705
706 if (Write::DoEval(env)) {
707 Write::FileOut(env, AplusT);
708 }
709
710 if (!local) {
711 env->SetVar("last", prev_last); // Restore implicit last
712 env->SetVar("current_frame", prev_current_frame); // Restore current_frame
713 }
714
715 return tmpframe;
716
717 };
718
719 Write::~Write(void) {
720 if (linecheck == -2) { //write at end
721 Write::FileOut(0, append ? AplusT : WplusT); // Allow for retruncating at actual end
722 }
723 if (!flush) fclose(fout);
724
725 delete[] arglist;
726 };
727
728 void Write::FileOut(IScriptEnvironment* env, const char* mode) {
729 int i;
730 if (flush) {
731 fout = fopen(filename, mode);
732 if (!fout) {
733 if (env) env->ThrowError("Write: File '%s' cannot be opened.", filename);
734 return;
735 }
736 }
737 for (i= ( (linecheck==1) ? 1 : 0) ; i<arrsize; i++ ) {
738 fputs(arglist[i].string, fout);
739 }
740 fputs("\n", fout);
741 if (flush) {
742 fclose(fout);
743 }
744 }
745
746 bool Write::DoEval( IScriptEnvironment* env) {
747 bool keep_this_line = true;
748 int i;
749 AVSValue expr;
750 AVSValue result;
751
752 for (i=0; i<arrsize; i++) {
753 expr = arglist[i].expression;
754
755 if ( (linecheck==1) && (i==0)) {
756 try {
757 if (expr.IsFunction()) {
758 result = env->Invoke3(child, expr.AsFunction(), AVSValue(nullptr, 0));
759 }
760 else {
761 expr = expr.AsString(EMPTY);
762 result = env->Invoke("Eval", expr);
763 }
764 if (!result.AsBool(true)) {
765 keep_this_line = false;
766 break;
767 }
768 } catch (const AvisynthError&) {
769 // env->ThrowError("Write: Can't eval linecheck expression!"); // results in KEEPING the line
770 }
771 } else {
772 try {
773 if (expr.IsFunction()) {
774 result = env->Invoke3(child, expr.AsFunction(), AVSValue(nullptr, 0));
775 }
776 else {
777 expr = expr.AsString(EMPTY);
778 result = env->Invoke("Eval", expr);
779 }
780 result = env->Invoke("string",result); //convert all results to a string
781 arglist[i].string = result.AsString(EMPTY);
782 } catch (const AvisynthError &error) {
783 arglist[i].string = env->SaveString(error.msg);
784 }
785 }
786 }
787 return keep_this_line;
788 }
789
790 int __stdcall Write::SetCacheHints(int cachehints, int frame_range)
791 {
792 switch (cachehints)
793 {
794 case CACHE_GET_MTMODE:
795 return MT_SERIALIZED;
796 }
797 return 0; // We do not pass cache requests upwards.
798 }
799
800 AVSValue __cdecl Write::Create(AVSValue args, void*, IScriptEnvironment* env)
801 {
802 bool runtime_local_default = false;
803 // Param 2: string/function or array of strings/functions
804 if (args[2].IsFunction() || (args[2].IsArray() && args[2].ArraySize() > 0 && args[2][0].IsFunction()))
805 runtime_local_default = true;
806 // Avisynth compatibility: false, Neo: true. functions are legacy Neo
807
808 return new Write(args[0].AsClip(), args[1].AsString(EMPTY), args[2], 0, args[3].AsBool(true),args[4].AsBool(true), args[5].AsBool(runtime_local_default), env);
809 }
810
811 AVSValue __cdecl Write::Create_If(AVSValue args, void*, IScriptEnvironment* env)
812 {
813 bool runtime_local_default = false;
814 // Param 2: string/function or array of strings/functions
815 if (args[2].IsFunction() || (args[2].IsArray() && args[2].ArraySize() > 0 && args[2][0].IsFunction()))
816 runtime_local_default = true;
817 // Avisynth compatibility: false, Neo: true. functions are legacy Neo
818
819 return new Write(args[0].AsClip(), args[1].AsString(EMPTY), args[2], 1, args[3].AsBool(true),args[4].AsBool(true), args[5].AsBool(runtime_local_default), env);
820 }
821
822 AVSValue __cdecl Write::Create_Start(AVSValue args, void*, IScriptEnvironment* env)
823 {
824 bool runtime_local_default = false;
825 // Param 2: string/function or array of strings/functions
826 /*if (args[2].IsFunction() || (args[2].IsArray() && args[2].ArraySize() > 0 && args[2][0].IsFunction()))
827 runtime_local_default = true;
828 */
829 // Avisynth compatibility: false, Neo: also false as of 2020.03.28
830
831 return new Write(args[0].AsClip(), args[1].AsString(EMPTY), args[2], -1, args[3].AsBool(false), true, args[4].AsBool(runtime_local_default), env);
832 }
833
834 AVSValue __cdecl Write::Create_End(AVSValue args, void*, IScriptEnvironment* env)
835 {
836 bool runtime_local_default = false;
837 // Param 2: string/function or array of strings/functions
838 /*
839 if (args[2].IsFunction() || (args[2].IsArray() && args[2].ArraySize() > 0 && args[2][0].IsFunction()))
840 runtime_local_default = true;
841 */
842 // Avisynth compatibility: false, Neo: also false as of 2020.03.28
843
844 return new Write(args[0].AsClip(), args[1].AsString(EMPTY), args[2], -2, args[3].AsBool(true), args[4].AsBool(runtime_local_default), true, env);
845 }
846
847
848 UseVar::UseVar(PClip _child, AVSValue vars, IScriptEnvironment* env)
849 : GenericVideoFilter(_child)
850 {
851 vars_.resize(vars.ArraySize());
852 for (int i = 0; i < vars.ArraySize(); ++i) {
853 auto name = vars_[i].name = vars[i].AsString();
854 if (!env->GetVarTry(name, &vars_[i].val)) {
855 env->ThrowError("UseVar: No variable named %s", name);
856 }
857 }
858 }
859
860 UseVar::~UseVar() { }
861
862 PVideoFrame __stdcall UseVar::GetFrame(int n, IScriptEnvironment* env_)
863 {
864 InternalEnvironment* IEnv = GetAndRevealCamouflagedEnv(env_);
865 IScriptEnvironment* env = static_cast<IScriptEnvironment*>(IEnv);
866
867 GlobalVarFrame var_frame(IEnv); // allocate new frame
868
869 // set variables
870 for (int i = 0; i < (int)vars_.size(); ++i) {
871 env->SetGlobalVar(vars_[i].name, vars_[i].val);
872 }
873
874 return child->GetFrame(n, env);
875 }
876
877 int __stdcall UseVar::SetCacheHints(int cachehints, int frame_range) {
878 switch (cachehints)
879 {
880 case CACHE_GET_MTMODE:
881 return MT_NICE_FILTER;
882 case CACHE_GET_DEV_TYPE:
883 return (child->GetVersion() >= 5) ? child->SetCacheHints(CACHE_GET_DEV_TYPE, 0) : 0;
884 }
885 return 0; // We do not pass cache requests upwards.
886 }
887
888 AVSValue __cdecl UseVar::Create(AVSValue args, void* user_data, IScriptEnvironment* env)
889 {
890 return new UseVar(args[0].AsClip(), args[1], env);
891 }
892
893 #define W_DIVISOR 5 // Width divisor for onscreen messages
894
895
896 // Avisynth+ frame property support
897 //**************************************************
898 // propSet, propSetInt, propSetFloat, propSetString
899
900 SetProperty::SetProperty(PClip _child, const char* name, const AVSValue& value, const int kind,
901 const int mode, IScriptEnvironment* env)
902 : GenericVideoFilter(_child)
903 , name(name)
904 , value(value)
905 , kind(kind)
906 , append_mode(mode)
907 { }
908
909 SetProperty::~SetProperty() { }
910
911 PVideoFrame __stdcall SetProperty::GetFrame(int n, IScriptEnvironment* env)
912 {
913 // parameter type to set, comes different for each script function version
914 int propType = kind;
915
916 // 0: auto by result type
917 // 1: integer from function
918 // 2: float from function
919 // 3: char (null terminated data) from function
920 // 4: array from function (all elements have the same type)
921 // 5: clip from function
922 // 10: integer from direct
923 // 11: float from direct
924 // 12: string from direct
925 // 13: array from direct (all elements have the same type)
926 // 14: clip from direct
927
928 AVSValue result;
929 const char* error_msg = nullptr;
930 if (value.IsFunction()) {
931 PFunction func = value.AsFunction();
932 try {
933 const AVSValue empty_args_array = AVSValue(nullptr, 0); // invoke's parameter is const AVSValue&, don't do it inline.
934 result = env->Invoke3(child, func, empty_args_array);
935 }
936 catch (IScriptEnvironment::NotFound) {
937 error_msg = env->Sprintf("AddProperties: Invalid function parameter type '%s'(%s)\n"
938 "Function should have no argument",
939 func->GetDefinition()->param_types, func->ToString(env));
940 }
941 catch (const AvisynthError& error) {
942 error_msg = env->Sprintf("%s\nAddProperties: Error in %s",
943 error.msg, func->ToString(env));
944 }
945 }
946 else {
947 // direct input
948 result = value;
949 propType = 0; // autodetect type
950 }
951
952 PVideoFrame frame = child->GetFrame(n, env);
953
954 if (error_msg) {
955 env->MakeWritable(&frame);
956 env->ApplyMessage(&frame, vi, error_msg, vi.width / W_DIVISOR, 0xa0a0a0, 0, 0);
957 return frame;
958 }
959
960 /*
961 usage:
962 propSet("hello",1)
963 ScriptClip("""propSetInt("frameluma",func(AverageLuma))""")
964 ScriptClip("""propSet("frameluma2",AverageLuma)""")
965 ScriptClip("""SubTitle(string(propGetInt("frameluma")))""")
966 or
967 ps = func(propSetterFunc) # make function object from function
968 ScriptClip(ps) # pass function object to scriptclip
969 ScriptClip(function[](clip c) { SubTitle(string(propGetInt("frameprop_demo")), y=20) })
970 ScriptClip(function[](clip c) { SubTitle(string(propGetInt("frameprop_demo2")), y=40) })
971 function propSetterFunc(Clip x) {
972 x
973 propSetInt("frameprop_demo", func(AverageLuma))
974 propSetInt("frameprop_demo2", function[]() { current_frame })
975 }
976 */
977
978 try {
979 // check auto
980 if (propType == 0) {
981 // 'u'nset, 'i'nteger, 'f'loat, 's'string, 'c'lip, 'v'ideoframe, 'm'ethod };
982 if (result.IsInt())
983 propType = 1;
984 else if (result.IsFloat())
985 propType = 2;
986 else if (result.IsString())
987 propType = 3;
988 else if (result.IsArray())
989 propType = 4;
990 else if (result.IsClip())
991 propType = 5;
992 else
993 env->ThrowError("Invalid return type (Was a %s)", GetAVSTypeName(result));
994 }
995
996 // env->MakeWritable(&frame);
997 // do we need this? Yes! better: MakePropertyWritable since V9 interface
998
999 // Check setting a constant value to the same as it was before (int, float, string case)
1000 // We'd avoid even MakePropertyWritable and return a fully unaltered frame
1001 do {
1002 if (append_mode != AVSPropAppendMode::PROPAPPENDMODE_REPLACE) break; // only optimize replace
1003 if (propType != 1 && propType != 2 && propType != 3) break; // only optimize traditional types
1004
1005 const AVSMap* avsmap_r = env->getFramePropsRO(frame);
1006 auto size = env->propNumElements(avsmap_r, name); // 1 if single item, >1 size of array, 0 if not exists
1007 if(size != 1) break; // no such property or property is an array
1008
1009 char x = env->propGetType(avsmap_r, name);
1010 if (propType == 1 && x != 'i') break; // int does not match
1011 if (propType == 2 && x != 'f') break; // float does not match
1012 if (propType == 3 && x != 's') break; // string does not match
1013
1014 if (propType == 1) {
1015 if (!result.IsInt()) break; // IsInt checks for Long as well
1016 auto val = env->propGetInt(avsmap_r, name, 0, nullptr);
1017 if (val == result.AsLong()) return frame; // value match -> return unaltered
1018 // v11: AsLong instead of AsInt
1019 }
1020 else if (propType == 2) {
1021 if (!result.IsFloat()) break;
1022 auto val = env->propGetFloat(avsmap_r, name, 0, nullptr);
1023 if (val == result.AsFloat()) return frame; // value match -> return unaltered
1024 // memo: AsFloat returns double
1025 }
1026 else if (propType == 3) {
1027 if (!result.IsString()) break;
1028 const char* val_to_set = result.AsString();
1029 if (val_to_set == nullptr) break;
1030 auto length_to_set = strlen(val_to_set);
1031 auto length = env->propGetDataSize(avsmap_r, name, 0, nullptr);
1032 if (length != length_to_set) break; // different size
1033 const char* val_storage = env->propGetData(avsmap_r, name, 0, nullptr);
1034 if(std::memcmp(val_to_set, val_storage, length) == 0) return frame; // value match -> return unaltered
1035 }
1036 break;
1037 } while (0);
1038
1039 env->MakePropertyWritable(&frame);
1040 AVSMap* avsmap = env->getFramePropsRW(frame);
1041
1042 int res = 0;
1043
1044 // special case: zero sized array -> entry deleted
1045 if (result.IsArray() && result.ArraySize() == 0)
1046 res = env->propDeleteKey(avsmap, name); // 0 is success
1047 else if (propType == 1 && result.IsInt()) // IsInt checks for Long as well
1048 res = env->propSetInt(avsmap, name, result.AsLong(), append_mode); // v11: AsLong instead of AsInt
1049 else if (propType == 2 && result.IsFloat())
1050 res = env->propSetFloat(avsmap, name, result.AsFloat(), append_mode);
1051 else if (propType == 3 && result.IsString())
1052 {
1053 const char* s = result.AsString(); // no need for SaveString, it has its own storage
1054 // assume string: AVSPropDataTypeHint::DATATYPEHINT_UTF8
1055 res = env->propSetDataH(avsmap, name, s, -1, AVSPropDataTypeHint::PROPDATATYPEHINT_UTF8, append_mode); // -1: auto string length
1056 // test res = env->propSetDataH(avsmap, name, s, -1, AVSPropDataTypeHint::DATATYPEHINT_BINARY, append_mode);
1057 }
1058 else if (propType == 4 && result[0].IsInt())
1059 {
1060 int size = result.ArraySize();
1061 std::vector<int64_t> int64array(size); // avs can do int only, temporary array needed
1062 for (int i = 0; i < size; i++) {
1063 if (!result[i].IsInt()) // IsInt checks for Long as well
1064 env->ThrowError("Wrong data type in property '%s': all array elements should be the same (integer) type", name);
1065 int64array[i] = result[i].AsLong(); // all elements should be int
1066 }
1067 res = env->propSetIntArray(avsmap, name, int64array.data(), size);
1068 }
1069 else if (propType == 4 && result[0].IsFloat())
1070 {
1071 int size = result.ArraySize();
1072 std::vector<double> d_array(size); // avs can do float only, temporary array needed
1073 for (int i = 0; i < size; i++) {
1074 if (!result[i].IsFloat())
1075 env->ThrowError("Wrong data type in property '%s': all array elements should be the same (float) type", name);
1076 d_array[i] = result[i].AsFloat(); // all elements should be float/double or int/long
1077 }
1078 res = env->propSetFloatArray(avsmap, name, d_array.data(), size);
1079 }
1080 else if (propType == 4 && result[0].IsString())
1081 {
1082 const int size = result.ArraySize();
1083 // no such api like propSetDataArray
1084 env->propDeleteKey(avsmap, name);
1085 for (int i = 0; i < size; i++) {
1086 if (!result[i].IsString())
1087 env->ThrowError("Wrong data type in property '%s': all array elements should be the same (string) type", name);
1088 // assume string: AVSPropDataTypeHint::DATATYPEHINT_UTF8
1089 res = env->propSetDataH(avsmap, name, result[i].AsString(), -1, AVSPropDataTypeHint::PROPDATATYPEHINT_UTF8, AVSPropAppendMode::PROPAPPENDMODE_APPEND); // all elements should be string
1090 if (res)
1091 break;
1092 }
1093 }
1094 else if (propType == 4 && result[0].IsClip())
1095 {
1096 int size = result.ArraySize();
1097 // no such api like propSetClipArray
1098 env->propDeleteKey(avsmap, name);
1099 for (int i = 0; i < size; i++) {
1100 if (!result[i].IsClip())
1101 env->ThrowError("Wrong data type in property '%s': all array elements should be the same (Clip) type", name);
1102 PClip clip = result[i].AsClip();
1103 res = env->propSetClip(avsmap, name, clip, AVSPropAppendMode::PROPAPPENDMODE_APPEND); // all elements should be Clip
1104 if (res)
1105 break;
1106 }
1107 }
1108 else if (propType == 5 && result.IsClip()) {
1109 PClip clp = result.AsClip();
1110 res = env->propSetClip(avsmap, name, clp, append_mode);
1111 }
1112 else
1113 {
1114 env->ThrowError("Wrong data type, property '%s' type is not %s", name, GetAVSTypeName(result));
1115 }
1116
1117 if (res)
1118 env->ThrowError("error setting property '%s', error = %d", name, res); // fixme: res reasons
1119 }
1120 catch (const AvisynthError& error) {
1121 error_msg = env->Sprintf("propAdd: %s", error.msg);
1122 }
1123
1124 if (error_msg) {
1125 env->MakeWritable(&frame);
1126 env->ApplyMessage(&frame, vi, error_msg, vi.width / W_DIVISOR, 0xa0a0a0, 0, 0);
1127 }
1128
1129 return frame;
1130 }
1131
1132 int __stdcall SetProperty::SetCacheHints(int cachehints, int frame_range)
1133 {
1134 AVS_UNUSED(frame_range);
1135 switch (cachehints)
1136 {
1137 case CACHE_GET_MTMODE:
1138 return MT_NICE_FILTER;
1139 }
1140 return 0; // We do not pass cache requests upwards.
1141 }
1142
1143 AVSValue __cdecl SetProperty::Create(AVSValue args, void* user_data, IScriptEnvironment* env)
1144 {
1145 const int kind = (int)(intptr_t)user_data;
1146 const int defaultMode = AVSPropAppendMode::PROPAPPENDMODE_REPLACE;
1147
1148 int mode = AVSPropAppendMode::PROPAPPENDMODE_REPLACE;
1149 if(kind != 4) // at propSetArray there is no mode parameter
1150 mode = args[3].AsInt(defaultMode);
1151
1152 /*
1153 paReplace = 0,
1154 paAppend = 1,
1155 paTouch = 2
1156 */
1157 return new SetProperty(args[0].AsClip(), args[1].AsString(), args[2], kind, mode, env);
1158 }
1159
1160 //**************************************************
1161 // helper for property name list
1162
1163 2 static bool PropNamesToArray(const char* filtername, AVSValue& _propNames, std::vector<std::string>& propNames, IScriptEnvironment* env)
1164 {
1165
1/2
✓ Branch 3 → 4 taken 2 times.
✗ Branch 3 → 5 not taken.
2 if (!_propNames.Defined())
1166 2 return false;
1167 const int size = _propNames.ArraySize();
1168 propNames.resize(size);
1169 for (int i = 0; i < size; i++) {
1170 const char* name = _propNames[i].AsString();
1171 if (name == nullptr || !*name)
1172 env->ThrowError("%s error: list contains empty name.", filtername);
1173
1174 // Recognize
1175 // - wildcards: "Myprops_*" or "ab*cd"
1176 // - regex with ^ $ delimiters
1177
1178 std::string nameStr(name);
1179
1180 // Check if it's already a regex
1181 if (nameStr.front() == '^' && nameStr.back() == '$') {
1182 try {
1183 std::regex re(nameStr);
1184 }
1185 catch (const std::regex_error&) {
1186 env->ThrowError("%s error: invalid regex string.", filtername);
1187 }
1188 }
1189 else {
1190 // Convert wildcard to regex
1191 if (nameStr.find('*') != std::string::npos) {
1192 nameStr = std::regex_replace(nameStr, std::regex("\\*"), ".*");
1193 // Add regex delimiters
1194 nameStr = "^" + nameStr + "$";
1195 }
1196 try {
1197 std::regex re(nameStr);
1198 }
1199 catch (const std::regex_error&) {
1200 env->ThrowError("%s error: invalid regex string while converting * wildcard.", filtername);
1201 }
1202 }
1203 propNames[i] = nameStr;
1204 }
1205 return true;
1206 }
1207
1208 //**************************************************
1209 // propDelete
1210
1211 DeleteProperty::DeleteProperty(PClip _child, AVSValue _propNames, IScriptEnvironment* env)
1212 : GenericVideoFilter(_child)
1213 {
1214 propNames_defined = PropNamesToArray("propDelete", _propNames, propNames, env);
1215 }
1216
1217 DeleteProperty::~DeleteProperty() { }
1218
1219 PVideoFrame __stdcall DeleteProperty::GetFrame(int n, IScriptEnvironment* env)
1220 {
1221 PVideoFrame frame = child->GetFrame(n, env);
1222
1223 // Erase only if needed
1224 const AVSMap* avsmap_test = env->getFramePropsRO(frame);
1225 if (0 == env->propNumKeys(avsmap_test))
1226 return frame;
1227
1228 /*
1229 usage:
1230 propDelete("frameluma")
1231 propDelete(["_Matrix","prop2"])
1232 */
1233
1234 env->MakePropertyWritable(&frame);
1235
1236 AVSMap* avsmap = env->getFramePropsRW(frame);
1237
1238 /*
1239 // ignore when property does not exist
1240 if (!res) {
1241 const char *error_msg = env->Sprintf("propDelete: error deleting property '%s'", name);
1242 env->ApplyMessage(&frame, vi, error_msg, vi.width / W_DIVISOR, 0xa0a0a0, 0, 0);
1243 }
1244 */
1245 // delete selected names
1246 // positive list
1247 for (auto& s : propNames) {
1248 const char* key = s.c_str();
1249 if (s.front() == '^' && s.back() == '$') {
1250 // positive regex list
1251 const int numKeys = env->propNumKeys(avsmap);
1252 int index = 0;
1253 for (int i = 0; i < numKeys; i++) {
1254 const char* key = env->propGetKey(avsmap, index);
1255 if (std::regex_match(key, std::regex(s)))
1256 env->propDeleteKey(avsmap, key); // 0 is success, ignored, index does not change
1257 else
1258 index++;
1259 }
1260 }
1261 else {
1262 env->propDeleteKey(avsmap, key); // 0 is success, ignored
1263 }
1264 }
1265
1266 return frame;
1267 }
1268
1269 int __stdcall DeleteProperty::SetCacheHints(int cachehints, int frame_range)
1270 {
1271 AVS_UNUSED(frame_range);
1272 switch (cachehints)
1273 {
1274 case CACHE_GET_MTMODE:
1275 return MT_NICE_FILTER;
1276 }
1277 return 0; // We do not pass cache requests upwards.
1278 }
1279
1280 AVSValue __cdecl DeleteProperty::Create(AVSValue args, void*, IScriptEnvironment* env)
1281 {
1282 return new DeleteProperty(args[0].AsClip(), args[1], env);
1283 }
1284
1285 //**************************************************
1286 // propDelete
1287
1288 ClearProperties::ClearProperties(PClip _child, IScriptEnvironment* env)
1289 : GenericVideoFilter(_child)
1290 { }
1291
1292 ClearProperties::~ClearProperties() { }
1293
1294 PVideoFrame __stdcall ClearProperties::GetFrame(int n, IScriptEnvironment* env)
1295 {
1296 PVideoFrame frame = child->GetFrame(n, env);
1297
1298 /*
1299 usage:
1300 ScriptClip("""propClear()""")
1301 */
1302
1303 // Erase only if needed
1304 const AVSMap* avsmap_test = env->getFramePropsRO(frame);
1305 if (0 != env->propNumKeys(avsmap_test)) {
1306 env->MakePropertyWritable(&frame);
1307 AVSMap* avsmap = env->getFramePropsRW(frame);
1308 env->clearMap(avsmap);
1309 }
1310
1311 return frame;
1312 }
1313
1314 int __stdcall ClearProperties::SetCacheHints(int cachehints, int frame_range)
1315 {
1316 AVS_UNUSED(frame_range);
1317 switch (cachehints)
1318 {
1319 case CACHE_GET_MTMODE:
1320 return MT_NICE_FILTER;
1321 }
1322 return 0; // We do not pass cache requests upwards.
1323 }
1324
1325 AVSValue __cdecl ClearProperties::Create(AVSValue args, void*, IScriptEnvironment* env)
1326 {
1327 return new ClearProperties(args[0].AsClip(), env);
1328 }
1329
1330 //**************************************************
1331 // PropPassthrough — compatibility helper for old filters that predate frame properties
1332
1333 PropPassthrough::PropPassthrough(PClip _child, PClip _prop_src, IScriptEnvironment* env)
1334 : GenericVideoFilter(_child), prop_src(_prop_src)
1335 { }
1336
1337 PVideoFrame __stdcall PropPassthrough::GetFrame(int n, IScriptEnvironment* env)
1338 {
1339 PVideoFrame out = child->GetFrame(n, env);
1340 // Self-healing: if the filter was updated to use NewVideoFrameP it will already
1341 // carry properties. In that case trust it and skip the copy.
1342 const AVSMap* out_map = env->getFramePropsRO(out);
1343 if (env->propNumKeys(out_map) > 0)
1344 return out;
1345 PVideoFrame src = prop_src->GetFrame(n, env);
1346 env->MakePropertyWritable(&out);
1347 env->copyFrameProps(src, out);
1348 return out;
1349 }
1350
1351 int __stdcall PropPassthrough::SetCacheHints(int cachehints, int frame_range)
1352 {
1353 AVS_UNUSED(frame_range);
1354 switch (cachehints)
1355 {
1356 case CACHE_GET_MTMODE:
1357 return MT_NICE_FILTER;
1358 }
1359 return 0;
1360 }
1361
1362 //**************************************************
1363 // propCopy
1364
1365 1 CopyProperties::CopyProperties(PClip _child, PClip _child2, bool _merge, AVSValue _propNames, bool _exclude, IScriptEnvironment* env)
1366
3/6
✓ Branch 2 → 3 taken 1 time.
✗ Branch 2 → 11 not taken.
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 9 not taken.
✓ Branch 5 → 6 taken 1 time.
✗ Branch 5 → 16 not taken.
1 : GenericVideoFilter(_child), child2(_child2), merge(_merge), exclude(_exclude)
1367 {
1368
1/2
✓ Branch 7 → 8 taken 1 time.
✗ Branch 7 → 12 not taken.
1 propNames_defined = PropNamesToArray("propCopy", _propNames, propNames, env);
1369 1 }
1370
1371 1 CopyProperties::~CopyProperties() { }
1372
1373 AVSValue __cdecl CopyProperties::Create(AVSValue args, void*, IScriptEnvironment* env)
1374 {
1375 const bool merge = args[2].AsBool(false);
1376 const bool exclude = args[4].AsBool(false);
1377 return new CopyProperties(args[0].AsClip(), args[1].AsClip(), merge, args[3], exclude, env);
1378 }
1379
1380 int __stdcall CopyProperties::SetCacheHints(int cachehints, int frame_range)
1381 {
1382 AVS_UNUSED(frame_range);
1383 switch (cachehints)
1384 {
1385 case CACHE_GET_MTMODE:
1386 return MT_NICE_FILTER;
1387 }
1388 return 0; // We do not pass cache requests upwards.
1389 }
1390
1391 static void CopyOneFrameProp(const char* key, AVSMap* mapv, const AVSMap* avsmap_from, IScriptEnvironment* env)
1392 {
1393 const int numElements = env->propNumElements(avsmap_from, key);
1394 char propType = env->propGetType(avsmap_from, key);
1395 int error;
1396
1397 // int and float (int64 and double) supports array get set
1398 if (propType == AVSPropTypes::PROPTYPE_INT) {
1399 auto srcval = env->propGetIntArray(avsmap_from, key, &error);
1400 env->propSetIntArray(mapv, key, srcval, numElements);
1401 }
1402 else if (propType == AVSPropTypes::PROPTYPE_FLOAT) {
1403 auto srcval = env->propGetFloatArray(avsmap_from, key, &error);
1404 env->propSetFloatArray(mapv, key, srcval, numElements);
1405 }
1406 else {
1407 // assumes the property is cleared beforehand
1408
1409 // frame, clip and data (string)
1410 if (propType == AVSPropTypes::PROPTYPE_FRAME) {
1411 for (int index = 0; index < numElements; index++) {
1412 auto src = env->propGetFrame(avsmap_from, key, index, &error);
1413 env->propSetFrame(mapv, key, src, AVSPropAppendMode::PROPAPPENDMODE_APPEND);
1414 }
1415 }
1416 else if (propType == AVSPropTypes::PROPTYPE_CLIP) {
1417 for (int index = 0; index < numElements; index++) {
1418 auto src = env->propGetClip(avsmap_from, key, index, &error);
1419 env->propSetClip(mapv, key, src, AVSPropAppendMode::PROPAPPENDMODE_APPEND);
1420 }
1421 }
1422 else if (propType == AVSPropTypes::PROPTYPE_DATA) {
1423 for (int index = 0; index < numElements; index++) {
1424 // string, byte array in general
1425 auto src = env->propGetData(avsmap_from, key, index, &error);
1426 auto size = env->propGetDataSize(avsmap_from, key, index, &error);
1427 auto typehint = env->propGetDataTypeHint(avsmap_from, key, index, &error); // v11
1428 env->propSetDataH(mapv, key, src, size, typehint, AVSPropAppendMode::PROPAPPENDMODE_APPEND);
1429 }
1430 }
1431 }
1432 }
1433
1434 1 PVideoFrame __stdcall CopyProperties::GetFrame(int n, IScriptEnvironment* env)
1435 {
1436
1/2
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 156 not taken.
1 PVideoFrame frame = child->GetFrame(n, env);
1437
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 154 taken 1 time.
1 PVideoFrame frame2 = child2->GetFrame(n, env);
1438
1439 const AVSMap* avsmap_from = env->getFramePropsRO(frame2);
1440 const int propNum = env->propNumKeys(avsmap_from);
1441
1442 if (0 == propNum) {
1443 // source has no properties at all
1444 if (!merge) {
1445 // exact copy: make target empty as well.
1446 // Erase only if needed
1447 const AVSMap* avsmap_to_test = env->getFramePropsRO(frame);
1448 if (0 != env->propNumKeys(avsmap_to_test)) {
1449 env->MakePropertyWritable(&frame);
1450 AVSMap* avsmap_to = env->getFramePropsRW(frame);
1451 env->clearMap(avsmap_to);
1452 }
1453 }
1454 // source has no properties
1455 return frame;
1456 }
1457
1458 env->MakePropertyWritable(&frame);
1459
1460 if (!merge) {
1461 // exact copy
1462 if (!propNames_defined)
1463 env->copyFrameProps(frame2, frame);
1464 else {
1465 // copy only selected / not excluded names
1466 AVSMap* mapv = env->getFramePropsRW(frame);
1467 env->clearMap(mapv); // clear all
1468 if (!exclude) {
1469 // positive list
1470 for (auto& s : propNames) {
1471 const char* key = s.c_str();
1472 if (s.front() == '^' && s.back() == '$') {
1473 // positive regex list
1474 const int numKeys = env->propNumKeys(avsmap_from);
1475 for (int i = 0; i < numKeys; i++) {
1476 const char* key = env->propGetKey(avsmap_from, i);
1477 if (std::regex_match(key, std::regex(s))) {
1478 // regex match -> copy
1479 CopyOneFrameProp(key, mapv, avsmap_from, env);
1480 }
1481 }
1482 }
1483 else {
1484 CopyOneFrameProp(key, mapv, avsmap_from, env);
1485 }
1486 }
1487 }
1488 else {
1489 // negative list
1490 const int numKeys = env->propNumKeys(avsmap_from);
1491 for (int i = 0; i < numKeys; i++) {
1492 const char* key = env->propGetKey(avsmap_from, i);
1493 bool matchFound = false;
1494 for (auto& s : propNames) {
1495 if (s.front() == '^' && s.back() == '$') {
1496 // negative regex list
1497 if (std::regex_match(key, std::regex(s))) {
1498 matchFound = true;
1499 break;
1500 }
1501 }
1502 else if (s == key) {
1503 matchFound = true;
1504 break;
1505 }
1506 }
1507 if (!matchFound) {
1508 // not in negative list -> copy
1509 CopyOneFrameProp(key, mapv, avsmap_from, env);
1510 }
1511 }
1512 }
1513 }
1514 return frame;
1515 }
1516
1517 // merge
1518 AVSMap* mapv = env->getFramePropsRW(frame);
1519
1520 // add from source, replace if needed
1521 const int numKeys = env->propNumKeys(avsmap_from);
1522 for (int i = 0; i < numKeys; i++) {
1523 const char* key = env->propGetKey(avsmap_from, i);
1524 // merge if no list specified or name is in the list
1525
1526 bool need_copy = true;
1527
1528 if (propNames_defined) {
1529 need_copy = false;
1530 for (const auto& s : propNames) {
1531 if (s.front() == '^' && s.back() == '$') {
1532 // regex match
1533 if (std::regex_match(key, std::regex(s))) {
1534 need_copy = true;
1535 break;
1536 }
1537 }
1538 else if (s == key) {
1539 // plain string match
1540 need_copy = true;
1541 break;
1542 }
1543 }
1544 if (exclude) {
1545 need_copy = !need_copy;
1546 }
1547 }
1548
1549 if(need_copy) {
1550 env->propDeleteKey(mapv, key); // delete if existed
1551 CopyOneFrameProp(key, mapv, avsmap_from, env);
1552 }
1553 }
1554
1555 return frame;
1556 1 }
1557
1558 //**************************************************
1559 // propShow
1560
1561 1 ShowProperties::ShowProperties(PClip _child, int size, bool showtype,
1562 const char *_font, int _text_color, int _halo_color, bool _bold,
1563 1 int _x, int _y, int _align, AVSValue _propNames, IScriptEnvironment* env)
1564 1 : GenericVideoFilter(_child), size(size), showtype(showtype),
1565 1 fontname(_font), textcolor(_text_color), halocolor(_halo_color), bold(_bold),
1566
2/4
✓ Branch 2 → 3 taken 1 time.
✗ Branch 2 → 10 not taken.
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 8 not taken.
1 x(_x), y(_y), align(_align)
1567 {
1568
1/2
✓ Branch 6 → 7 taken 1 time.
✗ Branch 6 → 11 not taken.
1 propNames_defined = PropNamesToArray("propShow", _propNames, propNames, env);
1569 1 }
1570
1571 2 ShowProperties::~ShowProperties() { }
1572
1573 PVideoFrame __stdcall ShowProperties::GetFrame(int n, IScriptEnvironment* env)
1574 {
1575 PVideoFrame frame = child->GetFrame(n, env);
1576
1577 const AVSMap* avsmap = env->getFramePropsRO(frame);
1578
1579 const int propNum = env->propNumKeys(avsmap);
1580
1581 if (0 == propNum)
1582 return frame;
1583
1584 std::stringstream ss;
1585 if (!propNames_defined) // no header displayed if filter is given
1586 ss << "Number of keys = " << std::to_string(propNum) << std::endl;
1587
1588 for (int index = 0; index < propNum; index++) {
1589 const char* propName = env->propGetKey(avsmap, index);
1590
1591 bool display = true;
1592 if (propNames_defined) {
1593 display = false;
1594 for (const auto& s : propNames) {
1595 if (s.front() == '^' && s.back() == '$') {
1596 // regex match
1597 if (std::regex_match(propName, std::regex(s))) {
1598 display = true;
1599 break;
1600 }
1601 }
1602 else if (s == propName) {
1603 // plain string match
1604 display = true;
1605 break;
1606 }
1607 }
1608 }
1609
1610 if (display) {
1611 std::string propName_s = propName;
1612
1613 const char propType = env->propGetType(avsmap, propName);
1614 ss << propName;
1615 if (showtype)
1616 ss << " (" << propType << ")";
1617 ss << " = ";
1618
1619 const int propNumElements = env->propNumElements(avsmap, propName);
1620
1621 int error;
1622
1623 if (propType == 'u') {
1624 // unSet: undefined value
1625 ss << "<unset!>";
1626 }
1627 else if (propType == 'i') {
1628 if (propNumElements > 1)
1629 ss << "[";
1630 const int64_t* arr = env->propGetIntArray(avsmap, propName, &error);
1631 for (int i = 0; i < propNumElements; ++i) {
1632 ss << std::to_string(arr[i]);
1633 if (i < propNumElements - 1)
1634 ss << ", ";
1635 }
1636 if (propNumElements > 1)
1637 ss << "]";
1638
1639 if (propName_s == "_ColorRange") {
1640 ss << " = ";
1641 switch (arr[0]) {
1642 case ColorRange_Compat_e::AVS_COLORRANGE_LIMITED: ss << "limited"; break;
1643 case ColorRange_Compat_e::AVS_COLORRANGE_FULL: ss << "full"; break;
1644 }
1645 }
1646 else if (propName_s == "_Matrix") {
1647 ss << " = ";
1648 switch (arr[0]) {
1649 case Matrix_e::AVS_MATRIX_RGB: ss << "rgb"; break;
1650 case Matrix_e::AVS_MATRIX_BT709: ss << "709"; break;
1651 case Matrix_e::AVS_MATRIX_UNSPECIFIED: ss << "unspec"; break;
1652 case Matrix_e::AVS_MATRIX_ST170_M: ss << "170m"; break;
1653 case Matrix_e::AVS_MATRIX_ST240_M: ss << "240m"; break;
1654 case Matrix_e::AVS_MATRIX_BT470_BG: ss << "470bg (601)"; break;
1655 case Matrix_e::AVS_MATRIX_BT470_M: ss << "470m (fcc)"; break;
1656 case Matrix_e::AVS_MATRIX_YCGCO: ss << "YCgCo"; break;
1657 case Matrix_e::AVS_MATRIX_BT2020_NCL: ss << "2020ncl (2020)"; break;
1658 case Matrix_e::AVS_MATRIX_BT2020_CL: ss << "2020cl"; break;
1659 case Matrix_e::AVS_MATRIX_CHROMATICITY_DERIVED_CL: ss << "chromacl"; break;
1660 case Matrix_e::AVS_MATRIX_CHROMATICITY_DERIVED_NCL: ss << "chromancl"; break;
1661 case Matrix_e::AVS_MATRIX_ICTCP: ss << "ictcp"; break;
1662 case Matrix_e::AVS_MATRIX_AVERAGE: ss << "AVERAGE-Legacy Avisynth"; break;
1663 }
1664 }
1665 else if (propName_s == "_ChromaLocation") {
1666 ss << " = ";
1667 switch (arr[0]) {
1668 case ChromaLocation_e::AVS_CHROMA_LEFT: ss << "left (mpeg2)"; break;
1669 case ChromaLocation_e::AVS_CHROMA_CENTER: ss << "center (mpeg1, jpeg)"; break;
1670 case ChromaLocation_e::AVS_CHROMA_TOP_LEFT: ss << "top_left"; break;
1671 case ChromaLocation_e::AVS_CHROMA_TOP: ss << "top"; break;
1672 case ChromaLocation_e::AVS_CHROMA_BOTTOM_LEFT: ss << "bottom_left"; break;
1673 case ChromaLocation_e::AVS_CHROMA_BOTTOM: ss << "bottom"; break;
1674 case ChromaLocation_e::AVS_CHROMA_DV: ss << "dv-Legacy Avisynth"; break;
1675 }
1676 }
1677 }
1678 else if (propType == 'f') {
1679 if (propNumElements > 1)
1680 ss << "[";
1681 const double* arr = env->propGetFloatArray(avsmap, propName, &error);
1682 for (int i = 0; i < propNumElements; ++i) {
1683 ss << std::to_string(arr[i]);
1684 if (i < propNumElements - 1)
1685 ss << ", ";
1686 }
1687 if (propNumElements > 1)
1688 ss << "]";
1689 }
1690 else if (propType == 's') {
1691 if (propNumElements > 1)
1692 ss << "[";
1693 for (int i = 0; i < propNumElements; ++i) {
1694 const char* s = env->propGetData(avsmap, propName, i, &error);
1695 const int type = env->propGetDataTypeHint(avsmap, propName, i, &error);
1696 if (type == AVSPropDataTypeHint::PROPDATATYPEHINT_BINARY)
1697 {
1698 auto len = env->propGetDataSize(avsmap, propName, i, &error);
1699 ss << "Data length=" << len << ": [";
1700 // print up to 16 comma separated bytes in HEX, ... at the end if there are more.
1701 int count = std::min(len, 16);
1702 for (int i = 0; i < count; ++i) {
1703 if (i > 0) ss << ",";
1704 ss << std::uppercase << std::hex << std::setw(2) << std::setfill('0') << (static_cast<unsigned int>(s[i]) & 0xFF);
1705 }
1706 if (len > 16) ss << "...";
1707 ss << "]";
1708 }
1709 else
1710 {
1711 // AVSPropDataTypeHint::DATATYPEHINT_UTF8 and AVSPropDataTypeHint::DATATYPEHINT_UNKNOWN
1712 // suppose string
1713 ss << "\"" << s << "\"";
1714 }
1715 if (i < propNumElements - 1)
1716 ss << ", ";
1717 }
1718 if (propNumElements > 1)
1719 ss << "]";
1720 }
1721 else {
1722 ss << "<cannot display!>";
1723 }
1724 ss << std::endl;
1725 }
1726 }
1727
1728 std::string t = ss.str();
1729 const char* text = t.c_str();
1730 AVSValue args[10] = { child, text, size, fontname, textcolor, halocolor, bold, x, y, align };
1731 const char* argnames[10] = { 0, 0, "size", "font", "text_color", "halo_color", "bold", "x", "y", "align" };
1732 PClip child2 = env->Invoke("Text", AVSValue(args, 10), argnames).AsClip();
1733 frame = child2->GetFrame(n, env);
1734
1735 return frame;
1736 }
1737
1738 int __stdcall ShowProperties::SetCacheHints(int cachehints, int frame_range)
1739 {
1740 AVS_UNUSED(frame_range);
1741 switch (cachehints)
1742 {
1743 case CACHE_GET_MTMODE:
1744 return MT_NICE_FILTER;
1745 }
1746 return 0; // We do not pass cache requests upwards.
1747 }
1748
1749 1 AVSValue __cdecl ShowProperties::Create(AVSValue args, void*, IScriptEnvironment* env)
1750 {
1751 // 0 1 2 3 4 5 6 7 8 9 10
1752 // c[size]i[showtype]b[font]s[text_color]i[halo_color]i[bold]b[x]f[y]f[align]i[props]s+
1753
1754
2/4
✓ Branch 2 → 3 taken 1 time.
✗ Branch 2 → 77 not taken.
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 77 not taken.
1 PClip clip = args[0].AsClip();
1755
1756
2/4
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 75 not taken.
✓ Branch 5 → 6 taken 1 time.
✗ Branch 5 → 75 not taken.
1 const int size = args[1].AsInt(16);
1757
2/4
✓ Branch 6 → 7 taken 1 time.
✗ Branch 6 → 75 not taken.
✓ Branch 7 → 8 taken 1 time.
✗ Branch 7 → 75 not taken.
1 const bool showtype = args[2].AsBool(false);
1758
2/4
✓ Branch 8 → 9 taken 1 time.
✗ Branch 8 → 75 not taken.
✓ Branch 9 → 10 taken 1 time.
✗ Branch 9 → 75 not taken.
1 const char* font = args[3].AsString("Terminus");
1759
2/4
✓ Branch 10 → 11 taken 1 time.
✗ Branch 10 → 75 not taken.
✓ Branch 11 → 12 taken 1 time.
✗ Branch 11 → 75 not taken.
1 const int text_color = args[4].AsInt(0xFFFF00);
1760
2/4
✓ Branch 12 → 13 taken 1 time.
✗ Branch 12 → 75 not taken.
✓ Branch 13 → 14 taken 1 time.
✗ Branch 13 → 75 not taken.
1 const int halo_color = args[5].AsInt(0);
1761
2/4
✓ Branch 14 → 15 taken 1 time.
✗ Branch 14 → 75 not taken.
✓ Branch 15 → 16 taken 1 time.
✗ Branch 15 → 75 not taken.
1 const bool bold = args[6].AsBool(false);
1762
1763
2/4
✓ Branch 16 → 17 taken 1 time.
✗ Branch 16 → 75 not taken.
✓ Branch 17 → 18 taken 1 time.
✗ Branch 17 → 75 not taken.
1 const int align = args[9].AsInt(7); // default top left
1764
1765 1 const int info_default_x = 0; // subtitle: 8, Info: 4
1766 1 const int PIXEL_MUL_FACTOR = 1; // like "Text"
1767 // similar to SubTitle/FilterInfo.Create
1768 int defx, defy;
1769 1 bool x_center = false;
1770 1 bool y_center = false;
1771
1772
1/4
✓ Branch 18 → 19 taken 1 time.
✗ Branch 18 → 20 not taken.
✗ Branch 18 → 21 not taken.
✗ Branch 18 → 24 not taken.
1 switch (align) {
1773 1 case 1: case 4: case 7: defx = 8; break;
1774 case 2: case 5: case 8:
1775 defx = 0; // n/a if not set later
1776 x_center = true;
1777 break;
1778 case 3: case 6: case 9: defx = clip->GetVideoInfo().width - info_default_x; break; // subtitle: 8, Info: 4
1779 default: defx = info_default_x; break;
1780 }
1781
1782
1/4
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 29 not taken.
✓ Branch 25 → 30 taken 1 time.
✗ Branch 25 → 31 not taken.
1 switch (align) {
1783 case 1: case 2: case 3: defy = clip->GetVideoInfo().height - 2; break; // bottom alignment 2 pixel above
1784 case 4: case 5: case 6:
1785 defy = 0; // n/a if not set later
1786 y_center = true;
1787 break;
1788 1 case 7: case 8: case 9: defy = 0; break;
1789 default: defy = (size + 4) / 8; break;
1790 }
1791
1792
2/4
✓ Branch 32 → 33 taken 1 time.
✗ Branch 32 → 75 not taken.
✓ Branch 33 → 34 taken 1 time.
✗ Branch 33 → 75 not taken.
1 const bool isXdefined = args[7].Defined();
1793
2/4
✓ Branch 34 → 35 taken 1 time.
✗ Branch 34 → 75 not taken.
✓ Branch 35 → 36 taken 1 time.
✗ Branch 35 → 75 not taken.
1 const bool isYdefined = args[8].Defined();
1794
1795
2/4
✓ Branch 36 → 37 taken 1 time.
✗ Branch 36 → 75 not taken.
✓ Branch 37 → 38 taken 1 time.
✗ Branch 37 → 75 not taken.
1 int x = int(args[7].AsDblDef(defx) * PIXEL_MUL_FACTOR + 0.5);
1796
2/4
✓ Branch 38 → 39 taken 1 time.
✗ Branch 38 → 75 not taken.
✓ Branch 39 → 40 taken 1 time.
✗ Branch 39 → 75 not taken.
1 int y = int(args[8].AsDblDef(defy) * PIXEL_MUL_FACTOR + 0.5);
1797
1798
1/4
✗ Branch 40 → 41 not taken.
✓ Branch 40 → 45 taken 1 time.
✗ Branch 41 → 42 not taken.
✗ Branch 41 → 45 not taken.
1 if (!isXdefined && x_center)
1799 x = (clip->GetVideoInfo().width >> 1) * PIXEL_MUL_FACTOR;
1800
1801
1/4
✗ Branch 45 → 46 not taken.
✓ Branch 45 → 50 taken 1 time.
✗ Branch 46 → 47 not taken.
✗ Branch 46 → 50 not taken.
1 if (!isYdefined && y_center)
1802 y = (clip->GetVideoInfo().height >> 1) * PIXEL_MUL_FACTOR;
1803
1804
1805
2/4
✓ Branch 50 → 51 taken 1 time.
✗ Branch 50 → 52 not taken.
✗ Branch 51 → 52 not taken.
✓ Branch 51 → 53 taken 1 time.
1 if ((align < 1) || (align > 9))
1806 env->ThrowError("propShow: Align values are 1 - 9 mapped to your numeric pad");
1807
1808
7/16
✓ Branch 53 → 54 taken 1 time.
✗ Branch 53 → 75 not taken.
✓ Branch 54 → 55 taken 1 time.
✗ Branch 54 → 71 not taken.
✓ Branch 55 → 56 taken 1 time.
✗ Branch 55 → 71 not taken.
✓ Branch 56 → 57 taken 1 time.
✗ Branch 56 → 68 not taken.
✓ Branch 57 → 58 taken 1 time.
✗ Branch 57 → 66 not taken.
✓ Branch 58 → 59 taken 1 time.
✗ Branch 58 → 66 not taken.
✗ Branch 61 → 62 not taken.
✓ Branch 61 → 63 taken 1 time.
✗ Branch 72 → 73 not taken.
✗ Branch 72 → 74 not taken.
2 return new ShowProperties(clip, size, showtype, font, text_color, halo_color, bold, x, y, align, args[10], env);
1809 1 }
1810