summaryrefslogtreecommitdiffstats
path: root/libass
diff options
context:
space:
mode:
authorrcombs <rcombs@rcombs.me>2020-09-16 23:37:02 -0500
committerOleg Oshmyan <chortos@inbox.lv>2020-09-19 17:49:41 +0300
commit9b04e56ff6e1ed82bc48ac7307e627bdbbaf082d (patch)
tree6a0ca65efd623df0dfbce30aecef185d5c60fc98 /libass
parent6bb17b7e7b783844e66335a7293c82c93ea757d3 (diff)
downloadlibass-9b04e56ff6e1ed82bc48ac7307e627bdbbaf082d.tar.bz2
libass-9b04e56ff6e1ed82bc48ac7307e627bdbbaf082d.tar.xz
ass_parse: avoid UB and match vsfilter on negative-accel color animation
Providing a negative acceleration to \t could result in undefined behavior due to overflow in float->int conversion. This codifies the same behavior we currently have on x86, which matches vsfilter's, without actually invoking UB. Additionally, vsfilter color animations work subtly differently than ours did. We used to lerp each individual color component's byte value, while vsfilter performs the lerp on the component _in place within a larger int_. This didn't result in major issues for most cases, but could probably result in subtle rounding errors, and gave vastly different results for \t with negative acceleration. Negative \t acceleration is probably completely useless, but our behavior was wacky in a different way from vsfilter's, and I'd rather have portable wackiness than libass-specific wackiness. It might still be possible to invoke UB in negative-acceleration \t using tags other than colors; we should fix those cases as well.
Diffstat (limited to 'libass')
-rw-r--r--libass/ass_parse.c29
1 files changed, 25 insertions, 4 deletions
diff --git a/libass/ass_parse.c b/libass/ass_parse.c
index 5c0ea40..935dbb0 100644
--- a/libass/ass_parse.c
+++ b/libass/ass_parse.c
@@ -128,21 +128,42 @@ void update_font(ASS_Renderer *render_priv)
render_priv->state.font = ass_font_new(render_priv, &desc);
}
+static double calc_anim(double new, double old, double pwr)
+{
+ return (1 - pwr) * old + new * pwr;
+}
+
+static int32_t calc_anim_int32(uint32_t new, uint32_t old, double pwr)
+{
+ double ret = calc_anim(new, old, pwr);
+
+ // Avoid UB on out-of-range values; match x86 behavior
+ if (isnan(ret) || ret < INT32_MIN || ret > INT32_MAX)
+ return INT32_MIN;
+
+ return ret;
+}
+
/**
* \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);
+ uint32_t co = ass_bswap32(*var);
+ uint32_t cn = ass_bswap32(new);
+
+ uint32_t cc = (calc_anim_int32(cn & 0xff0000, co & 0xff0000, pwr) & 0xff0000) |
+ (calc_anim_int32(cn & 0x00ff00, co & 0x00ff00, pwr) & 0x00ff00) |
+ (calc_anim_int32(cn & 0x0000ff, co & 0x0000ff, pwr) & 0x0000ff);
+
+ (*var) = (ass_bswap32(cc & 0xffffff)) | _a(*var);
}
// like change_color, but for alpha component only
inline void change_alpha(uint32_t *var, int32_t new, double pwr)
{
- *var = (*var & 0xFFFFFF00) | (uint8_t) (_a(*var) * (1 - pwr) + new * pwr);
+ *var = (*var & 0xFFFFFF00) | (uint8_t)calc_anim_int32(_a(new), _a(*var), pwr);
}
/**