summaryrefslogtreecommitdiffstats
path: root/libass
diff options
context:
space:
mode:
authorOleg Oshmyan <chortos@inbox.lv>2015-05-23 00:06:40 +0300
committerOleg Oshmyan <chortos@inbox.lv>2015-05-25 00:16:55 +0300
commit36db47b89cd5f4edfb2b02780ff2ddd8022190db (patch)
treea7f90c2fe0c4a103de2894b3587d3738d1518003 /libass
parent409ce02b6b9f9147af548cdc46695350af97d801 (diff)
downloadlibass-36db47b89cd5f4edfb2b02780ff2ddd8022190db.tar.bz2
libass-36db47b89cd5f4edfb2b02780ff2ddd8022190db.tar.xz
Parse and animate all colors and alpha values like VSFilter
* Allow exactly one of these prefixes in header values: 0x, 0X, &h, &H. Note that "0x0xFFFFFF" is a correct value, as the first 0x is consumed by the parser and the second by the string-to-number conversion following strtol semantics. * Allow arbitrary numbers of leading & and H (and not h) in any order in override tag values. * Reduce header values modulo 2**32 instead of saturating them to LLONG_MIN/MAX. * Saturate override tag values to INT32_MIN/MAX rather than to LLONG_MIN/MAX. * Don't fiddle with bytes in alpha override tag values. (They can be outside of the 0..255 range.) Also change the byte swapping code to be more sensible. Fixes #80. Fixes #145. Fixes #178. Also fixes our behavior in the case described in https://code.google.com/p/xy-vsfilter/issues/detail?id=80.
Diffstat (limited to 'libass')
-rw-r--r--libass/ass.c5
-rw-r--r--libass/ass_parse.c50
-rw-r--r--libass/ass_parse.h2
-rw-r--r--libass/ass_utils.c123
-rw-r--r--libass/ass_utils.h16
5 files changed, 135 insertions, 61 deletions
diff --git a/libass/ass.c b/libass/ass.c
index 594cca3..c9855ea 100644
--- a/libass/ass.c
+++ b/libass/ass.c
@@ -242,10 +242,7 @@ static int numpad2align(int val)
while (*token == '*') ++token; \
target->name = strdup(token);
-#define COLORVAL(name) \
- } else if (strcasecmp(tname, #name) == 0) { \
- target->name = string2color(track->library, token, 0);
-
+#define COLORVAL(name) ANYVAL(name,parse_color_header)
#define INTVAL(name) ANYVAL(name,atoi)
#define FPVAL(name) ANYVAL(name,ass_atof)
#define TIMEVAL(name) \
diff --git a/libass/ass_parse.c b/libass/ass_parse.c
index a639dc5..d9749fd 100644
--- a/libass/ass_parse.c
+++ b/libass/ass_parse.c
@@ -184,11 +184,11 @@ static void change_color(uint32_t *var, uint32_t new, double pwr)
}
// like change_color, but for alpha component only
-inline void change_alpha(uint32_t *var, uint32_t new, double pwr)
+inline void change_alpha(uint32_t *var, int32_t new, double pwr)
{
*var =
(_r(*var) << 24) + (_g(*var) << 16) + (_b(*var) << 8) +
- (uint32_t) (_a(*var) * (1 - pwr) + _a(new) * pwr);
+ (uint8_t) (_a(*var) * (1 - pwr) + new * pwr);
}
/**
@@ -545,23 +545,20 @@ char *parse_tag(ASS_Renderer *render_priv, char *p, char *end, double pwr)
render_priv->state.family = family;
update_font(render_priv);
} else if (tag("alpha")) {
- uint32_t val;
int i;
- int hex = render_priv->track->track_type == TRACK_TYPE_ASS;
if (nargs) {
- val = string2color(render_priv->library, args->start, hex);
- unsigned char a = val >> 24;
+ int32_t a = parse_alpha_tag(args->start);
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, 1);
+ _a(render_priv->state.style->PrimaryColour), 1);
change_alpha(&render_priv->state.c[1],
- render_priv->state.style->SecondaryColour, 1);
+ _a(render_priv->state.style->SecondaryColour), 1);
change_alpha(&render_priv->state.c[2],
- render_priv->state.style->OutlineColour, 1);
+ _a(render_priv->state.style->OutlineColour), 1);
change_alpha(&render_priv->state.c[3],
- render_priv->state.style->BackColour, 1);
+ _a(render_priv->state.style->BackColour), 1);
}
// FIXME: simplify
} else if (tag("an")) {
@@ -715,10 +712,8 @@ char *parse_tag(ASS_Renderer *render_priv, char *p, char *end, double pwr)
render_priv->state.clip_drawing_mode = 0;
}
} else if (tag("c")) {
- uint32_t val;
- int hex = render_priv->track->track_type == TRACK_TYPE_ASS;
if (nargs) {
- val = string2color(render_priv->library, args->start, hex);
+ uint32_t val = parse_color_tag(args->start);
change_color(&render_priv->state.c[0], val, pwr);
} else
change_color(&render_priv->state.c[0],
@@ -728,39 +723,42 @@ char *parse_tag(ASS_Renderer *render_priv, char *p, char *end, double pwr)
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;
+ int32_t alpha;
+ uint32_t color;
assert((n >= '1') && (n <= '4'));
- if (nargs)
- val = string2color(render_priv->library, args->start, hex);
- else {
+ if (nargs) {
+ if (cmd == 'a')
+ alpha = parse_alpha_tag(args->start);
+ else
+ color = parse_color_tag(args->start);
+ } else {
switch (n) {
case '1':
- val = render_priv->state.style->PrimaryColour;
+ color = render_priv->state.style->PrimaryColour;
break;
case '2':
- val = render_priv->state.style->SecondaryColour;
+ color = render_priv->state.style->SecondaryColour;
break;
case '3':
- val = render_priv->state.style->OutlineColour;
+ color = render_priv->state.style->OutlineColour;
break;
case '4':
- val = render_priv->state.style->BackColour;
+ color = render_priv->state.style->BackColour;
break;
default:
- val = 0;
+ color = 0;
break; // impossible due to assert; avoid compilation warning
}
if (cmd == 'a')
- val <<= 24;
+ alpha = _a(color);
pwr = 1;
}
switch (cmd) {
case 'c':
- change_color(render_priv->state.c + cidx, val, pwr);
+ change_color(render_priv->state.c + cidx, color, pwr);
break;
case 'a':
- change_alpha(render_priv->state.c + cidx, val >> 24, pwr);
+ change_alpha(render_priv->state.c + cidx, alpha, pwr);
break;
default:
ass_msg(render_priv->library, MSGL_WARN, "Bad command: %c%c",
diff --git a/libass/ass_parse.h b/libass/ass_parse.h
index e8512b2..2b4cee0 100644
--- a/libass/ass_parse.h
+++ b/libass/ass_parse.h
@@ -36,7 +36,7 @@ void process_karaoke_effects(ASS_Renderer *render_priv);
unsigned get_next_char(ASS_Renderer *render_priv, char **str);
char *parse_tag(ASS_Renderer *render_priv, char *p, char *end, double pwr);
int event_has_hard_overrides(char *str);
-extern void change_alpha(uint32_t *var, uint32_t new, double pwr);
+extern void change_alpha(uint32_t *var, int32_t new, double pwr);
extern uint32_t mult_alpha(uint32_t a, uint32_t b);
diff --git a/libass/ass_utils.c b/libass/ass_utils.c
index b1c736c..a6a063b 100644
--- a/libass/ass_utils.c
+++ b/libass/ass_utils.c
@@ -165,50 +165,117 @@ int mystrtoll(char **p, long long *res)
return *p != start;
}
-int mystrtou32(char **p, int base, uint32_t *res)
+int mystrtod(char **p, double *res)
{
char *start = *p;
- *res = strtoll(*p, p, base);
+ *res = ass_strtod(*p, p);
return *p != start;
}
-int mystrtod(char **p, double *res)
+int mystrtoi32(char **p, int base, int32_t *res)
{
char *start = *p;
- *res = ass_strtod(*p, p);
+ long long temp_res = strtoll(*p, p, base);
+ *res = FFMINMAX(temp_res, INT32_MIN, INT32_MAX);
return *p != start;
}
-uint32_t string2color(ASS_Library *library, char *p, int hex)
+static int read_digits(char **str, int base, uint32_t *res)
{
- uint32_t color = 0;
- int base = hex ? 16 : 10;
+ char *p = *str;
+ char *start = p;
+ uint32_t val = 0;
+
+ while (1) {
+ int digit;
+ if (*p >= '0' && *p < base + '0')
+ digit = *p - '0';
+ else if (*p >= 'a' && *p < base - 10 + 'a')
+ digit = *p - 'a' + 10;
+ else if (*p >= 'A' && *p < base - 10 + 'A')
+ digit = *p - 'A' + 10;
+ else
+ break;
+ val = val * base + digit;
+ ++p;
+ }
+
+ *res = val;
+ *str = p;
+ return p != start;
+}
- if (*p == '&')
- while (*p == '&')
- ++p;
- else
- ass_msg(library, MSGL_DBG2, "suspicious color format: \"%s\"\n", p);
+/**
+ * \brief Convert a string to an integer reduced modulo 2**32
+ * Follows the rules for strtoul but reduces the number modulo 2**32
+ * instead of saturating it to 2**32 - 1.
+ */
+static int mystrtou32_modulo(char **p, int base, uint32_t *res)
+{
+ // This emulates scanf with %d or %x format as it works on
+ // Windows, because that's what is used by VSFilter. In practice,
+ // scanf works the same way on other platforms too, but
+ // the standard leaves its behavior on overflow undefined.
- if (*p == 'H' || *p == 'h') {
- ++p;
- mystrtou32(&p, 16, &color);
- } else
- mystrtou32(&p, base, &color);
+ // Unlike scanf and like strtoul, produce 0 for invalid inputs.
- while (*p == '&' || *p == 'H')
- ++p;
+ char *start = *p;
+ int sign = 1;
+
+ skip_spaces(p);
+
+ if (**p == '+')
+ ++*p;
+ else if (**p == '-')
+ sign = -1, ++*p;
- unsigned char *tmp = (unsigned char *) (&color);
- unsigned char b;
- b = tmp[0];
- tmp[0] = tmp[3];
- tmp[3] = b;
- b = tmp[1];
- tmp[1] = tmp[2];
- tmp[2] = b;
+ if (base == 16 && !strncasecmp(*p, "0x", 2))
+ *p += 2;
+
+ if (read_digits(p, base, res)) {
+ *res *= sign;
+ return 1;
+ } else {
+ *p = start;
+ return 0;
+ }
+}
+
+int32_t parse_alpha_tag(char *str)
+{
+ int32_t alpha = 0;
+
+ while (*str == '&' || *str == 'H')
+ ++str;
+
+ mystrtoi32(&str, 16, &alpha);
+ return alpha;
+}
+
+uint32_t parse_color_tag(char *str)
+{
+ int32_t color = 0;
+
+ while (*str == '&' || *str == 'H')
+ ++str;
+
+ mystrtoi32(&str, 16, &color);
+ return ass_bswap32((uint32_t) color);
+}
+
+uint32_t parse_color_header(char *str)
+{
+ uint32_t color = 0;
+ int base;
+
+ if (!strncasecmp(str, "&h", 2) || !strncasecmp(str, "0x", 2)) {
+ str += 2;
+ base = 16;
+ } else
+ base = 10;
- return color;
+ mystrtou32_modulo(&str, base, &color);
+ return ass_bswap32(color);
}
// Return a boolean value for a string
diff --git a/libass/ass_utils.h b/libass/ass_utils.h
index 2ad8c5b..45ebbb6 100644
--- a/libass/ass_utils.h
+++ b/libass/ass_utils.h
@@ -85,9 +85,11 @@ void skip_spaces(char **str);
void rskip_spaces(char **str, char *limit);
int mystrtoi(char **p, int *res);
int mystrtoll(char **p, long long *res);
-int mystrtou32(char **p, int base, uint32_t *res);
int mystrtod(char **p, double *res);
-uint32_t string2color(ASS_Library *library, char *p, int hex);
+int mystrtoi32(char **p, int base, int32_t *res);
+int32_t parse_alpha_tag(char *str);
+uint32_t parse_color_tag(char *str);
+uint32_t parse_color_header(char *str);
char parse_bool(char *str);
int parse_ycbcr_matrix(char *str);
unsigned ass_utf8_get_char(char **str);
@@ -111,6 +113,16 @@ static inline size_t ass_align(size_t alignment, size_t s)
return (s + (alignment - 1)) & ~(alignment - 1);
}
+static inline uint32_t ass_bswap32(uint32_t x)
+{
+#ifdef _MSC_VER
+ return _byteswap_ulong(x);
+#else
+ return (x & 0x000000FF) << 24 | (x & 0x0000FF00) << 8 |
+ (x & 0x00FF0000) >> 8 | (x & 0xFF000000) >> 24;
+#endif
+}
+
static inline int d6_to_int(int x)
{
return (x + 32) >> 6;