1 /*
2  * Copyright (C) 2009 Grigori Goronzy <greg@geekmind.org>
3  *
4  * This file is part of libass.
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include "config.h"
20 #include "ass_compat.h"
21 
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <math.h>
26 
27 #include "ass_library.h"
28 #include "ass_render.h"
29 #include "ass_parse.h"
30 
31 #define MAX_VALID_NARGS 7
32 #define MAX_BE 127
33 #define NBSP 0xa0   // unicode non-breaking space character
34 
35 struct arg {
36     char *start, *end;
37 };
38 
argtoi(struct arg arg)39 static inline int argtoi(struct arg arg)
40 {
41     int value;
42     mystrtoi(&arg.start, &value);
43     return value;
44 }
45 
argtoi32(struct arg arg)46 static inline int32_t argtoi32(struct arg arg)
47 {
48     int32_t value;
49     mystrtoi32(&arg.start, 10, &value);
50     return value;
51 }
52 
argtod(struct arg arg)53 static inline double argtod(struct arg arg)
54 {
55     double value;
56     mystrtod(&arg.start, &value);
57     return value;
58 }
59 
push_arg(struct arg * args,int * nargs,char * start,char * end)60 static inline void push_arg(struct arg *args, int *nargs, char *start, char *end)
61 {
62     if (*nargs <= MAX_VALID_NARGS) {
63         rskip_spaces(&end, start);
64         if (end > start) {
65             args[*nargs] = (struct arg) {start, end};
66             ++*nargs;
67         }
68     }
69 }
70 
71 /**
72  * \brief Check if starting part of (*p) matches sample.
73  * If true, shift p to the first symbol after the matching part.
74  */
mystrcmp(char ** p,const char * sample)75 static inline int mystrcmp(char **p, const char *sample)
76 {
77     char *p2;
78     for (p2 = *p; *sample != 0 && *p2 == *sample; p2++, sample++)
79         ;
80     if (*sample == 0) {
81         *p = p2;
82         return 1;
83     }
84     return 0;
85 }
86 
ensure_font_size(ASS_Renderer * priv,double size)87 double ensure_font_size(ASS_Renderer *priv, double size)
88 {
89     if (size < 1)
90         size = 1;
91     else if (size > priv->height * 2)
92         size = priv->height * 2;
93 
94     return size;
95 }
96 
97 /**
98  * \brief Change current font, using setting from render_priv->state.
99  */
update_font(ASS_Renderer * render_priv)100 void update_font(ASS_Renderer *render_priv)
101 {
102     unsigned val;
103     ASS_FontDesc desc;
104 
105     desc.family = render_priv->state.family;
106     if (!desc.family.str)
107         return;
108     if (desc.family.len && desc.family.str[0] == '@') {
109         desc.vertical = 1;
110         desc.family.str++;
111         desc.family.len--;
112     } else {
113         desc.vertical = 0;
114     }
115 
116     val = render_priv->state.bold;
117     // 0 = normal, 1 = bold, >1 = exact weight
118     if (val == 1 || val == -1)
119         val = 700;               // bold
120     else if (val <= 0)
121         val = 400;               // normal
122     desc.bold = val;
123 
124     val = render_priv->state.italic;
125     if (val == 1)
126         val = 100;              // italic
127     else if (val <= 0)
128         val = 0;                // normal
129     desc.italic = val;
130 
131     ass_cache_dec_ref(render_priv->state.font);
132     render_priv->state.font = ass_font_new(render_priv, &desc);
133 }
134 
135 /**
136  * \brief Convert double to int32_t without UB
137  * on out-of-range values; match x86 behavior
138  */
dtoi32(double val)139 static inline int32_t dtoi32(double val)
140 {
141     if (isnan(val) || val <= INT32_MIN || val >= INT32_MAX + 1LL)
142         return INT32_MIN;
143     return val;
144 }
145 
calc_anim(double new,double old,double pwr)146 static double calc_anim(double new, double old, double pwr)
147 {
148    return (1 - pwr) * old + new * pwr;
149 }
150 
calc_anim_int32(uint32_t new,uint32_t old,double pwr)151 static int32_t calc_anim_int32(uint32_t new, uint32_t old, double pwr)
152 {
153     return dtoi32(calc_anim(new, old, pwr));
154 }
155 
156 /**
157  * \brief Calculate a weighted average of two colors
158  * calculates c1*(1-a) + c2*a, but separately for each component except alpha
159  */
change_color(uint32_t * var,uint32_t new,double pwr)160 static void change_color(uint32_t *var, uint32_t new, double pwr)
161 {
162     uint32_t co = ass_bswap32(*var);
163     uint32_t cn = ass_bswap32(new);
164 
165     uint32_t cc = (calc_anim_int32(cn & 0xff0000, co & 0xff0000, pwr) & 0xff0000) |
166                   (calc_anim_int32(cn & 0x00ff00, co & 0x00ff00, pwr) & 0x00ff00) |
167                   (calc_anim_int32(cn & 0x0000ff, co & 0x0000ff, pwr) & 0x0000ff);
168 
169     (*var) = (ass_bswap32(cc & 0xffffff)) | _a(*var);
170 }
171 
172 // like change_color, but for alpha component only
change_alpha(uint32_t * var,int32_t new,double pwr)173 inline void change_alpha(uint32_t *var, int32_t new, double pwr)
174 {
175     *var = (*var & 0xFFFFFF00) | (uint8_t)calc_anim_int32(_a(new), _a(*var), pwr);
176 }
177 
178 /**
179  * \brief Multiply two alpha values
180  * \param a first value
181  * \param b second value
182  * \return result of multiplication
183  * At least one of the parameters must be less than or equal to 0xFF.
184  * The result is less than or equal to max(a, b, 0xFF).
185  */
mult_alpha(uint32_t a,uint32_t b)186 inline uint32_t mult_alpha(uint32_t a, uint32_t b)
187 {
188     return a - ((uint64_t) a * b + 0x7F) / 0xFF + b;
189 }
190 
191 /**
192  * \brief Calculate alpha value by piecewise linear function
193  * Used for \fad, \fade implementation.
194  */
195 static int
interpolate_alpha(long long now,int32_t t1,int32_t t2,int32_t t3,int32_t t4,int a1,int a2,int a3)196 interpolate_alpha(long long now, int32_t t1, int32_t t2, int32_t t3,
197                   int32_t t4, int a1, int a2, int a3)
198 {
199     int a;
200     double cf;
201 
202     if (now < t1) {
203         a = a1;
204     } else if (now < t2) {
205         cf = ((double) (int32_t) ((uint32_t) now - t1)) /
206                 (int32_t) ((uint32_t) t2 - t1);
207         a = a1 * (1 - cf) + a2 * cf;
208     } else if (now < t3) {
209         a = a2;
210     } else if (now < t4) {
211         cf = ((double) (int32_t) ((uint32_t) now - t3)) /
212                 (int32_t) ((uint32_t) t4 - t3);
213         a = a2 * (1 - cf) + a3 * cf;
214     } else {                    // now >= t4
215         a = a3;
216     }
217 
218     return a;
219 }
220 
221 /**
222  * Parse a vector clip into an outline, using the proper scaling
223  * parameters.  Translate it to correct for screen borders, if needed.
224  */
parse_vector_clip(ASS_Renderer * render_priv,struct arg * args,int nargs)225 static bool parse_vector_clip(ASS_Renderer *render_priv,
226                               struct arg *args, int nargs)
227 {
228     if (nargs != 1 && nargs != 2)
229         return false;
230 
231     int scale = 1;
232     if (nargs == 2)
233         scale = argtoi(args[0]);
234 
235     struct arg text = args[nargs - 1];
236     render_priv->state.clip_drawing_text.str = text.start;
237     render_priv->state.clip_drawing_text.len = text.end - text.start;
238     render_priv->state.clip_drawing_scale = scale;
239     return true;
240 }
241 
242 /**
243  * \brief Parse style override tags.
244  * \param p string to parse
245  * \param end end of string to parse, which must be '}', ')', or the first
246  *            of a number of spaces immediately preceding '}' or ')'
247  * \param pwr multiplier for some tag effects (comes from \t tags)
248  */
parse_tags(ASS_Renderer * render_priv,char * p,char * end,double pwr,bool nested)249 char *parse_tags(ASS_Renderer *render_priv, char *p, char *end, double pwr,
250                  bool nested)
251 {
252     for (char *q; p < end; p = q) {
253         while (*p != '\\' && p != end)
254             ++p;
255         if (*p != '\\')
256             break;
257         ++p;
258         if (p != end)
259             skip_spaces(&p);
260 
261         q = p;
262         while (*q != '(' && *q != '\\' && q != end)
263             ++q;
264         if (q == p)
265             continue;
266 
267         char *name_end = q;
268 
269         // Store one extra element to be able to detect excess arguments
270         struct arg args[MAX_VALID_NARGS + 1];
271         int nargs = 0;
272         bool has_backslash_arg = false;
273         for (int i = 0; i <= MAX_VALID_NARGS; ++i)
274             args[i].start = args[i].end = "";
275 
276         // Split parenthesized arguments. Do this for all tags and before
277         // any non-parenthesized argument because that's what VSFilter does.
278         if (*q == '(') {
279             ++q;
280             while (1) {
281                 if (q != end)
282                     skip_spaces(&q);
283 
284                 // Split on commas. If there is a backslash, ignore any
285                 // commas following it and lump everything starting from
286                 // the last comma, through the backslash and all the way
287                 // to the end of the argument string into a single argument.
288 
289                 char *r = q;
290                 while (*r != ',' && *r != '\\' && *r != ')' && r != end)
291                     ++r;
292 
293                 if (*r == ',') {
294                     push_arg(args, &nargs, q, r);
295                     q = r + 1;
296                 } else {
297                     // Swallow the rest of the parenthesized string. This could
298                     // be either a backslash-argument or simply the last argument.
299                     if (*r == '\\') {
300                         has_backslash_arg = true;
301                         while (*r != ')' && r != end)
302                             ++r;
303                     }
304                     push_arg(args, &nargs, q, r);
305                     q = r;
306                     // The closing parenthesis could be missing.
307                     if (q != end)
308                         ++q;
309                     break;
310                 }
311             }
312         }
313 
314 #define tag(name) (mystrcmp(&p, (name)) && (push_arg(args, &nargs, p, name_end), 1))
315 #define complex_tag(name) mystrcmp(&p, (name))
316 
317         // New tags introduced in vsfilter 2.39
318         if (tag("xbord")) {
319             double val;
320             if (nargs) {
321                 val = argtod(*args);
322                 val = render_priv->state.border_x * (1 - pwr) + val * pwr;
323                 val = (val < 0) ? 0 : val;
324             } else
325                 val = render_priv->state.style->Outline;
326             render_priv->state.border_x = val;
327         } else if (tag("ybord")) {
328             double val;
329             if (nargs) {
330                 val = argtod(*args);
331                 val = render_priv->state.border_y * (1 - pwr) + val * pwr;
332                 val = (val < 0) ? 0 : val;
333             } else
334                 val = render_priv->state.style->Outline;
335             render_priv->state.border_y = val;
336         } else if (tag("xshad")) {
337             double val;
338             if (nargs) {
339                 val = argtod(*args);
340                 val = render_priv->state.shadow_x * (1 - pwr) + val * pwr;
341             } else
342                 val = render_priv->state.style->Shadow;
343             render_priv->state.shadow_x = val;
344         } else if (tag("yshad")) {
345             double val;
346             if (nargs) {
347                 val = argtod(*args);
348                 val = render_priv->state.shadow_y * (1 - pwr) + val * pwr;
349             } else
350                 val = render_priv->state.style->Shadow;
351             render_priv->state.shadow_y = val;
352         } else if (tag("fax")) {
353             double val;
354             if (nargs) {
355                 val = argtod(*args);
356                 render_priv->state.fax =
357                     val * pwr + render_priv->state.fax * (1 - pwr);
358             } else
359                 render_priv->state.fax = 0.;
360         } else if (tag("fay")) {
361             double val;
362             if (nargs) {
363                 val = argtod(*args);
364                 render_priv->state.fay =
365                     val * pwr + render_priv->state.fay * (1 - pwr);
366             } else
367                 render_priv->state.fay = 0.;
368         } else if (complex_tag("iclip")) {
369             if (nargs == 4) {
370                 int x0, y0, x1, y1;
371                 x0 = argtoi(args[0]);
372                 y0 = argtoi(args[1]);
373                 x1 = argtoi(args[2]);
374                 y1 = argtoi(args[3]);
375                 render_priv->state.clip_x0 =
376                     render_priv->state.clip_x0 * (1 - pwr) + x0 * pwr;
377                 render_priv->state.clip_x1 =
378                     render_priv->state.clip_x1 * (1 - pwr) + x1 * pwr;
379                 render_priv->state.clip_y0 =
380                     render_priv->state.clip_y0 * (1 - pwr) + y0 * pwr;
381                 render_priv->state.clip_y1 =
382                     render_priv->state.clip_y1 * (1 - pwr) + y1 * pwr;
383                 render_priv->state.clip_mode = 1;
384             } else if (!render_priv->state.clip_drawing_text.str) {
385                 if (parse_vector_clip(render_priv, args, nargs))
386                     render_priv->state.clip_drawing_mode = 1;
387             }
388         } else if (tag("blur")) {
389             double val;
390             if (nargs) {
391                 val = argtod(*args);
392                 val = render_priv->state.blur * (1 - pwr) + val * pwr;
393                 val = (val < 0) ? 0 : val;
394                 val = (val > BLUR_MAX_RADIUS) ? BLUR_MAX_RADIUS : val;
395                 render_priv->state.blur = val;
396             } else
397                 render_priv->state.blur = 0.0;
398             // ASS standard tags
399         } else if (tag("fscx")) {
400             double val;
401             if (nargs) {
402                 val = argtod(*args) / 100;
403                 val = render_priv->state.scale_x * (1 - pwr) + val * pwr;
404                 val = (val < 0) ? 0 : val;
405             } else
406                 val = render_priv->state.style->ScaleX;
407             render_priv->state.scale_x = val;
408         } else if (tag("fscy")) {
409             double val;
410             if (nargs) {
411                 val = argtod(*args) / 100;
412                 val = render_priv->state.scale_y * (1 - pwr) + val * pwr;
413                 val = (val < 0) ? 0 : val;
414             } else
415                 val = render_priv->state.style->ScaleY;
416             render_priv->state.scale_y = val;
417         } else if (tag("fsc")) {
418             render_priv->state.scale_x = render_priv->state.style->ScaleX;
419             render_priv->state.scale_y = render_priv->state.style->ScaleY;
420         } else if (tag("fsp")) {
421             double val;
422             if (nargs) {
423                 val = argtod(*args);
424                 render_priv->state.hspacing =
425                     render_priv->state.hspacing * (1 - pwr) + val * pwr;
426             } else
427                 render_priv->state.hspacing = render_priv->state.style->Spacing;
428         } else if (tag("fs")) {
429             double val = 0;
430             if (nargs) {
431                 val = argtod(*args);
432                 if (*args->start == '+' || *args->start == '-')
433                     val = render_priv->state.font_size * (1 + pwr * val / 10);
434                 else
435                     val = render_priv->state.font_size * (1 - pwr) + val * pwr;
436             }
437             if (val <= 0)
438                 val = render_priv->state.style->FontSize;
439             render_priv->state.font_size = val;
440         } else if (tag("bord")) {
441             double val, xval, yval;
442             if (nargs) {
443                 val = argtod(*args);
444                 xval = render_priv->state.border_x * (1 - pwr) + val * pwr;
445                 yval = render_priv->state.border_y * (1 - pwr) + val * pwr;
446                 xval = (xval < 0) ? 0 : xval;
447                 yval = (yval < 0) ? 0 : yval;
448             } else
449                 xval = yval = render_priv->state.style->Outline;
450             render_priv->state.border_x = xval;
451             render_priv->state.border_y = yval;
452         } else if (complex_tag("move")) {
453             double x1, x2, y1, y2;
454             int32_t t1, t2, delta_t, t;
455             double x, y;
456             double k;
457             if (nargs == 4 || nargs == 6) {
458                 x1 = argtod(args[0]);
459                 y1 = argtod(args[1]);
460                 x2 = argtod(args[2]);
461                 y2 = argtod(args[3]);
462                 t1 = t2 = 0;
463                 if (nargs == 6) {
464                     t1 = argtoi32(args[4]);
465                     t2 = argtoi32(args[5]);
466                     if (t1 > t2) {
467                         long long tmp = t2;
468                         t2 = t1;
469                         t1 = tmp;
470                     }
471                 }
472             } else
473                 continue;
474             if (t1 <= 0 && t2 <= 0) {
475                 t1 = 0;
476                 t2 = render_priv->state.event->Duration;
477             }
478             delta_t = (uint32_t) t2 - t1;
479             t = render_priv->time - render_priv->state.event->Start;
480             if (t <= t1)
481                 k = 0.;
482             else if (t >= t2)
483                 k = 1.;
484             else
485                 k = ((double) (int32_t) ((uint32_t) t - t1)) / delta_t;
486             x = k * (x2 - x1) + x1;
487             y = k * (y2 - y1) + y1;
488             if (!(render_priv->state.evt_type & EVENT_POSITIONED)) {
489                 render_priv->state.pos_x = x;
490                 render_priv->state.pos_y = y;
491                 render_priv->state.detect_collisions = 0;
492                 render_priv->state.evt_type |= EVENT_POSITIONED;
493             }
494         } else if (tag("frx")) {
495             double val;
496             if (nargs) {
497                 val = argtod(*args);
498                 render_priv->state.frx =
499                     val * pwr + render_priv->state.frx * (1 - pwr);
500             } else
501                 render_priv->state.frx = 0.;
502         } else if (tag("fry")) {
503             double val;
504             if (nargs) {
505                 val = argtod(*args);
506                 render_priv->state.fry =
507                     val * pwr + render_priv->state.fry * (1 - pwr);
508             } else
509                 render_priv->state.fry = 0.;
510         } else if (tag("frz") || tag("fr")) {
511             double val;
512             if (nargs) {
513                 val = argtod(*args);
514                 render_priv->state.frz =
515                     val * pwr + render_priv->state.frz * (1 - pwr);
516             } else
517                 render_priv->state.frz =
518                     render_priv->state.style->Angle;
519         } else if (tag("fn")) {
520             char *start = args->start;
521             if (nargs && strncmp(start, "0", args->end - start)) {
522                 skip_spaces(&start);
523                 render_priv->state.family.str = start;
524                 render_priv->state.family.len = args->end - start;
525             } else {
526                 render_priv->state.family.str = render_priv->state.style->FontName;
527                 render_priv->state.family.len = strlen(render_priv->state.style->FontName);
528             }
529             update_font(render_priv);
530         } else if (tag("alpha")) {
531             int i;
532             if (nargs) {
533                 int32_t a = parse_alpha_tag(args->start);
534                 for (i = 0; i < 4; ++i)
535                     change_alpha(&render_priv->state.c[i], a, pwr);
536             } else {
537                 change_alpha(&render_priv->state.c[0],
538                              _a(render_priv->state.style->PrimaryColour), 1);
539                 change_alpha(&render_priv->state.c[1],
540                              _a(render_priv->state.style->SecondaryColour), 1);
541                 change_alpha(&render_priv->state.c[2],
542                              _a(render_priv->state.style->OutlineColour), 1);
543                 change_alpha(&render_priv->state.c[3],
544                              _a(render_priv->state.style->BackColour), 1);
545             }
546             // FIXME: simplify
547         } else if (tag("an")) {
548             int val = argtoi(*args);
549             if ((render_priv->state.parsed_tags & PARSED_A) == 0) {
550                 if (val >= 1 && val <= 9)
551                     render_priv->state.alignment = numpad2align(val);
552                 else
553                     render_priv->state.alignment =
554                         render_priv->state.style->Alignment;
555                 render_priv->state.parsed_tags |= PARSED_A;
556             }
557         } else if (tag("a")) {
558             int val = argtoi(*args);
559             if ((render_priv->state.parsed_tags & PARSED_A) == 0) {
560                 if (val >= 1 && val <= 11)
561                     // take care of a vsfilter quirk:
562                     // handle illegal \a8 and \a4 like \a5
563                     render_priv->state.alignment = ((val & 3) == 0) ? 5 : val;
564                 else
565                     render_priv->state.alignment =
566                         render_priv->state.style->Alignment;
567                 render_priv->state.parsed_tags |= PARSED_A;
568             }
569         } else if (complex_tag("pos")) {
570             double v1, v2;
571             if (nargs == 2) {
572                 v1 = argtod(args[0]);
573                 v2 = argtod(args[1]);
574             } else
575                 continue;
576             if (render_priv->state.evt_type & EVENT_POSITIONED) {
577                 ass_msg(render_priv->library, MSGL_V, "Subtitle has a new \\pos "
578                        "after \\move or \\pos, ignoring");
579             } else {
580                 render_priv->state.evt_type |= EVENT_POSITIONED;
581                 render_priv->state.detect_collisions = 0;
582                 render_priv->state.pos_x = v1;
583                 render_priv->state.pos_y = v2;
584             }
585         } else if (complex_tag("fade") || complex_tag("fad")) {
586             int a1, a2, a3;
587             int32_t t1, t2, t3, t4;
588             if (nargs == 2) {
589                 // 2-argument version (\fad, according to specs)
590                 a1 = 0xFF;
591                 a2 = 0;
592                 a3 = 0xFF;
593                 t1 = -1;
594                 t2 = argtoi32(args[0]);
595                 t3 = argtoi32(args[1]);
596                 t4 = -1;
597             } else if (nargs == 7) {
598                 // 7-argument version (\fade)
599                 a1 = argtoi(args[0]);
600                 a2 = argtoi(args[1]);
601                 a3 = argtoi(args[2]);
602                 t1 = argtoi32(args[3]);
603                 t2 = argtoi32(args[4]);
604                 t3 = argtoi32(args[5]);
605                 t4 = argtoi32(args[6]);
606             } else
607                 continue;
608             if (t1 == -1 && t4 == -1) {
609                 t1 = 0;
610                 t4 = render_priv->state.event->Duration;
611                 t3 = (uint32_t) t4 - t3;
612             }
613             if ((render_priv->state.parsed_tags & PARSED_FADE) == 0) {
614                 render_priv->state.fade =
615                     interpolate_alpha(render_priv->time -
616                             render_priv->state.event->Start, t1, t2,
617                             t3, t4, a1, a2, a3);
618                 render_priv->state.parsed_tags |= PARSED_FADE;
619             }
620         } else if (complex_tag("org")) {
621             double v1, v2;
622             if (nargs == 2) {
623                 v1 = argtod(args[0]);
624                 v2 = argtod(args[1]);
625             } else
626                 continue;
627             if (!render_priv->state.have_origin) {
628                 render_priv->state.org_x = v1;
629                 render_priv->state.org_y = v2;
630                 render_priv->state.have_origin = 1;
631                 render_priv->state.detect_collisions = 0;
632             }
633         } else if (complex_tag("t")) {
634             double accel;
635             int cnt = nargs - 1;
636             int32_t t1, t2, t, delta_t;
637             double k;
638             // VSFilter compatibility (because we can): parse the
639             // timestamps differently depending on argument count.
640             if (cnt == 3) {
641                 t1 = argtoi32(args[0]);
642                 t2 = argtoi32(args[1]);
643                 accel = argtod(args[2]);
644             } else if (cnt == 2) {
645                 t1 = dtoi32(argtod(args[0]));
646                 t2 = dtoi32(argtod(args[1]));
647                 accel = 1.;
648             } else if (cnt == 1) {
649                 t1 = 0;
650                 t2 = 0;
651                 accel = argtod(args[0]);
652             } else {
653                 t1 = 0;
654                 t2 = 0;
655                 accel = 1.;
656             }
657             render_priv->state.detect_collisions = 0;
658             if (t2 == 0)
659                 t2 = render_priv->state.event->Duration;
660             delta_t = (uint32_t) t2 - t1;
661             t = render_priv->time - render_priv->state.event->Start;        // FIXME: move to render_context
662             if (t < t1)
663                 k = 0.;
664             else if (t >= t2)
665                 k = 1.;
666             else {
667                 assert(delta_t != 0.);
668                 k = pow((double) (int32_t) ((uint32_t) t - t1) / delta_t, accel);
669             }
670             if (nested)
671                 pwr = k;
672             if (cnt < 0 || cnt > 3)
673                 continue;
674             // If there's no backslash in the arguments, there are no
675             // override tags, so it's pointless to try to parse them.
676             if (!has_backslash_arg)
677                 continue;
678             p = args[cnt].start;
679             if (args[cnt].end < end) {
680                 assert(!nested);
681                 p = parse_tags(render_priv, p, args[cnt].end, k, true);
682             } else {
683                 assert(q == end);
684                 // No other tags can possibly follow this \t tag,
685                 // so we don't need to restore pwr after parsing \t.
686                 // The recursive call is now essentially a tail call,
687                 // so optimize it away.
688                 pwr = k;
689                 nested = true;
690                 q = p;
691             }
692         } else if (complex_tag("clip")) {
693             if (nargs == 4) {
694                 int x0, y0, x1, y1;
695                 x0 = argtoi(args[0]);
696                 y0 = argtoi(args[1]);
697                 x1 = argtoi(args[2]);
698                 y1 = argtoi(args[3]);
699                 render_priv->state.clip_x0 =
700                     render_priv->state.clip_x0 * (1 - pwr) + x0 * pwr;
701                 render_priv->state.clip_x1 =
702                     render_priv->state.clip_x1 * (1 - pwr) + x1 * pwr;
703                 render_priv->state.clip_y0 =
704                     render_priv->state.clip_y0 * (1 - pwr) + y0 * pwr;
705                 render_priv->state.clip_y1 =
706                     render_priv->state.clip_y1 * (1 - pwr) + y1 * pwr;
707                 render_priv->state.clip_mode = 0;
708             } else if (!render_priv->state.clip_drawing_text.str) {
709                 if (parse_vector_clip(render_priv, args, nargs))
710                     render_priv->state.clip_drawing_mode = 0;
711             }
712         } else if (tag("c") || tag("1c")) {
713             if (nargs) {
714                 uint32_t val = parse_color_tag(args->start);
715                 change_color(&render_priv->state.c[0], val, pwr);
716             } else
717                 change_color(&render_priv->state.c[0],
718                              render_priv->state.style->PrimaryColour, 1);
719         } else if (tag("2c")) {
720             if (nargs) {
721                 uint32_t val = parse_color_tag(args->start);
722                 change_color(&render_priv->state.c[1], val, pwr);
723             } else
724                 change_color(&render_priv->state.c[1],
725                              render_priv->state.style->SecondaryColour, 1);
726         } else if (tag("3c")) {
727             if (nargs) {
728                 uint32_t val = parse_color_tag(args->start);
729                 change_color(&render_priv->state.c[2], val, pwr);
730             } else
731                 change_color(&render_priv->state.c[2],
732                              render_priv->state.style->OutlineColour, 1);
733         } else if (tag("4c")) {
734             if (nargs) {
735                 uint32_t val = parse_color_tag(args->start);
736                 change_color(&render_priv->state.c[3], val, pwr);
737             } else
738                 change_color(&render_priv->state.c[3],
739                              render_priv->state.style->BackColour, 1);
740         } else if (tag("1a")) {
741             if (nargs) {
742                 uint32_t val = parse_alpha_tag(args->start);
743                 change_alpha(&render_priv->state.c[0], val, pwr);
744             } else
745                 change_alpha(&render_priv->state.c[0],
746                              _a(render_priv->state.style->PrimaryColour), 1);
747         } else if (tag("2a")) {
748             if (nargs) {
749                 uint32_t val = parse_alpha_tag(args->start);
750                 change_alpha(&render_priv->state.c[1], val, pwr);
751             } else
752                 change_alpha(&render_priv->state.c[1],
753                              _a(render_priv->state.style->SecondaryColour), 1);
754         } else if (tag("3a")) {
755             if (nargs) {
756                 uint32_t val = parse_alpha_tag(args->start);
757                 change_alpha(&render_priv->state.c[2], val, pwr);
758             } else
759                 change_alpha(&render_priv->state.c[2],
760                              _a(render_priv->state.style->OutlineColour), 1);
761         } else if (tag("4a")) {
762             if (nargs) {
763                 uint32_t val = parse_alpha_tag(args->start);
764                 change_alpha(&render_priv->state.c[3], val, pwr);
765             } else
766                 change_alpha(&render_priv->state.c[3],
767                              _a(render_priv->state.style->BackColour), 1);
768         } else if (tag("r")) {
769             if (nargs) {
770                 int len = args->end - args->start;
771                 reset_render_context(render_priv,
772                         lookup_style_strict(render_priv->track, args->start, len));
773             } else
774                 reset_render_context(render_priv, NULL);
775         } else if (tag("be")) {
776             double dval;
777             if (nargs) {
778                 int val;
779                 dval = argtod(*args);
780                 // VSFilter always adds +0.5, even if the value is negative
781                 val = (int) (render_priv->state.be * (1 - pwr) + dval * pwr + 0.5);
782                 // Clamp to a safe upper limit, since high values need excessive CPU
783                 val = (val < 0) ? 0 : val;
784                 val = (val > MAX_BE) ? MAX_BE : val;
785                 render_priv->state.be = val;
786             } else
787                 render_priv->state.be = 0;
788         } else if (tag("b")) {
789             int val = argtoi(*args);
790             if (!nargs || !(val == 0 || val == 1 || val >= 100))
791                 val = render_priv->state.style->Bold;
792             render_priv->state.bold = val;
793             update_font(render_priv);
794         } else if (tag("i")) {
795             int val = argtoi(*args);
796             if (!nargs || !(val == 0 || val == 1))
797                 val = render_priv->state.style->Italic;
798             render_priv->state.italic = val;
799             update_font(render_priv);
800         } else if (tag("kf") || tag("K")) {
801             double val = 100;
802             if (nargs)
803                 val = argtod(*args);
804             render_priv->state.effect_type = EF_KARAOKE_KF;
805             if (render_priv->state.effect_timing)
806                 render_priv->state.effect_skip_timing +=
807                     render_priv->state.effect_timing;
808             render_priv->state.effect_timing = val * 10;
809         } else if (tag("ko")) {
810             double val = 100;
811             if (nargs)
812                 val = argtod(*args);
813             render_priv->state.effect_type = EF_KARAOKE_KO;
814             if (render_priv->state.effect_timing)
815                 render_priv->state.effect_skip_timing +=
816                     render_priv->state.effect_timing;
817             render_priv->state.effect_timing = val * 10;
818         } else if (tag("k")) {
819             double val = 100;
820             if (nargs)
821                 val = argtod(*args);
822             render_priv->state.effect_type = EF_KARAOKE;
823             if (render_priv->state.effect_timing)
824                 render_priv->state.effect_skip_timing +=
825                     render_priv->state.effect_timing;
826             render_priv->state.effect_timing = val * 10;
827         } else if (tag("shad")) {
828             double val, xval, yval;
829             if (nargs) {
830                 val = argtod(*args);
831                 xval = render_priv->state.shadow_x * (1 - pwr) + val * pwr;
832                 yval = render_priv->state.shadow_y * (1 - pwr) + val * pwr;
833                 // VSFilter compatibility: clip for \shad but not for \[xy]shad
834                 xval = (xval < 0) ? 0 : xval;
835                 yval = (yval < 0) ? 0 : yval;
836             } else
837                 xval = yval = render_priv->state.style->Shadow;
838             render_priv->state.shadow_x = xval;
839             render_priv->state.shadow_y = yval;
840         } else if (tag("s")) {
841             int val = argtoi(*args);
842             if (!nargs || !(val == 0 || val == 1))
843                 val = render_priv->state.style->StrikeOut;
844             if (val)
845                 render_priv->state.flags |= DECO_STRIKETHROUGH;
846             else
847                 render_priv->state.flags &= ~DECO_STRIKETHROUGH;
848         } else if (tag("u")) {
849             int val = argtoi(*args);
850             if (!nargs || !(val == 0 || val == 1))
851                 val = render_priv->state.style->Underline;
852             if (val)
853                 render_priv->state.flags |= DECO_UNDERLINE;
854             else
855                 render_priv->state.flags &= ~DECO_UNDERLINE;
856         } else if (tag("pbo")) {
857             double val = argtod(*args);
858             render_priv->state.pbo = val;
859         } else if (tag("p")) {
860             int val = argtoi(*args);
861             val = (val < 0) ? 0 : val;
862             render_priv->state.drawing_scale = val;
863         } else if (tag("q")) {
864             int val = argtoi(*args);
865             if (!nargs || !(val >= 0 && val <= 3))
866                 val = render_priv->track->WrapStyle;
867             render_priv->state.wrap_style = val;
868         } else if (tag("fe")) {
869             int val;
870             if (nargs)
871                 val = argtoi(*args);
872             else
873                 val = render_priv->state.style->Encoding;
874             render_priv->state.font_encoding = val;
875         }
876     }
877 
878     return p;
879 }
880 
apply_transition_effects(ASS_Renderer * render_priv,ASS_Event * event)881 void apply_transition_effects(ASS_Renderer *render_priv, ASS_Event *event)
882 {
883     int v[4];
884     int cnt;
885     char *p = event->Effect;
886 
887     if (!p || !*p)
888         return;
889 
890     cnt = 0;
891     while (cnt < 4 && (p = strchr(p, ';'))) {
892         v[cnt++] = atoi(++p);
893     }
894 
895     if (strncmp(event->Effect, "Banner;", 7) == 0) {
896         int delay;
897         if (cnt < 1) {
898             ass_msg(render_priv->library, MSGL_V,
899                     "Error parsing effect: '%s'", event->Effect);
900             return;
901         }
902         if (cnt >= 2 && v[1])   // left-to-right
903             render_priv->state.scroll_direction = SCROLL_LR;
904         else                    // right-to-left
905             render_priv->state.scroll_direction = SCROLL_RL;
906 
907         delay = v[0];
908         if (delay == 0)
909             delay = 1;          // ?
910         render_priv->state.scroll_shift =
911             (render_priv->time - render_priv->state.event->Start) / delay;
912         render_priv->state.evt_type |= EVENT_HSCROLL;
913         render_priv->state.detect_collisions = 0;
914         render_priv->state.wrap_style = 2;
915         return;
916     }
917 
918     if (strncmp(event->Effect, "Scroll up;", 10) == 0) {
919         render_priv->state.scroll_direction = SCROLL_BT;
920     } else if (strncmp(event->Effect, "Scroll down;", 12) == 0) {
921         render_priv->state.scroll_direction = SCROLL_TB;
922     } else {
923         ass_msg(render_priv->library, MSGL_DBG2,
924                 "Unknown transition effect: '%s'", event->Effect);
925         return;
926     }
927     // parse scroll up/down parameters
928     {
929         int delay;
930         int y0, y1;
931         if (cnt < 3) {
932             ass_msg(render_priv->library, MSGL_V,
933                     "Error parsing effect: '%s'", event->Effect);
934             return;
935         }
936         delay = v[2];
937         if (delay == 0)
938             delay = 1;          // ?
939         render_priv->state.scroll_shift =
940             (render_priv->time - render_priv->state.event->Start) / delay;
941         if (v[0] < v[1]) {
942             y0 = v[0];
943             y1 = v[1];
944         } else {
945             y0 = v[1];
946             y1 = v[0];
947         }
948         render_priv->state.scroll_y0 = y0;
949         render_priv->state.scroll_y1 = y1;
950         render_priv->state.evt_type |= EVENT_VSCROLL;
951         render_priv->state.detect_collisions = 0;
952     }
953 
954 }
955 
956 /**
957  * \brief determine karaoke effects
958  * Karaoke effects cannot be calculated during parse stage (get_next_char()),
959  * so they are done in a separate step.
960  * Parse stage: when karaoke style override is found, its parameters are stored in the next glyph's
961  * (the first glyph of the karaoke word)'s effect_type and effect_timing.
962  * This function:
963  * 1. sets effect_type for all glyphs in the word (_karaoke_ word)
964  * 2. sets effect_timing for all glyphs to x coordinate of the border line between the left and right karaoke parts
965  * (left part is filled with PrimaryColour, right one - with SecondaryColour).
966  */
process_karaoke_effects(ASS_Renderer * render_priv)967 void process_karaoke_effects(ASS_Renderer *render_priv)
968 {
969     long long tm_current = render_priv->time - render_priv->state.event->Start;
970 
971     int timing = 0, skip_timing = 0;
972     Effect effect_type = EF_NONE;
973     GlyphInfo *last_boundary = NULL;
974     for (int i = 0; i <= render_priv->text_info.length; i++) {
975         if (i < render_priv->text_info.length &&
976             !render_priv->text_info.glyphs[i].starts_new_run) {
977             // VSFilter compatibility: if we have \k12345\k0 without a run
978             // break, subsequent text is still part of the same karaoke word,
979             // the current word's starting and ending time stay unchanged,
980             // but the starting time of the next karaoke word is advanced.
981             skip_timing += render_priv->text_info.glyphs[i].effect_skip_timing;
982             continue;
983         }
984 
985         GlyphInfo *start = last_boundary;
986         GlyphInfo *end = render_priv->text_info.glyphs + i;
987         last_boundary = end;
988         if (!start)
989             continue;
990 
991         if (start->effect_type != EF_NONE)
992             effect_type = start->effect_type;
993         if (effect_type == EF_NONE)
994             continue;
995 
996         long long tm_start = timing + start->effect_skip_timing;
997         long long tm_end = tm_start + start->effect_timing;
998         timing = tm_end + skip_timing;
999         skip_timing = 0;
1000 
1001         if (effect_type != EF_KARAOKE_KF)
1002             tm_end = tm_start;
1003 
1004         int x;
1005         if (tm_current < tm_start)
1006             x = -100000000;
1007         else if (tm_current >= tm_end)
1008             x = 100000000;
1009         else {
1010             GlyphInfo *first_visible = start, *last_visible = end - 1;
1011             while (first_visible < last_visible && first_visible->skip)
1012                 ++first_visible;
1013             while (first_visible < last_visible && last_visible->skip)
1014                 --last_visible;
1015 
1016             int x_start = first_visible->pos.x;
1017             int x_end = last_visible->pos.x + last_visible->advance.x;
1018             double dt = (double) (tm_current - tm_start) / (tm_end - tm_start);
1019             double frz = fmod(start->frz, 360);
1020             if (frz > 90 && frz < 270) {
1021                 // Fill from right to left
1022                 dt = 1 - dt;
1023                 for (GlyphInfo *info = start; info < end; info++) {
1024                     uint32_t tmp = info->c[0];
1025                     info->c[0] = info->c[1];
1026                     info->c[1] = tmp;
1027                 }
1028             }
1029             x = x_start + lrint((x_end - x_start) * dt);
1030         }
1031 
1032         for (GlyphInfo *info = start; info < end; info++) {
1033             info->effect_type = effect_type;
1034             info->effect_timing = x - info->pos.x;
1035         }
1036     }
1037 }
1038 
1039 
1040 /**
1041  * \brief Get next ucs4 char from string, parsing UTF-8 and escapes
1042  * \param str string pointer
1043  * \return ucs4 code of the next char
1044  * On return str points to the unparsed part of the string
1045  */
get_next_char(ASS_Renderer * render_priv,char ** str)1046 unsigned get_next_char(ASS_Renderer *render_priv, char **str)
1047 {
1048     char *p = *str;
1049     unsigned chr;
1050     if (*p == '\t') {
1051         ++p;
1052         *str = p;
1053         return ' ';
1054     }
1055     if (*p == '\\') {
1056         if ((p[1] == 'N') || ((p[1] == 'n') &&
1057                               (render_priv->state.wrap_style == 2))) {
1058             p += 2;
1059             *str = p;
1060             return '\n';
1061         } else if (p[1] == 'n') {
1062             p += 2;
1063             *str = p;
1064             return ' ';
1065         } else if (p[1] == 'h') {
1066             p += 2;
1067             *str = p;
1068             return NBSP;
1069         } else if (p[1] == '{') {
1070             p += 2;
1071             *str = p;
1072             return '{';
1073         } else if (p[1] == '}') {
1074             p += 2;
1075             *str = p;
1076             return '}';
1077         }
1078     }
1079     chr = ass_utf8_get_char((char **) &p);
1080     *str = p;
1081     return chr;
1082 }
1083 
1084 // Return 1 if the event contains tags that will apply overrides the selective
1085 // style override code should not touch. Return 0 otherwise.
event_has_hard_overrides(char * str)1086 int event_has_hard_overrides(char *str)
1087 {
1088     // look for \pos and \move tags inside {...}
1089     // mirrors get_next_char, but is faster and doesn't change any global state
1090     while (*str) {
1091         if (str[0] == '\\' && str[1] != '\0') {
1092             str += 2;
1093         } else if (str[0] == '{') {
1094             str++;
1095             while (*str && *str != '}') {
1096                 if (*str == '\\') {
1097                     char *p = str + 1;
1098                     if (mystrcmp(&p, "pos") || mystrcmp(&p, "move") ||
1099                         mystrcmp(&p, "clip") || mystrcmp(&p, "iclip") ||
1100                         mystrcmp(&p, "org") || mystrcmp(&p, "pbo") ||
1101                         mystrcmp(&p, "p"))
1102                         return 1;
1103                 }
1104                 str++;
1105             }
1106         } else {
1107             str++;
1108         }
1109     }
1110     return 0;
1111 }
1112