Coverage Report

Created: 2025-11-04 06:44

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
5.28M
#define MAX_VALID_NARGS 7
32
3.61k
#define MAX_BE 127
33
2.11k
#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
161k
{
41
161k
    int32_t value;
42
161k
    mystrtoi32(&arg.start, 10, &value);
43
161k
    return value;
44
161k
}
45
46
static inline double argtod(struct arg arg)
47
171k
{
48
171k
    double value;
49
171k
    mystrtod(&arg.start, &value);
50
171k
    return value;
51
171k
}
52
53
static inline void push_arg(struct arg *args, int *nargs, char *start, char *end)
54
885k
{
55
885k
    if (*nargs <= MAX_VALID_NARGS) {
56
838k
        rskip_spaces(&end, start);
57
838k
        if (end > start) {
58
689k
            args[*nargs] = (struct arg) {start, end};
59
689k
            ++*nargs;
60
689k
        }
61
838k
    }
62
885k
}
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
20.6M
{
70
20.6M
    char *p2;
71
22.7M
    for (p2 = *p; *sample != 0 && *p2 == *sample; p2++, sample++)
72
2.14M
        ;
73
20.6M
    if (*sample == 0) {
74
462k
        *p = p2;
75
462k
        return 1;
76
462k
    }
77
20.1M
    return 0;
78
20.6M
}
79
80
/**
81
 * \brief Change current font, using setting from render_priv->state.
82
 */
83
void ass_update_font(RenderContext *state)
84
452k
{
85
452k
    unsigned val;
86
452k
    ASS_FontDesc desc;
87
88
452k
    desc.family = state->family;
89
452k
    if (!desc.family.str)
90
0
        return;
91
452k
    if (desc.family.len && desc.family.str[0] == '@') {
92
8.09k
        desc.vertical = 1;
93
8.09k
        desc.family.str++;
94
8.09k
        desc.family.len--;
95
443k
    } else {
96
443k
        desc.vertical = 0;
97
443k
    }
98
99
452k
    val = state->bold;
100
    // 0 = normal, 1 = bold, >1 = exact weight
101
452k
    if (val == 1 || val == -1)
102
2.55k
        val = 700;               // bold
103
449k
    else if (val <= 0)
104
12.6k
        val = 400;               // normal
105
452k
    desc.bold = val;
106
107
452k
    val = state->italic;
108
452k
    if (val == 1)
109
6.39k
        val = 100;              // italic
110
445k
    else if (val <= 0)
111
445k
        val = 0;                // normal
112
452k
    desc.italic = val;
113
114
452k
    state->font = ass_font_new(state->renderer, &desc);
115
452k
}
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
198k
{
123
198k
    if (isnan(val) || val <= INT32_MIN || val >= INT32_MAX + 1LL)
124
50.4k
        return INT32_MIN;
125
148k
    return val;
126
198k
}
127
128
static double calc_anim(double new, double old, double pwr)
129
112k
{
130
112k
   return (1 - pwr) * old + new * pwr;
131
112k
}
132
133
static int32_t calc_anim_int32(uint32_t new, uint32_t old, double pwr)
134
112k
{
135
112k
    return dtoi32(calc_anim(new, old, pwr));
136
112k
}
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
25.8k
{
144
25.8k
    uint32_t co = ass_bswap32(*var);
145
25.8k
    uint32_t cn = ass_bswap32(new);
146
147
25.8k
    uint32_t cc = (calc_anim_int32(cn & 0xff0000, co & 0xff0000, pwr) & 0xff0000) |
148
25.8k
                  (calc_anim_int32(cn & 0x00ff00, co & 0x00ff00, pwr) & 0x00ff00) |
149
25.8k
                  (calc_anim_int32(cn & 0x0000ff, co & 0x0000ff, pwr) & 0x0000ff);
150
151
25.8k
    (*var) = (ass_bswap32(cc & 0xffffff)) | _a(*var);
152
25.8k
}
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
35.1k
{
157
35.1k
    *var = (*var & 0xFFFFFF00) | (uint8_t)calc_anim_int32(new, _a(*var), pwr);
158
35.1k
}
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
1.23k
{
170
1.23k
    return a - ((uint64_t) a * b + 0x7F) / 0xFF + b;
171
1.23k
}
172
173
void ass_apply_fade(uint32_t *clr, int fade)
174
429k
{
175
    // VSFilter compatibility: apply fade only when it's positive
176
429k
    if (fade > 0)
177
1.23k
        change_alpha(clr, mult_alpha(_a(*clr), fade), 1);
178
429k
}
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.56k
{
188
4.56k
    int a;
189
4.56k
    double cf;
190
191
4.56k
    if (now < t1) {
192
308
        a = a1;
193
4.25k
    } else if (now < t2) {
194
346
        cf = ((double) (int32_t) ((uint32_t) now - t1)) /
195
346
                (int32_t) ((uint32_t) t2 - t1);
196
346
        a = a1 * (1 - cf) + a2 * cf;
197
3.90k
    } else if (now < t3) {
198
1.61k
        a = a2;
199
2.29k
    } else if (now < t4) {
200
379
        cf = ((double) (int32_t) ((uint32_t) now - t3)) /
201
379
                (int32_t) ((uint32_t) t4 - t3);
202
379
        a = a2 * (1 - cf) + a3 * cf;
203
1.91k
    } else {                    // now >= t4
204
1.91k
        a = a3;
205
1.91k
    }
206
207
4.56k
    return a;
208
4.56k
}
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
6.42k
{
217
6.42k
    if (nargs != 1 && nargs != 2)
218
2.02k
        return false;
219
220
4.40k
    int scale = 1;
221
4.40k
    if (nargs == 2)
222
2.77k
        scale = argtoi32(args[0]);
223
224
4.40k
    struct arg text = args[nargs - 1];
225
4.40k
    state->clip_drawing_text.str = text.start;
226
4.40k
    state->clip_drawing_text.len = text.end - text.start;
227
4.40k
    state->clip_drawing_scale = scale;
228
4.40k
    return true;
229
6.42k
}
230
231
static int32_t parse_alpha_tag(char *str)
232
14.9k
{
233
14.9k
    int32_t alpha = 0;
234
235
17.5k
    while (*str == '&' || *str == 'H')
236
2.61k
        ++str;
237
238
14.9k
    mystrtoi32(&str, 16, &alpha);
239
14.9k
    return alpha;
240
14.9k
}
241
242
static uint32_t parse_color_tag(char *str)
243
16.5k
{
244
16.5k
    int32_t color = 0;
245
246
18.0k
    while (*str == '&' || *str == 'H')
247
1.51k
        ++str;
248
249
16.5k
    mystrtoi32(&str, 16, &color);
250
16.5k
    return ass_bswap32((uint32_t) color);
251
16.5k
}
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
6.58k
{
263
6.58k
    int i;
264
20.9k
    for (i = track->n_styles - 1; i >= 0; --i) {
265
15.0k
        if (strncmp(track->styles[i].Name, name, len) == 0 &&
266
7.35k
            track->styles[i].Name[len] == '\0')
267
716
            return track->styles + i;
268
15.0k
    }
269
5.87k
    ass_msg(track->library, MSGL_WARN,
270
5.87k
            "[%p]: Warning: no style named '%.*s' found",
271
5.87k
            track, (int) len, name);
272
5.87k
    return NULL;
273
6.58k
}
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
205k
{
285
205k
    ASS_Renderer *render_priv = state->renderer;
286
757k
    for (char *q; p < end; p = q) {
287
846k
        while (*p != '\\' && p != end)
288
289k
            ++p;
289
557k
        if (*p != '\\')
290
5.45k
            break;
291
551k
        ++p;
292
551k
        if (p != end)
293
523k
            skip_spaces(&p);
294
295
551k
        q = p;
296
3.18M
        while (*q != '(' && *q != '\\' && q != end)
297
2.63M
            ++q;
298
551k
        if (q == p)
299
62.5k
            continue;
300
301
489k
        char *name_end = q;
302
303
        // Store one extra element to be able to detect excess arguments
304
489k
        struct arg args[MAX_VALID_NARGS + 1];
305
489k
        int nargs = 0;
306
489k
        bool has_backslash_arg = false;
307
4.40M
        for (int i = 0; i <= MAX_VALID_NARGS; ++i)
308
3.91M
            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
489k
        if (*q == '(') {
313
120k
            ++q;
314
541k
            while (1) {
315
541k
                if (q != end)
316
534k
                    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
541k
                char *r = q;
324
1.36M
                while (*r != ',' && *r != '\\' && *r != ')' && r != end)
325
823k
                    ++r;
326
327
541k
                if (*r == ',') {
328
420k
                    push_arg(args, &nargs, q, r);
329
420k
                    q = r + 1;
330
420k
                } else {
331
                    // Swallow the rest of the parenthesized string. This could
332
                    // be either a backslash-argument or simply the last argument.
333
120k
                    if (*r == '\\') {
334
44.6k
                        has_backslash_arg = true;
335
44.6k
                        char *paren = memchr(r, ')', end - r);
336
44.6k
                        if (paren)
337
27.2k
                            r = paren;
338
17.4k
                        else
339
17.4k
                            r = end;
340
44.6k
                    }
341
120k
                    push_arg(args, &nargs, q, r);
342
120k
                    q = r;
343
                    // The closing parenthesis could be missing.
344
120k
                    if (q != end)
345
30.3k
                        ++q;
346
120k
                    break;
347
120k
                }
348
541k
            }
349
120k
        }
350
351
14.7M
#define tag(name) (mystrcmp(&p, (name)) && (push_arg(args, &nargs, p, name_end), 1))
352
3.32M
#define complex_tag(name) mystrcmp(&p, (name))
353
354
        // New tags introduced in vsfilter 2.39
355
489k
        if (tag("xbord")) {
356
4.78k
            double val;
357
4.78k
            if (nargs) {
358
3.03k
                val = argtod(*args);
359
3.03k
                val = state->border_x * (1 - pwr) + val * pwr;
360
3.03k
                val = (val < 0) ? 0 : val;
361
3.03k
            } else
362
1.75k
                val = state->style->Outline;
363
4.78k
            state->border_x = val;
364
484k
        } else if (tag("ybord")) {
365
4.81k
            double val;
366
4.81k
            if (nargs) {
367
2.58k
                val = argtod(*args);
368
2.58k
                val = state->border_y * (1 - pwr) + val * pwr;
369
2.58k
                val = (val < 0) ? 0 : val;
370
2.58k
            } else
371
2.23k
                val = state->style->Outline;
372
4.81k
            state->border_y = val;
373
479k
        } else if (tag("xshad")) {
374
3.59k
            double val;
375
3.59k
            if (nargs) {
376
2.15k
                val = argtod(*args);
377
2.15k
                val = state->shadow_x * (1 - pwr) + val * pwr;
378
2.15k
            } else
379
1.43k
                val = state->style->Shadow;
380
3.59k
            state->shadow_x = val;
381
476k
        } else if (tag("yshad")) {
382
9.45k
            double val;
383
9.45k
            if (nargs) {
384
8.32k
                val = argtod(*args);
385
8.32k
                val = state->shadow_y * (1 - pwr) + val * pwr;
386
8.32k
            } else
387
1.12k
                val = state->style->Shadow;
388
9.45k
            state->shadow_y = val;
389
466k
        } else if (tag("fax")) {
390
3.51k
            double val;
391
3.51k
            if (nargs) {
392
1.85k
                val = argtod(*args);
393
1.85k
                state->fax =
394
1.85k
                    val * pwr + state->fax * (1 - pwr);
395
1.85k
            } else
396
1.66k
                state->fax = 0.;
397
463k
        } else if (tag("fay")) {
398
4.81k
            double val;
399
4.81k
            if (nargs) {
400
2.73k
                val = argtod(*args);
401
2.73k
                state->fay =
402
2.73k
                    val * pwr + state->fay * (1 - pwr);
403
2.73k
            } else
404
2.08k
                state->fay = 0.;
405
458k
        } else if (complex_tag("iclip")) {
406
4.72k
            if (nargs == 4) {
407
717
                int32_t x0, y0, x1, y1;
408
717
                x0 = argtoi32(args[0]);
409
717
                y0 = argtoi32(args[1]);
410
717
                x1 = argtoi32(args[2]);
411
717
                y1 = argtoi32(args[3]);
412
717
                state->clip_x0 =
413
717
                    state->clip_x0 * (1 - pwr) + x0 * pwr;
414
717
                state->clip_x1 =
415
717
                    state->clip_x1 * (1 - pwr) + x1 * pwr;
416
717
                state->clip_y0 =
417
717
                    state->clip_y0 * (1 - pwr) + y0 * pwr;
418
717
                state->clip_y1 =
419
717
                    state->clip_y1 * (1 - pwr) + y1 * pwr;
420
717
                state->clip_mode = 1;
421
4.00k
            } else if (!state->clip_drawing_text.str) {
422
2.36k
                if (parse_vector_clip(state, args, nargs))
423
1.51k
                    state->clip_drawing_mode = 1;
424
2.36k
            }
425
453k
        } else if (tag("blur")) {
426
3.65k
            double val;
427
3.65k
            if (nargs) {
428
2.59k
                val = argtod(*args);
429
2.59k
                val = state->blur * (1 - pwr) + val * pwr;
430
2.59k
                val = (val < 0) ? 0 : val;
431
2.59k
                val = (val > BLUR_MAX_RADIUS) ? BLUR_MAX_RADIUS : val;
432
2.59k
                state->blur = val;
433
2.59k
            } else
434
1.06k
                state->blur = 0.0;
435
            // ASS standard tags
436
449k
        } else if (tag("fscx")) {
437
3.42k
            double val;
438
3.42k
            if (nargs) {
439
2.18k
                val = argtod(*args) / 100;
440
2.18k
                val = state->scale_x * (1 - pwr) + val * pwr;
441
2.18k
                val = (val < 0) ? 0 : val;
442
2.18k
            } else
443
1.23k
                val = state->style->ScaleX;
444
3.42k
            state->scale_x = val;
445
446k
        } else if (tag("fscy")) {
446
3.20k
            double val;
447
3.20k
            if (nargs) {
448
1.98k
                val = argtod(*args) / 100;
449
1.98k
                val = state->scale_y * (1 - pwr) + val * pwr;
450
1.98k
                val = (val < 0) ? 0 : val;
451
1.98k
            } else
452
1.22k
                val = state->style->ScaleY;
453
3.20k
            state->scale_y = val;
454
443k
        } else if (tag("fsc")) {
455
3.43k
            state->scale_x = state->style->ScaleX;
456
3.43k
            state->scale_y = state->style->ScaleY;
457
439k
        } else if (tag("fsp")) {
458
5.55k
            double val;
459
5.55k
            if (nargs) {
460
3.07k
                val = argtod(*args);
461
3.07k
                state->hspacing =
462
3.07k
                    state->hspacing * (1 - pwr) + val * pwr;
463
3.07k
            } else
464
2.48k
                state->hspacing = state->style->Spacing;
465
434k
        } else if (tag("fs")) {
466
7.54k
            double val = 0;
467
7.54k
            if (nargs) {
468
4.69k
                val = argtod(*args);
469
4.69k
                if (*args->start == '+' || *args->start == '-')
470
1.95k
                    val = state->font_size * (1 + pwr * val / 10);
471
2.73k
                else
472
2.73k
                    val = state->font_size * (1 - pwr) + val * pwr;
473
4.69k
            }
474
7.54k
            if (val <= 0)
475
5.55k
                val = state->style->FontSize;
476
7.54k
            state->font_size = val;
477
426k
        } else if (tag("bord")) {
478
3.09k
            double val, xval, yval;
479
3.09k
            if (nargs) {
480
2.28k
                val = argtod(*args);
481
2.28k
                xval = state->border_x * (1 - pwr) + val * pwr;
482
2.28k
                yval = state->border_y * (1 - pwr) + val * pwr;
483
2.28k
                xval = (xval < 0) ? 0 : xval;
484
2.28k
                yval = (yval < 0) ? 0 : yval;
485
2.28k
            } else
486
808
                xval = yval = state->style->Outline;
487
3.09k
            state->border_x = xval;
488
3.09k
            state->border_y = yval;
489
423k
        } else if (complex_tag("move")) {
490
9.51k
            double x1, x2, y1, y2;
491
9.51k
            int32_t t1, t2, delta_t, t;
492
9.51k
            double x, y;
493
9.51k
            double k;
494
9.51k
            if (nargs == 4 || nargs == 6) {
495
6.81k
                x1 = argtod(args[0]);
496
6.81k
                y1 = argtod(args[1]);
497
6.81k
                x2 = argtod(args[2]);
498
6.81k
                y2 = argtod(args[3]);
499
6.81k
                t1 = t2 = 0;
500
6.81k
                if (nargs == 6) {
501
3.85k
                    t1 = argtoi32(args[4]);
502
3.85k
                    t2 = argtoi32(args[5]);
503
3.85k
                    if (t1 > t2) {
504
2.37k
                        long long tmp = t2;
505
2.37k
                        t2 = t1;
506
2.37k
                        t1 = tmp;
507
2.37k
                    }
508
3.85k
                }
509
6.81k
            } else
510
2.70k
                continue;
511
6.81k
            if (t1 <= 0 && t2 <= 0) {
512
3.40k
                t1 = 0;
513
3.40k
                t2 = state->event->Duration;
514
3.40k
            }
515
6.81k
            delta_t = (uint32_t) t2 - t1;
516
6.81k
            t = render_priv->time - state->event->Start;
517
6.81k
            if (t <= t1)
518
2.54k
                k = 0.;
519
4.26k
            else if (t >= t2)
520
2.07k
                k = 1.;
521
2.19k
            else
522
2.19k
                k = ((double) (int32_t) ((uint32_t) t - t1)) / delta_t;
523
6.81k
            x = k * (x2 - x1) + x1;
524
6.81k
            y = k * (y2 - y1) + y1;
525
6.81k
            if (!(state->evt_type & EVENT_POSITIONED)) {
526
5.41k
                state->pos_x = x;
527
5.41k
                state->pos_y = y;
528
5.41k
                state->detect_collisions = 0;
529
5.41k
                state->evt_type |= EVENT_POSITIONED;
530
5.41k
            }
531
414k
        } else if (tag("frx")) {
532
4.65k
            double val;
533
4.65k
            if (nargs) {
534
3.67k
                val = argtod(*args);
535
3.67k
                state->frx =
536
3.67k
                    val * pwr + state->frx * (1 - pwr);
537
3.67k
            } else
538
987
                state->frx = 0.;
539
409k
        } else if (tag("fry")) {
540
3.78k
            double val;
541
3.78k
            if (nargs) {
542
1.62k
                val = argtod(*args);
543
1.62k
                state->fry =
544
1.62k
                    val * pwr + state->fry * (1 - pwr);
545
1.62k
            } else
546
2.15k
                state->fry = 0.;
547
405k
        } else if (tag("frz") || tag("fr")) {
548
8.75k
            double val;
549
8.75k
            if (nargs) {
550
5.07k
                val = argtod(*args);
551
5.07k
                state->frz =
552
5.07k
                    val * pwr + state->frz * (1 - pwr);
553
5.07k
            } else
554
3.67k
                state->frz =
555
3.67k
                    state->style->Angle;
556
396k
        } else if (tag("fn")) {
557
7.38k
            char *start = args->start;
558
7.38k
            if (nargs && strncmp(start, "0", args->end - start)) {
559
4.79k
                skip_spaces(&start);
560
4.79k
                state->family.str = start;
561
4.79k
                state->family.len = args->end - start;
562
4.79k
            } else {
563
2.59k
                state->family.str = state->style->FontName;
564
2.59k
                state->family.len = strlen(state->style->FontName);
565
2.59k
            }
566
7.38k
            ass_update_font(state);
567
389k
        } else if (tag("alpha")) {
568
3.90k
            int i;
569
3.90k
            if (nargs) {
570
2.84k
                int32_t a = parse_alpha_tag(args->start);
571
14.2k
                for (i = 0; i < 4; ++i)
572
11.3k
                    change_alpha(&state->c[i], a, pwr);
573
2.84k
            } else {
574
1.06k
                change_alpha(&state->c[0],
575
1.06k
                             _a(state->style->PrimaryColour), 1);
576
1.06k
                change_alpha(&state->c[1],
577
1.06k
                             _a(state->style->SecondaryColour), 1);
578
1.06k
                change_alpha(&state->c[2],
579
1.06k
                             _a(state->style->OutlineColour), 1);
580
1.06k
                change_alpha(&state->c[3],
581
1.06k
                             _a(state->style->BackColour), 1);
582
1.06k
            }
583
            // FIXME: simplify
584
385k
        } else if (tag("an")) {
585
8.57k
            int32_t val = argtoi32(*args);
586
8.57k
            if ((state->parsed_tags & PARSED_A) == 0) {
587
6.11k
                if (val >= 1 && val <= 9)
588
2.43k
                    state->alignment = numpad2align(val);
589
3.67k
                else
590
3.67k
                    state->alignment =
591
3.67k
                        state->style->Alignment;
592
6.11k
                state->parsed_tags |= PARSED_A;
593
6.11k
            }
594
377k
        } else if (tag("a")) {
595
13.2k
            int32_t val = argtoi32(*args);
596
13.2k
            if ((state->parsed_tags & PARSED_A) == 0) {
597
8.19k
                if (val >= 1 && val <= 11)
598
                    // take care of a vsfilter quirk:
599
                    // handle illegal \a8 and \a4 like \a5
600
2.34k
                    state->alignment = ((val & 3) == 0) ? 5 : val;
601
5.84k
                else
602
5.84k
                    state->alignment =
603
5.84k
                        state->style->Alignment;
604
8.19k
                state->parsed_tags |= PARSED_A;
605
8.19k
            }
606
363k
        } else if (complex_tag("pos")) {
607
2.83k
            double v1, v2;
608
2.83k
            if (nargs == 2) {
609
1.84k
                v1 = argtod(args[0]);
610
1.84k
                v2 = argtod(args[1]);
611
1.84k
            } else
612
985
                continue;
613
1.84k
            if (state->evt_type & EVENT_POSITIONED) {
614
736
                ass_msg(render_priv->library, MSGL_V, "Subtitle has a new \\pos "
615
736
                       "after \\move or \\pos, ignoring");
616
1.11k
            } else {
617
1.11k
                state->evt_type |= EVENT_POSITIONED;
618
1.11k
                state->detect_collisions = 0;
619
1.11k
                state->pos_x = v1;
620
1.11k
                state->pos_y = v2;
621
1.11k
            }
622
361k
        } else if (complex_tag("fade") || complex_tag("fad")) {
623
7.29k
            int32_t a1, a2, a3;
624
7.29k
            int32_t t1, t2, t3, t4;
625
7.29k
            if (nargs == 2) {
626
                // 2-argument version (\fad, according to specs)
627
3.47k
                a1 = 0xFF;
628
3.47k
                a2 = 0;
629
3.47k
                a3 = 0xFF;
630
3.47k
                t1 = -1;
631
3.47k
                t2 = argtoi32(args[0]);
632
3.47k
                t3 = argtoi32(args[1]);
633
3.47k
                t4 = -1;
634
3.81k
            } else if (nargs == 7) {
635
                // 7-argument version (\fade)
636
1.99k
                a1 = argtoi32(args[0]);
637
1.99k
                a2 = argtoi32(args[1]);
638
1.99k
                a3 = argtoi32(args[2]);
639
1.99k
                t1 = argtoi32(args[3]);
640
1.99k
                t2 = argtoi32(args[4]);
641
1.99k
                t3 = argtoi32(args[5]);
642
1.99k
                t4 = argtoi32(args[6]);
643
1.99k
            } else
644
1.82k
                continue;
645
5.46k
            if (t1 == -1 && t4 == -1) {
646
3.47k
                t1 = 0;
647
3.47k
                t4 = state->event->Duration;
648
3.47k
                t3 = (uint32_t) t4 - t3;
649
3.47k
            }
650
5.46k
            if ((state->parsed_tags & PARSED_FADE) == 0) {
651
4.56k
                state->fade =
652
4.56k
                    interpolate_alpha(render_priv->time -
653
4.56k
                            state->event->Start, t1, t2,
654
4.56k
                            t3, t4, a1, a2, a3);
655
4.56k
                state->parsed_tags |= PARSED_FADE;
656
4.56k
            }
657
353k
        } else if (complex_tag("org")) {
658
3.19k
            double v1, v2;
659
3.19k
            if (nargs == 2) {
660
1.97k
                v1 = argtod(args[0]);
661
1.97k
                v2 = argtod(args[1]);
662
1.97k
            } else
663
1.22k
                continue;
664
1.97k
            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
350k
        } else if (complex_tag("t")) {
671
55.5k
            double accel;
672
55.5k
            int cnt = nargs - 1;
673
55.5k
            int32_t t1, t2, t, delta_t;
674
55.5k
            double k;
675
            // VSFilter compatibility (because we can): parse the
676
            // timestamps differently depending on argument count.
677
55.5k
            if (cnt == 3) {
678
1.75k
                t1 = argtoi32(args[0]);
679
1.75k
                t2 = argtoi32(args[1]);
680
1.75k
                accel = argtod(args[2]);
681
53.7k
            } else if (cnt == 2) {
682
9.50k
                t1 = dtoi32(argtod(args[0]));
683
9.50k
                t2 = dtoi32(argtod(args[1]));
684
9.50k
                accel = 1.;
685
44.2k
            } else if (cnt == 1) {
686
3.30k
                t1 = 0;
687
3.30k
                t2 = 0;
688
3.30k
                accel = argtod(args[0]);
689
40.9k
            } else {
690
40.9k
                t1 = 0;
691
40.9k
                t2 = 0;
692
40.9k
                accel = 1.;
693
40.9k
            }
694
55.5k
            state->detect_collisions = 0;
695
55.5k
            if (t2 == 0)
696
47.4k
                t2 = state->event->Duration;
697
55.5k
            delta_t = (uint32_t) t2 - t1;
698
55.5k
            t = render_priv->time - state->event->Start;        // FIXME: move to render_context
699
55.5k
            if (t < t1)
700
1.90k
                k = 0.;
701
53.6k
            else if (t >= t2)
702
5.95k
                k = 1.;
703
47.6k
            else {
704
47.6k
                assert(delta_t != 0.);
705
47.6k
                k = pow((double) (int32_t) ((uint32_t) t - t1) / delta_t, accel);
706
47.6k
            }
707
55.5k
            if (nested)
708
11.8k
                pwr = k;
709
55.5k
            if (cnt < 0 || cnt > 3)
710
12.9k
                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
42.5k
            if (!has_backslash_arg)
714
5.93k
                continue;
715
36.6k
            p = args[cnt].start;
716
36.6k
            if (args[cnt].end < end) {
717
23.8k
                assert(!nested);
718
23.8k
                p = ass_parse_tags(state, p, args[cnt].end, k, true);
719
23.8k
            } else {
720
12.7k
                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
12.7k
                pwr = k;
726
12.7k
                nested = true;
727
12.7k
                q = p;
728
12.7k
            }
729
295k
        } else if (complex_tag("clip")) {
730
5.51k
            if (nargs == 4) {
731
711
                int32_t x0, y0, x1, y1;
732
711
                x0 = argtoi32(args[0]);
733
711
                y0 = argtoi32(args[1]);
734
711
                x1 = argtoi32(args[2]);
735
711
                y1 = argtoi32(args[3]);
736
711
                state->clip_x0 =
737
711
                    state->clip_x0 * (1 - pwr) + x0 * pwr;
738
711
                state->clip_x1 =
739
711
                    state->clip_x1 * (1 - pwr) + x1 * pwr;
740
711
                state->clip_y0 =
741
711
                    state->clip_y0 * (1 - pwr) + y0 * pwr;
742
711
                state->clip_y1 =
743
711
                    state->clip_y1 * (1 - pwr) + y1 * pwr;
744
711
                state->clip_mode = 0;
745
4.80k
            } else if (!state->clip_drawing_text.str) {
746
4.06k
                if (parse_vector_clip(state, args, nargs))
747
2.89k
                    state->clip_drawing_mode = 0;
748
4.06k
            }
749
289k
        } else if (tag("c") || tag("1c")) {
750
13.2k
            if (nargs) {
751
9.21k
                uint32_t val = parse_color_tag(args->start);
752
9.21k
                change_color(&state->c[0], val, pwr);
753
9.21k
            } else
754
4.02k
                change_color(&state->c[0],
755
4.02k
                             state->style->PrimaryColour, 1);
756
276k
        } else if (tag("2c")) {
757
4.29k
            if (nargs) {
758
2.82k
                uint32_t val = parse_color_tag(args->start);
759
2.82k
                change_color(&state->c[1], val, pwr);
760
2.82k
            } else
761
1.46k
                change_color(&state->c[1],
762
1.46k
                             state->style->SecondaryColour, 1);
763
271k
        } else if (tag("3c")) {
764
3.21k
            if (nargs) {
765
1.62k
                uint32_t val = parse_color_tag(args->start);
766
1.62k
                change_color(&state->c[2], val, pwr);
767
1.62k
            } else
768
1.59k
                change_color(&state->c[2],
769
1.59k
                             state->style->OutlineColour, 1);
770
268k
        } else if (tag("4c")) {
771
5.10k
            if (nargs) {
772
2.85k
                uint32_t val = parse_color_tag(args->start);
773
2.85k
                change_color(&state->c[3], val, pwr);
774
2.85k
            } else
775
2.25k
                change_color(&state->c[3],
776
2.25k
                             state->style->BackColour, 1);
777
263k
        } else if (tag("1a")) {
778
4.58k
            if (nargs) {
779
3.11k
                uint32_t val = parse_alpha_tag(args->start);
780
3.11k
                change_alpha(&state->c[0], val, pwr);
781
3.11k
            } else
782
1.46k
                change_alpha(&state->c[0],
783
1.46k
                             _a(state->style->PrimaryColour), 1);
784
259k
        } else if (tag("2a")) {
785
5.23k
            if (nargs) {
786
3.29k
                uint32_t val = parse_alpha_tag(args->start);
787
3.29k
                change_alpha(&state->c[1], val, pwr);
788
3.29k
            } else
789
1.94k
                change_alpha(&state->c[1],
790
1.94k
                             _a(state->style->SecondaryColour), 1);
791
253k
        } else if (tag("3a")) {
792
4.67k
            if (nargs) {
793
3.16k
                uint32_t val = parse_alpha_tag(args->start);
794
3.16k
                change_alpha(&state->c[2], val, pwr);
795
3.16k
            } else
796
1.50k
                change_alpha(&state->c[2],
797
1.50k
                             _a(state->style->OutlineColour), 1);
798
249k
        } else if (tag("4a")) {
799
3.78k
            if (nargs) {
800
2.51k
                uint32_t val = parse_alpha_tag(args->start);
801
2.51k
                change_alpha(&state->c[3], val, pwr);
802
2.51k
            } else
803
1.27k
                change_alpha(&state->c[3],
804
1.27k
                             _a(state->style->BackColour), 1);
805
245k
        } else if (tag("r")) {
806
10.4k
            if (nargs) {
807
6.58k
                int len = args->end - args->start;
808
6.58k
                ass_reset_render_context(state,
809
6.58k
                        lookup_style_strict(render_priv->track, args->start, len));
810
6.58k
            } else
811
3.84k
                ass_reset_render_context(state, NULL);
812
234k
        } else if (tag("be")) {
813
5.19k
            double dval;
814
5.19k
            if (nargs) {
815
3.31k
                int32_t val;
816
3.31k
                dval = argtod(*args);
817
                // VSFilter always adds +0.5, even if the value is negative
818
3.31k
                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.31k
                val = (val < 0) ? 0 : val;
821
3.31k
                val = (val > MAX_BE) ? MAX_BE : val;
822
3.31k
                state->be = val;
823
3.31k
            } else
824
1.87k
                state->be = 0;
825
229k
        } else if (tag("b")) {
826
22.5k
            int32_t val = argtoi32(*args);
827
22.5k
            if (!nargs || !(val == 0 || val == 1 || val >= 100))
828
6.82k
                val = state->style->Bold;
829
22.5k
            state->bold = val;
830
22.5k
            ass_update_font(state);
831
207k
        } else if (tag("i")) {
832
36.2k
            int32_t val = argtoi32(*args);
833
36.2k
            if (!nargs || !(val == 0 || val == 1))
834
24.3k
                val = state->style->Italic;
835
36.2k
            state->italic = val;
836
36.2k
            ass_update_font(state);
837
170k
        } else if (tag("kt")) {
838
            // v4++
839
6.23k
            double val = 0;
840
6.23k
            if (nargs)
841
4.36k
                val = argtod(*args) * 10;
842
6.23k
            state->effect_skip_timing = dtoi32(val);
843
6.23k
            state->effect_timing = 0;
844
6.23k
            state->reset_effect = true;
845
164k
        } else if (tag("kf") || tag("K")) {
846
10.0k
            double val = 100;
847
10.0k
            if (nargs)
848
7.95k
                val = argtod(*args);
849
10.0k
            state->effect_type = EF_KARAOKE_KF;
850
10.0k
            state->effect_skip_timing +=
851
10.0k
                    (uint32_t) state->effect_timing;
852
10.0k
            state->effect_timing = dtoi32(val * 10);
853
154k
        } else if (tag("ko")) {
854
33.8k
            double val = 100;
855
33.8k
            if (nargs)
856
32.5k
                val = argtod(*args);
857
33.8k
            state->effect_type = EF_KARAOKE_KO;
858
33.8k
            state->effect_skip_timing +=
859
33.8k
                    (uint32_t) state->effect_timing;
860
33.8k
            state->effect_timing = dtoi32(val * 10);
861
120k
        } else if (tag("k")) {
862
13.6k
            double val = 100;
863
13.6k
            if (nargs)
864
10.2k
                val = argtod(*args);
865
13.6k
            state->effect_type = EF_KARAOKE;
866
13.6k
            state->effect_skip_timing +=
867
13.6k
                    (uint32_t) state->effect_timing;
868
13.6k
            state->effect_timing = dtoi32(val * 10);
869
107k
        } else if (tag("shad")) {
870
4.89k
            double val, xval, yval;
871
4.89k
            if (nargs) {
872
2.41k
                val = argtod(*args);
873
2.41k
                xval = state->shadow_x * (1 - pwr) + val * pwr;
874
2.41k
                yval = state->shadow_y * (1 - pwr) + val * pwr;
875
                // VSFilter compatibility: clip for \shad but not for \[xy]shad
876
2.41k
                xval = (xval < 0) ? 0 : xval;
877
2.41k
                yval = (yval < 0) ? 0 : yval;
878
2.41k
            } else
879
2.47k
                xval = yval = state->style->Shadow;
880
4.89k
            state->shadow_x = xval;
881
4.89k
            state->shadow_y = yval;
882
102k
        } else if (tag("s")) {
883
7.92k
            int32_t val = argtoi32(*args);
884
7.92k
            if (!nargs || !(val == 0 || val == 1))
885
3.31k
                val = state->style->StrikeOut;
886
7.92k
            if (val)
887
847
                state->flags |= DECO_STRIKETHROUGH;
888
7.07k
            else
889
7.07k
                state->flags &= ~DECO_STRIKETHROUGH;
890
94.2k
        } else if (tag("u")) {
891
8.33k
            int32_t val = argtoi32(*args);
892
8.33k
            if (!nargs || !(val == 0 || val == 1))
893
4.52k
                val = state->style->Underline;
894
8.33k
            if (val)
895
1.06k
                state->flags |= DECO_UNDERLINE;
896
7.27k
            else
897
7.27k
                state->flags &= ~DECO_UNDERLINE;
898
85.9k
        } else if (tag("pbo")) {
899
3.63k
            double val = argtod(*args);
900
3.63k
            state->pbo = val;
901
82.3k
        } else if (tag("p")) {
902
8.67k
            int32_t val = argtoi32(*args);
903
8.67k
            val = (val < 0) ? 0 : val;
904
8.67k
            state->drawing_scale = val;
905
73.6k
        } else if (tag("q")) {
906
12.3k
            int32_t val = argtoi32(*args);
907
12.3k
            if (!nargs || !(val >= 0 && val <= 3))
908
8.92k
                val = render_priv->track->WrapStyle;
909
12.3k
            state->wrap_style = val;
910
61.3k
        } else if (tag("fe")) {
911
5.06k
            int32_t val;
912
5.06k
            if (nargs)
913
3.05k
                val = argtoi32(*args);
914
2.01k
            else
915
2.01k
                val = state->style->Encoding;
916
5.06k
            state->font_encoding = val;
917
5.06k
        }
918
489k
    }
919
920
205k
    return p;
921
205k
}
922
923
void ass_apply_transition_effects(RenderContext *state)
924
375k
{
925
375k
    ASS_Renderer *render_priv = state->renderer;
926
375k
    int v[4];
927
375k
    int cnt;
928
375k
    ASS_Event *event = state->event;
929
375k
    char *p = event->Effect;
930
931
375k
    if (!p || !*p)
932
297k
        return;
933
934
77.8k
    cnt = 0;
935
100k
    while (cnt < 4 && (p = strchr(p, ';'))) {
936
23.0k
        v[cnt++] = atoi(++p);
937
23.0k
    }
938
939
77.8k
    ASS_Vector layout_res = ass_layout_res(render_priv);
940
77.8k
    if (strncmp(event->Effect, "Banner;", 7) == 0) {
941
4.60k
        double delay;
942
4.60k
        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
4.60k
        if (cnt >= 2 && v[1])   // left-to-right
948
2.09k
            state->scroll_direction = SCROLL_LR;
949
2.50k
        else                    // right-to-left
950
2.50k
            state->scroll_direction = SCROLL_RL;
951
952
4.60k
        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
4.60k
        double scale_x = ((double) layout_res.x) / render_priv->track->PlayResX;
966
4.60k
        delay = ((int) FFMAX(delay / scale_x, 1)) * scale_x;
967
4.60k
        state->scroll_shift =
968
4.60k
            (render_priv->time - event->Start) / delay;
969
4.60k
        state->evt_type |= EVENT_HSCROLL;
970
4.60k
        state->detect_collisions = 0;
971
4.60k
        state->wrap_style = 2;
972
4.60k
        return;
973
4.60k
    }
974
975
73.2k
    if (strncmp(event->Effect, "Scroll up;", 10) == 0) {
976
3.30k
        state->scroll_direction = SCROLL_BT;
977
69.9k
    } else if (strncmp(event->Effect, "Scroll down;", 12) == 0) {
978
734
        state->scroll_direction = SCROLL_TB;
979
69.1k
    } else {
980
69.1k
        ass_msg(render_priv->library, MSGL_DBG2,
981
69.1k
                "Unknown transition effect: '%s'", event->Effect);
982
69.1k
        return;
983
69.1k
    }
984
    // parse scroll up/down parameters
985
4.04k
    {
986
4.04k
        double delay;
987
4.04k
        int y0, y1;
988
4.04k
        if (cnt < 3) {
989
1.34k
            ass_msg(render_priv->library, MSGL_V,
990
1.34k
                    "Error parsing effect: '%s'", event->Effect);
991
1.34k
            return;
992
1.34k
        }
993
2.69k
        delay = v[2];
994
        // See explanation for Banner
995
2.69k
        double scale_y = ((double) layout_res.y) / render_priv->track->PlayResY;
996
2.69k
        delay = ((int) FFMAX(delay / scale_y, 1)) * scale_y;
997
2.69k
        state->scroll_shift =
998
2.69k
            (render_priv->time - event->Start) / delay;
999
2.69k
        if (v[0] < v[1]) {
1000
1.00k
            y0 = v[0];
1001
1.00k
            y1 = v[1];
1002
1.69k
        } else {
1003
1.69k
            y0 = v[1];
1004
1.69k
            y1 = v[0];
1005
1.69k
        }
1006
2.69k
        state->scroll_y0 = y0;
1007
2.69k
        state->scroll_y1 = y1;
1008
2.69k
        state->evt_type |= EVENT_VSCROLL;
1009
2.69k
        state->detect_collisions = 0;
1010
2.69k
    }
1011
1012
2.69k
}
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
108k
{
1027
108k
    TextInfo *text_info = &state->text_info;
1028
108k
    long long tm_current = state->renderer->time - state->event->Start;
1029
1030
108k
    int32_t timing = 0, skip_timing = 0;
1031
108k
    Effect effect_type = EF_NONE;
1032
108k
    GlyphInfo *last_boundary = NULL;
1033
108k
    bool has_reset = false;
1034
948k
    for (int i = 0; i <= text_info->length; i++) {
1035
839k
        if (i < text_info->length &&
1036
730k
            !text_info->glyphs[i].starts_new_run) {
1037
1038
618k
            if (text_info->glyphs[i].reset_effect) {
1039
49
                has_reset = true;
1040
49
                skip_timing = 0;
1041
49
            }
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
618k
            skip_timing += (uint32_t) text_info->glyphs[i].effect_skip_timing;
1048
618k
            continue;
1049
618k
        }
1050
1051
221k
        GlyphInfo *start = last_boundary;
1052
221k
        GlyphInfo *end = text_info->glyphs + i;
1053
221k
        last_boundary = end;
1054
221k
        if (!start)
1055
108k
            continue;
1056
1057
112k
        if (start->effect_type != EF_NONE)
1058
6.86k
            effect_type = start->effect_type;
1059
112k
        if (effect_type == EF_NONE)
1060
105k
            continue;
1061
1062
6.87k
        if (start->reset_effect)
1063
49
            timing = 0;
1064
1065
6.87k
        long long tm_start = timing + start->effect_skip_timing;
1066
6.87k
        long long tm_end = tm_start + start->effect_timing;
1067
6.87k
        timing = !has_reset * tm_end + skip_timing;
1068
6.87k
        skip_timing = 0;
1069
6.87k
        has_reset = false;
1070
1071
6.87k
        if (effect_type != EF_KARAOKE_KF)
1072
4.30k
            tm_end = tm_start;
1073
1074
6.87k
        int x;
1075
6.87k
        if (tm_current < tm_start)
1076
372
            x = -100000000;
1077
6.50k
        else if (tm_current >= tm_end)
1078
6.15k
            x = 100000000;
1079
354
        else {
1080
354
            GlyphInfo *first_visible = start, *last_visible = end - 1;
1081
354
            while (first_visible < last_visible && first_visible->skip)
1082
0
                ++first_visible;
1083
354
            while (first_visible < last_visible && last_visible->skip)
1084
0
                --last_visible;
1085
1086
354
            int x_start = first_visible->pos.x;
1087
354
            int x_end = last_visible->pos.x + last_visible->advance.x;
1088
354
            double dt = (double) (tm_current - tm_start) / (tm_end - tm_start);
1089
354
            double frz = fmod(start->frz, 360);
1090
354
            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
354
            x = x_start + ass_lrint((x_end - x_start) * dt);
1100
354
        }
1101
1102
29.5k
        for (GlyphInfo *info = start; info < end; info++) {
1103
22.6k
            info->effect_type = effect_type;
1104
22.6k
            info->effect_timing = x - info->pos.x;
1105
22.6k
        }
1106
6.87k
    }
1107
108k
}
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
727k
{
1118
727k
    char *p = *str;
1119
727k
    unsigned chr;
1120
727k
    if (*p == '\t') {
1121
2.19k
        ++p;
1122
2.19k
        *str = p;
1123
2.19k
        return ' ';
1124
2.19k
    }
1125
725k
    if (*p == '\\') {
1126
52.3k
        if ((p[1] == 'N') || ((p[1] == 'n') &&
1127
1.69k
                              (state->wrap_style == 2))) {
1128
1.48k
            p += 2;
1129
1.48k
            *str = p;
1130
1.48k
            return '\n';
1131
50.8k
        } else if (p[1] == 'n') {
1132
971
            p += 2;
1133
971
            *str = p;
1134
971
            return ' ';
1135
49.8k
        } else if (p[1] == 'h') {
1136
2.11k
            p += 2;
1137
2.11k
            *str = p;
1138
2.11k
            return NBSP;
1139
47.7k
        } else if (p[1] == '{') {
1140
881
            p += 2;
1141
881
            *str = p;
1142
881
            return '{';
1143
46.8k
        } else if (p[1] == '}') {
1144
1.46k
            p += 2;
1145
1.46k
            *str = p;
1146
1.46k
            return '}';
1147
1.46k
        }
1148
52.3k
    }
1149
718k
    chr = ass_utf8_get_char((char **) &p);
1150
718k
    *str = p;
1151
718k
    return chr;
1152
725k
}
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
368k
{
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
1.08M
    while (*str) {
1161
742k
        if (str[0] == '\\' && str[1] != '\0') {
1162
14.6k
            str += 2;
1163
728k
        } else if (str[0] == '{') {
1164
201k
            str++;
1165
4.74M
            while (*str && *str != '}') {
1166
4.56M
                if (*str == '\\') {
1167
561k
                    char *p = str + 1;
1168
561k
                    if (mystrcmp(&p, "pos") || mystrcmp(&p, "move") ||
1169
548k
                        mystrcmp(&p, "clip") || mystrcmp(&p, "iclip") ||
1170
541k
                        mystrcmp(&p, "org") || mystrcmp(&p, "pbo") ||
1171
537k
                        mystrcmp(&p, "p"))
1172
29.5k
                        return 1;
1173
561k
                }
1174
4.53M
                str++;
1175
4.53M
            }
1176
526k
        } else {
1177
526k
            str++;
1178
526k
        }
1179
742k
    }
1180
338k
    return 0;
1181
368k
}