Bug 1011166 - Improve the workarounds cairo does when rendering large gradients with pixman. r=roc, r=jrmuizel, a=sledru
authorMarkus Stange <mstange@themasta.com>
Thu, 21 Aug 2014 13:57:55 +0200
changeset 216845 a703ff0c7861
parent 216844 c043fec932a6
child 216846 f2933e32b654
push id3940
push userryanvm@gmail.com
push date2014-09-25 16:25 +0000
treeherdermozilla-beta@09dcf9d94d33 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc, jrmuizel, sledru
bugs1011166
milestone33.0
Bug 1011166 - Improve the workarounds cairo does when rendering large gradients with pixman. r=roc, r=jrmuizel, a=sledru
gfx/cairo/cairo/src/cairo-image-surface.c
gfx/cairo/cairo/src/cairo-pattern.c
layout/reftests/css-gradients/large-gradient-1-ref.html
layout/reftests/css-gradients/large-gradient-1.html
layout/reftests/css-gradients/large-gradient-2-ref.html
layout/reftests/css-gradients/large-gradient-2.html
layout/reftests/css-gradients/large-gradient-3-ref.html
layout/reftests/css-gradients/large-gradient-3.html
layout/reftests/css-gradients/large-gradient-4-ref.html
layout/reftests/css-gradients/large-gradient-4.html
layout/reftests/css-gradients/reftest.list
--- a/gfx/cairo/cairo/src/cairo-image-surface.c
+++ b/gfx/cairo/cairo/src/cairo-image-surface.c
@@ -1129,28 +1129,30 @@ static pixman_image_t *
 	 * Having a function to compute the required transformation to
 	 * "normalize" a given bounding box would be generally useful -
 	 * cf linear patterns, gradient patterns, surface patterns...
 	 */
 	if (_cairo_fixed_integer_ceil (xdim) > PIXMAN_MAX_INT ||
 	    _cairo_fixed_integer_ceil (ydim) > PIXMAN_MAX_INT)
 	{
 	    double sf;
+	    cairo_matrix_t scale;
 
 	    if (xdim > ydim)
 		sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (xdim);
 	    else
 		sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (ydim);
 
 	    p1.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.x) * sf);
 	    p1.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.y) * sf);
 	    p2.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.x) * sf);
 	    p2.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.y) * sf);
 
-	    cairo_matrix_scale (&matrix, sf, sf);
+	    cairo_matrix_init_scale (&scale, sf, sf);
+	    cairo_matrix_multiply (&matrix, &matrix, &scale);
 	}
 	else
 	{
 	    p1.x = _cairo_fixed_to_16_16 (linear->p1.x);
 	    p1.y = _cairo_fixed_to_16_16 (linear->p1.y);
 	    p2.x = _cairo_fixed_to_16_16 (linear->p2.x);
 	    p2.y = _cairo_fixed_to_16_16 (linear->p2.y);
 	}
@@ -1177,41 +1179,50 @@ static pixman_image_t *
     }
 
     if (pixman_stops != pixman_stops_static)
 	free (pixman_stops);
 
     if (unlikely (pixman_image == NULL))
 	return NULL;
 
-    tx = pattern->base.matrix.x0;
-    ty = pattern->base.matrix.y0;
-    if (! _cairo_matrix_is_translation (&pattern->base.matrix) ||
+    tx = matrix.x0;
+    ty = matrix.y0;
+    if (! _cairo_matrix_is_translation (&matrix) ||
 	! _nearest_sample (pattern->base.filter, &tx, &ty))
     {
 	pixman_transform_t pixman_transform;
 
 	if (tx != 0. || ty != 0.) {
 	    cairo_matrix_t m, inv;
 	    cairo_status_t status;
-	    double x, y;
-
-	    /* pixman also limits the [xy]_offset to 16 bits so evenly
-	     * spread the bits between the two.
+	    double x, y, max_x, max_y;
+
+	    /* Pixman also limits the [xy]_offset to 16 bits. We try to evenly
+	     * spread the bits between the two, but we need to ensure that
+	     * fabs (tx + extents->x + extents->width) < PIXMAN_MAX_INT &&
+	     * fabs (ty + extents->y + extents->height) < PIXMAN_MAX_INT,
+	     * otherwise the gradient won't render.
 	     */
-	    inv = pattern->base.matrix;
+	    inv = matrix;
 	    status = cairo_matrix_invert (&inv);
 	    assert (status == CAIRO_STATUS_SUCCESS);
 
 	    x = _cairo_lround (inv.x0 / 2);
 	    y = _cairo_lround (inv.y0 / 2);
+
+	    max_x = PIXMAN_MAX_INT - 1 - fabs (extents->x + extents->width);
+	    x = x > max_x ? max_x : (x < -max_x ? -max_x : x);
+	    max_y = PIXMAN_MAX_INT - 1 - fabs (extents->y + extents->height);
+	    y = y > max_y ? max_y : (y < -max_y ? -max_y : y);
+
 	    tx = -x;
 	    ty = -y;
 	    cairo_matrix_init_translate (&inv, x, y);
-	    cairo_matrix_multiply (&m, &inv, &pattern->base.matrix);
+	    cairo_matrix_multiply (&m, &inv, &matrix);
 	    _cairo_matrix_to_pixman_matrix (&m, &pixman_transform,
 					    extents->x + extents->width/2.,
 					    extents->y + extents->height/2.);
 	} else {
 	    tx = ty = 0;
 	    _cairo_matrix_to_pixman_matrix (&pattern->base.matrix,
 					    &pixman_transform,
 					    extents->x + extents->width/2.,
--- a/gfx/cairo/cairo/src/cairo-pattern.c
+++ b/gfx/cairo/cairo/src/cairo-pattern.c
@@ -1382,28 +1382,30 @@ static cairo_int_status_t
 	 * "normalize" a given bounding box would be generally useful -
 	 * cf linear patterns, gradient patterns, surface patterns...
 	 */
 #define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */
 	if (_cairo_fixed_integer_ceil (xdim) > PIXMAN_MAX_INT ||
 	    _cairo_fixed_integer_ceil (ydim) > PIXMAN_MAX_INT)
 	{
 	    double sf;
+	    cairo_matrix_t scale;
 
 	    if (xdim > ydim)
 		sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (xdim);
 	    else
 		sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (ydim);
 
 	    p1.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.x) * sf);
 	    p1.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.y) * sf);
 	    p2.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.x) * sf);
 	    p2.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.y) * sf);
 
-	    cairo_matrix_scale (&matrix, sf, sf);
+	    cairo_matrix_init_scale (&scale, sf, sf);
+	    cairo_matrix_multiply (&matrix, &matrix, &scale);
 	}
 	else
 	{
 	    p1.x = _cairo_fixed_to_16_16 (linear->p1.x);
 	    p1.y = _cairo_fixed_to_16_16 (linear->p1.y);
 	    p2.x = _cairo_fixed_to_16_16 (linear->p2.x);
 	    p2.y = _cairo_fixed_to_16_16 (linear->p2.y);
 	}
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-gradients/large-gradient-1-ref.html
@@ -0,0 +1,23 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="utf-8">
+<title>Make sure that large gradient backgrounds are painted even at extreme scroll positions</title>
+
+<style>
+
+html {
+  background: white;
+  overflow: hidden;
+}
+
+body {
+  margin: 0;
+  height: 1000px;
+  background-image: linear-gradient(rgb(210,210,210) 0px, rgb(215,215,215) 1000px);
+}
+
+</style>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-gradients/large-gradient-1.html
@@ -0,0 +1,40 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<html lang="en" class="reftest-wait">
+<meta charset="utf-8">
+<title>Make sure that large gradient backgrounds are painted even at extreme scroll positions</title>
+<!-- See https://bugzilla.mozilla.org/show_bug.cgi?id=1011166 -->
+
+<style>
+
+html {
+  background: white;
+  overflow: hidden;
+}
+
+body {
+  margin: 0;
+  height: 51000px; /* = 255 * 200 */
+  background-image: linear-gradient(rgb(0,0,0) 0px, rgb(255,255,255) 51000px);
+}
+
+div {
+  height: 1000px;
+  background-color: red;
+}
+
+</style>
+
+<div></div>
+
+<script>
+
+window.addEventListener("MozReftestInvalidate", function () {
+  document.documentElement.scrollTop = 210 * 200; // 42000 > 32768
+  document.documentElement.removeAttribute("class");
+});
+
+</script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-gradients/large-gradient-2-ref.html
@@ -0,0 +1,17 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="utf-8">
+<title>Make sure that large gradient backgrounds are painted even at extreme scroll positions</title>
+
+<style>
+
+html {
+  background: lime;
+  overflow: hidden;
+}
+
+</style>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-gradients/large-gradient-2.html
@@ -0,0 +1,40 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<html lang="en" class="reftest-wait">
+<meta charset="utf-8">
+<title>Make sure that large gradient backgrounds are painted even at extreme scroll positions</title>
+<!-- See https://bugzilla.mozilla.org/show_bug.cgi?id=1011166 -->
+
+<style>
+
+html {
+  background: white;
+  overflow: hidden;
+}
+
+body {
+  margin: 0;
+  height: 51000px;
+  background-image: linear-gradient(black 0px, lime 2000px);
+}
+
+div {
+  height: 1000px;
+  background-color: red;
+}
+
+</style>
+
+<div></div>
+
+<script>
+
+window.addEventListener("MozReftestInvalidate", function () {
+  document.documentElement.scrollTop = 42000; // > 32768
+  document.documentElement.removeAttribute("class");
+});
+
+</script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-gradients/large-gradient-3-ref.html
@@ -0,0 +1,23 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="utf-8">
+<title>Make sure that large gradient backgrounds are painted even at extreme scroll positions</title>
+
+<style>
+
+html {
+  background: white;
+  overflow: hidden;
+}
+
+body {
+  margin: 0;
+  height: 1000px;
+  background-image: linear-gradient(rgb(210,210,210) 0px, rgb(215,215,215) 4000px);
+}
+
+</style>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-gradients/large-gradient-3.html
@@ -0,0 +1,40 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<html lang="en" class="reftest-wait">
+<meta charset="utf-8">
+<title>Make sure that large gradient backgrounds are painted even at extreme scroll positions</title>
+<!-- See https://bugzilla.mozilla.org/show_bug.cgi?id=1011166 -->
+
+<style>
+
+html {
+  background: white;
+  overflow: hidden;
+}
+
+body {
+  margin: 0;
+  height: 204000px; /* = 255 * 800 */
+  background-image: linear-gradient(rgb(0,0,0) 0px, rgb(255,255,255) 204000px);
+}
+
+div {
+  height: 1000px;
+  background-color: red;
+}
+
+</style>
+
+<div></div>
+
+<script>
+
+window.addEventListener("MozReftestInvalidate", function () {
+  document.documentElement.scrollTop = 210 * 800; // 168000 > 131072 == 2^17
+  document.documentElement.removeAttribute("class");
+});
+
+</script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-gradients/large-gradient-4-ref.html
@@ -0,0 +1,17 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="utf-8">
+<title>Make sure that large gradient backgrounds are painted even at extreme scroll positions</title>
+
+<style>
+
+html {
+  background: lime;
+  overflow: hidden;
+}
+
+</style>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-gradients/large-gradient-4.html
@@ -0,0 +1,40 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<html lang="en" class="reftest-wait">
+<meta charset="utf-8">
+<title>Make sure that large gradient backgrounds are painted even at extreme scroll positions</title>
+<!-- See https://bugzilla.mozilla.org/show_bug.cgi?id=1011166 -->
+
+<style>
+
+html {
+  background: white;
+  overflow: hidden;
+}
+
+body {
+  margin: 0;
+  height: 204000px; /* = 255 * 800 */;
+  background-image: linear-gradient(black 0px, lime 2000px);
+}
+
+div {
+  height: 1000px;
+  background-color: red;
+}
+
+</style>
+
+<div></div>
+
+<script>
+
+window.addEventListener("MozReftestInvalidate", function () {
+  document.documentElement.scrollTop = 210 * 800; // 168000 > 131072 == 2^17
+  document.documentElement.removeAttribute("class");
+});
+
+</script>
--- a/layout/reftests/css-gradients/reftest.list
+++ b/layout/reftests/css-gradients/reftest.list
@@ -139,8 +139,12 @@ fuzzy-if(d2d,47,400) == linear-onestoppo
 == radial-onestopposition-1a.html radial-onestopposition-1-ref.html
 == radial-onestopposition-1b.html radial-onestopposition-1-ref.html
 == radial-onestopposition-1c.html radial-onestopposition-1-ref.html
 == repeating-linear-onestopposition-1.html orange-square.html
 == repeating-radial-onestopposition-1a.html orange-square.html
 == repeating-radial-onestopposition-1b.html orange-square.html
 == repeating-radial-onestopposition-1c.html orange-square.html
 == bug-916535-background-repeat-linear.html bug-916535-background-repeat-linear-ref.html
+fuzzy(1,800000) == large-gradient-1.html large-gradient-1-ref.html
+== large-gradient-2.html large-gradient-2-ref.html
+fuzzy(1,800000) == large-gradient-3.html large-gradient-3-ref.html
+== large-gradient-4.html large-gradient-4-ref.html