]> granicus.if.org Git - libass/commitdiff
Parse and animate all colors and alpha values like VSFilter
authorOleg Oshmyan <chortos@inbox.lv>
Fri, 22 May 2015 21:06:40 +0000 (00:06 +0300)
committerOleg Oshmyan <chortos@inbox.lv>
Sun, 24 May 2015 21:16:55 +0000 (00:16 +0300)
* 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.

libass/ass.c
libass/ass_parse.c
libass/ass_parse.h
libass/ass_utils.c
libass/ass_utils.h

index 594cca36e24ecb388bcbb161d1eb659a82d50db8..c9855ea4a75441de42291e8b2ed4aaf11cdd5042 100644 (file)
@@ -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) \
index a639dc5809d1694b60c2e63e2bde02be3075430e..d9749fdf670c79dc147b673641cd5b00300e8b34 100644 (file)
@@ -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",
index e8512b2a7d5b53f0a4d1cc4992f6ee76e76ef9a4..2b4cee087e7136dc956fcb7b3117b97bccbe925c 100644 (file)
@@ -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);
 
 
index b1c736c3719afdec4aa7c95393fc4283bfb9c618..a6a063b1d0613d2f622e00029eaec7836ad495a1 100644 (file)
@@ -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
index 2ad8c5b077ed2056265dd24a982788b71b1bd20e..45ebbb62d73454b1bcc3a2c842dfb6061212f182 100644 (file)
@@ -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;