From 7ca4ef7b0c5ceb50d85a138c8a58c1a1983f6ad1 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Mon, 4 Apr 2016 14:53:46 +0200 Subject: [PATCH] cssease: Optimize with Newton's method Idea stolen from Webkit's implementation. --- gtk/gtkcsseasevalue.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/gtk/gtkcsseasevalue.c b/gtk/gtkcsseasevalue.c index 402696f1d8..409ac1ad5f 100644 --- a/gtk/gtkcsseasevalue.c +++ b/gtk/gtkcsseasevalue.c @@ -452,6 +452,12 @@ cubic (double a, double b, double c, double t) return ((a * t + b) * t + c) * t; } +static inline double +cubic_deriv (double a, double b, double c, double t) +{ + return (3 * a * t + 2 * b) * t + c; +} + double _gtk_css_ease_value_transform (const GtkCssValue *ease, double progress) @@ -471,6 +477,7 @@ _gtk_css_ease_value_transform (const GtkCssValue *ease, double ax, bx, cx; double ay, by, cy; double tmin, t, tmax; + guint i; cx = 3.0 * ease->u.cubic.x1; bx = 3.0 * (ease->u.cubic.x2 - ease->u.cubic.x1) - cx; @@ -479,6 +486,24 @@ _gtk_css_ease_value_transform (const GtkCssValue *ease, by = 3.0 * (ease->u.cubic.y2 - ease->u.cubic.y1) - cy; ay = 1.0 - cy - by; + /* Try with Newton's method first */ + t = progress; + for (i = 0; i < 8; i++) + { + double sample, deriv; + + sample = cubic (ax, bx, cx, t); + if (fabs (sample - t) < epsilon) + return cubic (ay, by, cy, t); + + deriv = cubic_deriv (ax, bx, cx, t); + if (deriv < epsilon) + return cubic (ay, by, cy, t); + + t = t - (sample - t) / deriv; + } + + /* Fallback if Newton doesn't converge */ tmin = 0.0; tmax = 1.0; t = progress;