Coverage Report

Created: 2026-10-17 07:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libass/libass/ass_parse.c
Line
Count
Source
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
3.82M
#define MAX_VALID_NARGS 7
32
4.04k
#define MAX_BE 127
33
751
#define NBSP 0xa0   // unicode non-breaking space character
34
35
struct arg {
36
    char *start, *end;
37
};
38
39
static inline int32_t argtoi32(struct arg arg)
40
137k
{
41
137k
    int32_t value;
42
137k
    mystrtoi32(&arg.start, 10, &value);
43
137k
    return value;
44
137k
}
45
46
static inline double argtod(struct arg arg)
47
115k
{
48
115k
    double value;
49
115k
    mystrtod(&arg.start, &value);
50
115k
    return value;
51
115k
}
52
53
static inline void push_arg(struct arg *args, int *nargs, char *start, char *end)
54
682k
{
55
682k
    if (*nargs <= MAX_VALID_NARGS) {
56
643k
        rskip_spaces(&end, start);
57
643k
        if (end > start) {
58
542k
            args[*nargs] = (struct arg) {start, end};
59
542k
            ++*nargs;
60
542k
        }
61
643k
    }
62
682k
}
63
64
/**
65
 * \brief Check if starting part of (*p) matches sample.
66
 * If true, shift p to the first symbol after the matching part.
67
 */
68
static inline int mystrcmp(char **p, const char *sample)
69
14.4M
{
70
14.4M
    char *p2;
71
16.1M
    for (p2 = *p; *sample != 0 && *p2 == *sample; p2++, sample++)
72
1.75M
        ;
73
14.4M
    if (*sample == 0) {
74
338k
        *p = p2;
75
338k
        return 1;
76
338k
    }
77
14.0M
    return 0;
78
14.4M
}
79
80
/**
81
 * \brief Change current font, using setting from render_priv->state.
82
 */
83
void ass_update_font(RenderContext *state)
84
349k
{
85
349k
    unsigned val;
86
349k
    ASS_FontDesc desc;
87
88
349k
    desc.family = state->family;
89
349k
    if (!desc.family.str)
90
0
        return;
91
349k
    if (desc.family.len && desc.family.str[0] == '@') {
92
4.49k
        desc.vertical = 1;
93
4.49k
        desc.family.str++;
94
4.49k
        desc.family.len--;
95
345k
    } else {
96
345k
        desc.vertical = 0;
97
345k
    }
98
99
349k
    val = state->bold;
100
    // 0 = normal, 1 = bold, >1 = exact weight
101
349k
    if (val == 1 || val == -1)
102
3.14k
        val = 700;               // bold
103
346k
    else if (val <= 0)
104
10.8k
        val = 400;               // normal
105
349k
    desc.bold = val;
106
107
349k
    val = state->italic;
108
349k
    if (val == 1)
109
9.06k
        val = 100;              // italic
110
340k
    else if (val <= 0)
111
340k
        val = 0;                // normal
112
349k
    desc.italic = val;
113
114
349k
    state->font = ass_font_new(state->renderer, &desc);
115
349k
}
116
117
/**
118
 * \brief Convert double to int32_t without UB
119
 * on out-of-range values; match x86 behavior
120
 */
121
static inline int32_t dtoi32(double val)
122
162k
{
123
162k
    if (ass_isnan(val) || val <= INT32_MIN || val >= INT32_MAX + 1LL)
124
22.4k
        return INT32_MIN;
125
140k
    return val;
126
162k
}
127
128
static double calc_anim(double new, double old, double pwr)
129
118k
{
130
118k
   return (1 - pwr) * old + new * pwr;
131
118k
}
132
133
static int32_t calc_anim_int32(uint32_t new, uint32_t old, double pwr)
134
118k
{
135
118k
    return dtoi32(calc_anim(new, old, pwr));
136
118k
}
137
138
/**
139
 * \brief Calculate a weighted average of two colors
140
 * calculates c1*(1-a) + c2*a, but separately for each component except alpha
141
 */
142
static void change_color(uint32_t *var, uint32_t new, double pwr)
143
28.7k
{
144
28.7k
    uint32_t co = ass_bswap32(*var);
145
28.7k
    uint32_t cn = ass_bswap32(new);
146
147
28.7k
    uint32_t cc = (calc_anim_int32(cn & 0xff0000, co & 0xff0000, pwr) & 0xff0000) |
148
28.7k
                  (calc_anim_int32(cn & 0x00ff00, co & 0x00ff00, pwr) & 0x00ff00) |
149
28.7k
                  (calc_anim_int32(cn & 0x0000ff, co & 0x0000ff, pwr) & 0x0000ff);
150
151
28.7k
    (*var) = (ass_bswap32(cc & 0xffffff)) | _a(*var);
152
28.7k
}
153
154
// like change_color, but for alpha component only
155
static inline void change_alpha(uint32_t *var, int32_t new, double pwr)
156
32.5k
{
157
32.5k
    *var = (*var & 0xFFFFFF00) | (uint8_t)calc_anim_int32(new, _a(*var), pwr);
158
32.5k
}
159
160
/**
161
 * \brief Multiply two alpha values
162
 * \param a first value
163
 * \param b second value
164
 * \return result of multiplication
165
 * At least one of the parameters must be less than or equal to 0xFF.
166
 * The result is less than or equal to max(a, b, 0xFF).
167
 */
168
static inline uint32_t mult_alpha(uint32_t a, uint32_t b)
169
68
{
170
68
    return a - ((uint64_t) a * b + 0x7F) / 0xFF + b;
171
68
}
172
173
void ass_apply_fade(uint32_t *clr, int fade)
174
312k
{
175
    // VSFilter compatibility: apply fade only when it's positive
176
312k
    if (fade > 0)
177
68
        change_alpha(clr, mult_alpha(_a(*clr), fade), 1);
178
312k
}
179
180
/**
181
 * \brief Calculate alpha value by piecewise linear function
182
 * Used for \fad, \fade implementation.
183
 */
184
static int
185
interpolate_alpha(long long now, int32_t t1, int32_t t2, int32_t t3,
186
                  int32_t t4, int a1, int a2, int a3)
187
4.05k
{
188
4.05k
    int a;
189
4.05k
    double cf;
190
191
4.05k
    if (now < t1) {
192
734
        a = a1;
193
3.31k
    } else if (now < t2) {
194
314
        cf = ((double) (int32_t) ((uint32_t) now - t1)) /
195
314
                (int32_t) ((uint32_t) t2 - t1);
196
314
        a = a1 * (1 - cf) + a2 * cf;
197
3.00k
    } else if (now < t3) {
198
880
        a = a2;
199
2.12k
    } else if (now < t4) {
200
340
        cf = ((double) (int32_t) ((uint32_t) now - t3)) /
201
340
                (int32_t) ((uint32_t) t4 - t3);
202
340
        a = a2 * (1 - cf) + a3 * cf;
203
1.78k
    } else {                    // now >= t4
204
1.78k
        a = a3;
205
1.78k
    }
206
207
4.05k
    return a;
208
4.05k
}
209
210
/**
211
 * Parse a vector clip into an outline, using the proper scaling
212
 * parameters.  Translate it to correct for screen borders, if needed.
213
 */
214
static bool parse_vector_clip(RenderContext *state,
215
                              struct arg *args, int nargs)
216
7.78k
{
217
7.78k
    if (nargs != 1 && nargs != 2)
218
2.14k
        return false;
219
220
5.63k
    int scale = 1;
221
5.63k
    if (nargs == 2)
222
4.29k
        scale = FFMAX(argtoi32(args[0]), 1);
223
224
5.63k
    struct arg text = args[nargs - 1];
225
5.63k
    state->clip_drawing_text.str = text.start;
226
5.63k
    state->clip_drawing_text.len = text.end - text.start;
227
5.63k
    state->clip_drawing_scale = scale;
228
5.63k
    return true;
229
7.78k
}
230
231
static int32_t parse_alpha_tag(char *str)
232
14.0k
{
233
14.0k
    int32_t alpha = 0;
234
235
17.6k
    while (*str == '&' || *str == 'H')
236
3.62k
        ++str;
237
238
14.0k
    mystrtoi32(&str, 16, &alpha);
239
14.0k
    return alpha;
240
14.0k
}
241
242
static uint32_t parse_color_tag(char *str)
243
19.1k
{
244
19.1k
    int32_t color = 0;
245
246
20.5k
    while (*str == '&' || *str == 'H')
247
1.41k
        ++str;
248
249
19.1k
    mystrtoi32(&str, 16, &color);
250
19.1k
    return ass_bswap32((uint32_t) color);
251
19.1k
}
252
253
/**
254
 * \brief find style by name as in \r
255
 * \param track track
256
 * \param name style name
257
 * \param len style name length
258
 * \return style in track->styles
259
 * Returns NULL if no style has the given name.
260
 */
261
static ASS_Style *lookup_style_strict(ASS_Track *track, char *name, size_t len)
262
5.98k
{
263
5.98k
    int i;
264
13.6k
    for (i = track->n_styles - 1; i >= 0; --i) {
265
8.51k
        if (strncmp(track->styles[i].Name, name, len) == 0 &&
266
2.48k
            track->styles[i].Name[len] == '\0')
267
832
            return track->styles + i;
268
8.51k
    }
269
5.15k
    ass_msg(track->library, MSGL_WARN,
270
5.15k
            "[%p]: Warning: no style named '%.*s' found",
271
5.15k
            track, (int) len, name);
272
5.15k
    return NULL;
273
5.98k
}
274
275
/**
276
 * \brief Parse style override tags.
277
 * \param p string to parse
278
 * \param end end of string to parse, which must be '}', ')', or the first
279
 *            of a number of spaces immediately preceding '}' or ')'
280
 * \param pwr multiplier for some tag effects (comes from \t tags)
281
 */
282
char *ass_parse_tags(RenderContext *state, char *p, char *end, double pwr,
283
                     bool nested)
284
138k
{
285
138k
    ASS_Renderer *render_priv = state->renderer;
286
506k
    for (char *q; p < end; p = q) {
287
621k
        while (*p != '\\' && p != end)
288
248k
            ++p;
289
372k
        if (*p != '\\')
290
3.59k
            break;
291
368k
        ++p;
292
368k
        if (p != end)
293
362k
            skip_spaces(&p);
294
295
368k
        q = p;
296
2.13M
        while (*q != '(' && *q != '\\' && q != end)
297
1.76M
            ++q;
298
368k
        if (q == p)
299
19.2k
            continue;
300
301
349k
        char *name_end = q;
302
303
        // Store one extra element to be able to detect excess arguments
304
349k
        struct arg args[MAX_VALID_NARGS + 1];
305
349k
        int nargs = 0;
306
349k
        bool has_backslash_arg = false;
307
3.14M
        for (int i = 0; i <= MAX_VALID_NARGS; ++i)
308
2.79M
            args[i].start = args[i].end = "";
309
310
        // Split parenthesized arguments. Do this for all tags and before
311
        // any non-parenthesized argument because that's what VSFilter does.
312
349k
        if (*q == '(') {
313
78.8k
            ++q;
314
423k
            while (1) {
315
423k
                if (q != end)
316
420k
                    skip_spaces(&q);
317
318
                // Split on commas. If there is a backslash, ignore any
319
                // commas following it and lump everything starting from
320
                // the last comma, through the backslash and all the way
321
                // to the end of the argument string into a single argument.
322
323
423k
                char *r = q;
324
973k
                while (*r != ',' && *r != '\\' && *r != ')' && r != end)
325
550k
                    ++r;
326
327
423k
                if (*r == ',') {
328
344k
                    push_arg(args, &nargs, q, r);
329
344k
                    q = r + 1;
330
344k
                } else {
331
                    // Swallow the rest of the parenthesized string. This could
332
                    // be either a backslash-argument or simply the last argument.
333
78.8k
                    if (*r == '\\') {
334
15.8k
                        has_backslash_arg = true;
335
15.8k
                        char *paren = memchr(r, ')', end - r);
336
15.8k
                        if (paren)
337
2.61k
                            r = paren;
338
13.2k
                        else
339
13.2k
                            r = end;
340
15.8k
                    }
341
78.8k
                    push_arg(args, &nargs, q, r);
342
78.8k
                    q = r;
343
                    // The closing parenthesis could be missing.
344
78.8k
                    if (q != end)
345
4.49k
                        ++q;
346
78.8k
                    break;
347
78.8k
                }
348
423k
            }
349
78.8k
        }
350
351
10.4M
#define tag(name) (mystrcmp(&p, (name)) && (push_arg(args, &nargs, p, name_end), 1))
352
2.33M
#define complex_tag(name) mystrcmp(&p, (name))
353
354
        // New tags introduced in vsfilter 2.39
355
349k
        if (tag("xbord")) {
356
2.91k
            double val;
357
2.91k
            if (nargs) {
358
1.84k
                val = argtod(*args);
359
1.84k
                val = state->border_x * (1 - pwr) + val * pwr;
360
1.84k
                val = (val < 0) ? 0 : val;
361
1.84k
            } else
362
1.06k
                val = state->style->Outline;
363
2.91k
            state->border_x = val;
364
346k
        } else if (tag("ybord")) {
365
3.21k
            double val;
366
3.21k
            if (nargs) {
367
1.98k
                val = argtod(*args);
368
1.98k
                val = state->border_y * (1 - pwr) + val * pwr;
369
1.98k
                val = (val < 0) ? 0 : val;
370
1.98k
            } else
371
1.23k
                val = state->style->Outline;
372
3.21k
            state->border_y = val;
373
343k
        } else if (tag("xshad")) {
374
3.31k
            double val;
375
3.31k
            if (nargs) {
376
1.59k
                val = argtod(*args);
377
1.59k
                val = state->shadow_x * (1 - pwr) + val * pwr;
378
1.59k
            } else
379
1.71k
                val = state->style->Shadow;
380
3.31k
            state->shadow_x = val;
381
339k
        } else if (tag("yshad")) {
382
3.64k
            double val;
383
3.64k
            if (nargs) {
384
2.72k
                val = argtod(*args);
385
2.72k
                val = state->shadow_y * (1 - pwr) + val * pwr;
386
2.72k
            } else
387
923
                val = state->style->Shadow;
388
3.64k
            state->shadow_y = val;
389
336k
        } else if (tag("fax")) {
390
3.34k
            double val;
391
3.34k
            if (nargs) {
392
1.89k
                val = argtod(*args);
393
1.89k
                state->fax =
394
1.89k
                    val * pwr + state->fax * (1 - pwr);
395
1.89k
            } else
396
1.44k
                state->fax = 0.;
397
332k
        } else if (tag("fay")) {
398
2.53k
            double val;
399
2.53k
            if (nargs) {
400
1.61k
                val = argtod(*args);
401
1.61k
                state->fay =
402
1.61k
                    val * pwr + state->fay * (1 - pwr);
403
1.61k
            } else
404
916
                state->fay = 0.;
405
330k
        } else if (complex_tag("iclip")) {
406
4.93k
            if (nargs == 4) {
407
725
                int32_t x0, y0, x1, y1;
408
725
                x0 = argtoi32(args[0]);
409
725
                y0 = argtoi32(args[1]);
410
725
                x1 = argtoi32(args[2]);
411
725
                y1 = argtoi32(args[3]);
412
725
                state->clip_x0 =
413
725
                    state->clip_x0 * (1 - pwr) + x0 * pwr;
414
725
                state->clip_x1 =
415
725
                    state->clip_x1 * (1 - pwr) + x1 * pwr;
416
725
                state->clip_y0 =
417
725
                    state->clip_y0 * (1 - pwr) + y0 * pwr;
418
725
                state->clip_y1 =
419
725
                    state->clip_y1 * (1 - pwr) + y1 * pwr;
420
725
                state->clip_mode = 1;
421
4.20k
            } else if (!state->clip_drawing_text.str) {
422
3.49k
                if (parse_vector_clip(state, args, nargs))
423
2.61k
                    state->clip_drawing_mode = 1;
424
3.49k
            }
425
325k
        } else if (tag("blur")) {
426
2.95k
            double val;
427
2.95k
            if (nargs) {
428
2.11k
                val = argtod(*args);
429
2.11k
                val = state->blur * (1 - pwr) + val * pwr;
430
2.11k
                val = (val < 0) ? 0 : val;
431
2.11k
                val = (val > BLUR_MAX_RADIUS) ? BLUR_MAX_RADIUS : val;
432
2.11k
                state->blur = val;
433
2.11k
            } else
434
839
                state->blur = 0.0;
435
            // ASS standard tags
436
322k
        } else if (tag("fscx")) {
437
2.62k
            double val;
438
2.62k
            if (nargs) {
439
1.82k
                val = argtod(*args) / 100;
440
1.82k
                val = state->scale_x * (1 - pwr) + val * pwr;
441
1.82k
                val = (val < 0) ? 0 : val;
442
1.82k
            } else
443
792
                val = state->style->ScaleX;
444
2.62k
            state->scale_x = val;
445
319k
        } else if (tag("fscy")) {
446
2.64k
            double val;
447
2.64k
            if (nargs) {
448
1.83k
                val = argtod(*args) / 100;
449
1.83k
                val = state->scale_y * (1 - pwr) + val * pwr;
450
1.83k
                val = (val < 0) ? 0 : val;
451
1.83k
            } else
452
810
                val = state->style->ScaleY;
453
2.64k
            state->scale_y = val;
454
317k
        } else if (tag("fsc")) {
455
2.42k
            state->scale_x = state->style->ScaleX;
456
2.42k
            state->scale_y = state->style->ScaleY;
457
314k
        } else if (tag("fsp")) {
458
3.46k
            double val;
459
3.46k
            if (nargs) {
460
1.78k
                val = argtod(*args);
461
1.78k
                state->hspacing =
462
1.78k
                    state->hspacing * (1 - pwr) + val * pwr;
463
1.78k
            } else
464
1.68k
                state->hspacing = state->style->Spacing;
465
311k
        } else if (tag("fs")) {
466
5.00k
            double val = 0;
467
5.00k
            if (nargs) {
468
3.43k
                val = argtod(*args);
469
3.43k
                if (*args->start == '+' || *args->start == '-')
470
1.59k
                    val = state->font_size * (1 + pwr * val / 10);
471
1.83k
                else
472
1.83k
                    val = state->font_size * (1 - pwr) + val * pwr;
473
3.43k
            }
474
5.00k
            if (val <= 0)
475
3.28k
                val = state->style->FontSize;
476
5.00k
            state->font_size = val;
477
306k
        } else if (tag("bord")) {
478
2.67k
            double val, xval, yval;
479
2.67k
            if (nargs) {
480
1.88k
                val = argtod(*args);
481
1.88k
                xval = state->border_x * (1 - pwr) + val * pwr;
482
1.88k
                yval = state->border_y * (1 - pwr) + val * pwr;
483
1.88k
                xval = (xval < 0) ? 0 : xval;
484
1.88k
                yval = (yval < 0) ? 0 : yval;
485
1.88k
            } else
486
791
                xval = yval = state->style->Outline;
487
2.67k
            state->border_x = xval;
488
2.67k
            state->border_y = yval;
489
303k
        } else if (complex_tag("move")) {
490
8.32k
            double x1, x2, y1, y2;
491
8.32k
            int32_t t1, t2, delta_t, t;
492
8.32k
            double x, y;
493
8.32k
            double k;
494
8.32k
            if (nargs == 4 || nargs == 6) {
495
6.48k
                x1 = argtod(args[0]);
496
6.48k
                y1 = argtod(args[1]);
497
6.48k
                x2 = argtod(args[2]);
498
6.48k
                y2 = argtod(args[3]);
499
6.48k
                t1 = t2 = 0;
500
6.48k
                if (nargs == 6) {
501
3.34k
                    t1 = argtoi32(args[4]);
502
3.34k
                    t2 = argtoi32(args[5]);
503
3.34k
                    if (t1 > t2) {
504
2.25k
                        long long tmp = t2;
505
2.25k
                        t2 = t1;
506
2.25k
                        t1 = tmp;
507
2.25k
                    }
508
3.34k
                }
509
6.48k
            } else
510
1.84k
                continue;
511
6.48k
            if (t1 <= 0 && t2 <= 0) {
512
3.32k
                t1 = 0;
513
3.32k
                t2 = state->event->Duration;
514
3.32k
            }
515
6.48k
            delta_t = (uint32_t) t2 - t1;
516
6.48k
            t = render_priv->time - state->event->Start;
517
6.48k
            if (t <= t1)
518
2.29k
                k = 0.;
519
4.18k
            else if (t >= t2)
520
1.92k
                k = 1.;
521
2.26k
            else
522
2.26k
                k = ((double) (int32_t) ((uint32_t) t - t1)) / delta_t;
523
6.48k
            x = k * (x2 - x1) + x1;
524
6.48k
            y = k * (y2 - y1) + y1;
525
6.48k
            if (!(state->evt_type & EVENT_POSITIONED)) {
526
5.29k
                state->pos_x = x;
527
5.29k
                state->pos_y = y;
528
5.29k
                state->detect_collisions = 0;
529
5.29k
                state->evt_type |= EVENT_POSITIONED;
530
5.29k
            }
531
295k
        } else if (tag("frx")) {
532
2.75k
            double val;
533
2.75k
            if (nargs) {
534
1.98k
                val = argtod(*args);
535
1.98k
                state->frx =
536
1.98k
                    val * pwr + state->frx * (1 - pwr);
537
1.98k
            } else
538
765
                state->frx = 0.;
539
292k
        } else if (tag("fry")) {
540
2.90k
            double val;
541
2.90k
            if (nargs) {
542
1.63k
                val = argtod(*args);
543
1.63k
                state->fry =
544
1.63k
                    val * pwr + state->fry * (1 - pwr);
545
1.63k
            } else
546
1.27k
                state->fry = 0.;
547
289k
        } else if (tag("frz") || tag("fr")) {
548
6.72k
            double val;
549
6.72k
            if (nargs) {
550
3.86k
                val = argtod(*args);
551
3.86k
                state->frz =
552
3.86k
                    val * pwr + state->frz * (1 - pwr);
553
3.86k
            } else
554
2.85k
                state->frz =
555
2.85k
                    state->style->Angle;
556
282k
        } else if (tag("fn")) {
557
6.60k
            char *start = args->start;
558
6.60k
            if (nargs && strncmp(start, "0", args->end - start)) {
559
4.76k
                skip_spaces(&start);
560
4.76k
                state->family.str = start;
561
4.76k
                state->family.len = args->end - start;
562
4.76k
            } else {
563
1.83k
                state->family.str = state->style->FontName;
564
1.83k
                state->family.len = strlen(state->style->FontName);
565
1.83k
            }
566
6.60k
            ass_update_font(state);
567
276k
        } else if (tag("alpha")) {
568
3.64k
            int i;
569
3.64k
            if (nargs) {
570
2.37k
                int32_t a = parse_alpha_tag(args->start);
571
11.8k
                for (i = 0; i < 4; ++i)
572
9.50k
                    change_alpha(&state->c[i], a, pwr);
573
2.37k
            } else {
574
1.26k
                change_alpha(&state->c[0],
575
1.26k
                             _a(state->style->PrimaryColour), 1);
576
1.26k
                change_alpha(&state->c[1],
577
1.26k
                             _a(state->style->SecondaryColour), 1);
578
1.26k
                change_alpha(&state->c[2],
579
1.26k
                             _a(state->style->OutlineColour), 1);
580
1.26k
                change_alpha(&state->c[3],
581
1.26k
                             _a(state->style->BackColour), 1);
582
1.26k
            }
583
            // FIXME: simplify
584
272k
        } else if (tag("an")) {
585
7.47k
            int32_t val = argtoi32(*args);
586
7.47k
            if ((state->parsed_tags & PARSED_A) == 0) {
587
4.97k
                if (val >= 1 && val <= 9)
588
2.38k
                    state->alignment = numpad2align(val);
589
2.58k
                else
590
2.58k
                    state->alignment =
591
2.58k
                        state->style->Alignment;
592
4.97k
                state->parsed_tags |= PARSED_A;
593
4.97k
            }
594
265k
        } else if (tag("a")) {
595
12.5k
            int32_t val = argtoi32(*args);
596
12.5k
            if ((state->parsed_tags & PARSED_A) == 0) {
597
8.61k
                if (val >= 1 && val <= 11)
598
                    // take care of a vsfilter quirk:
599
                    // handle illegal \a8 and \a4 like \a5
600
2.28k
                    state->alignment = ((val & 3) == 0) ? 5 : val;
601
6.33k
                else
602
6.33k
                    state->alignment =
603
6.33k
                        state->style->Alignment;
604
8.61k
                state->parsed_tags |= PARSED_A;
605
8.61k
            }
606
252k
        } else if (complex_tag("pos")) {
607
3.29k
            double v1, v2;
608
3.29k
            if (nargs == 2) {
609
2.02k
                v1 = argtod(args[0]);
610
2.02k
                v2 = argtod(args[1]);
611
2.02k
            } else
612
1.27k
                continue;
613
2.02k
            if (state->evt_type & EVENT_POSITIONED) {
614
1.01k
                ass_msg(render_priv->library, MSGL_V, "Subtitle has a new \\pos "
615
1.01k
                       "after \\move or \\pos, ignoring");
616
1.01k
            } else {
617
1.00k
                state->evt_type |= EVENT_POSITIONED;
618
1.00k
                state->detect_collisions = 0;
619
1.00k
                state->pos_x = v1;
620
1.00k
                state->pos_y = v2;
621
1.00k
            }
622
249k
        } else if (complex_tag("fade") || complex_tag("fad")) {
623
7.13k
            int32_t a1, a2, a3;
624
7.13k
            int32_t t1, t2, t3, t4;
625
7.13k
            if (nargs == 2) {
626
                // 2-argument version (\fad, according to specs)
627
3.50k
                a1 = 0xFF;
628
3.50k
                a2 = 0;
629
3.50k
                a3 = 0xFF;
630
3.50k
                t1 = -1;
631
3.50k
                t2 = argtoi32(args[0]);
632
3.50k
                t3 = argtoi32(args[1]);
633
3.50k
                t4 = -1;
634
3.62k
            } else if (nargs == 7) {
635
                // 7-argument version (\fade)
636
1.82k
                a1 = argtoi32(args[0]);
637
1.82k
                a2 = argtoi32(args[1]);
638
1.82k
                a3 = argtoi32(args[2]);
639
1.82k
                t1 = argtoi32(args[3]);
640
1.82k
                t2 = argtoi32(args[4]);
641
1.82k
                t3 = argtoi32(args[5]);
642
1.82k
                t4 = argtoi32(args[6]);
643
1.82k
            } else
644
1.80k
                continue;
645
5.33k
            if (t1 == -1 && t4 == -1) {
646
3.50k
                t1 = 0;
647
3.50k
                t4 = state->event->Duration;
648
3.50k
                t3 = (uint32_t) t4 - t3;
649
3.50k
            }
650
5.33k
            if ((state->parsed_tags & PARSED_FADE) == 0) {
651
4.05k
                state->fade =
652
4.05k
                    interpolate_alpha(render_priv->time -
653
4.05k
                            state->event->Start, t1, t2,
654
4.05k
                            t3, t4, a1, a2, a3);
655
4.05k
                state->parsed_tags |= PARSED_FADE;
656
4.05k
            }
657
242k
        } else if (complex_tag("org")) {
658
3.14k
            double v1, v2;
659
3.14k
            if (nargs == 2) {
660
2.05k
                v1 = argtod(args[0]);
661
2.05k
                v2 = argtod(args[1]);
662
2.05k
            } else
663
1.08k
                continue;
664
2.05k
            if (!state->have_origin) {
665
957
                state->org_x = v1;
666
957
                state->org_y = v2;
667
957
                state->have_origin = 1;
668
957
                state->detect_collisions = 0;
669
957
            }
670
239k
        } else if (complex_tag("t")) {
671
19.6k
            double accel;
672
19.6k
            int cnt = nargs - 1;
673
19.6k
            int32_t t1, t2, t, delta_t;
674
19.6k
            double k;
675
            // VSFilter compatibility (because we can): parse the
676
            // timestamps differently depending on argument count.
677
19.6k
            if (cnt == 3) {
678
967
                t1 = argtoi32(args[0]);
679
967
                t2 = argtoi32(args[1]);
680
967
                accel = argtod(args[2]);
681
18.6k
            } else if (cnt == 2) {
682
7.09k
                t1 = dtoi32(argtod(args[0]));
683
7.09k
                t2 = dtoi32(argtod(args[1]));
684
7.09k
                accel = 1.;
685
11.5k
            } else if (cnt == 1) {
686
2.89k
                t1 = 0;
687
2.89k
                t2 = 0;
688
2.89k
                accel = argtod(args[0]);
689
8.67k
            } else {
690
8.67k
                t1 = 0;
691
8.67k
                t2 = 0;
692
8.67k
                accel = 1.;
693
8.67k
            }
694
19.6k
            state->detect_collisions = 0;
695
19.6k
            if (t2 == 0)
696
14.3k
                t2 = state->event->Duration;
697
19.6k
            delta_t = (uint32_t) t2 - t1;
698
19.6k
            t = render_priv->time - state->event->Start;        // FIXME: move to render_context
699
19.6k
            if (t < t1)
700
1.59k
                k = 0.;
701
18.0k
            else if (t >= t2)
702
4.90k
                k = 1.;
703
13.1k
            else {
704
13.1k
                assert(delta_t != 0.);
705
13.1k
                k = pow((double) (int32_t) ((uint32_t) t - t1) / delta_t, accel);
706
13.1k
            }
707
19.6k
            if (nested)
708
8.05k
                pwr = k;
709
19.6k
            if (cnt < 0 || cnt > 3)
710
4.46k
                continue;
711
            // If there's no backslash in the arguments, there are no
712
            // override tags, so it's pointless to try to parse them.
713
15.1k
            if (!has_backslash_arg)
714
3.08k
                continue;
715
12.0k
            p = args[cnt].start;
716
12.0k
            if (args[cnt].end < end) {
717
2.61k
                assert(!nested);
718
2.61k
                p = ass_parse_tags(state, p, args[cnt].end, k, true);
719
9.47k
            } else {
720
9.47k
                assert(q == end);
721
                // No other tags can possibly follow this \t tag,
722
                // so we don't need to restore pwr after parsing \t.
723
                // The recursive call is now essentially a tail call,
724
                // so optimize it away.
725
9.47k
                pwr = k;
726
9.47k
                nested = true;
727
9.47k
                q = p;
728
9.47k
            }
729
219k
        } else if (complex_tag("clip")) {
730
6.33k
            if (nargs == 4) {
731
714
                int32_t x0, y0, x1, y1;
732
714
                x0 = argtoi32(args[0]);
733
714
                y0 = argtoi32(args[1]);
734
714
                x1 = argtoi32(args[2]);
735
714
                y1 = argtoi32(args[3]);
736
714
                state->clip_x0 =
737
714
                    state->clip_x0 * (1 - pwr) + x0 * pwr;
738
714
                state->clip_x1 =
739
714
                    state->clip_x1 * (1 - pwr) + x1 * pwr;
740
714
                state->clip_y0 =
741
714
                    state->clip_y0 * (1 - pwr) + y0 * pwr;
742
714
                state->clip_y1 =
743
714
                    state->clip_y1 * (1 - pwr) + y1 * pwr;
744
714
                state->clip_mode = 0;
745
5.62k
            } else if (!state->clip_drawing_text.str) {
746
4.29k
                if (parse_vector_clip(state, args, nargs))
747
3.02k
                    state->clip_drawing_mode = 0;
748
4.29k
            }
749
213k
        } else if (tag("c") || tag("1c")) {
750
14.2k
            if (nargs) {
751
8.90k
                uint32_t val = parse_color_tag(args->start);
752
8.90k
                change_color(&state->c[0], val, pwr);
753
8.90k
            } else
754
5.30k
                change_color(&state->c[0],
755
5.30k
                             state->style->PrimaryColour, 1);
756
199k
        } else if (tag("2c")) {
757
7.75k
            if (nargs) {
758
6.15k
                uint32_t val = parse_color_tag(args->start);
759
6.15k
                change_color(&state->c[1], val, pwr);
760
6.15k
            } else
761
1.59k
                change_color(&state->c[1],
762
1.59k
                             state->style->SecondaryColour, 1);
763
191k
        } else if (tag("3c")) {
764
2.72k
            if (nargs) {
765
1.66k
                uint32_t val = parse_color_tag(args->start);
766
1.66k
                change_color(&state->c[2], val, pwr);
767
1.66k
            } else
768
1.05k
                change_color(&state->c[2],
769
1.05k
                             state->style->OutlineColour, 1);
770
188k
        } else if (tag("4c")) {
771
4.03k
            if (nargs) {
772
2.43k
                uint32_t val = parse_color_tag(args->start);
773
2.43k
                change_color(&state->c[3], val, pwr);
774
2.43k
            } else
775
1.59k
                change_color(&state->c[3],
776
1.59k
                             state->style->BackColour, 1);
777
184k
        } else if (tag("1a")) {
778
5.84k
            if (nargs) {
779
3.98k
                uint32_t val = parse_alpha_tag(args->start);
780
3.98k
                change_alpha(&state->c[0], val, pwr);
781
3.98k
            } else
782
1.85k
                change_alpha(&state->c[0],
783
1.85k
                             _a(state->style->PrimaryColour), 1);
784
178k
        } else if (tag("2a")) {
785
4.72k
            if (nargs) {
786
3.37k
                uint32_t val = parse_alpha_tag(args->start);
787
3.37k
                change_alpha(&state->c[1], val, pwr);
788
3.37k
            } else
789
1.34k
                change_alpha(&state->c[1],
790
1.34k
                             _a(state->style->SecondaryColour), 1);
791
173k
        } else if (tag("3a")) {
792
4.24k
            if (nargs) {
793
2.37k
                uint32_t val = parse_alpha_tag(args->start);
794
2.37k
                change_alpha(&state->c[2], val, pwr);
795
2.37k
            } else
796
1.87k
                change_alpha(&state->c[2],
797
1.87k
                             _a(state->style->OutlineColour), 1);
798
169k
        } else if (tag("4a")) {
799
3.13k
            if (nargs) {
800
1.95k
                uint32_t val = parse_alpha_tag(args->start);
801
1.95k
                change_alpha(&state->c[3], val, pwr);
802
1.95k
            } else
803
1.18k
                change_alpha(&state->c[3],
804
1.18k
                             _a(state->style->BackColour), 1);
805
166k
        } else if (tag("r")) {
806
9.03k
            if (nargs) {
807
5.98k
                int len = args->end - args->start;
808
5.98k
                ass_reset_render_context(state,
809
5.98k
                        lookup_style_strict(render_priv->track, args->start, len));
810
5.98k
            } else
811
3.04k
                ass_reset_render_context(state, NULL);
812
157k
        } else if (tag("be")) {
813
5.62k
            double dval;
814
5.62k
            if (nargs) {
815
3.39k
                int32_t val;
816
3.39k
                dval = argtod(*args);
817
                // VSFilter always adds +0.5, even if the value is negative
818
3.39k
                val = dtoi32(state->be * (1 - pwr) + dval * pwr + 0.5);
819
                // Clamp to a safe upper limit, since high values need excessive CPU
820
3.39k
                val = (val < 0) ? 0 : val;
821
3.39k
                val = (val > MAX_BE) ? MAX_BE : val;
822
3.39k
                state->be = val;
823
3.39k
            } else
824
2.23k
                state->be = 0;
825
151k
        } else if (tag("b")) {
826
20.6k
            int32_t val = argtoi32(*args);
827
20.6k
            if (!nargs || !(val == 0 || val == 1 || val >= 100))
828
5.37k
                val = state->style->Bold;
829
20.6k
            state->bold = val;
830
20.6k
            ass_update_font(state);
831
131k
        } else if (tag("i")) {
832
15.4k
            int32_t val = argtoi32(*args);
833
15.4k
            if (!nargs || !(val == 0 || val == 1))
834
9.63k
                val = state->style->Italic;
835
15.4k
            state->italic = val;
836
15.4k
            ass_update_font(state);
837
115k
        } else if (tag("kt")) {
838
            // v4++
839
5.31k
            double val = 0;
840
5.31k
            if (nargs)
841
3.97k
                val = argtod(*args) * 10;
842
5.31k
            state->effect_skip_timing = dtoi32(val);
843
5.31k
            state->effect_timing = 0;
844
5.31k
            state->reset_effect = true;
845
110k
        } else if (tag("kf") || tag("K")) {
846
9.20k
            double val = 100;
847
9.20k
            if (nargs)
848
6.90k
                val = argtod(*args);
849
9.20k
            state->effect_type = EF_KARAOKE_KF;
850
9.20k
            state->effect_skip_timing +=
851
9.20k
                    (uint32_t) state->effect_timing;
852
9.20k
            state->effect_timing = dtoi32(val * 10);
853
101k
        } else if (tag("ko")) {
854
5.25k
            double val = 100;
855
5.25k
            if (nargs)
856
3.62k
                val = argtod(*args);
857
5.25k
            state->effect_type = EF_KARAOKE_KO;
858
5.25k
            state->effect_skip_timing +=
859
5.25k
                    (uint32_t) state->effect_timing;
860
5.25k
            state->effect_timing = dtoi32(val * 10);
861
96.0k
        } else if (tag("k")) {
862
6.61k
            double val = 100;
863
6.61k
            if (nargs)
864
4.70k
                val = argtod(*args);
865
6.61k
            state->effect_type = EF_KARAOKE;
866
6.61k
            state->effect_skip_timing +=
867
6.61k
                    (uint32_t) state->effect_timing;
868
6.61k
            state->effect_timing = dtoi32(val * 10);
869
89.4k
        } else if (tag("shad")) {
870
2.82k
            double val, xval, yval;
871
2.82k
            if (nargs) {
872
2.01k
                val = argtod(*args);
873
2.01k
                xval = state->shadow_x * (1 - pwr) + val * pwr;
874
2.01k
                yval = state->shadow_y * (1 - pwr) + val * pwr;
875
                // VSFilter compatibility: clip for \shad but not for \[xy]shad
876
2.01k
                xval = (xval < 0) ? 0 : xval;
877
2.01k
                yval = (yval < 0) ? 0 : yval;
878
2.01k
            } else
879
809
                xval = yval = state->style->Shadow;
880
2.82k
            state->shadow_x = xval;
881
2.82k
            state->shadow_y = yval;
882
86.5k
        } else if (tag("s")) {
883
7.43k
            int32_t val = argtoi32(*args);
884
7.43k
            if (!nargs || !(val == 0 || val == 1))
885
3.98k
                val = state->style->StrikeOut;
886
7.43k
            if (val)
887
1.26k
                state->flags |= DECO_STRIKETHROUGH;
888
6.16k
            else
889
6.16k
                state->flags &= ~DECO_STRIKETHROUGH;
890
79.1k
        } else if (tag("u")) {
891
6.46k
            int32_t val = argtoi32(*args);
892
6.46k
            if (!nargs || !(val == 0 || val == 1))
893
3.42k
                val = state->style->Underline;
894
6.46k
            if (val)
895
968
                state->flags |= DECO_UNDERLINE;
896
5.49k
            else
897
5.49k
                state->flags &= ~DECO_UNDERLINE;
898
72.7k
        } else if (tag("pbo")) {
899
6.26k
            double val = argtod(*args);
900
6.26k
            state->pbo = val;
901
66.4k
        } else if (tag("p")) {
902
7.44k
            int32_t val = argtoi32(*args);
903
7.44k
            val = (val < 0) ? 0 : val;
904
7.44k
            state->drawing_scale = val;
905
58.9k
        } else if (tag("q")) {
906
6.94k
            int32_t val = argtoi32(*args);
907
6.94k
            if (!nargs || !(val >= 0 && val <= 3))
908
3.76k
                val = render_priv->track->WrapStyle;
909
6.94k
            state->wrap_style = val;
910
52.0k
        } else if (tag("fe")) {
911
14.6k
            int32_t val;
912
14.6k
            if (nargs)
913
12.9k
                val = argtoi32(*args);
914
1.72k
            else
915
1.72k
                val = state->style->Encoding;
916
14.6k
            state->font_encoding = val;
917
14.6k
        }
918
349k
    }
919
920
138k
    return p;
921
138k
}
922
923
void ass_apply_transition_effects(RenderContext *state)
924
297k
{
925
297k
    ASS_Renderer *render_priv = state->renderer;
926
297k
    int v[4];
927
297k
    int cnt;
928
297k
    ASS_Event *event = state->event;
929
297k
    char *p = event->Effect;
930
931
297k
    if (!p || !*p)
932
173k
        return;
933
934
124k
    cnt = 0;
935
142k
    while (cnt < 4 && (p = strchr(p, ';'))) {
936
18.1k
        v[cnt++] = atoi(++p);
937
18.1k
    }
938
939
124k
    ASS_Vector layout_res = ass_layout_res(render_priv);
940
124k
    if (strncmp(event->Effect, "Banner;", 7) == 0) {
941
3.34k
        double delay;
942
3.34k
        if (cnt < 1) {
943
0
            ass_msg(render_priv->library, MSGL_V,
944
0
                    "Error parsing effect: '%s'", event->Effect);
945
0
            return;
946
0
        }
947
3.34k
        if (cnt >= 2 && v[1])   // left-to-right
948
1.31k
            state->scroll_direction = SCROLL_LR;
949
2.03k
        else                    // right-to-left
950
2.03k
            state->scroll_direction = SCROLL_RL;
951
952
3.34k
        delay = v[0];
953
        // VSF works in storage coordinates, but scales delay to PlayRes canvas
954
        // before applying max(scaled_ delay, 1). This means, if scaled_delay < 1
955
        // (esp. delay=0) we end up with 1 ms per _storage pixel_ without any
956
        // PlayRes scaling.
957
        // The way libass deals with delay, it is automatically relative to the
958
        // PlayRes canvas, so we only want to "unscale" the small delay values.
959
        //
960
        // VSF also casts the scaled delay to int, which if not emulated leads to
961
        // easily noticeable deviations from VSFilter as the effect goes on.
962
        // To achieve both we need to keep our Playres-relative delay with high precision,
963
        // but must temporarily convert to storage-relative and truncate and take the
964
        // maxuimum there, before converting back.
965
3.34k
        double scale_x = ((double) layout_res.x) / render_priv->track->PlayResX;
966
3.34k
        delay = ((int) FFMAX(delay / scale_x, 1)) * scale_x;
967
3.34k
        state->scroll_shift =
968
3.34k
            (render_priv->time - event->Start) / delay;
969
3.34k
        state->evt_type |= EVENT_HSCROLL;
970
3.34k
        state->detect_collisions = 0;
971
3.34k
        state->wrap_style = 2;
972
3.34k
        return;
973
3.34k
    }
974
975
121k
    if (strncmp(event->Effect, "Scroll up;", 10) == 0) {
976
2.46k
        state->scroll_direction = SCROLL_BT;
977
118k
    } else if (strncmp(event->Effect, "Scroll down;", 12) == 0) {
978
727
        state->scroll_direction = SCROLL_TB;
979
118k
    } else {
980
118k
        ass_msg(render_priv->library, MSGL_DBG2,
981
118k
                "Unknown transition effect: '%s'", event->Effect);
982
118k
        return;
983
118k
    }
984
    // parse scroll up/down parameters
985
3.19k
    {
986
3.19k
        double delay;
987
3.19k
        int y0, y1;
988
3.19k
        if (cnt < 3) {
989
1.43k
            ass_msg(render_priv->library, MSGL_V,
990
1.43k
                    "Error parsing effect: '%s'", event->Effect);
991
1.43k
            return;
992
1.43k
        }
993
1.75k
        delay = v[2];
994
        // See explanation for Banner
995
1.75k
        double scale_y = ((double) layout_res.y) / render_priv->track->PlayResY;
996
1.75k
        delay = ((int) FFMAX(delay / scale_y, 1)) * scale_y;
997
1.75k
        state->scroll_shift =
998
1.75k
            (render_priv->time - event->Start) / delay;
999
1.75k
        if (v[0] < v[1]) {
1000
762
            y0 = v[0];
1001
762
            y1 = v[1];
1002
991
        } else {
1003
991
            y0 = v[1];
1004
991
            y1 = v[0];
1005
991
        }
1006
1.75k
        state->scroll_y0 = y0;
1007
1.75k
        state->scroll_y1 = y1;
1008
1.75k
        state->evt_type |= EVENT_VSCROLL;
1009
1.75k
        state->detect_collisions = 0;
1010
1.75k
    }
1011
1012
1.75k
}
1013
1014
/**
1015
 * \brief determine karaoke effects
1016
 * Karaoke effects cannot be calculated during parse stage (ass_get_next_char()),
1017
 * so they are done in a separate step.
1018
 * Parse stage: when karaoke style override is found, its parameters are stored in the next glyph's
1019
 * (the first glyph of the karaoke word)'s effect_type and effect_timing.
1020
 * This function:
1021
 * 1. sets effect_type for all glyphs in the word (_karaoke_ word)
1022
 * 2. sets effect_timing for all glyphs to x coordinate of the border line between the left and right karaoke parts
1023
 * (left part is filled with PrimaryColour, right one - with SecondaryColour).
1024
 */
1025
void ass_process_karaoke_effects(RenderContext *state)
1026
78.8k
{
1027
78.8k
    TextInfo *text_info = &state->text_info;
1028
78.8k
    long long tm_current = state->renderer->time - state->event->Start;
1029
1030
78.8k
    int32_t timing = 0, skip_timing = 0;
1031
78.8k
    Effect effect_type = EF_NONE;
1032
78.8k
    GlyphInfo *last_boundary = NULL;
1033
78.8k
    bool has_reset = false;
1034
536k
    for (int i = 0; i <= text_info->length; i++) {
1035
458k
        if (i < text_info->length &&
1036
379k
            !text_info->glyphs[i].starts_new_run) {
1037
1038
288k
            if (text_info->glyphs[i].reset_effect) {
1039
0
                has_reset = true;
1040
0
                skip_timing = 0;
1041
0
            }
1042
1043
            // VSFilter compatibility: if we have \k12345\k0 without a run
1044
            // break, subsequent text is still part of the same karaoke word,
1045
            // the current word's starting and ending time stay unchanged,
1046
            // but the starting time of the next karaoke word is advanced.
1047
288k
            skip_timing += (uint32_t) text_info->glyphs[i].effect_skip_timing;
1048
288k
            continue;
1049
288k
        }
1050
1051
169k
        GlyphInfo *start = last_boundary;
1052
169k
        GlyphInfo *end = text_info->glyphs + i;
1053
169k
        last_boundary = end;
1054
169k
        if (!start)
1055
78.8k
            continue;
1056
1057
91.1k
        if (start->effect_type != EF_NONE)
1058
2.17k
            effect_type = start->effect_type;
1059
91.1k
        if (effect_type == EF_NONE)
1060
88.9k
            continue;
1061
1062
2.21k
        if (start->reset_effect)
1063
82
            timing = 0;
1064
1065
2.21k
        long long tm_start = timing + start->effect_skip_timing;
1066
2.21k
        long long tm_end = tm_start + start->effect_timing;
1067
2.21k
        timing = !has_reset * tm_end + skip_timing;
1068
2.21k
        skip_timing = 0;
1069
2.21k
        has_reset = false;
1070
1071
2.21k
        if (effect_type != EF_KARAOKE_KF)
1072
932
            tm_end = tm_start;
1073
1074
2.21k
        int x;
1075
2.21k
        if (tm_current < tm_start)
1076
177
            x = -100000000;
1077
2.04k
        else if (tm_current >= tm_end)
1078
1.93k
            x = 100000000;
1079
104
        else {
1080
104
            GlyphInfo *first_visible = start, *last_visible = end - 1;
1081
104
            while (first_visible < last_visible && first_visible->skip)
1082
0
                ++first_visible;
1083
104
            while (first_visible < last_visible && last_visible->skip)
1084
0
                --last_visible;
1085
1086
104
            int x_start = first_visible->pos.x;
1087
104
            int x_end = last_visible->pos.x + last_visible->advance.x;
1088
104
            double dt = (double) (tm_current - tm_start) / (tm_end - tm_start);
1089
104
            double frz = fmod(start->frz, 360);
1090
104
            if (frz > 90 && frz < 270) {
1091
                // Fill from right to left
1092
0
                dt = 1 - dt;
1093
0
                for (GlyphInfo *info = start; info < end; info++) {
1094
0
                    uint32_t tmp = info->c[0];
1095
0
                    info->c[0] = info->c[1];
1096
0
                    info->c[1] = tmp;
1097
0
                }
1098
0
            }
1099
104
            x = x_start + ass_lrint((x_end - x_start) * dt);
1100
104
        }
1101
1102
8.67k
        for (GlyphInfo *info = start; info < end; info++) {
1103
6.45k
            info->effect_type = effect_type;
1104
6.45k
            info->effect_timing = x - info->pos.x;
1105
6.45k
        }
1106
2.21k
    }
1107
78.8k
}
1108
1109
1110
/**
1111
 * \brief Get next ucs4 char from string, parsing UTF-8 and escapes
1112
 * \param str string pointer
1113
 * \return ucs4 code of the next char
1114
 * On return str points to the unparsed part of the string
1115
 */
1116
unsigned ass_get_next_char(RenderContext *state, char **str)
1117
376k
{
1118
376k
    char *p = *str;
1119
376k
    unsigned chr;
1120
376k
    if (*p == '\t') {
1121
43.6k
        ++p;
1122
43.6k
        *str = p;
1123
43.6k
        return ' ';
1124
43.6k
    }
1125
333k
    if (*p == '\\') {
1126
25.3k
        if ((p[1] == 'N') || ((p[1] == 'n') &&
1127
1.83k
                              (state->wrap_style == 2))) {
1128
1.71k
            p += 2;
1129
1.71k
            *str = p;
1130
1.71k
            return '\n';
1131
23.6k
        } else if (p[1] == 'n') {
1132
1.12k
            p += 2;
1133
1.12k
            *str = p;
1134
1.12k
            return ' ';
1135
22.4k
        } else if (p[1] == 'h') {
1136
751
            p += 2;
1137
751
            *str = p;
1138
751
            return NBSP;
1139
21.7k
        } else if (p[1] == '{') {
1140
738
            p += 2;
1141
738
            *str = p;
1142
738
            return '{';
1143
21.0k
        } else if (p[1] == '}') {
1144
859
            p += 2;
1145
859
            *str = p;
1146
859
            return '}';
1147
859
        }
1148
25.3k
    }
1149
327k
    chr = ass_utf8_get_char((char **) &p);
1150
327k
    *str = p;
1151
327k
    return chr;
1152
333k
}
1153
1154
// Return 1 if the event contains tags that will apply overrides the selective
1155
// style override code should not touch. Return 0 otherwise.
1156
int ass_event_has_hard_overrides(char *str)
1157
292k
{
1158
    // look for \pos and \move tags inside {...}
1159
    // mirrors ass_get_next_char, but is faster and doesn't change any global state
1160
768k
    while (*str) {
1161
501k
        if (str[0] == '\\' && str[1] != '\0') {
1162
10.4k
            str += 2;
1163
491k
        } else if (str[0] == '{') {
1164
145k
            str++;
1165
3.11M
            while (*str && *str != '}') {
1166
2.99M
                if (*str == '\\') {
1167
365k
                    char *p = str + 1;
1168
365k
                    if (mystrcmp(&p, "pos") || mystrcmp(&p, "move") ||
1169
356k
                        mystrcmp(&p, "clip") || mystrcmp(&p, "iclip") ||
1170
348k
                        mystrcmp(&p, "org") || mystrcmp(&p, "pbo") ||
1171
343k
                        mystrcmp(&p, "p"))
1172
26.4k
                        return 1;
1173
365k
                }
1174
2.96M
                str++;
1175
2.96M
            }
1176
345k
        } else {
1177
345k
            str++;
1178
345k
        }
1179
501k
    }
1180
266k
    return 0;
1181
292k
}