Mercurial > mplayer.hg
view libass/ass_parse.c @ 34161:64a0c61c6f18
Add a vd_ffmpeg option to make the decoder discard frames until the first
keyframe after startup or seek.
Restore the previous default behaviour since in actual playback scenarios
long delays are far more annoying than seeing a few broken frames.
author | reimar |
---|---|
date | Mon, 24 Oct 2011 17:44:13 +0000 |
parents | 88eebbbbd6a0 |
children | 6e7f60f6f9d4 |
line wrap: on
line source
/* * Copyright (C) 2009 Grigori Goronzy <greg@geekmind.org> * * This file is part of libass. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "config.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #include "ass_render.h" #include "ass_parse.h" #define MAX_BE 127 #define NBSP 0xa0 // unicode non-breaking space character #define skip_to(x) while ((*p != (x)) && (*p != '}') && (*p != 0)) { ++p;} #define skip(x) if (*p == (x)) ++p; else { return p; } #define skipopt(x) if (*p == (x)) { ++p; } /** * \brief Check if starting part of (*p) matches sample. * If true, shift p to the first symbol after the matching part. */ static inline int mystrcmp(char **p, const char *sample) { int len = strlen(sample); if (strncmp(*p, sample, len) == 0) { (*p) += len; return 1; } else return 0; } static void change_font_size(ASS_Renderer *render_priv, double sz) { double size = sz * render_priv->font_scale; if (size < 1) size = 1; else if (size > render_priv->height * 2) size = render_priv->height * 2; ass_font_set_size(render_priv->state.font, size); render_priv->state.font_size = sz; } /** * \brief Change current font, using setting from render_priv->state. */ void update_font(ASS_Renderer *render_priv) { unsigned val; ASS_FontDesc desc; desc.treat_family_as_pattern = render_priv->state.treat_family_as_pattern; if (render_priv->state.family[0] == '@') { desc.vertical = 1; desc.family = strdup(render_priv->state.family + 1); } else { desc.vertical = 0; desc.family = strdup(render_priv->state.family); } val = render_priv->state.bold; // 0 = normal, 1 = bold, >1 = exact weight if (val == 1 || val == -1) val = 200; // bold else if (val <= 0) val = 80; // normal desc.bold = val; val = render_priv->state.italic; if (val == 1 || val == -1) val = 110; // italic else if (val <= 0) val = 0; // normal desc.italic = val; render_priv->state.font = ass_font_new(render_priv->cache.font_cache, render_priv->library, render_priv->ftlibrary, render_priv->fontconfig_priv, &desc); free(desc.family); if (render_priv->state.font) change_font_size(render_priv, render_priv->state.font_size); } /** * \brief Change border width * negative value resets border to style value */ void change_border(ASS_Renderer *render_priv, double border_x, double border_y) { int bord; if (!render_priv->state.font) return; if (border_x < 0 && border_y < 0) { if (render_priv->state.style->BorderStyle == 1 || render_priv->state.style->BorderStyle == 3) border_x = border_y = render_priv->state.style->Outline; else border_x = border_y = 1.; } render_priv->state.border_x = border_x; render_priv->state.border_y = border_y; bord = 64 * border_x * render_priv->border_scale; if (bord > 0 && border_x == border_y) { if (!render_priv->state.stroker) { int error; error = FT_Stroker_New(render_priv->ftlibrary, &render_priv->state.stroker); if (error) { ass_msg(render_priv->library, MSGL_V, "failed to get stroker"); render_priv->state.stroker = 0; } } if (render_priv->state.stroker) FT_Stroker_Set(render_priv->state.stroker, bord, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); } else { FT_Stroker_Done(render_priv->state.stroker); render_priv->state.stroker = 0; } } /** * \brief Calculate a weighted average of two colors * calculates c1*(1-a) + c2*a, but separately for each component except alpha */ static void change_color(uint32_t *var, uint32_t new, double pwr) { (*var) = ((uint32_t) (_r(*var) * (1 - pwr) + _r(new) * pwr) << 24) + ((uint32_t) (_g(*var) * (1 - pwr) + _g(new) * pwr) << 16) + ((uint32_t) (_b(*var) * (1 - pwr) + _b(new) * pwr) << 8) + _a(*var); } // like change_color, but for alpha component only inline void change_alpha(uint32_t *var, uint32_t new, double pwr) { *var = (_r(*var) << 24) + (_g(*var) << 16) + (_b(*var) << 8) + (uint32_t) (_a(*var) * (1 - pwr) + _a(new) * pwr); } /** * \brief Multiply two alpha values * \param a first value * \param b second value * \return result of multiplication * Parameters and result are limited by 0xFF. */ inline uint32_t mult_alpha(uint32_t a, uint32_t b) { return 0xFF - (0xFF - a) * (0xFF - b) / 0xFF; } /** * \brief Calculate alpha value by piecewise linear function * Used for \fad, \fade implementation. */ static unsigned interpolate_alpha(long long now, long long t1, long long t2, long long t3, long long t4, unsigned a1, unsigned a2, unsigned a3) { unsigned a; double cf; if (now <= t1) { a = a1; } else if (now >= t4) { a = a3; } else if (now < t2) { // and > t1 cf = ((double) (now - t1)) / (t2 - t1); a = a1 * (1 - cf) + a2 * cf; } else if (now > t3) { cf = ((double) (now - t3)) / (t4 - t3); a = a2 * (1 - cf) + a3 * cf; } else { // t2 <= now <= t3 a = a2; } return a; } /** * Parse a vector clip into an outline, using the proper scaling * parameters. Translate it to correct for screen borders, if needed. */ static char *parse_vector_clip(ASS_Renderer *render_priv, char *p) { int scale = 1; int res = 0; ASS_Drawing *drawing = render_priv->state.clip_drawing; if (drawing && drawing->glyph) FT_Done_Glyph((FT_Glyph) drawing->glyph); ass_drawing_free(drawing); render_priv->state.clip_drawing = ass_drawing_new( render_priv->fontconfig_priv, render_priv->state.font, render_priv->ftlibrary); drawing = render_priv->state.clip_drawing; skipopt('('); res = mystrtoi(&p, &scale); skipopt(',') if (!res) scale = 1; drawing->scale = scale; drawing->scale_x = render_priv->font_scale_x * render_priv->font_scale; drawing->scale_y = render_priv->font_scale; while (*p != ')' && *p != '}' && p != 0) ass_drawing_add_char(drawing, *p++); skipopt(')'); return p; } /** * \brief Parse style override tag. * \param p string to parse * \param pwr multiplier for some tag effects (comes from \t tags) */ static char *parse_tag(ASS_Renderer *render_priv, char *p, double pwr) { skip_to('\\'); skip('\\'); if ((*p == '}') || (*p == 0)) return p; // New tags introduced in vsfilter 2.39 if (mystrcmp(&p, "xbord")) { double val; if (mystrtod(&p, &val)) val = render_priv->state.border_x * (1 - pwr) + val * pwr; else val = -1.; change_border(render_priv, val, render_priv->state.border_y); } else if (mystrcmp(&p, "ybord")) { double val; if (mystrtod(&p, &val)) val = render_priv->state.border_y * (1 - pwr) + val * pwr; else val = -1.; change_border(render_priv, render_priv->state.border_x, val); } else if (mystrcmp(&p, "xshad")) { double val; if (mystrtod(&p, &val)) val = render_priv->state.shadow_x * (1 - pwr) + val * pwr; else val = 0.; render_priv->state.shadow_x = val; } else if (mystrcmp(&p, "yshad")) { double val; if (mystrtod(&p, &val)) val = render_priv->state.shadow_y * (1 - pwr) + val * pwr; else val = 0.; render_priv->state.shadow_y = val; } else if (mystrcmp(&p, "fax")) { double val; if (mystrtod(&p, &val)) render_priv->state.fax = val * pwr + render_priv->state.fax * (1 - pwr); else render_priv->state.fax = 0.; } else if (mystrcmp(&p, "fay")) { double val; if (mystrtod(&p, &val)) render_priv->state.fay = val * pwr + render_priv->state.fay * (1 - pwr); else render_priv->state.fay = 0.; } else if (mystrcmp(&p, "iclip")) { int x0, y0, x1, y1; int res = 1; char *start = p; skipopt('('); res &= mystrtoi(&p, &x0); skipopt(','); res &= mystrtoi(&p, &y0); skipopt(','); res &= mystrtoi(&p, &x1); skipopt(','); res &= mystrtoi(&p, &y1); skipopt(')'); if (res) { render_priv->state.clip_x0 = render_priv->state.clip_x0 * (1 - pwr) + x0 * pwr; render_priv->state.clip_x1 = render_priv->state.clip_x1 * (1 - pwr) + x1 * pwr; render_priv->state.clip_y0 = render_priv->state.clip_y0 * (1 - pwr) + y0 * pwr; render_priv->state.clip_y1 = render_priv->state.clip_y1 * (1 - pwr) + y1 * pwr; render_priv->state.clip_mode = 1; } else if (!render_priv->state.clip_drawing) { p = parse_vector_clip(render_priv, start); render_priv->state.clip_drawing_mode = 1; } else render_priv->state.clip_mode = 0; } else if (mystrcmp(&p, "blur")) { double val; if (mystrtod(&p, &val)) { val = render_priv->state.blur * (1 - pwr) + val * pwr; val = (val < 0) ? 0 : val; val = (val > BLUR_MAX_RADIUS) ? BLUR_MAX_RADIUS : val; render_priv->state.blur = val; } else render_priv->state.blur = 0.0; // ASS standard tags } else if (mystrcmp(&p, "fsc")) { char tp = *p++; double val; if (tp == 'x') { if (mystrtod(&p, &val)) { val /= 100; render_priv->state.scale_x = render_priv->state.scale_x * (1 - pwr) + val * pwr; } else render_priv->state.scale_x = render_priv->state.style->ScaleX; } else if (tp == 'y') { if (mystrtod(&p, &val)) { val /= 100; render_priv->state.scale_y = render_priv->state.scale_y * (1 - pwr) + val * pwr; } else render_priv->state.scale_y = render_priv->state.style->ScaleY; } } else if (mystrcmp(&p, "fsp")) { double val; if (mystrtod(&p, &val)) render_priv->state.hspacing = render_priv->state.hspacing * (1 - pwr) + val * pwr; else render_priv->state.hspacing = render_priv->state.style->Spacing; } else if (mystrcmp(&p, "fs+")) { double val; if (mystrtod(&p, &val)) { val = render_priv->state.font_size + pwr * val; } else val = render_priv->state.style->FontSize; if (render_priv->state.font) change_font_size(render_priv, val); } else if (mystrcmp(&p, "fs-")) { double val; if (mystrtod(&p, &val)) val = render_priv->state.font_size - pwr * val; else val = render_priv->state.style->FontSize; if (render_priv->state.font) change_font_size(render_priv, val); } else if (mystrcmp(&p, "fs")) { double val; if (mystrtod(&p, &val)) val = render_priv->state.font_size * (1 - pwr) + val * pwr; else val = render_priv->state.style->FontSize; if (render_priv->state.font) change_font_size(render_priv, val); } else if (mystrcmp(&p, "bord")) { double val; if (mystrtod(&p, &val)) { if (render_priv->state.border_x == render_priv->state.border_y) val = render_priv->state.border_x * (1 - pwr) + val * pwr; } else val = -1.; // reset to default change_border(render_priv, val, val); } else if (mystrcmp(&p, "move")) { double x1, x2, y1, y2; long long t1, t2, delta_t, t; double x, y; double k; skip('('); mystrtod(&p, &x1); skip(','); mystrtod(&p, &y1); skip(','); mystrtod(&p, &x2); skip(','); mystrtod(&p, &y2); if (*p == ',') { skip(','); mystrtoll(&p, &t1); skip(','); mystrtoll(&p, &t2); ass_msg(render_priv->library, MSGL_DBG2, "movement6: (%f, %f) -> (%f, %f), (%" PRId64 " .. %" PRId64 ")\n", x1, y1, x2, y2, (int64_t) t1, (int64_t) t2); } else { t1 = 0; t2 = render_priv->state.event->Duration; ass_msg(render_priv->library, MSGL_DBG2, "movement: (%f, %f) -> (%f, %f)", x1, y1, x2, y2); } skip(')'); delta_t = t2 - t1; t = render_priv->time - render_priv->state.event->Start; if (t < t1) k = 0.; else if (t > t2) k = 1.; else k = ((double) (t - t1)) / delta_t; x = k * (x2 - x1) + x1; y = k * (y2 - y1) + y1; if (render_priv->state.evt_type != EVENT_POSITIONED) { render_priv->state.pos_x = x; render_priv->state.pos_y = y; render_priv->state.detect_collisions = 0; render_priv->state.evt_type = EVENT_POSITIONED; } } else if (mystrcmp(&p, "frx")) { double val; if (mystrtod(&p, &val)) { val *= M_PI / 180; render_priv->state.frx = val * pwr + render_priv->state.frx * (1 - pwr); } else render_priv->state.frx = 0.; } else if (mystrcmp(&p, "fry")) { double val; if (mystrtod(&p, &val)) { val *= M_PI / 180; render_priv->state.fry = val * pwr + render_priv->state.fry * (1 - pwr); } else render_priv->state.fry = 0.; } else if (mystrcmp(&p, "frz") || mystrcmp(&p, "fr")) { double val; if (mystrtod(&p, &val)) { val *= M_PI / 180; render_priv->state.frz = val * pwr + render_priv->state.frz * (1 - pwr); } else render_priv->state.frz = M_PI * render_priv->state.style->Angle / 180.; } else if (mystrcmp(&p, "fn")) { char *start = p; char *family; skip_to('\\'); if (p > start) { family = malloc(p - start + 1); strncpy(family, start, p - start); family[p - start] = '\0'; } else family = strdup(render_priv->state.style->FontName); free(render_priv->state.family); render_priv->state.family = family; update_font(render_priv); } else if (mystrcmp(&p, "alpha")) { uint32_t val; int i; int hex = render_priv->track->track_type == TRACK_TYPE_ASS; if (strtocolor(render_priv->library, &p, &val, hex)) { unsigned char a = val >> 24; for (i = 0; i < 4; ++i) change_alpha(&render_priv->state.c[i], a, pwr); } else { change_alpha(&render_priv->state.c[0], render_priv->state.style->PrimaryColour, pwr); change_alpha(&render_priv->state.c[1], render_priv->state.style->SecondaryColour, pwr); change_alpha(&render_priv->state.c[2], render_priv->state.style->OutlineColour, pwr); change_alpha(&render_priv->state.c[3], render_priv->state.style->BackColour, pwr); } // FIXME: simplify } else if (mystrcmp(&p, "an")) { int val; if (mystrtoi(&p, &val) && val) { int v = (val - 1) / 3; // 0, 1 or 2 for vertical alignment ass_msg(render_priv->library, MSGL_DBG2, "an %d", val); if (v != 0) v = 3 - v; val = ((val - 1) % 3) + 1; // horizontal alignment val += v * 4; ass_msg(render_priv->library, MSGL_DBG2, "align %d", val); if ((render_priv->state.parsed_tags & PARSED_A) == 0) { render_priv->state.alignment = val; render_priv->state.parsed_tags |= PARSED_A; } } else render_priv->state.alignment = render_priv->state.style->Alignment; } else if (mystrcmp(&p, "a")) { int val; if (mystrtoi(&p, &val) && val) { if ((render_priv->state.parsed_tags & PARSED_A) == 0) { // take care of a vsfilter quirk: handle illegal \a8 like \a5 render_priv->state.alignment = (val == 8) ? 5 : val; render_priv->state.parsed_tags |= PARSED_A; } } else render_priv->state.alignment = render_priv->state.style->Alignment; } else if (mystrcmp(&p, "pos")) { double v1, v2; skip('('); mystrtod(&p, &v1); skip(','); mystrtod(&p, &v2); skip(')'); ass_msg(render_priv->library, MSGL_DBG2, "pos(%f, %f)", v1, v2); if (render_priv->state.evt_type == EVENT_POSITIONED) { ass_msg(render_priv->library, MSGL_V, "Subtitle has a new \\pos " "after \\move or \\pos, ignoring"); } else { render_priv->state.evt_type = EVENT_POSITIONED; render_priv->state.detect_collisions = 0; render_priv->state.pos_x = v1; render_priv->state.pos_y = v2; } } else if (mystrcmp(&p, "fad")) { int a1, a2, a3; long long t1, t2, t3, t4; if (*p == 'e') ++p; // either \fad or \fade skip('('); mystrtoi(&p, &a1); skip(','); mystrtoi(&p, &a2); if (*p == ')') { // 2-argument version (\fad, according to specs) // a1 and a2 are fade-in and fade-out durations t1 = 0; t4 = render_priv->state.event->Duration; t2 = a1; t3 = t4 - a2; a1 = 0xFF; a2 = 0; a3 = 0xFF; } else { // 6-argument version (\fade) // a1 and a2 (and a3) are opacity values skip(','); mystrtoi(&p, &a3); skip(','); mystrtoll(&p, &t1); skip(','); mystrtoll(&p, &t2); skip(','); mystrtoll(&p, &t3); skip(','); mystrtoll(&p, &t4); } skip(')'); if ((render_priv->state.parsed_tags & PARSED_FADE) == 0) { render_priv->state.fade = interpolate_alpha(render_priv->time - render_priv->state.event->Start, t1, t2, t3, t4, a1, a2, a3); render_priv->state.parsed_tags |= PARSED_FADE; } } else if (mystrcmp(&p, "org")) { int v1, v2; skip('('); mystrtoi(&p, &v1); skip(','); mystrtoi(&p, &v2); skip(')'); ass_msg(render_priv->library, MSGL_DBG2, "org(%d, %d)", v1, v2); if (!render_priv->state.have_origin) { render_priv->state.org_x = v1; render_priv->state.org_y = v2; render_priv->state.have_origin = 1; render_priv->state.detect_collisions = 0; } } else if (mystrcmp(&p, "t")) { double v[3]; int v1, v2; double v3; int cnt; long long t1, t2, t, delta_t; double k; skip('('); for (cnt = 0; cnt < 3; ++cnt) { if (*p == '\\') break; mystrtod(&p, &v[cnt]); skip(','); } if (cnt == 3) { v1 = v[0]; v2 = (v[1] < v1) ? render_priv->state.event->Duration : v[1]; v3 = v[2]; } else if (cnt == 2) { v1 = v[0]; v2 = (v[1] < v1) ? render_priv->state.event->Duration : v[1]; v3 = 1.; } else if (cnt == 1) { v1 = 0; v2 = render_priv->state.event->Duration; v3 = v[0]; } else { // cnt == 0 v1 = 0; v2 = render_priv->state.event->Duration; v3 = 1.; } render_priv->state.detect_collisions = 0; t1 = v1; t2 = v2; delta_t = v2 - v1; if (v3 < 0.) v3 = 0.; t = render_priv->time - render_priv->state.event->Start; // FIXME: move to render_context if (t <= t1) k = 0.; else if (t >= t2) k = 1.; else { assert(delta_t != 0.); k = pow(((double) (t - t1)) / delta_t, v3); } while (*p == '\\') p = parse_tag(render_priv, p, k); // maybe k*pwr ? no, specs forbid nested \t's skip_to(')'); // in case there is some unknown tag or a comment skip(')'); } else if (mystrcmp(&p, "clip")) { char *start = p; int x0, y0, x1, y1; int res = 1; skipopt('('); res &= mystrtoi(&p, &x0); skipopt(','); res &= mystrtoi(&p, &y0); skipopt(','); res &= mystrtoi(&p, &x1); skipopt(','); res &= mystrtoi(&p, &y1); skipopt(')'); if (res) { render_priv->state.clip_x0 = render_priv->state.clip_x0 * (1 - pwr) + x0 * pwr; render_priv->state.clip_x1 = render_priv->state.clip_x1 * (1 - pwr) + x1 * pwr; render_priv->state.clip_y0 = render_priv->state.clip_y0 * (1 - pwr) + y0 * pwr; render_priv->state.clip_y1 = render_priv->state.clip_y1 * (1 - pwr) + y1 * pwr; // Might be a vector clip } else if (!render_priv->state.clip_drawing) { p = parse_vector_clip(render_priv, start); render_priv->state.clip_drawing_mode = 0; } else { render_priv->state.clip_x0 = 0; render_priv->state.clip_y0 = 0; render_priv->state.clip_x1 = render_priv->track->PlayResX; render_priv->state.clip_y1 = render_priv->track->PlayResY; } } else if (mystrcmp(&p, "c")) { uint32_t val; int hex = render_priv->track->track_type == TRACK_TYPE_ASS; if (!strtocolor(render_priv->library, &p, &val, hex)) val = render_priv->state.style->PrimaryColour; ass_msg(render_priv->library, MSGL_DBG2, "color: %X", val); change_color(&render_priv->state.c[0], val, pwr); } else if ((*p >= '1') && (*p <= '4') && (++p) && (mystrcmp(&p, "c") || mystrcmp(&p, "a"))) { char n = *(p - 2); int cidx = n - '1'; char cmd = *(p - 1); uint32_t val; int hex = render_priv->track->track_type == TRACK_TYPE_ASS; assert((n >= '1') && (n <= '4')); if (!strtocolor(render_priv->library, &p, &val, hex)) switch (n) { case '1': val = render_priv->state.style->PrimaryColour; break; case '2': val = render_priv->state.style->SecondaryColour; break; case '3': val = render_priv->state.style->OutlineColour; break; case '4': val = render_priv->state.style->BackColour; break; default: val = 0; break; // impossible due to assert; avoid compilation warning } switch (cmd) { case 'c': change_color(render_priv->state.c + cidx, val, pwr); break; case 'a': change_alpha(render_priv->state.c + cidx, val >> 24, pwr); break; default: ass_msg(render_priv->library, MSGL_WARN, "Bad command: %c%c", n, cmd); break; } ass_msg(render_priv->library, MSGL_DBG2, "single c/a at %f: %c%c = %X", pwr, n, cmd, render_priv->state.c[cidx]); } else if (mystrcmp(&p, "r")) { reset_render_context(render_priv); } else if (mystrcmp(&p, "be")) { int val; if (mystrtoi(&p, &val)) { // Clamp to a safe upper limit, since high values need excessive CPU val = (val < 0) ? 0 : val; val = (val > MAX_BE) ? MAX_BE : val; render_priv->state.be = val; } else render_priv->state.be = 0; } else if (mystrcmp(&p, "b")) { int b; if (mystrtoi(&p, &b)) { if (pwr >= .5) render_priv->state.bold = b; } else render_priv->state.bold = render_priv->state.style->Bold; update_font(render_priv); } else if (mystrcmp(&p, "i")) { int i; if (mystrtoi(&p, &i)) { if (pwr >= .5) render_priv->state.italic = i; } else render_priv->state.italic = render_priv->state.style->Italic; update_font(render_priv); } else if (mystrcmp(&p, "kf") || mystrcmp(&p, "K")) { int val = 0; mystrtoi(&p, &val); render_priv->state.effect_type = EF_KARAOKE_KF; if (render_priv->state.effect_timing) render_priv->state.effect_skip_timing += render_priv->state.effect_timing; render_priv->state.effect_timing = val * 10; } else if (mystrcmp(&p, "ko")) { int val = 0; mystrtoi(&p, &val); render_priv->state.effect_type = EF_KARAOKE_KO; if (render_priv->state.effect_timing) render_priv->state.effect_skip_timing += render_priv->state.effect_timing; render_priv->state.effect_timing = val * 10; } else if (mystrcmp(&p, "k")) { int val = 0; mystrtoi(&p, &val); render_priv->state.effect_type = EF_KARAOKE; if (render_priv->state.effect_timing) render_priv->state.effect_skip_timing += render_priv->state.effect_timing; render_priv->state.effect_timing = val * 10; } else if (mystrcmp(&p, "shad")) { double val; if (mystrtod(&p, &val)) { if (render_priv->state.shadow_x == render_priv->state.shadow_y) val = render_priv->state.shadow_x * (1 - pwr) + val * pwr; } else val = 0.; render_priv->state.shadow_x = render_priv->state.shadow_y = val; } else if (mystrcmp(&p, "s")) { int val; if (mystrtoi(&p, &val) && val) render_priv->state.flags |= DECO_STRIKETHROUGH; else render_priv->state.flags &= ~DECO_STRIKETHROUGH; } else if (mystrcmp(&p, "u")) { int val; if (mystrtoi(&p, &val) && val) render_priv->state.flags |= DECO_UNDERLINE; else render_priv->state.flags &= ~DECO_UNDERLINE; } else if (mystrcmp(&p, "pbo")) { double val = 0; if (mystrtod(&p, &val)) render_priv->state.drawing->pbo = val; } else if (mystrcmp(&p, "p")) { int val; if (!mystrtoi(&p, &val)) val = 0; if (val) render_priv->state.drawing->scale = val; render_priv->state.drawing_mode = !!val; } else if (mystrcmp(&p, "q")) { int val; if (!mystrtoi(&p, &val)) val = render_priv->track->WrapStyle; render_priv->state.wrap_style = val; } return p; } void apply_transition_effects(ASS_Renderer *render_priv, ASS_Event *event) { int v[4]; int cnt; char *p = event->Effect; if (!p || !*p) return; cnt = 0; while (cnt < 4 && (p = strchr(p, ';'))) { v[cnt++] = atoi(++p); } if (strncmp(event->Effect, "Banner;", 7) == 0) { int delay; if (cnt < 1) { ass_msg(render_priv->library, MSGL_V, "Error parsing effect: '%s'", event->Effect); return; } if (cnt >= 2 && v[1] == 0) // right-to-left render_priv->state.scroll_direction = SCROLL_RL; else // left-to-right render_priv->state.scroll_direction = SCROLL_LR; delay = v[0]; if (delay == 0) delay = 1; // ? render_priv->state.scroll_shift = (render_priv->time - render_priv->state.event->Start) / delay; render_priv->state.evt_type = EVENT_HSCROLL; return; } if (strncmp(event->Effect, "Scroll up;", 10) == 0) { render_priv->state.scroll_direction = SCROLL_BT; } else if (strncmp(event->Effect, "Scroll down;", 12) == 0) { render_priv->state.scroll_direction = SCROLL_TB; } else { ass_msg(render_priv->library, MSGL_DBG2, "Unknown transition effect: '%s'", event->Effect); return; } // parse scroll up/down parameters { int delay; int y0, y1; if (cnt < 3) { ass_msg(render_priv->library, MSGL_V, "Error parsing effect: '%s'", event->Effect); return; } delay = v[2]; if (delay == 0) delay = 1; // ? render_priv->state.scroll_shift = (render_priv->time - render_priv->state.event->Start) / delay; if (v[0] < v[1]) { y0 = v[0]; y1 = v[1]; } else { y0 = v[1]; y1 = v[0]; } if (y1 == 0) y1 = render_priv->track->PlayResY; // y0=y1=0 means fullscreen scrolling render_priv->state.clip_y0 = y0; render_priv->state.clip_y1 = y1; render_priv->state.evt_type = EVENT_VSCROLL; render_priv->state.detect_collisions = 0; } } /** * \brief Get next ucs4 char from string, parsing and executing style overrides * \param str string pointer * \return ucs4 code of the next char * On return str points to the unparsed part of the string */ unsigned get_next_char(ASS_Renderer *render_priv, char **str) { char *p = *str; unsigned chr; if (*p == '{') { // '\0' goes here p++; while (1) { p = parse_tag(render_priv, p, 1.); if (*p == '}') { // end of tag p++; if (*p == '{') { p++; continue; } else break; } else if (*p != '\\') ass_msg(render_priv->library, MSGL_V, "Unable to parse: '%.30s'", p); if (*p == 0) break; } } if (*p == '\t') { ++p; *str = p; return ' '; } if (*p == '\\') { if ((p[1] == 'N') || ((p[1] == 'n') && (render_priv->state.wrap_style == 2))) { p += 2; *str = p; return '\n'; } else if (p[1] == 'n') { p += 2; *str = p; return ' '; } else if (p[1] == 'h') { p += 2; *str = p; return NBSP; } else if (p[1] == '{') { p += 2; *str = p; return '{'; } else if (p[1] == '}') { p += 2; *str = p; return '}'; } } chr = ass_utf8_get_char((char **) &p); *str = p; return chr; }