[cairo-commit] 10 commits - doc/public src/cairo-device.c src/cairo-gl-composite.c src/cairo-gstate.c src/cairo.h src/cairo-image-surface.c src/cairoint.h src/cairo-mesh-pattern-rasterizer.c src/cairo-misc.c src/cairo-pattern.c src/cairo-pdf-shading.c src/cairo-pdf-shading-private.h src/cairo-pdf-surface.c src/cairo-ps-surface.c src/cairo-region.c src/cairo-script-surface.c src/cairo-spans.c src/cairo-surface.c src/cairo-svg-surface.c src/cairo-types-private.h src/cairo-win32-printing-surface.c src/cairo-xcb-surface-core.c src/cairo-xcb-surface-render.c src/cairo-xlib-surface.c src/Makefile.sources test/Makefile.am test/Makefile.sources test/mesh-pattern-accuracy.c test/mesh-pattern-accuracy.image16.ref.png test/mesh-pattern-accuracy.ref.png test/mesh-pattern.c test/mesh-pattern-conical.c test/mesh-pattern-conical.image16.ref.png test/mesh-pattern-conical.ref.png test/mesh-pattern-control-points.c test/mesh-pattern-control-points.image16.ref.png test/mesh-pattern-control-points.ref .png test/mesh-pattern-fold.c test/mesh-pattern-fold.image16.ref.png test/mesh-pattern-fold.ref.png test/mesh-pattern.image16.ref.png test/mesh-pattern-overlap.c test/mesh-pattern-overlap.image16.ref.png test/mesh-pattern-overlap.ref.png test/mesh-pattern.ref.png test/mesh-pattern-transformed.c test/mesh-pattern-transformed.image16.ref.png test/mesh-pattern-transformed.ref.png test/pattern-getters.c test/pattern-get-type.c util/cairo-gobject util/cairo-script util/cairo-trace

Andrea Canciani ranma42 at kemper.freedesktop.org
Sun Jan 2 04:50:27 PST 2011


 doc/public/cairo-sections.txt                    |   13 
 doc/public/language-bindings.xml                 |   13 
 src/Makefile.sources                             |    9 
 src/cairo-device.c                               |    1 
 src/cairo-gl-composite.c                         |    1 
 src/cairo-gstate.c                               |   44 
 src/cairo-image-surface.c                        |   25 
 src/cairo-mesh-pattern-rasterizer.c              |  943 +++++++++++++++++
 src/cairo-misc.c                                 |    2 
 src/cairo-pattern.c                              | 1266 ++++++++++++++++++++++-
 src/cairo-pdf-shading-private.h                  |   99 +
 src/cairo-pdf-shading.c                          |  277 +++++
 src/cairo-pdf-surface.c                          |  153 ++
 src/cairo-ps-surface.c                           |   84 +
 src/cairo-region.c                               |    1 
 src/cairo-script-surface.c                       |   85 +
 src/cairo-spans.c                                |    2 
 src/cairo-surface.c                              |    1 
 src/cairo-svg-surface.c                          |    6 
 src/cairo-types-private.h                        |   46 
 src/cairo-win32-printing-surface.c               |    3 
 src/cairo-xcb-surface-core.c                     |    1 
 src/cairo-xcb-surface-render.c                   |    3 
 src/cairo-xlib-surface.c                         |    1 
 src/cairo.h                                      |   71 +
 src/cairoint.h                                   |   23 
 test/Makefile.am                                 |   42 
 test/Makefile.sources                            |    9 
 test/mesh-pattern-accuracy.c                     |   99 +
 test/mesh-pattern-accuracy.image16.ref.png       |binary
 test/mesh-pattern-accuracy.ref.png               |binary
 test/mesh-pattern-conical.c                      |  135 ++
 test/mesh-pattern-conical.image16.ref.png        |binary
 test/mesh-pattern-conical.ref.png                |binary
 test/mesh-pattern-control-points.c               |  114 ++
 test/mesh-pattern-control-points.image16.ref.png |binary
 test/mesh-pattern-control-points.ref.png         |binary
 test/mesh-pattern-fold.c                         |   82 +
 test/mesh-pattern-fold.image16.ref.png           |binary
 test/mesh-pattern-fold.ref.png                   |binary
 test/mesh-pattern-overlap.c                      |   76 +
 test/mesh-pattern-overlap.image16.ref.png        |binary
 test/mesh-pattern-overlap.ref.png                |binary
 test/mesh-pattern-transformed.c                  |  107 +
 test/mesh-pattern-transformed.image16.ref.png    |binary
 test/mesh-pattern-transformed.ref.png            |binary
 test/mesh-pattern.c                              |   94 +
 test/mesh-pattern.image16.ref.png                |binary
 test/mesh-pattern.ref.png                        |binary
 test/pattern-get-type.c                          |    7 
 test/pattern-getters.c                           |   82 +
 util/cairo-gobject/cairo-gobject-enums.c         |    2 
 util/cairo-script/cairo-script-operators.c       |  201 +++
 util/cairo-trace/trace.c                         |    1 
 54 files changed, 4187 insertions(+), 37 deletions(-)

New commits:
commit 5d5d24c6a26cbabfc25067f0ea885f5705c4987c
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Thu Dec 23 11:44:09 2010 +0100

    doc: Add documentation for the mesh API
    
    The documentation content is in the comments of the functions.

diff --git a/doc/public/cairo-sections.txt b/doc/public/cairo-sections.txt
index 88debec..bd25a8c 100644
--- a/doc/public/cairo-sections.txt
+++ b/doc/public/cairo-sections.txt
@@ -277,6 +277,19 @@ cairo_pattern_create_linear
 cairo_pattern_get_linear_points
 cairo_pattern_create_radial
 cairo_pattern_get_radial_circles
+cairo_pattern_create_mesh
+cairo_pattern_mesh_begin_patch
+cairo_pattern_mesh_end_patch
+cairo_pattern_mesh_move_to
+cairo_pattern_mesh_line_to
+cairo_pattern_mesh_curve_to
+cairo_pattern_mesh_set_control_point
+cairo_pattern_mesh_set_corner_color_rgb
+cairo_pattern_mesh_set_corner_color_rgba
+cairo_pattern_mesh_get_patch_count
+cairo_pattern_mesh_get_path
+cairo_pattern_mesh_get_control_point
+cairo_pattern_mesh_get_corner_color_rgba
 cairo_pattern_reference
 cairo_pattern_destroy
 cairo_pattern_status
diff --git a/doc/public/language-bindings.xml b/doc/public/language-bindings.xml
index f405fda..c9a810c 100644
--- a/doc/public/language-bindings.xml
+++ b/doc/public/language-bindings.xml
@@ -529,6 +529,19 @@ cairo_pattern_t
          <link linkend="cairo-pattern-add-color-stop-rgba"><function>cairo_pattern_add_color_stop_rgba()</function></link>
       cairo_linear_gradient_t (<link linkend="cairo-pattern-create-linear"><function>cairo_pattern_create_linear()</function></link>)
       cairo_radial_gradient_t (<link linkend="cairo-pattern-create-radial"><function>cairo_pattern_create_radial()</function></link>)
+   cairo_mesh_t (<link linkend="cairo-pattern-create-mesh"><function>cairo_pattern_create_mesh()</function></link>)
+         <link linkend="cairo-pattern-mesh-begin-patch"><function>cairo_pattern_mesh_begin_patch()</function></link>
+         <link linkend="cairo-pattern-mesh-end-patch"><function>cairo_pattern_mesh_end_patch()</function></link>
+         <link linkend="cairo-pattern-mesh-move-to"><function>cairo_pattern_mesh_move_to()</function></link>
+         <link linkend="cairo-pattern-mesh-line-to"><function>cairo_pattern_mesh_line_to()</function></link>
+         <link linkend="cairo-pattern-mesh-curve-to"><function>cairo_pattern_mesh_curve_to()</function></link>
+         <link linkend="cairo-pattern-mesh-set-control-point"><function>cairo_pattern_mesh_set_control_point()</function></link>
+         <link linkend="cairo-pattern-mesh-set-corner-color-rgb"><function>cairo_pattern_mesh_set_corner_color_rgb()</function></link>
+         <link linkend="cairo-pattern-mesh-set-corner-color-rgba"><function>cairo_pattern_mesh_set_corner_color_rgba()</function></link>
+         <link linkend="cairo-pattern-mesh-get-patch-count"><function>cairo_pattern_mesh_get_patch_count()</function></link>
+         <link linkend="cairo-pattern-mesh-get-path"><function>cairo_pattern_mesh_get_path()</function></link>
+         <link linkend="cairo-pattern-mesh-get-control-point"><function>cairo_pattern_mesh_get_control_point()</function></link>
+         <link linkend="cairo-pattern-mesh-get-corner-color-rgba"><function>cairo_pattern_mesh_get_corner_color_rgba()</function></link>
     </programlisting>
     <para>
     </para>
commit a8344f98e7f4d463c85629d00420f2c8433aff9f
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Fri Dec 24 16:43:53 2010 +0100

    test: Add tests for mesh patterns
    
    mesh-pattern tests a mesh pattern with non-opaque two overlapping
    patches.
    
    mesh-pattern-accuracy tests the accuracy of the color computed in each
    point of a patch. It can point out defects in rasterizers which rely
    on mesh subdivision only use the mesh shape instead of both shape and
    color to decide when the tensor-product patches can be approximated
    with Gouraud-shaded triangles.
    
    mesh-pattern-conical is an example of how a conical gradient can be
    approximated with a mesh pattern.
    
    mesh-pattern-control-points tests a mesh pattern with control points
    in non-default position to verify that their position affects the
    color as expected.
    
    mesh-pattern-fold tests a mesh pattern with a patch which folds along
    both sides.
    
    mesh-pattern-overlap tests a mesh pattern with a patch which folds
    along just one side.
    
    mesh-pattern-transformed tests a mesh pattern with non-identity
    transform matrix.

diff --git a/test/Makefile.am b/test/Makefile.am
index 8932e3e..9935116 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -853,6 +853,20 @@ REFERENCE_IMAGES = \
 	mask.svg.rgb24.xfail.png \
 	mask.xlib.ref.png \
 	mask.xlib.rgb24.ref.png \
+	mesh-pattern.image16.ref.png \
+	mesh-pattern.ref.png \
+	mesh-pattern-accuracy.image16.ref.png \
+	mesh-pattern-accuracy.ref.png \
+	mesh-pattern-conical.image16.ref.png \
+	mesh-pattern-conical.ref.png \
+	mesh-pattern-control-points.image16.ref.png \
+	mesh-pattern-control-points.ref.png \
+	mesh-pattern-fold.image16.ref.png \
+	mesh-pattern-fold.ref.png \
+	mesh-pattern-overlap.image16.ref.png \
+	mesh-pattern-overlap.ref.png \
+	mesh-pattern-transformed.image16.ref.png \
+	mesh-pattern-transformed.ref.png \
 	mime-data.pdf.ref.png \
 	mime-data.ps.ref.png \
 	mime-data.ref.png \
diff --git a/test/Makefile.sources b/test/Makefile.sources
index 2026a08..27b8a27 100644
--- a/test/Makefile.sources
+++ b/test/Makefile.sources
@@ -166,6 +166,13 @@ test_sources = \
 	mask-surface-ctm.c				\
 	mask-transformed-image.c			\
 	mask-transformed-similar.c			\
+	mesh-pattern.c				        \
+	mesh-pattern-accuracy.c				\
+	mesh-pattern-conical.c				\
+	mesh-pattern-control-points.c			\
+	mesh-pattern-fold.c		        	\
+	mesh-pattern-overlap.c		        	\
+	mesh-pattern-transformed.c		        \
 	mime-data.c					\
 	miter-precision.c				\
 	move-to-show-surface.c				\
diff --git a/test/mesh-pattern-accuracy.c b/test/mesh-pattern-accuracy.c
new file mode 100644
index 0000000..0f756c5
--- /dev/null
+++ b/test/mesh-pattern-accuracy.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright © 2009 Adrian Johnson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Adrian Johnson <ajohnson at redneon.com>
+ */
+
+#include "cairo-test.h"
+#include <float.h>
+
+#define SIZE 256
+
+/* This test is designed to test the accuracy of the rendering of mesh
+ * patterns.
+ *
+ * Color accuracy is tested by a square patch covering the whole
+ * surface with black and white corners.
+ *
+ * Extents accuracy is checked by a small red square patch at the
+ * center of the surface which should measure 2x2 pixels.
+ */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *pattern;
+    double offset;
+
+    cairo_test_paint_checkered (cr);
+
+    pattern = cairo_pattern_create_mesh ();
+
+    cairo_pattern_mesh_begin_patch (pattern);
+
+    cairo_pattern_mesh_move_to (pattern, 0, 0);
+    cairo_pattern_mesh_line_to (pattern, 1, 0);
+    cairo_pattern_mesh_line_to (pattern, 1, 1);
+    cairo_pattern_mesh_line_to (pattern, 0, 1);
+
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 0, 0, 0, 0);
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 1, 1, 1, 1);
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 2, 0, 0, 0);
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 3, 1, 1, 1);
+
+    cairo_pattern_mesh_end_patch (pattern);
+
+    cairo_pattern_mesh_begin_patch (pattern);
+
+    /* A small 1x1 red patch, that should be rendered as a 2x2 red
+     * square in the center of the image */
+
+    offset = 0.5 / SIZE;
+
+    cairo_pattern_mesh_move_to (pattern, 0.5 + offset, 0.5 + offset);
+    cairo_pattern_mesh_line_to (pattern, 0.5 + offset, 0.5 - offset);
+    cairo_pattern_mesh_line_to (pattern, 0.5 - offset, 0.5 - offset);
+    cairo_pattern_mesh_line_to (pattern, 0.5 - offset, 0.5 + offset);
+
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 0, 1, 0, 0);
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 1, 1, 0, 0);
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 2, 1, 0, 0);
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 3, 1, 0, 0);
+
+    cairo_pattern_mesh_end_patch (pattern);
+
+    cairo_scale (cr, SIZE, SIZE);
+
+    cairo_set_source (cr, pattern);
+    cairo_paint (cr);
+    cairo_pattern_destroy (pattern);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (mesh_pattern_accuracy,
+	    "Paint mesh pattern",
+	    "mesh,pattern", /* keywords */
+	    NULL, /* requirements */
+	    SIZE, SIZE,
+	    NULL, draw)
diff --git a/test/mesh-pattern-accuracy.image16.ref.png b/test/mesh-pattern-accuracy.image16.ref.png
new file mode 100644
index 0000000..a82e4fb
Binary files /dev/null and b/test/mesh-pattern-accuracy.image16.ref.png differ
diff --git a/test/mesh-pattern-accuracy.ref.png b/test/mesh-pattern-accuracy.ref.png
new file mode 100644
index 0000000..dfc19ff
Binary files /dev/null and b/test/mesh-pattern-accuracy.ref.png differ
diff --git a/test/mesh-pattern-conical.c b/test/mesh-pattern-conical.c
new file mode 100644
index 0000000..aa3e182
--- /dev/null
+++ b/test/mesh-pattern-conical.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright © 2009 Adrian Johnson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Adrian Johnson <ajohnson at redneon.com>
+ */
+
+#include "cairo-test.h"
+#include <math.h>
+
+#define PAT_WIDTH  100
+#define PAT_HEIGHT 100
+#define SIZE PAT_WIDTH
+#define PAD 2
+#define WIDTH (PAD + SIZE + PAD)
+#define HEIGHT WIDTH
+
+
+/*
+ * This test is designed to paint a mesh pattern which contains 8
+ * circular sectors approximating a conical gradient.
+ */
+
+#define CENTER_X 50
+#define CENTER_Y 50
+#define RADIUS   50
+
+static void
+sector_patch (cairo_pattern_t *pattern,
+	      double angle_A,
+	      double A_r, double A_g, double A_b,
+	      double angle_B,
+	      double B_r, double B_g, double B_b)
+{
+    double r_sin_A, r_cos_A;
+    double r_sin_B, r_cos_B;
+    double h;
+
+    r_sin_A = RADIUS * sin (angle_A);
+    r_cos_A = RADIUS * cos (angle_A);
+    r_sin_B = RADIUS * sin (angle_B);
+    r_cos_B = RADIUS * cos (angle_B);
+
+    h = 4.0/3.0 * tan ((angle_B - angle_A) / 4.0);
+
+    cairo_pattern_mesh_begin_patch (pattern);
+
+    cairo_pattern_mesh_move_to (pattern, CENTER_X, CENTER_Y);
+    cairo_pattern_mesh_line_to (pattern,
+				CENTER_X + r_cos_A,
+				CENTER_Y + r_sin_A);
+
+    cairo_pattern_mesh_curve_to (pattern,
+				 CENTER_X + r_cos_A - h * r_sin_A,
+				 CENTER_Y + r_sin_A + h * r_cos_A,
+				 CENTER_X + r_cos_B + h * r_sin_B,
+				 CENTER_Y + r_sin_B - h * r_cos_B,
+				 CENTER_X + r_cos_B,
+				 CENTER_Y + r_sin_B);
+
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 0, 1, 1, 1);
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 1, A_r, A_g, A_b);
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 2, B_r, B_g, B_b);
+
+    cairo_pattern_mesh_end_patch (pattern);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *pattern;
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_translate (cr, PAD, PAD);
+
+    pattern = cairo_pattern_create_mesh ();
+    sector_patch (pattern,
+		  0,         1, 0, 0,
+		  M_PI/4,    1, 1, 0);
+    sector_patch (pattern,
+		  M_PI/4,    0, 1, 0,
+		  M_PI/2,    0, 1, 1);
+    sector_patch (pattern,
+		  M_PI/2,    0, 0, 1,
+		  3*M_PI/4,  1, 0, 1);
+    sector_patch (pattern,
+		  3*M_PI/4,  1, 0, 0,
+		  M_PI,      1, 1, 0);
+    sector_patch (pattern,
+		  -M_PI,     1, 1, 0,
+		  -3*M_PI/4, 0, 1, 0);
+    sector_patch (pattern,
+		  -3*M_PI/4, 0, 1, 0,
+		  -M_PI/2,   0, 1, 1);
+    sector_patch (pattern,
+		  -M_PI/2,   0, 1, 1,
+		  -M_PI/4,   0, 0, 1);
+    sector_patch (pattern,
+		  -M_PI/4,   0, 0, 1,
+		  0,         1, 0, 0);
+
+    cairo_set_source (cr, pattern);
+    cairo_paint (cr);
+    cairo_pattern_destroy (pattern);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (mesh_pattern_conical,
+	    "Paint a conical pattern using a mesh pattern",
+	    "conical,mesh,pattern", /* keywords */
+	    NULL, /* requirements */
+	    WIDTH, HEIGHT,
+	    NULL, draw)
diff --git a/test/mesh-pattern-conical.image16.ref.png b/test/mesh-pattern-conical.image16.ref.png
new file mode 100644
index 0000000..b8f9416
Binary files /dev/null and b/test/mesh-pattern-conical.image16.ref.png differ
diff --git a/test/mesh-pattern-conical.ref.png b/test/mesh-pattern-conical.ref.png
new file mode 100644
index 0000000..f5dc21d
Binary files /dev/null and b/test/mesh-pattern-conical.ref.png differ
diff --git a/test/mesh-pattern-control-points.c b/test/mesh-pattern-control-points.c
new file mode 100644
index 0000000..54ff850
--- /dev/null
+++ b/test/mesh-pattern-control-points.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright © 2009 Adrian Johnson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Adrian Johnson <ajohnson at redneon.com>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 90
+#define PAD 10
+#define WIDTH (PAD + 2 * (SIZE + PAD))
+#define HEIGHT (PAD + SIZE + PAD)
+
+
+/*
+ * This test is designed to paint a two mesh patches. One with default
+ * control points and one with a control point at a no default
+ * location.  The control points of both of them are drawn as squares
+ * to make them visible.
+ */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *pattern;
+    unsigned int i, j;
+    unsigned int num_patches;
+    double x, y;
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_translate (cr, PAD, PAD);
+
+    pattern = cairo_pattern_create_mesh ();
+    cairo_pattern_mesh_begin_patch (pattern);
+
+    cairo_pattern_mesh_move_to (pattern,    0,    0);
+    cairo_pattern_mesh_line_to (pattern, SIZE,    0);
+    cairo_pattern_mesh_line_to (pattern, SIZE, SIZE);
+    cairo_pattern_mesh_line_to (pattern,    0, SIZE);
+
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 0, 1, 0, 0);
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 1, 0, 1, 0);
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 2, 0, 0, 1);
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 3, 1, 1, 0);
+
+    cairo_pattern_mesh_set_control_point (pattern, 0, SIZE * .7, SIZE * .7);
+    cairo_pattern_mesh_set_control_point (pattern, 1, SIZE * .9, SIZE * .7);
+    cairo_pattern_mesh_set_control_point (pattern, 2, SIZE * .9, SIZE * .9);
+    cairo_pattern_mesh_set_control_point (pattern, 3, SIZE * .7, SIZE * .9);
+
+    cairo_pattern_mesh_end_patch (pattern);
+
+    cairo_pattern_mesh_begin_patch (pattern);
+
+    cairo_pattern_mesh_move_to (pattern,   SIZE + PAD,    0);
+    cairo_pattern_mesh_line_to (pattern, 2*SIZE + PAD,    0);
+    cairo_pattern_mesh_line_to (pattern, 2*SIZE + PAD, SIZE);
+    cairo_pattern_mesh_line_to (pattern,   SIZE + PAD, SIZE);
+
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 0, 1, 0, 0);
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 1, 0, 1, 0);
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 2, 0, 0, 1);
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 3, 1, 1, 0);
+
+    cairo_pattern_mesh_end_patch (pattern);
+
+    cairo_set_source (cr, pattern);
+    cairo_paint (cr);
+
+    /* mark the location of the control points */
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_pattern_mesh_get_patch_count (pattern, &num_patches);
+    for (i = 0; i < num_patches; i++) {
+	for (j = 0; j < 4; j++) {
+	    cairo_pattern_mesh_get_control_point (pattern, i, j, &x, &y);
+	    cairo_rectangle (cr, x - 5, y - 5, 10, 10);
+	    cairo_fill (cr);
+	}
+    }
+
+    cairo_pattern_destroy (pattern);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+
+CAIRO_TEST (mesh_pattern_control_points,
+	    "Paint mesh pattern with non default control points",
+	    "mesh,pattern", /* keywords */
+	    NULL, /* requirements */
+	    WIDTH, HEIGHT,
+	    NULL, draw)
diff --git a/test/mesh-pattern-control-points.image16.ref.png b/test/mesh-pattern-control-points.image16.ref.png
new file mode 100644
index 0000000..b664ef9
Binary files /dev/null and b/test/mesh-pattern-control-points.image16.ref.png differ
diff --git a/test/mesh-pattern-control-points.ref.png b/test/mesh-pattern-control-points.ref.png
new file mode 100644
index 0000000..841fc3e
Binary files /dev/null and b/test/mesh-pattern-control-points.ref.png differ
diff --git a/test/mesh-pattern-fold.c b/test/mesh-pattern-fold.c
new file mode 100644
index 0000000..586b0cd
--- /dev/null
+++ b/test/mesh-pattern-fold.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright © 2009 Adrian Johnson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Adrian Johnson <ajohnson at redneon.com>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 100
+#define PAD 15
+#define WIDTH (5*SIZE)
+#define HEIGHT (5*SIZE)
+
+
+/* This test is designed to paint a mesh pattern which folds along
+ * both parameters. */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *pattern;
+
+    cairo_test_paint_checkered (cr);
+
+    pattern = cairo_pattern_create_mesh ();
+
+    cairo_pattern_mesh_begin_patch (pattern);
+
+    cairo_pattern_mesh_move_to (pattern, 1, 1);
+
+    cairo_pattern_mesh_curve_to (pattern, 6, 0, -1, 0, 4, 1);
+    cairo_pattern_mesh_curve_to (pattern, 5, 6, 5, -1, 4, 4);
+    cairo_pattern_mesh_curve_to (pattern, -1, 3, 6, 3, 1, 4);
+    cairo_pattern_mesh_curve_to (pattern, 2, -1, 2, 6, 1, 1);
+
+    cairo_pattern_mesh_set_control_point (pattern, 0, 2, 3);
+    cairo_pattern_mesh_set_control_point (pattern, 1, 3, 3);
+    cairo_pattern_mesh_set_control_point (pattern, 2, 3, 2);
+    cairo_pattern_mesh_set_control_point (pattern, 3, 2, 2);
+
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 0, 1, 0, 0);
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 1, 0, 0, 1);
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 2, 0, 0, 0);
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 3, 0, 1, 0);
+
+    cairo_pattern_mesh_end_patch (pattern);
+
+    cairo_scale (cr, SIZE, SIZE);
+
+    cairo_set_source (cr, pattern);
+    cairo_paint (cr);
+    cairo_pattern_destroy (pattern);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (mesh_pattern_fold,
+	    "Paint a mesh pattern with complex folds",
+	    "mesh,pattern", /* keywords */
+	    NULL, /* requirements */
+	    WIDTH, HEIGHT,
+	    NULL, draw)
diff --git a/test/mesh-pattern-fold.image16.ref.png b/test/mesh-pattern-fold.image16.ref.png
new file mode 100644
index 0000000..4264ad2
Binary files /dev/null and b/test/mesh-pattern-fold.image16.ref.png differ
diff --git a/test/mesh-pattern-fold.ref.png b/test/mesh-pattern-fold.ref.png
new file mode 100644
index 0000000..6275b82
Binary files /dev/null and b/test/mesh-pattern-fold.ref.png differ
diff --git a/test/mesh-pattern-overlap.c b/test/mesh-pattern-overlap.c
new file mode 100644
index 0000000..63cf024
--- /dev/null
+++ b/test/mesh-pattern-overlap.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright © 2009 Adrian Johnson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Adrian Johnson <ajohnson at redneon.com>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 100
+#define PAD 15
+#define WIDTH (PAD + SIZE + PAD)
+#define HEIGHT WIDTH
+
+
+/* This test is designed to paint a mesh pattern with a simple
+ * fold. */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *pattern;
+
+    cairo_test_paint_checkered (cr);
+
+    cairo_translate (cr, PAD, PAD);
+
+    pattern = cairo_pattern_create_mesh ();
+
+    cairo_pattern_mesh_begin_patch (pattern);
+
+    cairo_pattern_mesh_move_to (pattern, 0, 0);
+    cairo_pattern_mesh_curve_to (pattern,  30, -30, 60,  30, 100, 0);
+    cairo_pattern_mesh_curve_to (pattern, 130, 140, 60, -40, 100, 100);
+    cairo_pattern_mesh_curve_to (pattern,  60,  70, 30, 130,   0, 100);
+    cairo_pattern_mesh_curve_to (pattern, -30, -40, 30, 140,   0, 0);
+
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 0, 1, 0, 0);
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 1, 0, 1, 0);
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 2, 0, 0, 1);
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 3, 1, 1, 0);
+
+    cairo_pattern_mesh_end_patch (pattern);
+
+    cairo_set_source (cr, pattern);
+    cairo_paint (cr);
+    cairo_pattern_destroy (pattern);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (mesh_pattern_overlap,
+	    "Paint a mesh pattern with a simple fold",
+	    "mesh,pattern", /* keywords */
+	    NULL, /* requirements */
+	    WIDTH, HEIGHT,
+	    NULL, draw)
diff --git a/test/mesh-pattern-overlap.image16.ref.png b/test/mesh-pattern-overlap.image16.ref.png
new file mode 100644
index 0000000..a67f7dd
Binary files /dev/null and b/test/mesh-pattern-overlap.image16.ref.png differ
diff --git a/test/mesh-pattern-overlap.ref.png b/test/mesh-pattern-overlap.ref.png
new file mode 100644
index 0000000..1394c9e
Binary files /dev/null and b/test/mesh-pattern-overlap.ref.png differ
diff --git a/test/mesh-pattern-transformed.c b/test/mesh-pattern-transformed.c
new file mode 100644
index 0000000..278e4a8
--- /dev/null
+++ b/test/mesh-pattern-transformed.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright © 2009 Adrian Johnson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Adrian Johnson <ajohnson at redneon.com>
+ */
+
+#include "cairo-test.h"
+
+#define PAT_WIDTH  170
+#define PAT_HEIGHT 170
+#define SIZE PAT_WIDTH
+#define PAD 10
+#define WIDTH 190
+#define HEIGHT 140
+
+
+/* This test is designed to paint a mesh pattern containing two
+ * overlapping patches transformed in different ways. */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *pattern;
+
+    cairo_test_paint_checkered (cr);
+
+    cairo_translate (cr, PAD, PAD);
+
+    pattern = cairo_pattern_create_mesh ();
+
+    cairo_pattern_mesh_begin_patch (pattern);
+
+    cairo_pattern_mesh_move_to (pattern, 0, 0);
+    cairo_pattern_mesh_curve_to (pattern, 30, -30,  60,  30, 100, 0);
+    cairo_pattern_mesh_curve_to (pattern, 60,  30, 130,  60, 100, 100);
+    cairo_pattern_mesh_curve_to (pattern, 60,  70,  30, 130,   0, 100);
+    cairo_pattern_mesh_curve_to (pattern, 30,  70, -30,  30,   0, 0);
+
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 0, 1, 0, 0);
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 1, 0, 1, 0);
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 2, 0, 0, 1);
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 3, 1, 1, 0);
+
+    cairo_pattern_mesh_end_patch (pattern);
+
+    cairo_pattern_mesh_begin_patch (pattern);
+
+    cairo_pattern_mesh_move_to (pattern, 50, 50);
+    cairo_pattern_mesh_curve_to (pattern, 80, 20, 110, 80, 150, 50);
+
+    cairo_pattern_mesh_curve_to (pattern, 110, 80, 180, 110, 150, 150);
+
+    cairo_pattern_mesh_curve_to (pattern, 110, 120, 80, 180, 50, 150);
+
+    cairo_pattern_mesh_curve_to (pattern, 80, 120, 20, 80, 50, 50);
+
+    cairo_pattern_mesh_set_corner_color_rgba (pattern, 0, 1, 0, 0, 0.3);
+    cairo_pattern_mesh_set_corner_color_rgb  (pattern, 1, 0, 1, 0);
+    cairo_pattern_mesh_set_corner_color_rgba (pattern, 2, 0, 0, 1, 0.3);
+    cairo_pattern_mesh_set_corner_color_rgb  (pattern, 3, 1, 1, 0);
+
+    cairo_pattern_mesh_end_patch (pattern);
+
+    cairo_scale (cr, .5, .5);
+
+    cairo_set_source (cr, pattern);
+    cairo_paint (cr);
+
+    cairo_translate (cr, PAT_WIDTH, PAT_HEIGHT);
+    cairo_translate (cr, PAT_WIDTH/2, PAT_HEIGHT/2);
+    cairo_rotate (cr, M_PI/4);
+    cairo_translate (cr, -PAT_WIDTH, -PAT_HEIGHT);
+    cairo_set_source (cr, pattern);
+    cairo_paint (cr);
+
+    cairo_pattern_destroy (pattern);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (mesh_pattern_transformed,
+	    "Paint mesh pattern with a transformation",
+	    "mesh,pattern", /* keywords */
+	    NULL, /* requirements */
+	    WIDTH, HEIGHT,
+	    NULL, draw)
+
diff --git a/test/mesh-pattern-transformed.image16.ref.png b/test/mesh-pattern-transformed.image16.ref.png
new file mode 100644
index 0000000..0645b86
Binary files /dev/null and b/test/mesh-pattern-transformed.image16.ref.png differ
diff --git a/test/mesh-pattern-transformed.ref.png b/test/mesh-pattern-transformed.ref.png
new file mode 100644
index 0000000..9aa482f
Binary files /dev/null and b/test/mesh-pattern-transformed.ref.png differ
diff --git a/test/mesh-pattern.c b/test/mesh-pattern.c
new file mode 100644
index 0000000..bcd49be
--- /dev/null
+++ b/test/mesh-pattern.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright © 2009 Adrian Johnson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Adrian Johnson <ajohnson at redneon.com>
+ */
+
+#include "cairo-test.h"
+
+#define PAT_WIDTH  170
+#define PAT_HEIGHT 170
+#define SIZE PAT_WIDTH
+#define PAD 2
+#define WIDTH (PAD + SIZE + PAD)
+#define HEIGHT WIDTH
+
+
+/* This test is designed to paint a mesh pattern. The mesh contains
+ * two overlapping patches */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *pattern;
+
+    cairo_test_paint_checkered (cr);
+
+    cairo_translate (cr, PAD, PAD);
+    cairo_translate (cr, 10, 10);
+
+    pattern = cairo_pattern_create_mesh ();
+
+    cairo_pattern_mesh_begin_patch (pattern);
+
+    cairo_pattern_mesh_move_to (pattern, 0, 0);
+    cairo_pattern_mesh_curve_to (pattern, 30, -30,  60,  30, 100, 0);
+    cairo_pattern_mesh_curve_to (pattern, 60,  30, 130,  60, 100, 100);
+    cairo_pattern_mesh_curve_to (pattern, 60,  70,  30, 130,   0, 100);
+    cairo_pattern_mesh_curve_to (pattern, 30,  70, -30,  30,   0, 0);
+
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 0, 1, 0, 0);
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 1, 0, 1, 0);
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 2, 0, 0, 1);
+    cairo_pattern_mesh_set_corner_color_rgb (pattern, 3, 1, 1, 0);
+
+    cairo_pattern_mesh_end_patch (pattern);
+
+    cairo_pattern_mesh_begin_patch (pattern);
+
+    cairo_pattern_mesh_move_to (pattern, 50, 50);
+    cairo_pattern_mesh_curve_to (pattern,  80,  20, 110,  80, 150, 50);
+    cairo_pattern_mesh_curve_to (pattern, 110,  80, 180, 110, 150, 150);
+    cairo_pattern_mesh_curve_to (pattern, 110, 120,  80, 180,  50, 150);
+    cairo_pattern_mesh_curve_to (pattern,  80, 120,  20,  80,  50, 50);
+
+    cairo_pattern_mesh_set_corner_color_rgba (pattern, 0, 1, 0, 0, 0.3);
+    cairo_pattern_mesh_set_corner_color_rgb  (pattern, 1, 0, 1, 0);
+    cairo_pattern_mesh_set_corner_color_rgba (pattern, 2, 0, 0, 1, 0.3);
+    cairo_pattern_mesh_set_corner_color_rgb  (pattern, 3, 1, 1, 0);
+
+    cairo_pattern_mesh_end_patch (pattern);
+
+    cairo_set_source (cr, pattern);
+    cairo_paint (cr);
+    cairo_pattern_destroy (pattern);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (mesh_pattern,
+	    "Paint mesh pattern",
+	    "mesh,pattern", /* keywords */
+	    NULL, /* requirements */
+	    WIDTH, HEIGHT,
+	    NULL, draw)
diff --git a/test/mesh-pattern.image16.ref.png b/test/mesh-pattern.image16.ref.png
new file mode 100644
index 0000000..bd63538
Binary files /dev/null and b/test/mesh-pattern.image16.ref.png differ
diff --git a/test/mesh-pattern.ref.png b/test/mesh-pattern.ref.png
new file mode 100644
index 0000000..1f76639
Binary files /dev/null and b/test/mesh-pattern.ref.png differ
commit b164187ff6e8e643428165370dd53f9ac5f87bb3
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Fri Dec 24 16:43:23 2010 +0100

    test: Extend pattern-get-type and pattern-getters for mesh patterns
    
    Add testing for mesh patterns to pattern-get-type and pattern-getters.

diff --git a/test/pattern-get-type.c b/test/pattern-get-type.c
index d2ef5bf..c807b14 100644
--- a/test/pattern-get-type.c
+++ b/test/pattern-get-type.c
@@ -29,7 +29,7 @@ static cairo_test_status_t
 preamble (cairo_test_context_t *Ctx)
 {
     cairo_surface_t *surface;
-    cairo_pattern_t *solid_rgb, *solid_rgba, *surface_pattern, *linear, *radial;
+    cairo_pattern_t *solid_rgb, *solid_rgba, *surface_pattern, *linear, *radial, *mesh;
     cairo_test_status_t result = CAIRO_TEST_SUCCESS;
 
     solid_rgb = cairo_pattern_create_rgb (0.0, 0.1, 0.2);
@@ -40,6 +40,7 @@ preamble (cairo_test_context_t *Ctx)
     linear = cairo_pattern_create_linear (0.0, 0.0, 10.0, 10.0);
     radial = cairo_pattern_create_radial (10.0, 10.0, 0.1,
 					  10.0, 10.0, 1.0);
+    mesh = cairo_pattern_create_mesh ();
 
     if (cairo_pattern_get_type (solid_rgb) != CAIRO_PATTERN_TYPE_SOLID)
 	result = CAIRO_TEST_FAILURE;
@@ -56,12 +57,16 @@ preamble (cairo_test_context_t *Ctx)
     if (cairo_pattern_get_type (radial) != CAIRO_PATTERN_TYPE_RADIAL)
 	result = CAIRO_TEST_FAILURE;
 
+    if (cairo_pattern_get_type (mesh) != CAIRO_PATTERN_TYPE_MESH)
+	result = CAIRO_TEST_FAILURE;
+
     cairo_pattern_destroy (solid_rgb);
     cairo_pattern_destroy (solid_rgba);
     cairo_pattern_destroy (surface_pattern);
     cairo_surface_destroy (surface);
     cairo_pattern_destroy (linear);
     cairo_pattern_destroy (radial);
+    cairo_pattern_destroy (mesh);
 
     return result;
 }
diff --git a/test/pattern-getters.c b/test/pattern-getters.c
index 657159c..2d7d904 100644
--- a/test/pattern-getters.c
+++ b/test/pattern-getters.c
@@ -183,6 +183,88 @@ draw (cairo_t *cr, int width, int height)
 	cairo_pattern_destroy (pat);
     }
 
+    /* Test mesh getters */
+    {
+	unsigned int count;
+	int i;
+	pat = cairo_pattern_create_mesh ();
+
+	status = cairo_pattern_mesh_get_patch_count (pat, &count);
+	CHECK_SUCCESS;
+
+	if (count != 0) {
+	    cairo_pattern_destroy (pat);
+	    return CAIRO_TEST_FAILURE;
+	}
+
+	cairo_pattern_mesh_begin_patch (pat);
+	cairo_pattern_mesh_move_to (pat, 0, 0);
+	cairo_pattern_mesh_line_to (pat, 0, 3);
+	cairo_pattern_mesh_line_to (pat, 3, 3);
+	cairo_pattern_mesh_line_to (pat, 3, 0);
+
+	status = cairo_pattern_mesh_get_patch_count (pat, &count);
+	CHECK_SUCCESS;
+
+	if (count != 0) {
+	    cairo_pattern_destroy (pat);
+	    return CAIRO_TEST_FAILURE;
+	}
+
+	cairo_pattern_mesh_end_patch (pat);
+
+	status = cairo_pattern_mesh_get_patch_count (pat, &count);
+	CHECK_SUCCESS;
+
+	if (count != 1) {
+	    cairo_pattern_destroy (pat);
+	    return CAIRO_TEST_FAILURE;
+	}
+
+	for (i = 0; i < 4; i++) {
+	    double cp_x[4] = { 1, 1, 2, 2 };
+	    double cp_y[4] = { 1, 2, 2, 1 };
+	    double x, y;
+
+	    status = cairo_pattern_mesh_get_control_point (pat, 0, i, &x, &y);
+	    CHECK_SUCCESS;
+
+	    if (!CAIRO_TEST_DOUBLE_EQUALS(x,cp_x[i]) ||
+		!CAIRO_TEST_DOUBLE_EQUALS(y,cp_y[i]))
+	    {
+		cairo_pattern_destroy (pat);
+		return CAIRO_TEST_FAILURE;
+	    }
+	}
+
+	cairo_pattern_mesh_begin_patch (pat);
+	cairo_pattern_mesh_move_to (pat, 0, 0);
+	cairo_pattern_mesh_line_to (pat, 1, 0);
+	cairo_pattern_mesh_line_to (pat, 1, 1);
+	cairo_pattern_mesh_set_corner_color_rgb (pat, 0, 1, 1, 1);
+	cairo_pattern_mesh_end_patch (pat);
+
+	for (i = 0; i < 4; i++) {
+	    double corner_color[4] = { 1, 0, 0, 1 };
+	    double a, r, g, b;
+
+	    status = cairo_pattern_mesh_get_corner_color_rgba (pat, 1, i,
+							       &r, &g, &b, &a);
+	    CHECK_SUCCESS;
+
+	    if (!CAIRO_TEST_DOUBLE_EQUALS(a,corner_color[i]) ||
+		!CAIRO_TEST_DOUBLE_EQUALS(r,corner_color[i]) ||
+		!CAIRO_TEST_DOUBLE_EQUALS(g,corner_color[i]) ||
+		!CAIRO_TEST_DOUBLE_EQUALS(b,corner_color[i]))
+	    {
+		cairo_pattern_destroy (pat);
+		return CAIRO_TEST_FAILURE;
+	    }
+	}
+
+	cairo_pattern_destroy (pat);
+    }
+
     cairo_set_source_rgb (cr, 0, 1, 0);
     cairo_paint (cr);
 
commit 96426fdf0154a9438595541a3b86d328ef730075
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Tue Dec 21 11:20:50 2010 +0100

    script: Add support for mesh patterns
    
    Extend CairoScript with operators based on the mesh pattern API.

diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c
index b3aa69d..9e12e57 100644
--- a/src/cairo-script-surface.c
+++ b/src/cairo-script-surface.c
@@ -957,6 +957,87 @@ _emit_radial_pattern (cairo_script_surface_t *surface,
 }
 
 static cairo_status_t
+_emit_mesh_pattern (cairo_script_surface_t *surface,
+		    const cairo_pattern_t *pattern)
+{
+    cairo_script_context_t *ctx = to_context (surface);
+    cairo_pattern_t *mesh;
+    cairo_status_t status;
+    unsigned int i, n;
+
+    mesh = (cairo_pattern_t *) pattern;
+    status = cairo_pattern_mesh_get_patch_count (mesh, &n);
+    if (unlikely (status))
+	return status;
+
+    _cairo_output_stream_printf (ctx->stream, "mesh");
+    for (i = 0; i < n; i++) {
+	cairo_path_t *path;
+	cairo_path_data_t *data;
+	int j;
+
+	_cairo_output_stream_printf (ctx->stream, "\n  mesh-begin-patch");
+
+	path = cairo_pattern_mesh_get_path (mesh, i);
+	if (unlikely (path->status))
+	    return path->status;
+
+	for (j = 0; j < path->num_data; j+=data[0].header.length) {
+	    data = &path->data[j];
+	    switch (data->header.type) {
+	    case CAIRO_PATH_MOVE_TO:
+		_cairo_output_stream_printf (ctx->stream,
+					     "\n  %f %f mesh-move-to",
+					     data[1].point.x, data[1].point.y);
+		break;
+	    case CAIRO_PATH_LINE_TO:
+		_cairo_output_stream_printf (ctx->stream,
+					     "\n  %f %f mesh-line-to",
+					     data[1].point.x, data[1].point.y);
+		break;
+	    case CAIRO_PATH_CURVE_TO:
+		_cairo_output_stream_printf (ctx->stream,
+					     "\n  %f %f %f %f %f %f mesh-curve-to",
+					     data[1].point.x, data[1].point.y,
+					     data[2].point.x, data[2].point.y,
+					     data[3].point.x, data[3].point.y);
+		break;
+	    case CAIRO_PATH_CLOSE_PATH:
+		break;
+	    }
+	}
+	cairo_path_destroy (path);
+
+	for (j = 0; j < 4; j++) {
+	    double x, y;
+
+	    status = cairo_pattern_mesh_get_control_point (mesh, i, j, &x, &y);
+	    if (unlikely (status))
+		return status;
+	    _cairo_output_stream_printf (ctx->stream,
+					 "\n  %d %f %f mesh-set-control-point",
+					 j, x, y);
+	}
+
+	for (j = 0; j < 4; j++) {
+	    double r, g, b, a;
+
+	    status = cairo_pattern_mesh_get_corner_color_rgba (mesh, i, j, &r, &g, &b, &a);
+	    if (unlikely (status))
+		return status;
+
+	    _cairo_output_stream_printf (ctx->stream,
+					 "\n  %d %f %f %f %f mesh-set-corner-color",
+					 j, r, g, b, a);
+	}
+
+	_cairo_output_stream_printf (ctx->stream, "\n  mesh-end-patch");
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
 _emit_recording_surface_pattern (cairo_script_surface_t *surface,
 				 cairo_recording_surface_t *source)
 {
@@ -1465,6 +1546,10 @@ _emit_pattern (cairo_script_surface_t *surface,
 	status = _emit_radial_pattern (surface, pattern);
 	is_default_extend = pattern->extend == CAIRO_EXTEND_GRADIENT_DEFAULT;
 	break;
+    case CAIRO_PATTERN_TYPE_MESH:
+	status = _emit_mesh_pattern (surface, pattern);
+	is_default_extend = TRUE;
+	break;
     case CAIRO_PATTERN_TYPE_SURFACE:
 	status = _emit_surface_pattern (surface, pattern);
 	is_default_extend = pattern->extend == CAIRO_EXTEND_SURFACE_DEFAULT;
diff --git a/util/cairo-script/cairo-script-operators.c b/util/cairo-script/cairo-script-operators.c
index 7d8297c..e447b2e 100644
--- a/util/cairo-script/cairo-script-operators.c
+++ b/util/cairo-script/cairo-script-operators.c
@@ -3592,6 +3592,199 @@ _matrix (csi_t *ctx)
 }
 
 static csi_status_t
+_mesh (csi_t *ctx)
+{
+    csi_object_t obj;
+
+    obj.type = CSI_OBJECT_TYPE_PATTERN;
+    obj.datum.pattern = cairo_pattern_create_mesh ();
+    return push (&obj);
+}
+
+static csi_status_t
+_mesh_begin_patch (csi_t *ctx)
+{
+    csi_status_t status;
+    cairo_pattern_t *pattern = NULL; /* silence the compiler */
+
+    check (1);
+
+    status = _csi_ostack_get_pattern (ctx, 0, &pattern);
+    if (_csi_unlikely (status))
+	return status;
+
+    cairo_pattern_mesh_begin_patch (pattern);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_mesh_curve_to (csi_t *ctx)
+{
+    csi_status_t status;
+    double x1, y1, x2, y2, x3, y3;
+    cairo_pattern_t *pattern = NULL; /* silence the compiler */
+
+    check (7);
+
+    status = _csi_ostack_get_number (ctx, 0, &y3);
+    if (_csi_unlikely (status))
+	return status;
+    status = _csi_ostack_get_number (ctx, 1, &x3);
+    if (_csi_unlikely (status))
+	return status;
+    status = _csi_ostack_get_number (ctx, 2, &y2);
+    if (_csi_unlikely (status))
+	return status;
+    status = _csi_ostack_get_number (ctx, 3, &x2);
+    if (_csi_unlikely (status))
+	return status;
+    status = _csi_ostack_get_number (ctx, 4, &y1);
+    if (_csi_unlikely (status))
+	return status;
+    status = _csi_ostack_get_number (ctx, 5, &x1);
+    if (_csi_unlikely (status))
+	return status;
+    status = _csi_ostack_get_pattern (ctx, 6, &pattern);
+    if (_csi_unlikely (status))
+	return status;
+
+    cairo_pattern_mesh_curve_to (pattern, x1, y1, x2, y2, x3, y3);
+
+    pop (6);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_mesh_end_patch (csi_t *ctx)
+{
+    csi_status_t status;
+    cairo_pattern_t *pattern = NULL; /* silence the compiler */
+
+    check (1);
+
+    status = _csi_ostack_get_pattern (ctx, 0, &pattern);
+    if (_csi_unlikely (status))
+	return status;
+
+    cairo_pattern_mesh_end_patch (pattern);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_mesh_line_to (csi_t *ctx)
+{
+    csi_status_t status;
+    double x, y;
+    cairo_pattern_t *pattern = NULL; /* silence the compiler */
+
+    check (3);
+
+    status = _csi_ostack_get_number (ctx, 0, &y);
+    if (_csi_unlikely (status))
+	return status;
+    status = _csi_ostack_get_number (ctx, 1, &x);
+    if (_csi_unlikely (status))
+	return status;
+    status = _csi_ostack_get_pattern (ctx, 2, &pattern);
+    if (_csi_unlikely (status))
+	return status;
+
+    cairo_pattern_mesh_line_to (pattern, x, y);
+
+    pop (2);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_mesh_move_to (csi_t *ctx)
+{
+    csi_status_t status;
+    double x, y;
+    cairo_pattern_t *pattern = NULL; /* silence the compiler */
+
+    check (3);
+
+    status = _csi_ostack_get_number (ctx, 0, &y);
+    if (_csi_unlikely (status))
+	return status;
+    status = _csi_ostack_get_number (ctx, 1, &x);
+    if (_csi_unlikely (status))
+	return status;
+    status = _csi_ostack_get_pattern (ctx, 2, &pattern);
+    if (_csi_unlikely (status))
+	return status;
+
+    cairo_pattern_mesh_move_to (pattern, x, y);
+
+    pop (2);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_mesh_set_control_point (csi_t *ctx)
+{
+    csi_status_t status;
+    double x, y;
+    csi_integer_t point;
+    cairo_pattern_t *pattern = NULL; /* silence the compiler */
+
+    check (4);
+
+    status = _csi_ostack_get_number (ctx, 0, &y);
+    if (_csi_unlikely (status))
+	return status;
+    status = _csi_ostack_get_number (ctx, 1, &x);
+    if (_csi_unlikely (status))
+	return status;
+    status = _csi_ostack_get_integer (ctx, 2, &point);
+    if (_csi_unlikely (status))
+	return status;
+    status = _csi_ostack_get_pattern (ctx, 3, &pattern);
+    if (_csi_unlikely (status))
+	return status;
+
+    cairo_pattern_mesh_set_control_point (pattern, point, x, y);
+
+    pop (3);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_mesh_set_corner_color (csi_t *ctx)
+{
+    csi_status_t status;
+    double r, g, b, a;
+    csi_integer_t corner;
+    cairo_pattern_t *pattern = NULL; /* silence the compiler */
+
+    check (6);
+
+    status = _csi_ostack_get_number (ctx, 0, &a);
+    if (_csi_unlikely (status))
+	return status;
+    status = _csi_ostack_get_number (ctx, 1, &b);
+    if (_csi_unlikely (status))
+	return status;
+    status = _csi_ostack_get_number (ctx, 2, &g);
+    if (_csi_unlikely (status))
+	return status;
+    status = _csi_ostack_get_number (ctx, 3, &r);
+    if (_csi_unlikely (status))
+	return status;
+    status = _csi_ostack_get_integer (ctx, 4, &corner);
+    if (_csi_unlikely (status))
+	return status;
+    status = _csi_ostack_get_pattern (ctx, 5, &pattern);
+    if (_csi_unlikely (status))
+	return status;
+
+    cairo_pattern_mesh_set_corner_color_rgba (pattern, corner, r, g, b, a);
+
+    pop (5);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
 _mod (csi_t *ctx)
 {
     csi_integer_t x, y;
@@ -6038,6 +6231,14 @@ _defs[] = {
     { "mark", _mark },
     { "mask", _mask },
     { "matrix", _matrix },
+    { "mesh", _mesh },
+    { "mesh-begin-patch", _mesh_begin_patch },
+    { "mesh-curve-to", _mesh_curve_to },
+    { "mesh-end-patch", _mesh_end_patch },
+    { "mesh-line-to", _mesh_line_to },
+    { "mesh-move-to", _mesh_move_to },
+    { "mesh-set-control-point", _mesh_set_control_point },
+    { "mesh-set-corner-color", _mesh_set_corner_color },
     { "mod", _mod },
     { "move-to", _move_to },
     { "mul", _mul },
commit c243f3ac9c451e0c203c820a80fb869bbec4c06d
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Thu Dec 9 11:01:46 2010 +0100

    pattern: Add public mesh pattern API
    
    Add public funcions to create and define mesh patterns and getters to
    examine their definition.

diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index 006e895..ef29ad3 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -31,6 +31,7 @@
 #include "cairoint.h"
 #include "cairo-error-private.h"
 #include "cairo-freed-pool-private.h"
+#include "cairo-path-private.h"
 
 #include <float.h>
 
@@ -786,6 +787,186 @@ cairo_pattern_create_radial (double cx0, double cy0, double radius0,
     return &pattern->base.base;
 }
 
+/* This order is specified in the diagram in the documentation for
+ * cairo_pattern_create_mesh() */
+static const int mesh_path_point_i[12] = { 0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 2, 1 };
+static const int mesh_path_point_j[12] = { 0, 1, 2, 3, 3, 3, 3, 2, 1, 0, 0, 0 };
+static const int mesh_control_point_i[4] = { 1, 1, 2, 2 };
+static const int mesh_control_point_j[4] = { 1, 2, 2, 1 };
+
+/**
+ * cairo_pattern_create_mesh:
+ *
+ * Create a new mesh pattern.
+ *
+ * Mesh patterns are tensor-product patch meshes (type 7 shadings in
+ * PDF). Mesh patterns may also be used to create other types of
+ * shadings that are special cases of tensor-product patch meshes such
+ * as Coons patch meshes (type 6 shading in PDF) and Gouraud-shaded
+ * triangle meshes (type 4 and 5 shadings in PDF).
+ *
+ * Mesh patterns consist of one or more tensor-product patches, which
+ * should be defined before using the mesh pattern. Using a mesh
+ * pattern with a partially defined patch as source or mask will put
+ * the context in an error status with a status of
+ * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
+ *
+ * A tensor-product patch is defined by 4 Bézier curves (side 0, 1, 2,
+ * 3) and by 4 additional control points (P0, P1, P2, P3) that provide
+ * further control over the patch and complete the definition of the
+ * tensor-product patch. The corner C0 is the first point of the
+ * patch.
+ *
+ * Degenerate sides are permitted so straight lines may be used. A
+ * zero length line on one side may be used to create 3 sided patches.
+ *
+ * <informalexample><programlisting>
+ *       C1     Side 1       C2
+ *        +---------------+
+ *        |               |
+ *        |  P1       P2  |
+ *        |               |
+ * Side 0 |               | Side 2
+ *        |               |
+ *        |               |
+ *        |  P0       P3  |
+ *        |               |
+ *        +---------------+
+ *      C0     Side 3        C3
+ * </programlisting></informalexample>
+ *
+ * Each patch is constructed by first calling
+ * cairo_pattern_mesh_begin_patch(), then cairo_pattern_mesh_move_to()
+ * to specify the first point in the patch (C0). Then the sides are
+ * specified with calls to cairo_pattern_mesh_curve_to() and
+ * cairo_pattern_mesh_line_to().
+ *
+ * The four additional control points (P0, P1, P2, P3) in a patch can
+ * be specified with cairo_pattern_mesh_set_control_point().
+ *
+ * At each corner of the patch (C0, C1, C2, C3) a color may be
+ * specified with cairo_pattern_mesh_set_corner_color_rgb() or
+ * cairo_pattern_mesh_set_corner_color_rgba(). Any corner whose color
+ * is not explicitly specified defaults to transparent black.
+ *
+ * A Coons patch is a special case of the tensor-product patch where
+ * the control points are implicitly defined by the sides of the
+ * patch. The default value for any control point not specified is the
+ * implicit value for a Coons patch, i.e. if no control points are
+ * specified the patch is a Coons patch.
+ *
+ * A triangle is a special case of the tensor-product patch where the
+ * control points are implicitly defined by the sides of the patch,
+ * all the sides are lines and one of them has length 0, i.e. if the
+ * patch is specified using just 3 lines, it is a triangle. If the
+ * corners connected by the 0-length side have the same color, the
+ * patch is a Gouraud-shaded triangle.
+ *
+ * Patches may be oriented differently to the above diagram. For
+ * example the first point could be at the top left. The diagram only
+ * shows the relationship between the sides, corners and control
+ * points. Regardless of where the first point is located, when
+ * specifying colors, corner 0 will always be the first point, corner
+ * 1 the point between side 0 and side 1 etc.
+ *
+ * Calling cairo_pattern_mesh_end_patch() completes the current
+ * patch. If less than 4 sides have been defined, the first missing
+ * side is defined as a line from the current point to the first point
+ * of the patch (C0) and the other sides are degenerate lines from C0
+ * to C0. The corners between the added sides will all be coincident
+ * with C0 of the patch and their color will be set to be the same as
+ * the color of C0.
+ *
+ * Additional patches may be added with additional calls to
+ * cairo_pattern_mesh_begin_patch()/cairo_pattern_mesh_end_patch().
+ *
+ * <informalexample><programlisting>
+ * cairo_pattern_t *mesh = cairo_pattern_mesh_create_mesh ();
+ *
+ * /&ast; Add a Coons patch &ast;/
+ * cairo_pattern_mesh_begin_patch (mesh);
+ * cairo_pattern_mesh_move_to (pattern, 0, 0);
+ * cairo_pattern_mesh_curve_to (pattern, 30, -30,  60,  30, 100, 0);
+ * cairo_pattern_mesh_curve_to (pattern, 60,  30, 130,  60, 100, 100);
+ * cairo_pattern_mesh_curve_to (pattern, 60,  70,  30, 130,   0, 100);
+ * cairo_pattern_mesh_curve_to (pattern, 30,  70, -30,  30,   0, 0);
+ * cairo_pattern_mesh_set_corner_color_rgb (pattern, 0, 1, 0, 0);
+ * cairo_pattern_mesh_set_corner_color_rgb (pattern, 1, 0, 1, 0);
+ * cairo_pattern_mesh_set_corner_color_rgb (pattern, 2, 0, 0, 1);
+ * cairo_pattern_mesh_set_corner_color_rgb (pattern, 3, 1, 1, 0);
+ * cairo_pattern_mesh_end_patch (mesh);
+ *
+ * /&ast; Add a Gouraud-shaded triangle &ast;/
+ * cairo_pattern_mesh_begin_patch (mesh)
+ * cairo_pattern_mesh_move_to (pattern, 100, 100);
+ * cairo_pattern_mesh_line_to (pattern, 130, 130);
+ * cairo_pattern_mesh_line_to (pattern, 130,  70);
+ * cairo_pattern_mesh_set_corner_color_rgb (pattern, 0, 1, 0, 0);
+ * cairo_pattern_mesh_set_corner_color_rgb (pattern, 1, 0, 1, 0);
+ * cairo_pattern_mesh_set_corner_color_rgb (pattern, 2, 0, 0, 1);
+ * cairo_pattern_mesh_end_patch (mesh)
+ * </programlisting></informalexample>
+ *
+ * When two patches overlap, the last one that has been added is drawn
+ * over the first one.
+ *
+ * When a patch folds over itself, points are sorted depending on
+ * their parameter coordinates inside the patch. The v coordinate
+ * ranges from 0 to 1 when moving from side 3 to side 1; the u
+ * coordinate ranges from 0 to 1 when going from side 0 to side
+ * 2. Points with higher v coordinate hide points with lower v
+ * coordinate. When two points have the same v coordinate, the one
+ * with higher u coordinate is above. This means that points nearer to
+ * side 1 are above points nearer to side 3; when this is not
+ * sufficient to decide which point is above (for example when both
+ * points belong to side 1 or side 3) points nearer to side 2 are
+ * above points nearer to side 0.
+ *
+ * For a complete definition of tensor-product patches, see the PDF
+ * specification (ISO32000), which describes the parametrization in
+ * detail.
+ *
+ * Note: The coordinates are always in pattern space. For a new
+ * pattern, pattern space is identical to user space, but the
+ * relationship between the spaces can be changed with
+ * cairo_pattern_set_matrix().
+ *
+ * Return value: the newly created #cairo_pattern_t if successful, or
+ * an error pattern in case of no memory. The caller owns the returned
+ * object and should call cairo_pattern_destroy() when finished with
+ * it.
+ *
+ * This function will always return a valid pointer, but if an error
+ * occurred the pattern status will be set to an error. To inspect the
+ * status of a pattern use cairo_pattern_status().
+ *
+ * Since: 1.12
+ **/
+cairo_pattern_t *
+cairo_pattern_create_mesh (void)
+{
+    cairo_mesh_pattern_t *pattern;
+
+    pattern =
+	_freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_MESH]);
+    if (unlikely (pattern == NULL)) {
+	pattern = malloc (sizeof (cairo_mesh_pattern_t));
+	if (unlikely (pattern == NULL)) {
+	    _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+	    return (cairo_pattern_t *) &_cairo_pattern_nil.base;
+	}
+    }
+
+    CAIRO_MUTEX_INITIALIZE ();
+
+    _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_MESH);
+    _cairo_array_init (&pattern->patches, sizeof (cairo_mesh_patch_t));
+    pattern->current_patch = NULL;
+    CAIRO_REFERENCE_COUNT_INIT (&pattern->base.ref_count, 1);
+
+    return &pattern->base;
+}
+
 /**
  * cairo_pattern_reference:
  * @pattern: a #cairo_pattern_t
@@ -951,6 +1132,451 @@ cairo_pattern_set_user_data (cairo_pattern_t		 *pattern,
 					    key, user_data, destroy);
 }
 
+/**
+ * cairo_pattern_mesh_begin_patch:
+ * @pattern: a #cairo_pattern_t
+ *
+ * Begin a patch in a mesh pattern.
+ *
+ * After calling this function, the patch shape should be defined with
+ * cairo_pattern_mesh_move_to(), cairo_pattern_mesh_line_to() and
+ * cairo_pattern_mesh_curve_to().
+ *
+ * After defining the patch, cairo_pattern_mesh_end_patch() must be
+ * called before using @pattern as a source or mask.
+ *
+ * Note: If @pattern is not a mesh pattern then @pattern will be put
+ * into an error status with a status of
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern already has a
+ * current patch, it will be put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_pattern_mesh_begin_patch (cairo_pattern_t *pattern)
+{
+    cairo_mesh_pattern_t *mesh;
+    cairo_status_t status;
+    cairo_mesh_patch_t *current_patch;
+    int i;
+
+    if (unlikely (pattern->status))
+	return;
+
+    if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) {
+	_cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+	return;
+    }
+
+    mesh = (cairo_mesh_pattern_t *) pattern;
+    if (unlikely (mesh->current_patch)) {
+	_cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+	return;
+    }
+
+    status = _cairo_array_allocate (&mesh->patches, 1, (void **) &current_patch);
+    if (unlikely (status)) {
+	_cairo_pattern_set_error (pattern, status);
+	return;
+    }
+
+    mesh->current_patch = current_patch;
+    mesh->current_side = -2; /* no current point */
+
+    for (i = 0; i < 4; i++)
+	mesh->has_control_point[i] = FALSE;
+
+    for (i = 0; i < 4; i++)
+	mesh->has_color[i] = FALSE;
+}
+
+
+static void
+_calc_control_point (cairo_mesh_patch_t *patch, int control_point)
+{
+    /* The Coons patch is a special case of the Tensor Product patch
+     * where the four control points are:
+     *
+     * P11 = S(1/3, 1/3)
+     * P12 = S(1/3, 2/3)
+     * P21 = S(2/3, 1/3)
+     * P22 = S(2/3, 2/3)
+     *
+     * where S is the gradient surface.
+     *
+     * When one or more control points has not been specified
+     * calculated the Coons patch control points are substituted. If
+     * no control points are specified the gradient will be a Coons
+     * patch.
+     *
+     * The equations below are defined in the ISO32000 standard.
+     */
+    cairo_point_double_t *p[3][3];
+    int cp_i, cp_j, i, j;
+
+    cp_i = mesh_control_point_i[control_point];
+    cp_j = mesh_control_point_j[control_point];
+
+    for (i = 0; i < 3; i++)
+	for (j = 0; j < 3; j++)
+	    p[i][j] = &patch->points[cp_i ^ i][cp_j ^ j];
+
+    p[0][0]->x = (- 4 * p[1][1]->x
+		  + 6 * (p[1][0]->x + p[0][1]->x)
+		  - 2 * (p[1][2]->x + p[2][1]->x)
+		  + 3 * (p[2][0]->x + p[0][2]->x)
+		  - 1 * p[2][2]->x) * (1. / 9);
+
+    p[0][0]->y = (- 4 * p[1][1]->y
+		  + 6 * (p[1][0]->y + p[0][1]->y)
+		  - 2 * (p[1][2]->y + p[2][1]->y)
+		  + 3 * (p[2][0]->y + p[0][2]->y)
+		  - 1 * p[2][2]->y) * (1. / 9);
+}
+
+/**
+ * cairo_pattern_mesh_end_patch:
+ * @pattern: a #cairo_pattern_t
+ *
+ * Indicates the end of the current patch in a mesh pattern.
+ *
+ * If the current patch has less than 4 sides, it is closed with a
+ * straight line from the current point to the first point of the
+ * patch as if cairo_pattern_mesh_line_to() was used.
+ *
+ * Note: If @pattern is not a mesh pattern then @pattern will be put
+ * into an error status with a status of
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern has no current
+ * patch or the current patch has no current point, @pattern will be
+ * put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_pattern_mesh_end_patch (cairo_pattern_t *pattern)
+{
+    cairo_mesh_pattern_t *mesh;
+    cairo_mesh_patch_t *current_patch;
+    int i;
+
+    if (unlikely (pattern->status))
+	return;
+
+    if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) {
+	_cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+	return;
+    }
+
+    mesh = (cairo_mesh_pattern_t *) pattern;
+    current_patch = mesh->current_patch;
+    if (unlikely (!current_patch)) {
+	_cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+	return;
+    }
+
+    if (unlikely (mesh->current_side == -2)) {
+	_cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+	return;
+    }
+
+    while (mesh->current_side < 3) {
+	int corner_num;
+
+	cairo_pattern_mesh_line_to (pattern,
+				    current_patch->points[0][0].x,
+				    current_patch->points[0][0].y);
+
+	corner_num = mesh->current_side + 1;
+	if (corner_num < 4 && ! mesh->has_color[corner_num]) {
+	    current_patch->colors[corner_num] = current_patch->colors[0];
+	    mesh->has_color[corner_num] = TRUE;
+	}
+    }
+
+    for (i = 0; i < 4; i++) {
+	if (! mesh->has_control_point[i])
+	    _calc_control_point (current_patch, i);
+    }
+
+    for (i = 0; i < 4; i++) {
+	if (! mesh->has_color[i])
+	    current_patch->colors[i] = *CAIRO_COLOR_TRANSPARENT;
+    }
+
+    mesh->current_patch = NULL;
+}
+
+/**
+ * cairo_pattern_mesh_curve_to:
+ * @pattern: a #cairo_pattern_t
+ * @x1: the X coordinate of the first control point
+ * @y1: the Y coordinate of the first control point
+ * @x2: the X coordinate of the second control point
+ * @y2: the Y coordinate of the second control point
+ * @x3: the X coordinate of the end of the curve
+ * @y3: the Y coordinate of the end of the curve
+ *
+ * Adds a cubic Bézier spline to the current patch from the current
+ * point to position (@x3, @y3) in pattern-space coordinates, using
+ * (@x1, @y1) and (@x2, @y2) as the control points.
+ *
+ * If the current patch has no current point before the call to
+ * cairo_pattern_mesh_curve_to(), this function will behave as if
+ * preceded by a call to cairo_pattern_mesh_move_to(@pattern, @x1,
+ * @y1).
+ *
+ * After this call the current point will be (@x3, @y3).
+ *
+ * Note: If @pattern is not a mesh pattern then @pattern will be put
+ * into an error status with a status of
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern has no current
+ * patch or the current patch already has 4 sides, @pattern will be
+ * put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_pattern_mesh_curve_to (cairo_pattern_t *pattern,
+			     double x1, double y1,
+			     double x2, double y2,
+			     double x3, double y3)
+{
+    cairo_mesh_pattern_t *mesh;
+    int current_point, i, j;
+
+    if (unlikely (pattern->status))
+	return;
+
+    if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) {
+	_cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+	return;
+    }
+
+    mesh = (cairo_mesh_pattern_t *) pattern;
+    if (unlikely (!mesh->current_patch)) {
+	_cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+	return;
+    }
+
+    if (unlikely (mesh->current_side == 3)) {
+	_cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+	return;
+    }
+
+    if (mesh->current_side == -2)
+	cairo_pattern_mesh_move_to (pattern, x1, y1);
+
+    assert (mesh->current_side >= -1);
+    assert (pattern->status == CAIRO_STATUS_SUCCESS);
+
+    mesh->current_side++;
+
+    current_point = 3 * mesh->current_side;
+
+    current_point++;
+    i = mesh_path_point_i[current_point];
+    j = mesh_path_point_j[current_point];
+    mesh->current_patch->points[i][j].x = x1;
+    mesh->current_patch->points[i][j].y = y1;
+
+    current_point++;
+    i = mesh_path_point_i[current_point];
+    j = mesh_path_point_j[current_point];
+    mesh->current_patch->points[i][j].x = x2;
+    mesh->current_patch->points[i][j].y = y2;
+
+    current_point++;
+    if (current_point < 12) {
+	i = mesh_path_point_i[current_point];
+	j = mesh_path_point_j[current_point];
+	mesh->current_patch->points[i][j].x = x3;
+	mesh->current_patch->points[i][j].y = y3;
+    }
+}
+slim_hidden_def (cairo_pattern_mesh_curve_to);
+
+/**
+ * cairo_pattern_mesh_line_to:
+ * @pattern: a #cairo_pattern_t
+ * @x: the X coordinate of the end of the new line
+ * @y: the Y coordinate of the end of the new line
+ *
+ * Adds a line to the current patch from the current point to position
+ * (@x, @y) in pattern-space coordinates.
+ *
+ * If there is no current point before the call to
+ * cairo_pattern_mesh_line_to() this function will behave as
+ * cairo_pattern_mesh_move_to(@pattern, @x, @y).
+ *
+ * After this call the current point will be (@x, @y).
+ *
+ * Note: If @pattern is not a mesh pattern then @pattern will be put
+ * into an error status with a status of
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern has no current
+ * patch or the current patch already has 4 sides, @pattern will be
+ * put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_pattern_mesh_line_to (cairo_pattern_t *pattern,
+			    double x, double y)
+{
+    cairo_mesh_pattern_t *mesh;
+    cairo_point_double_t last_point;
+    int last_point_idx, i, j;
+
+    if (unlikely (pattern->status))
+	return;
+
+    if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) {
+	_cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+	return;
+    }
+
+    mesh = (cairo_mesh_pattern_t *) pattern;
+    if (unlikely (!mesh->current_patch)) {
+	_cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+	return;
+    }
+
+    if (unlikely (mesh->current_side == 3)) {
+	_cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+	return;
+    }
+
+    if (mesh->current_side == -2) {
+	cairo_pattern_mesh_move_to (pattern, x, y);
+	return;
+    }
+
+    last_point_idx = 3 * (mesh->current_side + 1);
+    i = mesh_path_point_i[last_point_idx];
+    j = mesh_path_point_j[last_point_idx];
+
+    last_point = mesh->current_patch->points[i][j];
+
+    cairo_pattern_mesh_curve_to (pattern,
+				 (2 * last_point.x + x) * (1. / 3),
+				 (2 * last_point.y + y) * (1. / 3),
+				 (last_point.x + 2 * x) * (1. / 3),
+				 (last_point.y + 2 * y) * (1. / 3),
+				 x, y);
+}
+slim_hidden_def (cairo_pattern_mesh_line_to);
+
+/**
+ * cairo_pattern_mesh_move_to:
+ * @pattern: a #cairo_pattern_t
+ * @x: the X coordinate of the new position
+ * @y: the Y coordinate of the new position
+ *
+ * Define the first point of the current patch in a mesh pattern.
+ *
+ * After this call the current point will be (@x, @y).
+ *
+ * Note: If @pattern is not a mesh pattern then @pattern will be put
+ * into an error status with a status of
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern has no current
+ * patch or the current patch already has at leas one side, @pattern
+ * will be put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_pattern_mesh_move_to (cairo_pattern_t *pattern,
+			    double x, double y)
+{
+    cairo_mesh_pattern_t *mesh;
+
+    if (unlikely (pattern->status))
+	return;
+
+    if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) {
+	_cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+	return;
+    }
+
+    mesh = (cairo_mesh_pattern_t *) pattern;
+    if (unlikely (!mesh->current_patch)) {
+	_cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+	return;
+    }
+
+    if (unlikely (mesh->current_side >= 0)) {
+	_cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+	return;
+    }
+
+    mesh->current_side = -1;
+    mesh->current_patch->points[0][0].x = x;
+    mesh->current_patch->points[0][0].y = y;
+}
+slim_hidden_def (cairo_pattern_mesh_move_to);
+
+/**
+ * cairo_pattern_mesh_set_control_point:
+ * @pattern: a #cairo_pattern_t
+ * @point_num: the control point to set the position for
+ * @x: the X coordinate of the control point
+ * @y: the Y coordinate of the control point
+ *
+ * Set an internal control point of the current patch.
+ *
+ * Valid values for @point_num are from 0 to 3 and identify the
+ * control points as explained in cairo_pattern_create_mesh().
+ *
+ * Note: If @pattern is not a mesh pattern then @pattern will be put
+ * into an error status with a status of
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @point_num is not valid,
+ * @pattern will be put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_INDEX.  If @pattern has no current patch,
+ * @pattern will be put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_pattern_mesh_set_control_point (cairo_pattern_t *pattern,
+				      unsigned int     point_num,
+				      double           x,
+				      double           y)
+{
+    cairo_mesh_pattern_t *mesh;
+    int i, j;
+
+    if (unlikely (pattern->status))
+	return;
+
+    if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) {
+	_cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+	return;
+    }
+
+    if (unlikely (point_num > 3)) {
+	_cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_INDEX);
+	return;
+    }
+
+    mesh = (cairo_mesh_pattern_t *) pattern;
+    if (unlikely (!mesh->current_patch)) {
+	_cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+	return;
+    }
+
+    i = mesh_control_point_i[point_num];
+    j = mesh_control_point_j[point_num];
+
+    mesh->current_patch->points[i][j].x = x;
+    mesh->current_patch->points[i][j].y = y;
+    mesh->has_control_point[point_num] = TRUE;
+}
+
 /* make room for at least one more color stop */
 static cairo_status_t
 _cairo_pattern_gradient_grow (cairo_gradient_pattern_t *pattern)
@@ -993,7 +1619,127 @@ _cairo_pattern_gradient_grow (cairo_gradient_pattern_t *pattern)
 }
 
 static void
-_cairo_pattern_add_color_stop (cairo_gradient_pattern_t *pattern,
+_cairo_pattern_mesh_set_corner_color (cairo_mesh_pattern_t *mesh,
+				      unsigned int     corner_num,
+				      double red, double green, double blue,
+				      double alpha)
+{
+    cairo_color_t *color;
+
+    assert (mesh->current_patch);
+    assert (corner_num <= 3);
+
+    color = &mesh->current_patch->colors[corner_num];
+    color->red   = red;
+    color->green = green;
+    color->blue  = blue;
+    color->alpha = alpha;
+
+    color->red_short   = _cairo_color_double_to_short (red);
+    color->green_short = _cairo_color_double_to_short (green);
+    color->blue_short  = _cairo_color_double_to_short (blue);
+    color->alpha_short = _cairo_color_double_to_short (alpha);
+
+    mesh->has_color[corner_num] = TRUE;
+}
+
+/**
+ * cairo_pattern_mesh_set_corner_color_rgb:
+ * @pattern: a #cairo_pattern_t
+ * @corner_num: the corner to set the color for
+ * @red: red component of color
+ * @green: green component of color
+ * @blue: blue component of color
+ *
+ * Sets the color of a corner of the current patch in a mesh pattern.
+ *
+ * The color is specified in the same way as in cairo_set_source_rgb().
+ *
+ * Valid values for @corner_num are from 0 to 3 and identify the
+ * corners as explained in cairo_pattern_create_mesh().
+ *
+ * Note: If @pattern is not a mesh pattern then @pattern will be put
+ * into an error status with a status of
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @corner_num is not valid,
+ * @pattern will be put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_INDEX.  If @pattern has no current patch,
+ * @pattern will be put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_pattern_mesh_set_corner_color_rgb (cairo_pattern_t *pattern,
+					 unsigned int     corner_num,
+					 double red, double green, double blue)
+{
+    cairo_pattern_mesh_set_corner_color_rgba (pattern, corner_num, red, green, blue, 1.0);
+}
+
+/**
+ * cairo_pattern_mesh_set_corner_color_rgba:
+ * @pattern: a #cairo_pattern_t
+ * @corner_num: the corner to set the color for
+ * @red: red component of color
+ * @green: green component of color
+ * @blue: blue component of color
+ * @alpha: alpha component of color
+ *
+ * Sets the color of a corner of the current patch in a mesh pattern.
+ *
+ * The color is specified in the same way as in cairo_set_source_rgba().
+ *
+ * Valid values for @corner_num are from 0 to 3 and identify the
+ * corners as explained in cairo_pattern_create_mesh().
+ *
+ * Note: If @pattern is not a mesh pattern then @pattern will be put
+ * into an error status with a status of
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @corner_num is not valid,
+ * @pattern will be put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_INDEX.  If @pattern has no current patch,
+ * @pattern will be put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_pattern_mesh_set_corner_color_rgba (cairo_pattern_t *pattern,
+					  unsigned int     corner_num,
+					  double red, double green, double blue,
+					  double alpha)
+{
+    cairo_mesh_pattern_t *mesh;
+
+    if (unlikely (pattern->status))
+	return;
+
+    if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) {
+	_cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+	return;
+    }
+
+    if (unlikely (corner_num > 3)) {
+	_cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_INDEX);
+	return;
+    }
+
+    mesh = (cairo_mesh_pattern_t *) pattern;
+    if (unlikely (!mesh->current_patch)) {
+	_cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+	return;
+    }
+
+    red    = _cairo_restrict_value (red,    0.0, 1.0);
+    green  = _cairo_restrict_value (green,  0.0, 1.0);
+    blue   = _cairo_restrict_value (blue,   0.0, 1.0);
+    alpha  = _cairo_restrict_value (alpha,  0.0, 1.0);
+
+    _cairo_pattern_mesh_set_corner_color (mesh, corner_num, red, green, blue, alpha);
+}
+slim_hidden_def (cairo_pattern_mesh_set_corner_color_rgba);
+
+static void
+_cairo_pattern_add_color_stop (cairo_gradient_pattern_t	*pattern,
 			       double			 offset,
 			       double			 red,
 			       double			 green,
@@ -4124,6 +4870,262 @@ cairo_pattern_get_radial_circles (cairo_pattern_t *pattern,
     return CAIRO_STATUS_SUCCESS;
 }
 
+/**
+ * cairo_pattern_mesh_get_patch_count
+ * @pattern: a #cairo_pattern_t
+ * @count: return value for the number patches, or %NULL
+ *
+ * Gets the number of patches specified in the given mesh pattern.
+ *
+ * The number only includes patches which have been finished by
+ * calling cairo_pattern_mesh_end_patch(). For example it will be 0
+ * during the definition of the first patch.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS, or
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if @pattern is not a mesh
+ * pattern.
+ *
+ * Since: 1.12
+ */
+cairo_status_t
+cairo_pattern_mesh_get_patch_count (cairo_pattern_t *pattern,
+				    unsigned int *count)
+{
+    cairo_mesh_pattern_t *mesh = (cairo_mesh_pattern_t *) pattern;
+
+    if (unlikely (pattern->status))
+	return pattern->status;
+
+    if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH))
+	return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+
+    if (count) {
+	*count = _cairo_array_num_elements (&mesh->patches);
+	if (mesh->current_patch)
+	    *count -= 1;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * cairo_pattern_mesh_get_path
+ * @pattern: a #cairo_pattern_t
+ * @patch_num: the patch number to return data for
+ *
+ * Gets path defining the patch @patch_num for a mesh
+ * pattern.
+ *
+ * @patch_num can range 0 to 1 less than the number returned by
+ * cairo_pattern_mesh_get_patch_count().
+ *
+ * Return value: the path defining the patch, or a path with status
+ * %CAIRO_STATUS_INVALID_INDEX if @patch_num or @point_num is not
+ * valid for @pattern. If @pattern is not a mesh pattern, a path with
+ * status %CAIRO_STATUS_PATTERN_TYPE_MISMATCH is returned.
+ *
+ * Since: 1.12
+ */
+cairo_path_t *
+cairo_pattern_mesh_get_path (cairo_pattern_t *pattern,
+			     unsigned int patch_num)
+{
+    cairo_mesh_pattern_t *mesh = (cairo_mesh_pattern_t *) pattern;
+    const cairo_mesh_patch_t *patch;
+    cairo_path_t *path;
+    cairo_path_data_t *data;
+    unsigned int patch_count;
+    int l, current_point;
+
+    if (unlikely (pattern->status))
+	return _cairo_path_create_in_error (pattern->status);
+
+    if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH))
+	return _cairo_path_create_in_error (_cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH));
+
+    patch_count = _cairo_array_num_elements (&mesh->patches);
+    if (mesh->current_patch)
+	patch_count--;
+
+    if (unlikely (patch_num >= patch_count))
+	return _cairo_path_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_INDEX));
+
+    patch = _cairo_array_index_const (&mesh->patches, patch_num);
+
+    path = malloc (sizeof (cairo_path_t));
+    if (path == NULL)
+	return _cairo_path_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    path->num_data = 18;
+    path->data = _cairo_malloc_ab (path->num_data,
+				   sizeof (cairo_path_data_t));
+    if (path->data == NULL) {
+	free (path);
+	return _cairo_path_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+    }
+
+    data = path->data;
+    data[0].header.type = CAIRO_PATH_MOVE_TO;
+    data[0].header.length = 2;
+    data[1].point.x = patch->points[0][0].x;
+    data[1].point.y = patch->points[0][0].y;
+    data += data[0].header.length;
+
+    current_point = 0;
+
+    for (l = 0; l < 4; l++) {
+	int i, j, k;
+
+	data[0].header.type = CAIRO_PATH_CURVE_TO;
+	data[0].header.length = 4;
+
+	for (k = 1; k < 4; k++) {
+	    current_point = (current_point + 1) % 12;
+	    i = mesh_path_point_i[current_point];
+	    j = mesh_path_point_j[current_point];
+	    data[k].point.x = patch->points[i][j].x;
+	    data[k].point.y = patch->points[i][j].y;
+	}
+
+	data += data[0].header.length;
+    }
+
+    path->status = CAIRO_STATUS_SUCCESS;
+
+    return path;
+}
+
+/**
+ * cairo_pattern_mesh_get_corner_color_rgba
+ * @pattern: a #cairo_pattern_t
+ * @patch_num: the patch number to return data for
+ * @corner_num: the corner number to return data for
+ * @red: return value for red component of color, or %NULL
+ * @green: return value for green component of color, or %NULL
+ * @blue: return value for blue component of color, or %NULL
+ * @alpha: return value for alpha component of color, or %NULL
+ *
+ * Gets the color information in corner @corner_num of patch
+ * @patch_num for a mesh pattern.
+ *
+ * @patch_num can range 0 to 1 less than the number returned by
+ * cairo_pattern_mesh_get_patch_count().
+ *
+ * Valid values for @corner_num are from 0 to 3 and identify the
+ * corners as explained in cairo_pattern_create_mesh().
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS, or %CAIRO_STATUS_INVALID_INDEX
+ * if @patch_num or @corner_num is not valid for @pattern. If
+ * @pattern is not a mesh pattern, %CAIRO_STATUS_PATTERN_TYPE_MISMATCH
+ * is returned.
+ *
+ * Since: 1.12
+ **/
+cairo_status_t
+cairo_pattern_mesh_get_corner_color_rgba (cairo_pattern_t *pattern,
+					  unsigned int patch_num,
+					  unsigned int corner_num,
+					  double *red, double *green,
+					  double *blue, double *alpha)
+{
+    cairo_mesh_pattern_t *mesh = (cairo_mesh_pattern_t *) pattern;
+    unsigned int patch_count;
+    const cairo_mesh_patch_t *patch;
+
+    if (unlikely (pattern->status))
+	return pattern->status;
+
+    if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH))
+	return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+
+    if (unlikely (corner_num > 3))
+	return _cairo_error (CAIRO_STATUS_INVALID_INDEX);
+
+    patch_count = _cairo_array_num_elements (&mesh->patches);
+    if (mesh->current_patch)
+	patch_count--;
+
+    if (unlikely (patch_num >= patch_count))
+	return _cairo_error (CAIRO_STATUS_INVALID_INDEX);
+
+    patch = _cairo_array_index_const (&mesh->patches, patch_num);
+
+    if (red)
+	*red = patch->colors[corner_num].red;
+    if (green)
+	*green = patch->colors[corner_num].green;
+    if (blue)
+	*blue = patch->colors[corner_num].blue;
+    if (alpha)
+	*alpha = patch->colors[corner_num].alpha;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * cairo_pattern_mesh_get_control_point
+ * @pattern: a #cairo_pattern_t
+ * @patch_num: the patch number to return data for
+ * @point_num: the control point number to return data for
+ * @x: return value for the x coordinate of the control point, or %NULL
+ * @y: return value for the y coordinate of the control point, or %NULL
+ *
+ * Gets the control point @point_num of patch @patch_num for a mesh
+ * pattern.
+ *
+ * @patch_num can range 0 to 1 less than the number returned by
+ * cairo_pattern_mesh_get_patch_count().
+ *
+ * Valid values for @point_num are from 0 to 3 and identify the
+ * control points as explained in cairo_pattern_create_mesh().
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS, or %CAIRO_STATUS_INVALID_INDEX
+ * if @patch_num or @point_num is not valid for @pattern. If @pattern
+ * is not a mesh pattern, %CAIRO_STATUS_PATTERN_TYPE_MISMATCH is
+ * returned.
+ *
+ * Since: 1.12
+ **/
+cairo_status_t
+cairo_pattern_mesh_get_control_point (cairo_pattern_t *pattern,
+				      unsigned int patch_num,
+				      unsigned int point_num,
+				      double *x, double *y)
+{
+    cairo_mesh_pattern_t *mesh = (cairo_mesh_pattern_t *) pattern;
+    const cairo_mesh_patch_t *patch;
+    unsigned int patch_count;
+    int i, j;
+
+    if (pattern->status)
+	return pattern->status;
+
+    if (pattern->type != CAIRO_PATTERN_TYPE_MESH)
+	return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+
+    if (point_num > 3)
+	return _cairo_error (CAIRO_STATUS_INVALID_INDEX);
+
+    patch_count = _cairo_array_num_elements (&mesh->patches);
+    if (mesh->current_patch)
+	patch_count--;
+
+    if (unlikely (patch_num >= patch_count))
+	return _cairo_error (CAIRO_STATUS_INVALID_INDEX);
+
+    patch = _cairo_array_index_const (&mesh->patches, patch_num);
+
+    i = mesh_control_point_i[point_num];
+    j = mesh_control_point_j[point_num];
+
+    if (x)
+	*x = patch->points[i][j].x;
+    if (y)
+	*y = patch->points[i][j].y;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
 void
 _cairo_pattern_reset_static_data (void)
 {
diff --git a/src/cairo.h b/src/cairo.h
index 7412969..2addb68 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -2337,6 +2337,9 @@ cairo_pattern_create_radial (double cx0, double cy0, double radius0,
 			     double cx1, double cy1, double radius1);
 
 cairo_public cairo_pattern_t *
+cairo_pattern_create_mesh (void);
+
+cairo_public cairo_pattern_t *
 cairo_pattern_reference (cairo_pattern_t *pattern);
 
 cairo_public void
@@ -2412,6 +2415,42 @@ cairo_pattern_add_color_stop_rgba (cairo_pattern_t *pattern,
 				   double alpha);
 
 cairo_public void
+cairo_pattern_mesh_begin_patch (cairo_pattern_t *pattern);
+
+cairo_public void
+cairo_pattern_mesh_end_patch (cairo_pattern_t *pattern);
+
+cairo_public void
+cairo_pattern_mesh_curve_to (cairo_pattern_t *pattern,
+			     double x1, double y1,
+			     double x2, double y2,
+			     double x3, double y3);
+
+cairo_public void
+cairo_pattern_mesh_line_to (cairo_pattern_t *pattern,
+			    double x, double y);
+
+cairo_public void
+cairo_pattern_mesh_move_to (cairo_pattern_t *pattern,
+			    double x, double y);
+
+cairo_public void
+cairo_pattern_mesh_set_control_point (cairo_pattern_t *pattern,
+				      unsigned int point_num,
+				      double x, double y);
+
+cairo_public void
+cairo_pattern_mesh_set_corner_color_rgb (cairo_pattern_t *pattern,
+					 unsigned int corner_num,
+					 double red, double green, double blue);
+
+cairo_public void
+cairo_pattern_mesh_set_corner_color_rgba (cairo_pattern_t *pattern,
+					  unsigned int corner_num,
+					  double red, double green, double blue,
+					  double alpha);
+
+cairo_public void
 cairo_pattern_set_matrix (cairo_pattern_t      *pattern,
 			  const cairo_matrix_t *matrix);
 
@@ -2435,6 +2474,8 @@ cairo_pattern_get_matrix (cairo_pattern_t *pattern,
  * example, outside the surface bounds or outside the gradient
  * geometry).
  *
+ * Mesh patterns are not affected by the extend mode.
+ *
  * The default extend mode is %CAIRO_EXTEND_NONE for surface patterns
  * and %CAIRO_EXTEND_PAD for gradient patterns.
  *
@@ -2516,6 +2557,27 @@ cairo_pattern_get_radial_circles (cairo_pattern_t *pattern,
 				  double *x0, double *y0, double *r0,
 				  double *x1, double *y1, double *r1);
 
+cairo_public cairo_status_t
+cairo_pattern_mesh_get_patch_count (cairo_pattern_t *pattern,
+				    unsigned int *count);
+
+cairo_public cairo_path_t *
+cairo_pattern_mesh_get_path (cairo_pattern_t *pattern,
+			     unsigned int patch_num);
+
+cairo_public cairo_status_t
+cairo_pattern_mesh_get_corner_color_rgba (cairo_pattern_t *pattern,
+					  unsigned int patch_num,
+					  unsigned int corner_num,
+					  double *red, double *green,
+					  double *blue, double *alpha);
+
+cairo_public cairo_status_t
+cairo_pattern_mesh_get_control_point (cairo_pattern_t *pattern,
+				      unsigned int patch_num,
+				      unsigned int point_num,
+				      double *x, double *y);
+
 /* Matrix functions */
 
 cairo_public void
diff --git a/src/cairoint.h b/src/cairoint.h
index 95cc78f..1a762a1 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -2433,6 +2433,10 @@ slim_hidden_proto (cairo_pattern_create_rgb);
 slim_hidden_proto (cairo_pattern_create_rgba);
 slim_hidden_proto (cairo_pattern_destroy);
 slim_hidden_proto (cairo_pattern_get_extend);
+slim_hidden_proto (cairo_pattern_mesh_curve_to);
+slim_hidden_proto (cairo_pattern_mesh_line_to);
+slim_hidden_proto (cairo_pattern_mesh_move_to);
+slim_hidden_proto (cairo_pattern_mesh_set_corner_color_rgba);
 slim_hidden_proto_no_warn (cairo_pattern_reference);
 slim_hidden_proto (cairo_pattern_set_matrix);
 slim_hidden_proto (cairo_pop_group);
commit 30636206b0e2d50834cf75148cef351b8e49a52e
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Wed Nov 17 18:45:22 2010 +0100

    pdf,ps: Add native mesh pattern support
    
    PS and PDF have native support for mesh patterns, but they have encode
    mesh points and colors in an appropriate binary stream.
    
    cairo_pdf_shading_* functions implement the encoding, which is the
    same for PDF and PS.

diff --git a/src/Makefile.sources b/src/Makefile.sources
index eda2304..438d0c4 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -200,8 +200,8 @@ cairo_egl_sources =
 cairo_glx_sources =
 cairo_wgl_sources =
 
-_cairo_pdf_operators_private = cairo-pdf-operators-private.h
-_cairo_pdf_operators_sources = cairo-pdf-operators.c
+_cairo_pdf_operators_private = cairo-pdf-operators-private.h cairo-pdf-shading-private.h
+_cairo_pdf_operators_sources = cairo-pdf-operators.c cairo-pdf-shading.c
 cairo_private += $(_cairo_pdf_operators_private)
 cairo_sources += $(_cairo_pdf_operators_sources)
 
diff --git a/src/cairo-pdf-shading-private.h b/src/cairo-pdf-shading-private.h
new file mode 100644
index 0000000..dcf244e
--- /dev/null
+++ b/src/cairo-pdf-shading-private.h
@@ -0,0 +1,99 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Adrian Johnson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Adrian Johnson.
+ *
+ * Contributor(s):
+ *	Adrian Johnson <ajohnson at redneon.com>
+ */
+
+#ifndef CAIRO_PDF_SHADING_H
+#define CAIRO_PDF_SHADING_H
+
+#include "cairo-compiler-private.h"
+#include "cairo-types-private.h"
+
+
+typedef struct _cairo_pdf_shading {
+    int shading_type;
+    int bits_per_coordinate;
+    int bits_per_component;
+    int bits_per_flag;
+    double *decode_array;
+    int decode_array_length;
+    unsigned char *data;
+    unsigned long data_length;
+} cairo_pdf_shading_t;
+
+
+/**
+ * _cairo_pdf_shading_init_color:
+ * @shading: a #cairo_pdf_shading_t to initialize
+ * @pattern: the #cairo_mesh_pattern_t to initialize from
+ *
+ * Generate the PDF shading dictionary data for the a PDF type 7
+ * shading from RGB part of the specified mesh pattern.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful, possible errors
+ * include %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_status_t
+_cairo_pdf_shading_init_color (cairo_pdf_shading_t        *shading,
+			       const cairo_mesh_pattern_t *pattern);
+
+
+/**
+ * _cairo_pdf_shading_init_alpha:
+ * @shading: a #cairo_pdf_shading_t to initialize
+ * @pattern: the #cairo_mesh_pattern_t to initialize from
+ *
+ * Generate the PDF shading dictionary data for a PDF type 7
+ * shading from alpha part of the specified mesh pattern.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful, possible errors
+ * include %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_status_t
+_cairo_pdf_shading_init_alpha (cairo_pdf_shading_t        *shading,
+			       const cairo_mesh_pattern_t *pattern);
+
+/**
+ * _cairo_pdf_shading_fini:
+ * @shading: a #cairo_pdf_shading_t
+ *
+ * Free all resources associated with @shading.  After this call,
+ * @shading should not be used again without a subsequent call to
+ * _cairo_pdf_shading_init() again first.
+ **/
+cairo_private void
+_cairo_pdf_shading_fini (cairo_pdf_shading_t *shading);
+
+
+#endif /* CAIRO_PDF_SHADING_H */
diff --git a/src/cairo-pdf-shading.c b/src/cairo-pdf-shading.c
new file mode 100644
index 0000000..8a277ac
--- /dev/null
+++ b/src/cairo-pdf-shading.c
@@ -0,0 +1,277 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Adrian Johnson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Adrian Johnson.
+ *
+ * Contributor(s):
+ *	Adrian Johnson <ajohnson at redneon.com>
+ */
+
+#include "cairoint.h"
+
+#if CAIRO_HAS_PDF_OPERATORS
+
+#include "cairo-pdf-shading-private.h"
+#include "cairo-error-private.h"
+#include <float.h>
+
+static unsigned char *
+encode_coordinate (unsigned char *p, double c)
+{
+    uint32_t f;
+
+    f = c;
+    *p++ = f >> 24;
+    *p++ = (f >> 16) & 0xff;
+    *p++ = (f >> 8)  & 0xff;
+    *p++ = f & 0xff;
+
+    return p;
+}
+
+static unsigned char *
+encode_point (unsigned char *p, const cairo_point_double_t *point)
+{
+    p = encode_coordinate (p, point->x);
+    p = encode_coordinate (p, point->y);
+
+    return p;
+}
+
+static unsigned char *
+encode_color_component (unsigned char *p, double color)
+{
+    uint16_t c;
+
+    c = _cairo_color_double_to_short (color);
+    *p++ = c >> 8;
+    *p++ = c & 0xff;
+
+    return p;
+}
+
+static unsigned char *
+encode_color (unsigned char *p, const cairo_color_t *color)
+{
+    p = encode_color_component (p, color->red);
+    p = encode_color_component (p, color->green);
+    p = encode_color_component (p, color->blue);
+
+    return p;
+}
+
+static unsigned char *
+encode_alpha (unsigned char *p, const cairo_color_t *color)
+{
+    p = encode_color_component (p, color->alpha);
+
+    return p;
+}
+
+static cairo_status_t
+_cairo_pdf_shading_generate_decode_array (cairo_pdf_shading_t        *shading,
+					  const cairo_mesh_pattern_t *mesh,
+					  cairo_bool_t                is_alpha)
+{
+    unsigned int num_color_components, i;
+    cairo_bool_t is_valid;
+
+    if (is_alpha)
+	num_color_components = 1;
+    else
+	num_color_components = 3;
+
+    shading->decode_array_length = 4 + num_color_components * 2;
+    shading->decode_array = _cairo_malloc_ab (shading->decode_array_length,
+					      sizeof (double));
+    if (unlikely (shading->decode_array == NULL))
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    is_valid = _cairo_mesh_pattern_coord_box (mesh,
+					      &shading->decode_array[0],
+					      &shading->decode_array[2],
+					      &shading->decode_array[1],
+					      &shading->decode_array[3]);
+
+    assert (is_valid);
+    assert (shading->decode_array[1] - shading->decode_array[0] >= DBL_EPSILON);
+    assert (shading->decode_array[3] - shading->decode_array[2] >= DBL_EPSILON);
+
+    for (i = 0; i < num_color_components; i++) {
+	shading->decode_array[4 + 2*i] = 0;
+	shading->decode_array[5 + 2*i] = 1;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* The ISO32000 specification mandates this order for the points which
+ * define the patch. */
+static const int pdf_points_order_i[16] = {
+    0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 2, 1, 1, 1, 2, 2 };
+static const int pdf_points_order_j[16] = {
+    0, 1, 2, 3, 3, 3, 3, 2, 1, 0, 0, 0, 1, 2, 2, 1 };
+
+static cairo_status_t
+_cairo_pdf_shading_generate_data (cairo_pdf_shading_t        *shading,
+				  const cairo_mesh_pattern_t *mesh,
+				  cairo_bool_t                is_alpha)
+{
+    const cairo_mesh_patch_t *patch;
+    double x_off, y_off, x_scale, y_scale;
+    unsigned int num_patches;
+    unsigned int num_color_components;
+    unsigned char *p;
+    unsigned int i, j;
+
+    if (is_alpha)
+	num_color_components = 1;
+    else
+	num_color_components = 3;
+
+    num_patches = _cairo_array_num_elements (&mesh->patches);
+    patch = _cairo_array_index_const (&mesh->patches, 0);
+
+    /* Each patch requires:
+     *
+     * 1 flag - 1 byte
+     * 16 points. Each point is 2 coordinates. Each coordinate is
+     * stored in 4 bytes.
+     *
+     * 4 colors. Each color is stored in 2 bytes * num_color_components.
+     */
+    shading->data_length = num_patches * (1 + 16 * 2 * 4 + 4 * 2 * num_color_components);
+    shading->data = malloc (shading->data_length);
+    if (unlikely (shading->data == NULL))
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    x_off = shading->decode_array[0];
+    y_off = shading->decode_array[2];
+    x_scale = UINT32_MAX / (shading->decode_array[1] - x_off);
+    y_scale = UINT32_MAX / (shading->decode_array[3] - y_off);
+
+    p = shading->data;
+    for (i = 0; i < num_patches; i++) {
+	/* edge flag */
+	*p++ = 0;
+
+	/* 16 points */
+	for (j = 0; j < 16; j++) {
+	    cairo_point_double_t point;
+	    int pi, pj;
+
+	    pi = pdf_points_order_i[j];
+	    pj = pdf_points_order_j[j];
+	    point = patch[i].points[pi][pj];
+
+	    /* Transform the point as specified in the decode array */
+	    point.x -= x_off;
+	    point.y -= y_off;
+	    point.x *= x_scale;
+	    point.y *= y_scale;
+
+	    /* Make sure that rounding errors don't cause
+	     * wraparounds */
+	    point.x = _cairo_restrict_value (point.x, 0, UINT32_MAX);
+	    point.y = _cairo_restrict_value (point.y, 0, UINT32_MAX);
+
+	    p = encode_point (p, &point);
+	}
+
+	/* 4 colors */
+	for (j = 0; j < 4; j++) {
+	    if (is_alpha)
+		p = encode_alpha (p, &patch[i].colors[j]);
+	    else
+		p = encode_color (p, &patch[i].colors[j]);
+	}
+    }
+
+    assert (p == shading->data + shading->data_length);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_pdf_shading_init (cairo_pdf_shading_t        *shading,
+			 const cairo_mesh_pattern_t *mesh,
+			 cairo_bool_t                is_alpha)
+{
+    cairo_status_t status;
+
+    assert (mesh->base.status == CAIRO_STATUS_SUCCESS);
+    assert (mesh->current_patch == NULL);
+
+    shading->shading_type = 7;
+
+    /*
+     * Coordinates from the minimum to the maximum value of the mesh
+     * map to the [0..UINT32_MAX] range and are represented as
+     * uint32_t values.
+     *
+     * Color components are represented as uint16_t (in a 0.16 fixed
+     * point format, as in the rest of cairo).
+     */
+    shading->bits_per_coordinate = 32;
+    shading->bits_per_component = 16;
+    shading->bits_per_flag = 8;
+
+    shading->decode_array = NULL;
+    shading->data = NULL;
+
+    status = _cairo_pdf_shading_generate_decode_array (shading, mesh, is_alpha);
+    if (unlikely (status))
+	return status;
+
+    return _cairo_pdf_shading_generate_data (shading, mesh, is_alpha);
+}
+
+cairo_status_t
+_cairo_pdf_shading_init_color (cairo_pdf_shading_t        *shading,
+			       const cairo_mesh_pattern_t *pattern)
+{
+    return _cairo_pdf_shading_init (shading, pattern, FALSE);
+}
+
+cairo_status_t
+_cairo_pdf_shading_init_alpha (cairo_pdf_shading_t        *shading,
+			       const cairo_mesh_pattern_t *pattern)
+{
+    return _cairo_pdf_shading_init (shading, pattern, TRUE);
+}
+
+void
+_cairo_pdf_shading_fini (cairo_pdf_shading_t *shading)
+{
+    free (shading->data);
+    free (shading->decode_array);
+}
+
+#endif /* CAIRO_HAS_PDF_OPERATORS */
diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index e5c0cca..21dbf57 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -44,6 +44,7 @@
 #include "cairo-pdf.h"
 #include "cairo-pdf-surface-private.h"
 #include "cairo-pdf-operators-private.h"
+#include "cairo-pdf-shading-private.h"
 #include "cairo-analysis-surface-private.h"
 #include "cairo-composite-rectangles-private.h"
 #include "cairo-error-private.h"
@@ -1275,7 +1276,8 @@ _cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t		*surface,
 
     /* gradient patterns require an smask object to implement transparency */
     if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR ||
-        pattern->type == CAIRO_PATTERN_TYPE_RADIAL)
+	pattern->type == CAIRO_PATTERN_TYPE_RADIAL ||
+	pattern->type == CAIRO_PATTERN_TYPE_MESH)
     {
 	double min_alpha;
 
@@ -3340,6 +3342,150 @@ _cairo_pdf_surface_emit_gradient (cairo_pdf_surface_t    *surface,
 }
 
 static cairo_status_t
+_cairo_pdf_surface_emit_mesh_pattern (cairo_pdf_surface_t    *surface,
+				      cairo_pdf_pattern_t    *pdf_pattern)
+{
+    cairo_matrix_t pat_to_pdf;
+    cairo_status_t status;
+    cairo_pattern_t *pattern = pdf_pattern->pattern;
+    cairo_pdf_shading_t shading;
+    int i;
+    cairo_pdf_resource_t res;
+
+    pat_to_pdf = pattern->matrix;
+    status = cairo_matrix_invert (&pat_to_pdf);
+    /* cairo_pattern_set_matrix ensures the matrix is invertible */
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf);
+
+    status = _cairo_pdf_shading_init_color (&shading, (cairo_mesh_pattern_t *) pattern);
+    if (unlikely (status))
+	return status;
+
+    res = _cairo_pdf_surface_new_object (surface);
+    if (unlikely (res.id == 0))
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    _cairo_output_stream_printf (surface->output,
+				 "%d 0 obj\n"
+                                 "<< /ShadingType %d\n"
+                                 "   /ColorSpace /DeviceRGB\n"
+				 "   /BitsPerCoordinate %d\n"
+				 "   /BitsPerComponent %d\n"
+				 "   /BitsPerFlag %d\n"
+				 "   /Decode [",
+				 res.id,
+				 shading.shading_type,
+				 shading.bits_per_coordinate,
+				 shading.bits_per_component,
+				 shading.bits_per_flag);
+
+    for (i = 0; i < shading.decode_array_length; i++)
+	_cairo_output_stream_printf (surface->output, "%f ", shading.decode_array[i]);
+
+    _cairo_output_stream_printf (surface->output,
+				 "]\n"
+				 "   /Length %ld\n"
+				 ">>\n"
+				 "stream\n",
+				 shading.data_length);
+
+    _cairo_output_stream_write (surface->output, shading.data, shading.data_length);
+
+    _cairo_output_stream_printf (surface->output,
+				 "\nendstream\n"
+				 "endobj\n");
+
+    _cairo_pdf_shading_fini (&shading);
+
+    _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res);
+    _cairo_output_stream_printf (surface->output,
+                                 "%d 0 obj\n"
+                                 "<< /Type /Pattern\n"
+                                 "   /PatternType 2\n"
+                                 "   /Matrix [ %f %f %f %f %f %f ]\n"
+                                 "   /Shading %d 0 R\n"
+				 ">>\n"
+				 "endobj\n",
+				 pdf_pattern->pattern_res.id,
+                                 pat_to_pdf.xx, pat_to_pdf.yx,
+                                 pat_to_pdf.xy, pat_to_pdf.yy,
+                                 pat_to_pdf.x0, pat_to_pdf.y0,
+				 res.id);
+
+    if (pdf_pattern->gstate_res.id != 0) {
+	cairo_pdf_resource_t mask_resource;
+
+	/* Create pattern for SMask. */
+        res = _cairo_pdf_surface_new_object (surface);
+	if (unlikely (res.id == 0))
+	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+	status = _cairo_pdf_shading_init_alpha (&shading, (cairo_mesh_pattern_t *) pattern);
+	if (unlikely (status))
+	    return status;
+
+	_cairo_output_stream_printf (surface->output,
+				 "%d 0 obj\n"
+                                 "<< /ShadingType %d\n"
+                                 "   /ColorSpace /DeviceGray\n"
+				 "   /BitsPerCoordinate %d\n"
+				 "   /BitsPerComponent %d\n"
+				 "   /BitsPerFlag %d\n"
+				 "   /Decode [",
+				 res.id,
+				 shading.shading_type,
+				 shading.bits_per_coordinate,
+				 shading.bits_per_component,
+				 shading.bits_per_flag);
+
+	for (i = 0; i < shading.decode_array_length; i++)
+	    _cairo_output_stream_printf (surface->output, "%f ", shading.decode_array[i]);
+
+	_cairo_output_stream_printf (surface->output,
+				     "]\n"
+				     "   /Length %ld\n"
+				     ">>\n"
+				     "stream\n",
+				     shading.data_length);
+
+	_cairo_output_stream_write (surface->output, shading.data, shading.data_length);
+
+	_cairo_output_stream_printf (surface->output,
+				     "\nendstream\n"
+				     "endobj\n");
+	_cairo_pdf_shading_fini (&shading);
+
+        mask_resource = _cairo_pdf_surface_new_object (surface);
+	if (unlikely (mask_resource.id == 0))
+	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+	_cairo_output_stream_printf (surface->output,
+				     "%d 0 obj\n"
+				     "<< /Type /Pattern\n"
+				     "   /PatternType 2\n"
+				     "   /Matrix [ %f %f %f %f %f %f ]\n"
+				     "   /Shading %d 0 R\n"
+				     ">>\n"
+				     "endobj\n",
+				     mask_resource.id,
+				     pat_to_pdf.xx, pat_to_pdf.yx,
+				     pat_to_pdf.xy, pat_to_pdf.yy,
+				     pat_to_pdf.x0, pat_to_pdf.y0,
+				     res.id);
+
+	status = cairo_pdf_surface_emit_transparency_group (surface,
+						            pdf_pattern->gstate_res,
+							    mask_resource);
+	if (unlikely (status))
+	    return status;
+    }
+
+    return _cairo_output_stream_get_status (surface->output);
+}
+
+static cairo_status_t
 _cairo_pdf_surface_emit_pattern (cairo_pdf_surface_t *surface, cairo_pdf_pattern_t *pdf_pattern)
 {
     double old_width, old_height;
@@ -3366,6 +3512,10 @@ _cairo_pdf_surface_emit_pattern (cairo_pdf_surface_t *surface, cairo_pdf_pattern
 	status = _cairo_pdf_surface_emit_gradient (surface, pdf_pattern);
 	break;
 
+    case CAIRO_PATTERN_TYPE_MESH:
+	status = _cairo_pdf_surface_emit_mesh_pattern (surface, pdf_pattern);
+	break;
+
     default:
 	ASSERT_NOT_REACHED;
 	status = _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
@@ -5379,6 +5529,7 @@ _pattern_supported (const cairo_pattern_t *pattern)
     case CAIRO_PATTERN_TYPE_SOLID:
     case CAIRO_PATTERN_TYPE_LINEAR:
     case CAIRO_PATTERN_TYPE_RADIAL:
+    case CAIRO_PATTERN_TYPE_MESH:	
 	return TRUE;
 
     case CAIRO_PATTERN_TYPE_SURFACE:
diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
index 6296ee1..beab433 100644
--- a/src/cairo-ps-surface.c
+++ b/src/cairo-ps-surface.c
@@ -58,6 +58,7 @@
 #include "cairo-ps.h"
 #include "cairo-ps-surface-private.h"
 #include "cairo-pdf-operators-private.h"
+#include "cairo-pdf-shading-private.h"
 #include "cairo-composite-rectangles-private.h"
 #include "cairo-error-private.h"
 #include "cairo-scaled-font-subsets-private.h"
@@ -1750,6 +1751,7 @@ pattern_supported (cairo_ps_surface_t *surface, const cairo_pattern_t *pattern)
 
     case CAIRO_PATTERN_TYPE_LINEAR:
     case CAIRO_PATTERN_TYPE_RADIAL:
+    case CAIRO_PATTERN_TYPE_MESH:
 	return _gradient_pattern_supported (surface, pattern);
 
     case CAIRO_PATTERN_TYPE_SURFACE:
@@ -3373,6 +3375,81 @@ _cairo_ps_surface_emit_gradient (cairo_ps_surface_t       *surface,
 }
 
 static cairo_status_t
+_cairo_ps_surface_emit_mesh_pattern (cairo_ps_surface_t     *surface,
+				     cairo_mesh_pattern_t   *pattern)
+{
+    cairo_matrix_t pat_to_ps;
+    cairo_status_t status;
+    cairo_pdf_shading_t shading;
+    unsigned char *data_compressed;
+    unsigned long data_compressed_size;
+    int i;
+
+    if (_cairo_array_num_elements (&pattern->patches) == 0)
+        return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+    pat_to_ps = pattern->base.matrix;
+    status = cairo_matrix_invert (&pat_to_ps);
+    /* cairo_pattern_set_matrix ensures the matrix is invertible */
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    cairo_matrix_multiply (&pat_to_ps, &pat_to_ps, &surface->cairo_to_ps);
+
+    status = _cairo_pdf_shading_init_color (&shading, pattern);
+    if (unlikely (status))
+	return status;
+
+    _cairo_output_stream_printf (surface->stream,
+				 "<< /PatternType 2\n"
+				 "   /Shading\n"
+				 "   << /ShadingType %d\n"
+				 "      /ColorSpace /DeviceRGB\n"
+				 "      /DataSource currentfile /ASCII85Decode filter /LZWDecode filter\n"
+				 "      /BitsPerCoordinate %d\n"
+				 "      /BitsPerComponent %d\n"
+				 "      /BitsPerFlag %d\n"
+				 "      /Decode [",
+				 shading.shading_type,
+				 shading.bits_per_coordinate,
+				 shading.bits_per_component,
+				 shading.bits_per_flag);
+
+    for (i = 0; i < shading.decode_array_length; i++)
+	_cairo_output_stream_printf (surface->stream, "%f ", shading.decode_array[i]);
+
+    _cairo_output_stream_printf (surface->stream,
+				 "]\n"
+				 "   >>\n"
+				 ">>\n");
+
+    _cairo_output_stream_printf (surface->stream,
+				 "[ %f %f %f %f %f %f ]\n",
+				 pat_to_ps.xx, pat_to_ps.yx,
+                                 pat_to_ps.xy, pat_to_ps.yy,
+                                 pat_to_ps.x0, pat_to_ps.y0);
+    _cairo_output_stream_printf (surface->stream,
+				 "makepattern\n");
+
+    data_compressed_size = shading.data_length;
+    data_compressed = _cairo_lzw_compress (shading.data, &data_compressed_size);
+    if (unlikely (data_compressed == NULL))
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    status = _cairo_ps_surface_emit_base85_string (surface,
+						   data_compressed,
+						   data_compressed_size,
+						   FALSE);
+    _cairo_output_stream_printf (surface->stream,
+				 "\n"
+				 "setpattern\n");
+
+    free (data_compressed);
+    _cairo_pdf_shading_fini (&shading);
+
+    return status;
+}
+
+static cairo_status_t
 _cairo_ps_surface_emit_pattern (cairo_ps_surface_t *surface,
 				const cairo_pattern_t *pattern,
 				cairo_rectangle_int_t *extents,
@@ -3430,6 +3507,13 @@ _cairo_ps_surface_emit_pattern (cairo_ps_surface_t *surface,
 	if (unlikely (status))
 	    return status;
 	break;
+
+    case CAIRO_PATTERN_TYPE_MESH:
+	status = _cairo_ps_surface_emit_mesh_pattern (surface,
+					  (cairo_mesh_pattern_t *) pattern);
+	if (unlikely (status))
+	    return status;
+	break;
     }
 
     return CAIRO_STATUS_SUCCESS;
commit 8df122cb4bc7348025a74a890e9082073553d557
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Fri Dec 24 20:36:23 2010 +0100

    Add mesh gradient rasterizer
    
    Add an implementation of a fast and reasonably accurate
    non-antialiased mesh gradient rasterizer.

diff --git a/src/Makefile.sources b/src/Makefile.sources
index cc846b4..eda2304 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -136,6 +136,7 @@ cairo_sources = \
 	cairo-image-surface.c \
 	cairo-lzw.c \
 	cairo-matrix.c \
+	cairo-mesh-pattern-rasterizer.c \
 	cairo-misc.c \
 	cairo-mutex.c \
 	cairo-observer.c \
diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c
index 64d4e2d..07998cd 100644
--- a/src/cairo-image-surface.c
+++ b/src/cairo-image-surface.c
@@ -1685,6 +1685,31 @@ _pixman_image_for_pattern (const cairo_pattern_t *pattern,
     case CAIRO_PATTERN_TYPE_SURFACE:
 	return _pixman_image_for_surface ((const cairo_surface_pattern_t *) pattern,
 					  is_mask, extents, dst_device_transform, tx, ty);
+
+    case CAIRO_PATTERN_TYPE_MESH: {
+	cairo_surface_t *image;
+	pixman_image_t *r;
+	void * data;
+	int width, height, stride;
+
+	*tx = -extents->x;
+	*ty = -extents->y;
+	width = extents->width;
+	height = extents->height;
+
+	image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+	if (unlikely (image->status))
+	    return NULL;
+
+	stride = cairo_image_surface_get_stride (image);
+	data = cairo_image_surface_get_data (image);
+
+	_cairo_mesh_pattern_rasterize ((cairo_mesh_pattern_t *) pattern,
+				       data, width, height, stride, *tx, *ty);
+	r = pixman_image_ref (((cairo_image_surface_t *)image)->pixman_image);
+	cairo_surface_destroy (image);
+	return r;
+    }
     }
 }
 
diff --git a/src/cairo-mesh-pattern-rasterizer.c b/src/cairo-mesh-pattern-rasterizer.c
new file mode 100644
index 0000000..f95c484
--- /dev/null
+++ b/src/cairo-mesh-pattern-rasterizer.c
@@ -0,0 +1,943 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright 2009 Andrea Canciani
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Andrea Canciani.
+ *
+ * Contributor(s):
+ *	Andrea Canciani <ranma42 at gmail.com>
+ */
+
+#include "cairoint.h"
+
+/*
+ * Rasterizer for mesh patterns.
+ *
+ * This implementation is based on techniques derived from several
+ * papers (available from ACM):
+ *
+ * - Lien, Shantz and Pratt "Adaptive Forward Differencing for
+ *   Rendering Curves and Surfaces" (discussion of the AFD technique,
+ *   bound of 1/sqrt(2) on step length without proof)
+ *
+ * - Popescu and Rosen, "Forward rasterization" (description of
+ *   forward rasterization, proof of the previous bound)
+ *
+ * - Klassen, "Integer Forward Differencing of Cubic Polynomials:
+ *   Analysis and Algorithms"
+ *
+ * - Klassen, "Exact Integer Hybrid Subdivision and Forward
+ *   Differencing of Cubics" (improving the bound on the minimum
+ *   number of steps)
+ *
+ * - Chang, Shantz and Rocchetti, "Rendering Cubic Curves and Surfaces
+ *   with Integer Adaptive Forward Differencing" (analysis of forward
+ *   differencing applied to Bezier patches)
+ *
+ * Notes:
+ * - Poor performance expected in degenerate cases
+ *
+ * - Patches mostly outside the drawing area are drawn completely (and
+ *   clipped), wasting time
+ *
+ * - Both previous problems are greatly reduced by splitting until a
+ *   reasonably small size and clipping the new tiles: execution time
+ *   is quadratic in the convex-hull diameter instead than linear to
+ *   the painted area. Splitting the tiles doesn't change the painted
+ *   area but (usually) reduces the bounding box area (bbox area can
+ *   remain the same after splitting, but cannot grow)
+ *
+ * - The initial implementation used adaptive forward differencing,
+ *   but simple forward differencing scored better in benchmarks
+ *
+ * Idea:
+ *
+ * We do a sampling over the cubic patch with step du and dv (in the
+ * two parameters) that guarantees that any point of our sampling will
+ * be at most at 1/sqrt(2) from its adjacent points. In formulae
+ * (assuming B is the patch):
+ *
+ *   |B(u,v) - B(u+du,v)| < 1/sqrt(2)
+ *   |B(u,v) - B(u,v+dv)| < 1/sqrt(2)
+ *
+ * This means that every pixel covered by the patch will contain at
+ * least one of the samples, thus forward rasterization can be
+ * performed. Sketch of proof (from Popescu and Rosen):
+ *
+ * Let's take the P pixel we're interested into. If we assume it to be
+ * square, its boundaries define 9 regions on the plane:
+ *
+ * 1|2|3
+ * -+-+-
+ * 8|P|4
+ * -+-+-
+ * 7|6|5
+ *
+ * Let's check that the pixel P will contain at least one point
+ * assuming that it is covered by the patch.
+ *
+ * Since the pixel is covered by the patch, its center will belong to
+ * (at least) one of the quads:
+ *
+ *   {(B(u,v), B(u+du,v), B(u,v+dv), B(u+du,v+dv)) for u,v in [0,1]}
+ *
+ * If P doesn't contain any of the corners of the quad:
+ *
+ * - if one of the corners is in 1,3,5 or 7, other two of them have to
+ *   be in 2,4,6 or 8, thus if the last corner is not in P, the length
+ *   of one of the edges will be > 1/sqrt(2)
+ *
+ * - if none of the corners is in 1,3,5 or 7, all of them are in 2,4,6
+ *   and/or 8. If they are all in different regions, they can't
+ *   satisfy the distance constraint. If two of them are in the same
+ *   region (let's say 2), no point is in 6 and again it is impossible
+ *   to have the center of P in the quad respecting the distance
+ *   constraint (both these assertions can be checked by continuity
+ *   considering the length of the edges of a quad with the vertices
+ *   on the edges of P)
+ *
+ * Each of the cases led to a contradiction, so P contains at least
+ * one of the corners of the quad.
+ */
+
+/*
+ * Make sure that errors are less than 1 in fixed point math if you
+ * change these values.
+ *
+ * The error is amplified by about steps^3/4 times.
+ * The rasterizer always uses a number of steps that is a power of 2.
+ *
+ * 256 is the maximum allowed number of steps (to have error < 1)
+ * using 8.24 for the differences.
+ */
+#define STEPS_MAX_V 256.0
+#define STEPS_MAX_U 256.0
+
+/*
+ * If the patch/curve is only partially visible, split it to a finer
+ * resolution to get higher chances to clip (part of) it.
+ *
+ * These values have not been computed, but simply obtained
+ * empirically (by benchmarking some patches). They should never be
+ * greater than STEPS_MAX_V (or STEPS_MAX_U), but they can be as small
+ * as 1 (depending on how much you want to spend time in splitting the
+ * patch/curve when trying to save some rasterization time).
+ */
+#define STEPS_CLIP_V 64.0
+#define STEPS_CLIP_U 64.0
+
+
+/* Utils */
+static inline double
+sqlen (cairo_point_double_t p0, cairo_point_double_t p1)
+{
+    cairo_point_double_t delta;
+
+    delta.x = p0.x - p1.x;
+    delta.y = p0.y - p1.y;
+
+    return delta.x * delta.x + delta.y * delta.y;
+}
+
+static inline double
+max (double x, double y)
+{
+    return x > y ? x : y;
+}
+
+static inline double
+min (double x, double y)
+{
+    return x < y ? x : y;
+}
+
+static inline int16_t
+_color_delta_to_shifted_short (int32_t from, int32_t to, int shift)
+{
+    int32_t delta = to - from;
+
+    /* We need to round toward zero, because otherwise adding the
+     * delta 2^shift times can overflow */
+    if (delta >= 0)
+	return delta >> shift;
+    else
+	return -((-delta) >> shift);
+}
+
+/*
+ * Convert a number of steps to the equivalent shift.
+ *
+ * Input: the square of the minimum number of steps
+ *
+ * Output: the smallest integer x such that 2^x > steps
+ */
+static inline int
+sqsteps2shift (double steps_sq)
+{
+    int r;
+    frexp (max (1.0, steps_sq), &r);
+    return (r + 1) >> 1;
+}
+
+/*
+ * FD functions
+ *
+ * A Bezier curve is defined (with respect to a parameter t in
+ * [0,1]) from its nodes (x,y,z,w) like this:
+ *
+ *   B(t) = x(1-t)^3 + 3yt(1-t)^2 + 3zt^2(1-t) + wt^3
+ *
+ * To efficiently evaluate a Bezier curve, the rasterizer uses forward
+ * differences. Given x, y, z, w (the 4 nodes of the Bezier curve), it
+ * is possible to convert them to forward differences form and walk
+ * over the curve using fd_init (), fd_down () and fd_fwd ().
+ *
+ * f[0] is always the value of the Bezier curve for "current" t.
+ */
+
+/*
+ * Initialize the coefficient for forward differences.
+ *
+ * Input: x,y,z,w are the 4 nodes of the Bezier curve
+ *
+ * Output: f[i] is the i-th difference of the curve
+ *
+ * f[0] is the value of the curve for t==0, i.e. f[0]==x.
+ *
+ * The initial step is 1; this means that each step increases t by 1
+ * (so fd_init () immediately followed by fd_fwd (f) n times makes
+ * f[0] be the value of the curve for t==n).
+ */
+static inline void
+fd_init (double x, double y, double z, double w, double f[4])
+{
+    f[0] = x;
+    f[1] = w - x;
+    f[2] = 6. * (w - 2. * z + y);
+    f[3] = 6. * (w - 3. * z + 3. * y - x);
+}
+
+/*
+ * Halve the step of the coefficients for forward differences.
+ *
+ * Input: f[i] is the i-th difference of the curve
+ *
+ * Output: f[i] is the i-th difference of the curve with half the
+ *         original step
+ *
+ * f[0] is not affected, so the current t is not changed.
+ *
+ * The other coefficients are changed so that the step is half the
+ * original step. This means that doing fd_fwd (f) n times with the
+ * input f results in the same f[0] as doing fd_fwd (f) 2n times with
+ * the output f.
+ */
+static inline void
+fd_down (double f[4])
+{
+    f[3] *= 0.125;
+    f[2] = f[2] * 0.25 - f[3];
+    f[1] = (f[1] - f[2]) * 0.5;
+}
+
+/*
+ * Perform one step of forward differences along the curve.
+ *
+ * Input: f[i] is the i-th difference of the curve
+ *
+ * Output: f[i] is the i-th difference of the curve after one step
+ */
+static inline void
+fd_fwd (double f[4])
+{
+    f[0] += f[1];
+    f[1] += f[2];
+    f[2] += f[3];
+}
+
+/*
+ * Transform to integer forward differences.
+ *
+ * Input: d[n] is the n-th difference (in double precision)
+ *
+ * Output: i[n] is the n-th difference (in fixed point precision)
+ *
+ * i[0] is 9.23 fixed point, other differences are 4.28 fixed point.
+ */
+static inline void
+fd_fixed (double d[4], int32_t i[4])
+{
+    i[0] = _cairo_fixed_16_16_from_double (256 *  2 * d[0]);
+    i[1] = _cairo_fixed_16_16_from_double (256 * 16 * d[1]);
+    i[2] = _cairo_fixed_16_16_from_double (256 * 16 * d[2]);
+    i[3] = _cairo_fixed_16_16_from_double (256 * 16 * d[3]);
+}
+
+/*
+ * Perform one step of integer forward differences along the curve.
+ *
+ * Input: f[n] is the n-th difference
+ *
+ * Output: f[n] is the n-th difference
+ *
+ * f[0] is 9.23 fixed point, other differences are 4.28 fixed point.
+ */
+static inline void
+fd_fixed_fwd (int32_t f[4])
+{
+    f[0] += (f[1] >> 5) + ((f[1] >> 4) & 1);
+    f[1] += f[2];
+    f[2] += f[3];
+}
+
+/*
+ * Compute the minimum number of steps that guarantee that walking
+ * over a curve will leave no holes.
+ *
+ * Input: p[0..3] the nodes of the Bezier curve
+ *
+ * Returns: the square of the number of steps
+ *
+ * Idea:
+ *
+ * We want to make sure that at every step we move by less than
+ * 1/sqrt(2).
+ *
+ * The derivative of the cubic Bezier with nodes (p0, p1, p2, p3) is
+ * the quadratic Bezier with nodes (p1-p0, p2-p1, p3-p2) scaled by 3,
+ * so (since a Bezier curve is always bounded by its convex hull), we
+ * can say that:
+ *
+ *  max(|B'(t)|) <= 3 max (|p1-p0|, |p2-p1|, |p3-p2|)
+ *
+ * We can improve this by noticing that a quadratic Bezier (a,b,c) is
+ * bounded by the quad (a,lerp(a,b,t),lerp(b,c,t),c) for any t, so
+ * (substituting the previous values, using t=0.5 and simplifying):
+ *
+ *  max(|B'(t)|) <= 3 max (|p1-p0|, |p2-p0|/2, |p3-p1|/2, |p3-p2|)
+ *
+ * So, to guarantee a maximum step lenght of 1/sqrt(2) we must do:
+ *
+ *   3 max (|p1-p0|, |p2-p0|/2, |p3-p1|/2, |p3-p2|) sqrt(2) steps
+ */
+static inline double
+bezier_steps_sq (cairo_point_double_t p[4])
+{
+    double tmp = sqlen (p[0], p[1]);
+    tmp = max (tmp, sqlen (p[2], p[3]));
+    tmp = max (tmp, sqlen (p[0], p[2]) * .25);
+    tmp = max (tmp, sqlen (p[1], p[3]) * .25);
+    return 18.0 * tmp;
+}
+
+/*
+ * Split a 1D Bezier cubic using de Casteljau's algorithm.
+ *
+ * Input: x,y,z,w the nodes of the Bezier curve
+ *
+ * Output: x0,y0,z0,w0 and x1,y1,z1,w1 are respectively the nodes of
+ *         the first half and of the second half of the curve
+ *
+ * The output control nodes have to be distinct.
+ */
+static inline void
+split_bezier_1D (double  x,  double  y,  double  z,  double  w,
+		 double *x0, double *y0, double *z0, double *w0,
+		 double *x1, double *y1, double *z1, double *w1)
+{
+    double tmp;
+
+    *x0 = x;
+    *w1 = w;
+
+    tmp = 0.5 * (y + z);
+    *y0 = 0.5 * (x + y);
+    *z1 = 0.5 * (z + w);
+
+    *z0 = 0.5 * (*y0 + tmp);
+    *y1 = 0.5 * (tmp + *z1);
+
+    *w0 = *x1 = 0.5 * (*z0 + *y1);
+}
+
+/*
+ * Split a Bezier curve using de Casteljau's algorithm.
+ *
+ * Input: p[0..3] the nodes of the Bezier curve
+ *
+ * Output: fst_half[0..3] and snd_half[0..3] are respectively the
+ *         nodes of the first and of the second half of the curve
+ *
+ * fst_half and snd_half must be different, but they can be the same as
+ * nodes.
+ */
+static void
+split_bezier (cairo_point_double_t p[4],
+	      cairo_point_double_t fst_half[4],
+	      cairo_point_double_t snd_half[4])
+{
+    split_bezier_1D (p[0].x, p[1].x, p[2].x, p[3].x,
+		     &fst_half[0].x, &fst_half[1].x, &fst_half[2].x, &fst_half[3].x,
+		     &snd_half[0].x, &snd_half[1].x, &snd_half[2].x, &snd_half[3].x);
+
+    split_bezier_1D (p[0].y, p[1].y, p[2].y, p[3].y,
+		     &fst_half[0].y, &fst_half[1].y, &fst_half[2].y, &fst_half[3].y,
+		     &snd_half[0].y, &snd_half[1].y, &snd_half[2].y, &snd_half[3].y);
+}
+
+
+typedef enum _intersection {
+    INSIDE = -1, /* the interval is entirely contained in the reference interval */
+    OUTSIDE = 0, /* the interval has no intersection with the reference interval */
+    PARTIAL = 1  /* the interval intersects the reference interval (but is not fully inside it) */
+} intersection_t;
+
+/*
+ * Check if an interval if inside another.
+ *
+ * Input: a,b are the extrema of the first interval
+ *        c,d are the extrema of the second interval
+ *
+ * Returns: INSIDE  iff [a,b) intersection [c,d) = [a,b)
+ *          OUTSIDE iff [a,b) intersection [c,d) = {}
+ *          PARTIAL otherwise
+ *
+ * The function assumes a < b and c < d
+ *
+ * Note: Bitwise-anding the results along each component gives the
+ *       expected result for [a,b) x [A,B) intersection [c,d) x [C,D).
+ */
+static inline int
+intersect_interval (double a, double b, double c, double d)
+{
+    if (c <= a && b <= d)
+	return INSIDE;
+    else if (a >= d || b <= c)
+	return OUTSIDE;
+    else
+	return PARTIAL;
+}
+
+/*
+ * Set the color of a pixel.
+ *
+ * Input: data is the base pointer of the image
+ *        width, height are the dimensions of the image
+ *        stride is the stride in bytes between adjacent rows
+ *        x, y are the coordinates of the pixel to be colored
+ *        r,g,b,a are the color components of the color to be set
+ *
+ * Output: the (x,y) pixel in data has the (r,g,b,a) color
+ *
+ * The input color components are not premultiplied, but the data
+ * stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc,
+ * premultiplied).
+ *
+ * If the pixel to be set is outside the image, this function does
+ * nothing.
+ */
+static inline void
+draw_pixel (unsigned char *data, int width, int height, int stride,
+	    int x, int y, uint16_t r, uint16_t g, uint16_t b, uint16_t a)
+{
+    if (likely (0 <= x && 0 <= y && x < width && y < height)) {
+	uint32_t tr, tg, tb, ta;
+
+	/* Premultiply and round */
+	ta = a;
+	tr = r * ta + 0x8000;
+	tg = g * ta + 0x8000;
+	tb = b * ta + 0x8000;
+
+	tr += tr >> 16;
+	tg += tg >> 16;
+	tb += tb >> 16;
+
+	*((uint32_t*) (data + y*stride + 4*x)) = ((ta << 16) & 0xff000000) |
+	    ((tr >> 8) & 0xff0000) | ((tg >> 16) & 0xff00) | (tb >> 24);
+    }
+}
+
+/*
+ * Forward-rasterize a cubic curve using forward differences.
+ *
+ * Input: data is the base pointer of the image
+ *        width, height are the dimensions of the image
+ *        stride is the stride in bytes between adjacent rows
+ *        ushift is log2(n) if n is the number of desired steps
+ *        dxu[i], dyu[i] are the x,y forward differences of the curve
+ *        r0,g0,b0,a0 are the color components of the start point
+ *        r3,g3,b3,a3 are the color components of the end point
+ *
+ * Output: data will be changed to have the requested curve drawn in
+ *         the specified colors
+ *
+ * The input color components are not premultiplied, but the data
+ * stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc,
+ * premultiplied).
+ *
+ * The function draws n+1 pixels, that is from the point at step 0 to
+ * the point at step n, both included. This is the discrete equivalent
+ * to drawing the curve for values of the interpolation parameter in
+ * [0,1] (including both extremes).
+ */
+static inline void
+rasterize_bezier_curve (unsigned char *data, int width, int height, int stride,
+			int ushift, double dxu[4], double dyu[4],
+			uint16_t r0, uint16_t g0, uint16_t b0, uint16_t a0,
+			uint16_t r3, uint16_t g3, uint16_t b3, uint16_t a3)
+{
+    int32_t xu[4], yu[4];
+    int x0, y0, u, usteps = 1 << ushift;
+
+    uint16_t r = r0, g = g0, b = b0, a = a0;
+    int16_t dr = _color_delta_to_shifted_short (r0, r3, ushift);
+    int16_t dg = _color_delta_to_shifted_short (g0, g3, ushift);
+    int16_t db = _color_delta_to_shifted_short (b0, b3, ushift);
+    int16_t da = _color_delta_to_shifted_short (a0, a3, ushift);
+
+    fd_fixed (dxu, xu);
+    fd_fixed (dyu, yu);
+
+    /*
+     * Use (dxu[0],dyu[0]) as origin for the forward differences.
+     *
+     * This makes it possible to handle much larger coordinates (the
+     * ones that can be represented as cairo_fixed_t)
+     */
+    x0 = _cairo_fixed_from_double (dxu[0]);
+    y0 = _cairo_fixed_from_double (dyu[0]);
+    xu[0] = 0;
+    yu[0] = 0;
+
+    for (u = 0; u <= usteps; ++u) {
+	/*
+	 * This rasterizer assumes that pixels are integer aligned
+	 * squares, so a generic (x,y) point belongs to the pixel with
+	 * top-left coordinates (floor(x), floor(y))
+	 */
+
+	int x = _cairo_fixed_integer_floor (x0 + (xu[0] >> 15) + ((xu[0] >> 14) & 1));
+	int y = _cairo_fixed_integer_floor (y0 + (yu[0] >> 15) + ((yu[0] >> 14) & 1));
+
+	draw_pixel (data, width, height, stride, x, y, r, g, b, a);
+
+	fd_fixed_fwd (xu);
+	fd_fixed_fwd (yu);
+	r += dr;
+	g += dg;
+	b += db;
+	a += da;
+    }
+}
+
+/*
+ * Clip, split and rasterize a Bezier curve.
+ *
+ * Input: data is the base pointer of the image
+ *        width, height are the dimensions of the image
+ *        stride is the stride in bytes between adjacent rows
+ *        p[i] is the i-th node of the Bezier curve
+ *        c0[i] is the i-th color component at the start point
+ *        c3[i] is the i-th color component at the end point
+ *
+ * Output: data will be changed to have the requested curve drawn in
+ *         the specified colors
+ *
+ * The input color components are not premultiplied, but the data
+ * stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc,
+ * premultiplied).
+ *
+ * The color components are red, green, blue and alpha, in this order.
+ *
+ * The function guarantees that it will draw the curve with a step
+ * small enough to never have a distance above 1/sqrt(2) between two
+ * consecutive points (which is needed to ensure that no hole can
+ * appear when using this function to rasterize a patch).
+ */
+static void
+draw_bezier_curve (unsigned char *data, int width, int height, int stride,
+		   cairo_point_double_t p[4], double c0[4], double c3[4])
+{
+    double steps_sq;
+    int v;
+
+    /* Check visibility */
+    v = intersect_interval (min (min (p[0].y, p[1].y), min (p[2].y, p[3].y)),
+			    max (max (p[0].y, p[1].y), max (p[2].y, p[3].y)),
+			    0,
+			    height);
+    if (v == OUTSIDE)
+	return;
+
+    v &= intersect_interval (min (min (p[0].x, p[1].x), min (p[2].x, p[3].x)),
+			     max (max (p[0].x, p[1].x), max (p[2].x, p[3].x)),
+			     0,
+			     width);
+    if (v == OUTSIDE)
+	return;
+
+    steps_sq = bezier_steps_sq (p);
+    if (steps_sq >= (v == INSIDE ? STEPS_MAX_U * STEPS_MAX_U : STEPS_CLIP_U * STEPS_CLIP_U)) {
+	/*
+	 * The number of steps is greater than the threshold. This
+	 * means that either the error would become too big if we
+	 * directly rasterized it or that we can probably save some
+	 * time by splitting the curve and clipping part of it
+	 */
+	cairo_point_double_t first[4], second[4];
+	double midc[4];
+	split_bezier (p, first, second);
+	midc[0] = (c0[0] + c3[0]) * 0.5;
+	midc[1] = (c0[1] + c3[1]) * 0.5;
+	midc[2] = (c0[2] + c3[2]) * 0.5;
+	midc[3] = (c0[3] + c3[3]) * 0.5;
+	draw_bezier_curve (data, width, height, stride, first, c0, midc);
+	draw_bezier_curve (data, width, height, stride, second, midc, c3);
+    } else {
+	double xu[4], yu[4];
+	int ushift = sqsteps2shift (steps_sq), k;
+
+	fd_init (p[0].x, p[1].x, p[2].x, p[3].x, xu);
+	fd_init (p[0].y, p[1].y, p[2].y, p[3].y, yu);
+
+	for (k = 0; k < ushift; ++k) {
+	    fd_down (xu);
+	    fd_down (yu);
+	}
+
+	rasterize_bezier_curve (data, width, height, stride, ushift,
+				xu, yu,
+				_cairo_color_double_to_short (c0[0]),
+				_cairo_color_double_to_short (c0[1]),
+				_cairo_color_double_to_short (c0[2]),
+				_cairo_color_double_to_short (c0[3]),
+				_cairo_color_double_to_short (c3[0]),
+				_cairo_color_double_to_short (c3[1]),
+				_cairo_color_double_to_short (c3[2]),
+				_cairo_color_double_to_short (c3[3]));
+
+	/* Draw the end point, to make sure that we didn't leave it
+	 * out because of rounding */
+	draw_pixel (data, width, height, stride,
+		    _cairo_fixed_integer_floor (_cairo_fixed_from_double (p[3].x)),
+		    _cairo_fixed_integer_floor (_cairo_fixed_from_double (p[3].y)),
+		    _cairo_color_double_to_short (c3[0]),
+		    _cairo_color_double_to_short (c3[1]),
+		    _cairo_color_double_to_short (c3[2]),
+		    _cairo_color_double_to_short (c3[3]));
+    }
+}
+
+/*
+ * Forward-rasterize a cubic Bezier patch using forward differences.
+ *
+ * Input: data is the base pointer of the image
+ *        width, height are the dimensions of the image
+ *        stride is the stride in bytes between adjacent rows
+ *        vshift is log2(n) if n is the number of desired steps
+ *        p[i][j], p[i][j] are the the nodes of the Bezier patch
+ *        col[i][j] is the j-th color component of the i-th corner
+ *
+ * Output: data will be changed to have the requested patch drawn in
+ *         the specified colors
+ *
+ * The nodes of the patch are as follows:
+ *
+ * u\v 0    - >    1
+ * 0  p00 p01 p02 p03
+ * |  p10 p11 p12 p13
+ * v  p20 p21 p22 p23
+ * 1  p30 p31 p32 p33
+ *
+ * i.e. u varies along the first component (rows), v varies along the
+ * second one (columns).
+ *
+ * The color components are red, green, blue and alpha, in this order.
+ * c[0..3] are the colors in p00, p30, p03, p33 respectively
+ *
+ * The input color components are not premultiplied, but the data
+ * stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc,
+ * premultiplied).
+ *
+ * If the patch folds over itself, the part with the highest v
+ * parameter is considered above. If both have the same v, the one
+ * with the highest u parameter is above.
+ *
+ * The function draws n+1 curves, that is from the curve at step 0 to
+ * the curve at step n, both included. This is the discrete equivalent
+ * to drawing the patch for values of the interpolation parameter in
+ * [0,1] (including both extremes).
+ */
+static inline void
+rasterize_bezier_patch (unsigned char *data, int width, int height, int stride, int vshift,
+			cairo_point_double_t p[4][4], double col[4][4])
+{
+    double pv[4][2][4], cstart[4], cend[4], dcstart[4], dcend[4];
+    int vsteps, v, i, k;
+
+    vsteps = 1 << vshift;
+
+    /*
+     * pv[i][0] is the function (represented using forward
+     * differences) mapping v to the x coordinate of the i-th node of
+     * the Bezier curve with parameter u.
+     * (Likewise p[i][0] gives the y coordinate).
+     *
+     * This means that (pv[0][0][0],pv[0][1][0]),
+     * (pv[1][0][0],pv[1][1][0]), (pv[2][0][0],pv[2][1][0]) and
+     * (pv[3][0][0],pv[3][1][0]) are the nodes of the Bezier curve for
+     * the "current" v value (see the FD comments for more details).
+     */
+    for (i = 0; i < 4; ++i) {
+	fd_init (p[i][0].x, p[i][1].x, p[i][2].x, p[i][3].x, pv[i][0]);
+	fd_init (p[i][0].y, p[i][1].y, p[i][2].y, p[i][3].y, pv[i][1]);
+	for (k = 0; k < vshift; ++k) {
+	    fd_down (pv[i][0]);
+	    fd_down (pv[i][1]);
+	}
+    }
+
+    for (i = 0; i < 4; ++i) {
+	cstart[i]  = col[0][i];
+	cend[i]    = col[1][i];
+	dcstart[i] = (col[2][i] - col[0][i]) / vsteps;
+	dcend[i]   = (col[3][i] - col[1][i]) / vsteps;
+    }
+
+    for (v = 0; v <= vsteps; ++v) {
+	cairo_point_double_t nodes[4];
+	for (i = 0; i < 4; ++i) {
+	    nodes[i].x = pv[i][0][0];
+	    nodes[i].y = pv[i][1][0];
+	}
+
+	draw_bezier_curve (data, width, height, stride, nodes, cstart, cend);
+
+	for (i = 0; i < 4; ++i) {
+	    fd_fwd (pv[i][0]);
+	    fd_fwd (pv[i][1]);
+	    cstart[i] += dcstart[i];
+	    cend[i] += dcend[i];
+	}
+    }
+}
+
+/*
+ * Clip, split and rasterize a Bezier cubic patch.
+ *
+ * Input: data is the base pointer of the image
+ *        width, height are the dimensions of the image
+ *        stride is the stride in bytes between adjacent rows
+ *        p[i][j], p[i][j] are the nodes of the patch
+ *        col[i][j] is the j-th color component of the i-th corner
+ *
+ * Output: data will be changed to have the requested patch drawn in
+ *         the specified colors
+ *
+ * The nodes of the patch are as follows:
+ *
+ * u\v 0    - >    1
+ * 0  p00 p01 p02 p03
+ * |  p10 p11 p12 p13
+ * v  p20 p21 p22 p23
+ * 1  p30 p31 p32 p33
+ *
+ * i.e. u varies along the first component (rows), v varies along the
+ * second one (columns).
+ *
+ * The color components are red, green, blue and alpha, in this order.
+ * c[0..3] are the colors in p00, p30, p03, p33 respectively
+ *
+ * The input color components are not premultiplied, but the data
+ * stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc,
+ * premultiplied).
+ *
+ * If the patch folds over itself, the part with the highest v
+ * parameter is considered above. If both have the same v, the one
+ * with the highest u parameter is above.
+ *
+ * The function guarantees that it will draw the patch with a step
+ * small enough to never have a distance above 1/sqrt(2) between two
+ * adjacent points (which guarantees that no hole can appear).
+ *
+ * This function can be used to rasterize a tile of PDF type 7
+ * shadings (see http://www.adobe.com/devnet/pdf/pdf_reference.html).
+ */
+static void
+draw_bezier_patch (unsigned char *data, int width, int height, int stride,
+		     cairo_point_double_t p[4][4], double c[4][4])
+{
+    double top, bottom, left, right, steps_sq;
+    int i, j, v;
+
+    top = bottom = p[0][0].y;
+    for (i = 0; i < 4; ++i) {
+	for (j= 0; j < 4; ++j) {
+	    top    = min (top,    p[i][j].y);
+	    bottom = max (bottom, p[i][j].y);
+	}
+    }
+
+    v = intersect_interval (top, bottom, 0, height);
+    if (v == OUTSIDE)
+	return;
+
+    left = right = p[0][0].x;
+    for (i = 0; i < 4; ++i) {
+	for (j= 0; j < 4; ++j) {
+	    left  = min (left,  p[i][j].x);
+	    right = max (right, p[i][j].x);
+	}
+    }
+
+    v &= intersect_interval (left, right, 0, width);
+    if (v == OUTSIDE)
+	return;
+
+    steps_sq = 0;
+    for (i = 0; i < 4; ++i)
+	steps_sq = max (steps_sq, bezier_steps_sq (p[i]));
+
+    if (steps_sq >= (v == INSIDE ? STEPS_MAX_V * STEPS_MAX_V : STEPS_CLIP_V * STEPS_CLIP_V)) {
+	/* The number of steps is greater than the threshold. This
+	 * means that either the error would become too big if we
+	 * directly rasterized it or that we can probably save some
+	 * time by splitting the curve and clipping part of it. The
+	 * patch is only split in the v direction to guarantee that
+	 * rasterizing each part will overwrite parts with low v with
+	 * overlapping parts with higher v. */
+
+	cairo_point_double_t first[4][4], second[4][4];
+	double subc[4][4];
+
+	for (i = 0; i < 4; ++i)
+	    split_bezier (p[i], first[i], second[i]);
+
+	for (i = 0; i < 4; ++i) {
+	    subc[0][i] = c[0][i];
+	    subc[1][i] = c[1][i];
+	    subc[2][i] = 0.5 * (c[0][i] + c[2][i]);
+	    subc[3][i] = 0.5 * (c[1][i] + c[3][i]);
+	}
+
+	draw_bezier_patch (data, width, height, stride, first, subc);
+
+	for (i = 0; i < 4; ++i) {
+	    subc[0][i] = subc[2][i];
+	    subc[1][i] = subc[3][i];
+	    subc[2][i] = c[2][i];
+	    subc[3][i] = c[3][i];
+	}
+	draw_bezier_patch (data, width, height, stride, second, subc);
+    } else {
+	rasterize_bezier_patch (data, width, height, stride, sqsteps2shift (steps_sq), p, c);
+    }
+}
+
+/*
+ * Draw a tensor product shading pattern.
+ *
+ * Input: mesh is the mesh pattern
+ *        data is the base pointer of the image
+ *        width, height are the dimensions of the image
+ *        stride is the stride in bytes between adjacent rows
+ *
+ * Output: data will be changed to have the pattern drawn on it
+ *
+ * data is assumed to be clear and its content is assumed to be in
+ * CAIRO_FORMAT_ARGB32 (8 bpc, premultiplied).
+ *
+ * This function can be used to rasterize a PDF type 7 shading (see
+ * http://www.adobe.com/devnet/pdf/pdf_reference.html).
+ */
+void
+_cairo_mesh_pattern_rasterize (const cairo_mesh_pattern_t *mesh,
+			       void                       *data,
+			       int                         width,
+			       int                         height,
+			       int                         stride,
+			       double                      x_offset,
+			       double                      y_offset)
+{
+    cairo_point_double_t nodes[4][4];
+    double colors[4][4];
+    cairo_matrix_t p2u;
+    unsigned int i, j, k, n;
+    cairo_status_t status;
+    const cairo_mesh_patch_t *patch;
+    const cairo_color_t *c;
+
+    assert (mesh->base.status == CAIRO_STATUS_SUCCESS);
+    assert (mesh->current_patch == NULL);
+
+    p2u = mesh->base.matrix;
+    status = cairo_matrix_invert (&p2u);
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    n = _cairo_array_num_elements (&mesh->patches);
+    patch = _cairo_array_index_const (&mesh->patches, 0);
+    for (i = 0; i < n; i++) {
+	for (j = 0; j < 4; j++) {
+	    for (k = 0; k < 4; k++) {
+		nodes[j][k] = patch->points[j][k];
+		cairo_matrix_transform_point (&p2u, &nodes[j][k].x, &nodes[j][k].y);
+		nodes[j][k].x += x_offset;
+		nodes[j][k].y += y_offset;
+	    }
+	}
+
+	c = &patch->colors[0];
+	colors[0][0] = c->red;
+	colors[0][1] = c->green;
+	colors[0][2] = c->blue;
+	colors[0][3] = c->alpha;
+
+	c = &patch->colors[3];
+	colors[1][0] = c->red;
+	colors[1][1] = c->green;
+	colors[1][2] = c->blue;
+	colors[1][3] = c->alpha;
+
+	c = &patch->colors[1];
+	colors[2][0] = c->red;
+	colors[2][1] = c->green;
+	colors[2][2] = c->blue;
+	colors[2][3] = c->alpha;
+
+	c = &patch->colors[2];
+	colors[3][0] = c->red;
+	colors[3][1] = c->green;
+	colors[3][2] = c->blue;
+	colors[3][3] = c->alpha;
+
+	draw_bezier_patch (data, width, height, stride, nodes, colors);
+	patch++;
+    }
+}
diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index ad7a9ed..006e895 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -1597,6 +1597,54 @@ _cairo_pattern_acquire_surface_for_gradient (const cairo_gradient_pattern_t *pat
     return status;
 }
 
+static cairo_int_status_t
+_cairo_pattern_acquire_surface_for_mesh (const cairo_mesh_pattern_t *pattern,
+					 cairo_surface_t            *dst,
+					 int   	                     x,
+					 int                         y,
+					 unsigned int                width,
+					 unsigned int                height,
+					 cairo_surface_t           **out,
+					 cairo_surface_attributes_t *attr)
+{
+    cairo_surface_t *image;
+    void *data;
+    cairo_status_t status;
+    int clone_offset_x, clone_offset_y;
+    int stride;
+
+    image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+    if (unlikely (image->status))
+	return image->status;
+
+    stride = cairo_image_surface_get_stride (image);
+    data = cairo_image_surface_get_data (image);
+
+    _cairo_mesh_pattern_rasterize (pattern, data, width, height, stride, -x, -y);
+
+    attr->x_offset = -x;
+    attr->y_offset = -y;
+    attr->filter = CAIRO_FILTER_NEAREST;
+    attr->extend = pattern->base.extend;
+    cairo_matrix_init_identity (&attr->matrix);
+    attr->has_component_alpha = pattern->base.has_component_alpha;
+
+    if (_cairo_surface_is_image (dst)) {
+	*out = image;
+
+	return CAIRO_STATUS_SUCCESS;
+    }
+
+    status = _cairo_surface_clone_similar (dst, image,
+					   0, 0, width, height,
+					   &clone_offset_x,
+					   &clone_offset_y, out);
+
+    cairo_surface_destroy (image);
+
+    return status;
+}
+
 /* We maintain a small cache here, because we don't want to constantly
  * recreate surfaces for simple solid colors. */
 #define MAX_SURFACE_CACHE_SIZE 16
@@ -3229,6 +3277,12 @@ _cairo_pattern_acquire_surface (const cairo_pattern_t	   *pattern,
 							   surface_out,
 							   attributes);
 
+    case CAIRO_PATTERN_TYPE_MESH:
+	return _cairo_pattern_acquire_surface_for_mesh ((cairo_mesh_pattern_t *) pattern,
+							dst, x, y, width, height,
+							surface_out,
+							attributes);
+
     default:
 	ASSERT_NOT_REACHED;
 	return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
diff --git a/src/cairoint.h b/src/cairoint.h
index 7cbe0fd..95cc78f 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -2361,6 +2361,18 @@ _cairo_utf8_to_utf16 (const char *str,
 		      int	 *items_written);
 #endif
 
+/* cairo-mesh-pattern-rasterizer.c */
+
+cairo_private void
+_cairo_mesh_pattern_rasterize (const cairo_mesh_pattern_t *mesh,
+			       void                       *data,
+			       int                         width,
+			       int                         height,
+			       int                         stride,
+			       double                      x_offset,
+			       double                      y_offset);
+
+
 /* cairo-observer.c */
 
 cairo_private void
commit f3c34887bd59377f003e790a4039a3074ca01f7d
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Tue Dec 7 18:21:35 2010 +0100

    gstate: Disallow incomplete mesh gradient sources
    
    Mesh gradients are constructed using multiple commands, bracketed by
    explicit begin/end pairs. Using a mesh gradient inside a begin/end
    pair is not allowed.

diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c
index 7c54dcb..efb8267 100644
--- a/src/cairo-gstate.c
+++ b/src/cairo-gstate.c
@@ -1020,6 +1020,20 @@ _reduce_op (cairo_gstate_t *gstate)
     return op;
 }
 
+static cairo_status_t
+_cairo_gstate_get_pattern_status (const cairo_pattern_t *pattern)
+{
+    if (unlikely (pattern->type == CAIRO_PATTERN_TYPE_MESH &&
+		  ((const cairo_mesh_pattern_t *) pattern)->current_patch))
+    {
+	/* If current patch != NULL, the pattern is under construction
+	 * and cannot be used as a source */
+	return CAIRO_STATUS_INVALID_MESH_CONSTRUCTION;
+    }
+
+    return pattern->status;
+}
+
 cairo_status_t
 _cairo_gstate_paint (cairo_gstate_t *gstate)
 {
@@ -1029,8 +1043,9 @@ _cairo_gstate_paint (cairo_gstate_t *gstate)
     cairo_status_t status;
     cairo_operator_t op;
 
-    if (unlikely (gstate->source->status))
-	return gstate->source->status;
+    status = _cairo_gstate_get_pattern_status (gstate->source);
+    if (unlikely (status))
+	return status;
 
     if (gstate->op == CAIRO_OPERATOR_DEST)
 	return CAIRO_STATUS_SUCCESS;
@@ -1064,11 +1079,13 @@ _cairo_gstate_mask (cairo_gstate_t  *gstate,
     cairo_clip_t clip;
     cairo_status_t status;
 
-    if (unlikely (mask->status))
-	return mask->status;
+    status = _cairo_gstate_get_pattern_status (mask);
+    if (unlikely (status))
+	return status;
 
-    if (unlikely (gstate->source->status))
-	return gstate->source->status;
+    status = _cairo_gstate_get_pattern_status (gstate->source);
+    if (unlikely (status))
+	return status;
 
     if (gstate->op == CAIRO_OPERATOR_DEST)
 	return CAIRO_STATUS_SUCCESS;
@@ -1140,8 +1157,9 @@ _cairo_gstate_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path)
     cairo_clip_t clip;
     cairo_status_t status;
 
-    if (unlikely (gstate->source->status))
-	return gstate->source->status;
+    status = _cairo_gstate_get_pattern_status (gstate->source);
+    if (unlikely (status))
+	return status;
 
     if (gstate->op == CAIRO_OPERATOR_DEST)
 	return CAIRO_STATUS_SUCCESS;
@@ -1242,8 +1260,9 @@ _cairo_gstate_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path)
     cairo_clip_t clip;
     cairo_status_t status;
 
-    if (unlikely (gstate->source->status))
-	return gstate->source->status;
+    status = _cairo_gstate_get_pattern_status (gstate->source);
+    if (unlikely (status))
+	return status;
 
     if (gstate->op == CAIRO_OPERATOR_DEST)
 	return CAIRO_STATUS_SUCCESS;
@@ -1909,8 +1928,9 @@ _cairo_gstate_show_text_glyphs (cairo_gstate_t		   *gstate,
     cairo_status_t status;
     cairo_clip_t clip;
 
-    if (unlikely (gstate->source->status))
-	return gstate->source->status;
+    status = _cairo_gstate_get_pattern_status (gstate->source);
+    if (unlikely (status))
+	return status;
 
     if (gstate->op == CAIRO_OPERATOR_DEST)
 	return CAIRO_STATUS_SUCCESS;
commit ed24deaa2eaefb5e11ff900d4466474592f66d33
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Thu Dec 9 10:34:31 2010 +0100

    mesh: Add mesh pattern type and enum values
    
    Add the mesh pattern type and an error status to be used to report an
    incorrect construction of the pattern.
    
    Update the backends to make them ready to handle the new pattern type,
    even if it cannot be created yet.

diff --git a/src/cairo-device.c b/src/cairo-device.c
index 15b0484..a32b971 100644
--- a/src/cairo-device.c
+++ b/src/cairo-device.c
@@ -156,6 +156,7 @@ _cairo_device_create_in_error (cairo_status_t status)
     case CAIRO_STATUS_INVALID_WEIGHT:
     case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED:
     case CAIRO_STATUS_INVALID_CONTENT:
+    case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION:
     default:
 	_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
 	return (cairo_device_t *) &_nil_device;
diff --git a/src/cairo-gl-composite.c b/src/cairo-gl-composite.c
index 109e99b..5057fa6 100644
--- a/src/cairo-gl-composite.c
+++ b/src/cairo-gl-composite.c
@@ -291,6 +291,7 @@ _cairo_gl_operand_init (cairo_gl_operand_t *operand,
 	/* fall through */
 
     default:
+    case CAIRO_PATTERN_TYPE_MESH:
     case CAIRO_PATTERN_TYPE_SURFACE:
 	return _cairo_gl_pattern_texture_setup (operand,
 						pattern, dst,
diff --git a/src/cairo-misc.c b/src/cairo-misc.c
index 6037259..87cabcb 100644
--- a/src/cairo-misc.c
+++ b/src/cairo-misc.c
@@ -150,6 +150,8 @@ cairo_status_to_string (cairo_status_t status)
 	return "the device type is not appropriate for the operation";
     case CAIRO_STATUS_DEVICE_ERROR:
 	return "an operation to the device caused an unspecified error";
+    case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION:
+	return "invalid operation during mesh pattern construction";
     default:
     case CAIRO_STATUS_LAST_STATUS:
 	return "<unknown error status>";
diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index c67eac3..ad7a9ed 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -52,7 +52,7 @@
  */
 
 #if HAS_FREED_POOL
-static freed_pool_t freed_pattern_pool[4];
+static freed_pool_t freed_pattern_pool[5];
 #endif
 
 static const cairo_solid_pattern_t _cairo_pattern_nil = {
@@ -156,6 +156,9 @@ _cairo_pattern_init (cairo_pattern_t *pattern, cairo_pattern_type_t type)
     case CAIRO_PATTERN_TYPE_RADIAL:
 	VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_radial_pattern_t));
 	break;
+    case CAIRO_PATTERN_TYPE_MESH:
+	VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_mesh_pattern_t));
+	break;
     }
 #endif
 
@@ -221,6 +224,19 @@ _cairo_gradient_pattern_init_copy (cairo_gradient_pattern_t	  *pattern,
     return CAIRO_STATUS_SUCCESS;
 }
 
+static cairo_status_t
+_cairo_mesh_pattern_init_copy (cairo_mesh_pattern_t       *pattern,
+			       const cairo_mesh_pattern_t *other)
+{
+    *pattern = *other;
+
+    _cairo_array_init (&pattern->patches,  sizeof (cairo_mesh_patch_t));
+
+    return _cairo_array_append_multiple (&pattern->patches,
+					 _cairo_array_index_const (&other->patches, 0),
+					 _cairo_array_num_elements (&other->patches));
+}
+
 cairo_status_t
 _cairo_pattern_init_copy (cairo_pattern_t	*pattern,
 			  const cairo_pattern_t *other)
@@ -263,6 +279,18 @@ _cairo_pattern_init_copy (cairo_pattern_t	*pattern,
 	    return status;
 
     } break;
+    case CAIRO_PATTERN_TYPE_MESH: {
+	cairo_mesh_pattern_t *dst = (cairo_mesh_pattern_t *) pattern;
+	cairo_mesh_pattern_t *src = (cairo_mesh_pattern_t *) other;
+	cairo_status_t status;
+
+	VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_mesh_pattern_t)));
+
+	status = _cairo_mesh_pattern_init_copy (dst, src);
+	if (unlikely (status))
+	    return status;
+
+    } break;
     }
 
     /* The reference count and user_data array are unique to the copy. */
@@ -295,6 +323,9 @@ _cairo_pattern_init_static_copy (cairo_pattern_t	*pattern,
     case CAIRO_PATTERN_TYPE_RADIAL:
 	size = sizeof (cairo_radial_pattern_t);
 	break;
+    case CAIRO_PATTERN_TYPE_MESH:
+	size = sizeof (cairo_mesh_pattern_t);
+	break;
     }
 
     memcpy (pattern, other, size);
@@ -355,6 +386,12 @@ _cairo_pattern_fini (cairo_pattern_t *pattern)
 	if (gradient->stops && gradient->stops != gradient->stops_embedded)
 	    free (gradient->stops);
     } break;
+    case CAIRO_PATTERN_TYPE_MESH: {
+	cairo_mesh_pattern_t *mesh =
+	    (cairo_mesh_pattern_t *) pattern;
+
+	_cairo_array_fini (&mesh->patches);
+    } break;
     }
 
 #if HAVE_VALGRIND
@@ -371,6 +408,9 @@ _cairo_pattern_fini (cairo_pattern_t *pattern)
     case CAIRO_PATTERN_TYPE_RADIAL:
 	VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_radial_pattern_t));
 	break;
+    case CAIRO_PATTERN_TYPE_MESH:
+	VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_mesh_pattern_t));
+	break;
     }
 #endif
 }
@@ -398,6 +438,9 @@ _cairo_pattern_create_copy (cairo_pattern_t	  **pattern_out,
     case CAIRO_PATTERN_TYPE_RADIAL:
 	pattern = malloc (sizeof (cairo_radial_pattern_t));
 	break;
+    case CAIRO_PATTERN_TYPE_MESH:
+	pattern = malloc (sizeof (cairo_mesh_pattern_t));
+	break;
     default:
 	ASSERT_NOT_REACHED;
 	return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
@@ -2444,6 +2487,26 @@ _cairo_pattern_alpha_range (const cairo_pattern_t *pattern,
 	break;
     }
 
+    case CAIRO_PATTERN_TYPE_MESH: {
+	const cairo_mesh_pattern_t *mesh = (const cairo_mesh_pattern_t *) pattern;
+	const cairo_mesh_patch_t *patch = _cairo_array_index_const (&mesh->patches, 0);
+	unsigned int i, j, n = _cairo_array_num_elements (&mesh->patches);
+
+	assert (n >= 1);
+
+	alpha_min = alpha_max = patch[0].colors[0].alpha;
+	for (i = 0; i < n; i++) {
+	    for (j = 0; j < 4; j++) {
+		if (patch[i].colors[j].alpha < alpha_min)
+		    alpha_min = patch[i].colors[j].alpha;
+		else if (patch[i].colors[j].alpha > alpha_max)
+		    alpha_max = patch[i].colors[j].alpha;
+	    }
+	}
+
+	break;
+    }
+
     default:
 	ASSERT_NOT_REACHED;
 	/* fall through */
@@ -2461,6 +2524,63 @@ _cairo_pattern_alpha_range (const cairo_pattern_t *pattern,
 }
 
 /**
+ * _cairo_mesh_pattern_coord_box
+ *
+ * Convenience function to determine the range of the coordinates of
+ * the points used to define the patches of the mesh.
+ *
+ * This is guaranteed to contain the pattern extents, but might not be
+ * tight, just like a Bezier curve is always inside the convex hull of
+ * the control points.
+ *
+ * This function cannot be used while the mesh is being constructed.
+ *
+ * The function returns TRUE and sets the output parametes to define
+ * the coodrinate range if the mesh pattern contains at least one
+ * patch, otherwise it returns FALSE.
+ **/
+cairo_bool_t
+_cairo_mesh_pattern_coord_box (const cairo_mesh_pattern_t *mesh,
+			       double                     *out_xmin,
+			       double                     *out_ymin,
+			       double                     *out_xmax,
+			       double                     *out_ymax)
+{
+    const cairo_mesh_patch_t *patch;
+    unsigned int num_patches, i, j, k;
+    double x0, y0, x1, y1;
+
+    assert (mesh->current_patch == NULL);
+
+    num_patches = _cairo_array_num_elements (&mesh->patches);
+
+    if (num_patches == 0)
+	return FALSE;
+
+    patch = _cairo_array_index_const (&mesh->patches, 0);
+    x0 = x1 = patch->points[0][0].x;
+    y0 = y1 = patch->points[0][0].y;
+
+    for (i = 0; i < num_patches; i++) {
+	for (j = 0; j < 4; j++) {
+	    for (k = 0; k < 4; k++) {
+		x0 = MIN (x0, patch[i].points[j][k].x);
+		y0 = MIN (y0, patch[i].points[j][k].y);
+		x1 = MAX (x1, patch[i].points[j][k].x);
+		y1 = MAX (y1, patch[i].points[j][k].y);
+	    }
+	}
+    }
+
+    *out_xmin = x0;
+    *out_ymin = y0;
+    *out_xmax = x1;
+    *out_ymax = y1;
+
+    return TRUE;
+}
+
+/**
  * _cairo_gradient_pattern_is_solid
  *
  * Convenience function to determine whether a gradient pattern is
@@ -2527,6 +2647,22 @@ _cairo_gradient_pattern_is_solid (const cairo_gradient_pattern_t *gradient,
     return TRUE;
 }
 
+static cairo_bool_t
+_mesh_is_clear (const cairo_mesh_pattern_t *mesh)
+{
+    double x1, y1, x2, y2;
+    cairo_bool_t is_valid;
+
+    is_valid = _cairo_mesh_pattern_coord_box (mesh, &x1, &y1, &x2, &y2);
+    if (!is_valid)
+	return TRUE;
+
+    if (x2 - x1 < DBL_EPSILON || y2 - y1 < DBL_EPSILON)
+	return TRUE;
+
+    return FALSE;
+}
+
 /**
  * _cairo_pattern_is_opaque_solid
  *
@@ -2665,6 +2801,8 @@ _cairo_pattern_is_opaque (const cairo_pattern_t *abstract_pattern,
     case CAIRO_PATTERN_TYPE_LINEAR:
     case CAIRO_PATTERN_TYPE_RADIAL:
 	return _gradient_is_opaque (&pattern->gradient.base, extents);
+    case CAIRO_PATTERN_TYPE_MESH:
+	return FALSE;
     }
 
     ASSERT_NOT_REACHED;
@@ -2688,6 +2826,8 @@ _cairo_pattern_is_clear (const cairo_pattern_t *abstract_pattern)
     case CAIRO_PATTERN_TYPE_LINEAR:
     case CAIRO_PATTERN_TYPE_RADIAL:
 	return _gradient_is_clear (&pattern->gradient.base, NULL);
+    case CAIRO_PATTERN_TYPE_MESH:
+	return _mesh_is_clear (&pattern->mesh);
     }
 
     ASSERT_NOT_REACHED;
@@ -3306,6 +3446,29 @@ _cairo_pattern_get_extents (const cairo_pattern_t         *pattern,
 	}
 	break;
 
+    case CAIRO_PATTERN_TYPE_MESH:
+	{
+	    const cairo_mesh_pattern_t *mesh =
+		(const cairo_mesh_pattern_t *) pattern;
+	    double padx, pady;
+	    cairo_bool_t is_valid;
+
+	    is_valid = _cairo_mesh_pattern_coord_box (mesh, &x1, &y1, &x2, &y2);
+	    if (!is_valid)
+		goto EMPTY;
+
+	    padx = pady = 1.;
+	    cairo_matrix_transform_distance (&pattern->matrix, &padx, &pady);
+	    padx = fabs (padx);
+	    pady = fabs (pady);
+
+	    x1 -= padx;
+	    y1 -= pady;
+	    x2 += padx;
+	    y2 += pady;
+	}
+	break;
+
     default:
 	ASSERT_NOT_REACHED;
     }
@@ -3410,6 +3573,18 @@ _cairo_radial_pattern_hash (unsigned long hash,
 }
 
 static unsigned long
+_cairo_mesh_pattern_hash (unsigned long hash, const cairo_mesh_pattern_t *mesh)
+{
+    const cairo_mesh_patch_t *patch = _cairo_array_index_const (&mesh->patches, 0);
+    unsigned int i, n = _cairo_array_num_elements (&mesh->patches);
+
+    for (i = 0; i < n; i++)
+       hash = _cairo_hash_bytes (hash, patch + i, sizeof (cairo_mesh_patch_t));
+
+    return hash;
+}
+
+static unsigned long
 _cairo_surface_pattern_hash (unsigned long hash,
 			     const cairo_surface_pattern_t *surface)
 {
@@ -3446,6 +3621,8 @@ _cairo_pattern_hash (const cairo_pattern_t *pattern)
 	return _cairo_linear_pattern_hash (hash, (cairo_linear_pattern_t *) pattern);
     case CAIRO_PATTERN_TYPE_RADIAL:
 	return _cairo_radial_pattern_hash (hash, (cairo_radial_pattern_t *) pattern);
+    case CAIRO_PATTERN_TYPE_MESH:
+	return _cairo_mesh_pattern_hash (hash, (cairo_mesh_pattern_t *) pattern);
     case CAIRO_PATTERN_TYPE_SURFACE:
 	return _cairo_surface_pattern_hash (hash, (cairo_surface_pattern_t *) pattern);
     default:
@@ -3483,6 +3660,9 @@ _cairo_pattern_size (const cairo_pattern_t *pattern)
     case CAIRO_PATTERN_TYPE_RADIAL:
 	return sizeof (cairo_radial_pattern_t) +
 	    _cairo_gradient_pattern_color_stops_size (pattern);
+    case CAIRO_PATTERN_TYPE_MESH:
+	return sizeof (cairo_mesh_pattern_t) +
+	    _cairo_gradient_pattern_color_stops_size (pattern);
     default:
 	ASSERT_NOT_REACHED;
 	return 0;
@@ -3561,6 +3741,29 @@ _cairo_radial_pattern_equal (const cairo_radial_pattern_t *a,
 }
 
 static cairo_bool_t
+_cairo_mesh_pattern_equal (const cairo_mesh_pattern_t *a,
+			   const cairo_mesh_pattern_t *b)
+{
+    const cairo_mesh_patch_t *patch_a, *patch_b;
+    unsigned int i, num_patches_a, num_patches_b;
+
+    num_patches_a = _cairo_array_num_elements (&a->patches);
+    num_patches_b = _cairo_array_num_elements (&b->patches);
+
+    if (num_patches_a != num_patches_b)
+	return FALSE;
+
+    for (i = 0; i < num_patches_a; i++) {
+	patch_a = _cairo_array_index_const (&a->patches, i);
+	patch_b = _cairo_array_index_const (&a->patches, i);
+	if (memcmp (patch_a, patch_b, sizeof(cairo_mesh_patch_t)) != 0)
+	    return FALSE;
+    }
+
+    return TRUE;
+}
+
+static cairo_bool_t
 _cairo_surface_pattern_equal (const cairo_surface_pattern_t *a,
 			      const cairo_surface_pattern_t *b)
 {
@@ -3603,6 +3806,9 @@ _cairo_pattern_equal (const cairo_pattern_t *a, const cairo_pattern_t *b)
     case CAIRO_PATTERN_TYPE_RADIAL:
 	return _cairo_radial_pattern_equal ((cairo_radial_pattern_t *) a,
 					    (cairo_radial_pattern_t *) b);
+    case CAIRO_PATTERN_TYPE_MESH:
+	return _cairo_mesh_pattern_equal ((cairo_mesh_pattern_t *) a,
+					  (cairo_mesh_pattern_t *) b);
     case CAIRO_PATTERN_TYPE_SURFACE:
 	return _cairo_surface_pattern_equal ((cairo_surface_pattern_t *) a,
 					     (cairo_surface_pattern_t *) b);
diff --git a/src/cairo-region.c b/src/cairo-region.c
index 112b1d8..b15f151 100644
--- a/src/cairo-region.c
+++ b/src/cairo-region.c
@@ -104,6 +104,7 @@ _cairo_region_create_in_error (cairo_status_t status)
     case CAIRO_STATUS_INVALID_SLANT:
     case CAIRO_STATUS_INVALID_WEIGHT:
     case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED:
+    case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION:
     default:
 	_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
 	return (cairo_region_t *) &_cairo_region_nil;
diff --git a/src/cairo-spans.c b/src/cairo-spans.c
index a187b89..bffbdeb 100644
--- a/src/cairo-spans.c
+++ b/src/cairo-spans.c
@@ -204,6 +204,7 @@ _cairo_scan_converter_create_in_error (cairo_status_t status)
     case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: RETURN_NIL;
     case CAIRO_STATUS_DEVICE_TYPE_MISMATCH: RETURN_NIL;
     case CAIRO_STATUS_DEVICE_ERROR: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: RETURN_NIL;
     default:
 	break;
     }
@@ -314,6 +315,7 @@ _cairo_span_renderer_create_in_error (cairo_status_t status)
     case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: RETURN_NIL;
     case CAIRO_STATUS_DEVICE_TYPE_MISMATCH: RETURN_NIL;
     case CAIRO_STATUS_DEVICE_ERROR: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: RETURN_NIL;
     default:
 	break;
     }
diff --git a/src/cairo-surface.c b/src/cairo-surface.c
index b5ed334..71b7e45 100644
--- a/src/cairo-surface.c
+++ b/src/cairo-surface.c
@@ -3022,6 +3022,7 @@ _cairo_surface_create_in_error (cairo_status_t status)
     case CAIRO_STATUS_INVALID_SLANT:
     case CAIRO_STATUS_INVALID_WEIGHT:
     case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED:
+    case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION:
     default:
 	_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
 	return (cairo_surface_t *) &_cairo_surface_nil;
diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c
index ab0f0c7..89e13d0 100644
--- a/src/cairo-svg-surface.c
+++ b/src/cairo-svg-surface.c
@@ -934,6 +934,9 @@ _cairo_svg_surface_analyze_operation (cairo_svg_surface_t   *surface,
 	return CAIRO_INT_STATUS_UNSUPPORTED;
     }
 
+    if (pattern->type == CAIRO_PATTERN_TYPE_MESH)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
     /* SVG doesn't support extend reflect for image pattern */
     if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE &&
 	pattern->extend == CAIRO_EXTEND_REFLECT)
@@ -2035,6 +2038,9 @@ _cairo_svg_surface_emit_pattern (cairo_svg_surface_t   *surface,
     case CAIRO_PATTERN_TYPE_RADIAL:
 	return _cairo_svg_surface_emit_radial_pattern (surface, (cairo_radial_pattern_t *) pattern,
 						       output, is_stroke, parent_matrix);
+
+    case CAIRO_PATTERN_TYPE_MESH:
+	ASSERT_NOT_REACHED;
     }
     return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
 }
diff --git a/src/cairo-types-private.h b/src/cairo-types-private.h
index 1282b3f..a09b410 100644
--- a/src/cairo-types-private.h
+++ b/src/cairo-types-private.h
@@ -442,6 +442,51 @@ typedef union {
     cairo_radial_pattern_t radial;
 } cairo_gradient_pattern_union_t;
 
+/*
+ * A mesh patch is a tensor-product patch (bicubic Bezier surface
+ * patch). It has 16 control points. Each set of 4 points along the
+ * sides of the 4x4 grid of control points is a Bezier curve that
+ * defines one side of the patch. A color is assigned to each
+ * corner. The inner 4 points provide additional control over the
+ * shape and the color mapping.
+ *
+ * Cairo uses the same convention as the PDF Reference for numbering
+ * the points and side of the patch.
+ *
+ *
+ *                      Side 1
+ *
+ *          p[0][3] p[1][3] p[2][3] p[3][3]
+ * Side 0   p[0][2] p[1][2] p[2][2] p[3][2]  Side 2
+ *          p[0][1] p[1][1] p[2][1] p[3][1]
+ *          p[0][0] p[1][0] p[2][0] p[3][0]
+ *
+ *                      Side 3
+ *
+ *
+ *   Point            Color
+ *  -------------------------
+ *  points[0][0]    colors[0]
+ *  points[0][3]    colors[1]
+ *  points[3][3]    colors[2]
+ *  points[3][0]    colors[3]
+ */
+
+typedef struct _cairo_mesh_patch {
+    cairo_point_double_t points[4][4];
+    cairo_color_t colors[4];
+} cairo_mesh_patch_t;
+
+typedef struct _cairo_mesh_pattern {
+    cairo_pattern_t base;
+
+    cairo_array_t patches;
+    cairo_mesh_patch_t *current_patch;
+    int current_side;
+    cairo_bool_t has_control_point[4];
+    cairo_bool_t has_color[4];
+} cairo_mesh_pattern_t;
+
 typedef union {
     cairo_pattern_type_t	    type;
     cairo_pattern_t		    base;
@@ -449,6 +494,7 @@ typedef union {
     cairo_solid_pattern_t	    solid;
     cairo_surface_pattern_t	    surface;
     cairo_gradient_pattern_union_t  gradient;
+    cairo_mesh_pattern_t	    mesh;
 } cairo_pattern_union_t;
 
 /*
diff --git a/src/cairo-win32-printing-surface.c b/src/cairo-win32-printing-surface.c
index 4d472b0..dabeece 100644
--- a/src/cairo-win32-printing-surface.c
+++ b/src/cairo-win32-printing-surface.c
@@ -971,6 +971,9 @@ _cairo_win32_printing_surface_paint_pattern (cairo_win32_surface_t *surface,
     case CAIRO_PATTERN_TYPE_RADIAL:
 	return CAIRO_INT_STATUS_UNSUPPORTED;
 	break;
+
+    case CAIRO_PATTERN_TYPE_MESH:
+	ASSERT_NOT_REACHED;
     }
 
     return CAIRO_STATUS_SUCCESS;
diff --git a/src/cairo-xcb-surface-core.c b/src/cairo-xcb-surface-core.c
index f12d735..4bcbd8f 100644
--- a/src/cairo-xcb-surface-core.c
+++ b/src/cairo-xcb-surface-core.c
@@ -446,6 +446,7 @@ _cairo_xcb_pixmap_for_pattern (cairo_xcb_surface_t *target,
 	/* fallthrough */
     case CAIRO_PATTERN_TYPE_LINEAR:
     case CAIRO_PATTERN_TYPE_RADIAL:
+    case CAIRO_PATTERN_TYPE_MESH:
 	return _render_to_pixmap (target, pattern, extents);
 
     default:
diff --git a/src/cairo-xcb-surface-render.c b/src/cairo-xcb-surface-render.c
index 6a42225..e3d1ac4 100644
--- a/src/cairo-xcb-surface-render.c
+++ b/src/cairo-xcb-surface-render.c
@@ -425,7 +425,7 @@ _pattern_is_supported (uint32_t flags,
 	    return FALSE;
     }
 
-    return TRUE;
+    return pattern->type != CAIRO_PATTERN_TYPE_MESH;
 }
 
 static double
@@ -1445,6 +1445,7 @@ _cairo_xcb_picture_for_pattern (cairo_xcb_surface_t *target,
 	return _cairo_xcb_surface_picture (target,
 					   (cairo_surface_pattern_t *) pattern,
 					   extents);
+    case CAIRO_PATTERN_TYPE_MESH:
     default:
 	ASSERT_NOT_REACHED;
 	return _render_to_picture (target, pattern, extents);
diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
index 6d593c0..76cf5d1 100644
--- a/src/cairo-xlib-surface.c
+++ b/src/cairo-xlib-surface.c
@@ -2230,6 +2230,7 @@ _cairo_xlib_surface_acquire_pattern_surface (cairo_xlib_display_t *display,
 	ASSERT_NOT_REACHED;
     case CAIRO_PATTERN_TYPE_SOLID:
     case CAIRO_PATTERN_TYPE_SURFACE:
+    case CAIRO_PATTERN_TYPE_MESH:
 	break;
     }
 
diff --git a/src/cairo.h b/src/cairo.h
index a45bf14..7412969 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -270,6 +270,10 @@ typedef struct _cairo_user_data_key {
  * @CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: user-font method not implemented (Since 1.10)
  * @CAIRO_STATUS_DEVICE_TYPE_MISMATCH: the device type is not appropriate for the operation (Since 1.10)
  * @CAIRO_STATUS_DEVICE_ERROR: an operation to the device caused an unspecified error (Since 1.10)
+ * @CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: a mesh pattern
+ *   construction operation was used outside of a
+ *   cairo_pattern_mesh_begin_patch()/cairo_pattern_mesh_end_patch()
+ *   pair (Since 1.12)
  * @CAIRO_STATUS_LAST_STATUS: this is a special value indicating the number of
  *   status values defined in this enumeration.  When using this value, note
  *   that the version of cairo at run-time may have additional status values
@@ -321,6 +325,7 @@ typedef enum _cairo_status {
     CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED,
     CAIRO_STATUS_DEVICE_TYPE_MISMATCH,
     CAIRO_STATUS_DEVICE_ERROR,
+    CAIRO_STATUS_INVALID_MESH_CONSTRUCTION,
 
     CAIRO_STATUS_LAST_STATUS
 } cairo_status_t;
@@ -2360,6 +2365,7 @@ cairo_pattern_set_user_data (cairo_pattern_t		 *pattern,
  * @CAIRO_PATTERN_TYPE_SURFACE: The pattern is a based on a surface (an image).
  * @CAIRO_PATTERN_TYPE_LINEAR: The pattern is a linear gradient.
  * @CAIRO_PATTERN_TYPE_RADIAL: The pattern is a radial gradient.
+ * @CAIRO_PATTERN_TYPE_MESH: The pattern is a mesh.
  *
  * #cairo_pattern_type_t is used to describe the type of a given pattern.
  *
@@ -2387,7 +2393,8 @@ typedef enum _cairo_pattern_type {
     CAIRO_PATTERN_TYPE_SOLID,
     CAIRO_PATTERN_TYPE_SURFACE,
     CAIRO_PATTERN_TYPE_LINEAR,
-    CAIRO_PATTERN_TYPE_RADIAL
+    CAIRO_PATTERN_TYPE_RADIAL,
+    CAIRO_PATTERN_TYPE_MESH
 } cairo_pattern_type_t;
 
 cairo_public cairo_pattern_type_t
diff --git a/src/cairoint.h b/src/cairoint.h
index db2836c..7cbe0fd 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -2213,6 +2213,13 @@ cairo_private void
 _cairo_pattern_transform (cairo_pattern_t      *pattern,
 			  const cairo_matrix_t *ctm_inverse);
 
+cairo_private cairo_bool_t
+_cairo_mesh_pattern_coord_box (const cairo_mesh_pattern_t *mesh,
+			       double                     *out_xmin,
+			       double                     *out_ymin,
+			       double                     *out_xmax,
+			       double                     *out_ymax);
+
 cairo_private void
 _cairo_pattern_alpha_range (const cairo_pattern_t *gradient,
 			    double *out_min, double *out_max);
diff --git a/util/cairo-gobject/cairo-gobject-enums.c b/util/cairo-gobject/cairo-gobject-enums.c
index 1fcd3d0..152bfd7 100644
--- a/util/cairo-gobject/cairo-gobject-enums.c
+++ b/util/cairo-gobject/cairo-gobject-enums.c
@@ -49,6 +49,7 @@ cairo_gobject_status_get_type (void)
           { CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED, "CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED", "user-font-not-implemented" },
           { CAIRO_STATUS_DEVICE_TYPE_MISMATCH, "CAIRO_STATUS_DEVICE_TYPE_MISMATCH", "device-type-mismatch" },
           { CAIRO_STATUS_DEVICE_ERROR, "CAIRO_STATUS_DEVICE_ERROR", "device-error" },
+          { CAIRO_STATUS_INVALID_MESH_CONSTRUCTION, "CAIRO_STATUS_INVALID_MESH_CONSTRUCTION", "invalid-mesh-construction" },
           { CAIRO_STATUS_LAST_STATUS, "CAIRO_STATUS_LAST_STATUS", "last-status" },
           { 0, NULL, NULL }
       };
@@ -431,6 +432,7 @@ cairo_gobject_pattern_type_get_type (void)
           { CAIRO_PATTERN_TYPE_SURFACE, "CAIRO_PATTERN_TYPE_SURFACE", "surface" },
           { CAIRO_PATTERN_TYPE_LINEAR, "CAIRO_PATTERN_TYPE_LINEAR", "linear" },
           { CAIRO_PATTERN_TYPE_RADIAL, "CAIRO_PATTERN_TYPE_RADIAL", "radial" },
+          { CAIRO_PATTERN_TYPE_MESH, "CAIRO_PATTERN_TYPE_MESH", "mesh" },
           { 0, NULL, NULL }
       };
       GType type = g_enum_register_static (g_intern_static_string ("cairo_pattern_type_t"), values);
diff --git a/util/cairo-trace/trace.c b/util/cairo-trace/trace.c
index acdde93..b930506 100644
--- a/util/cairo-trace/trace.c
+++ b/util/cairo-trace/trace.c
@@ -1501,6 +1501,7 @@ _status_to_string (cairo_status_t status)
 	f(USER_FONT_NOT_IMPLEMENTED);
 	f(DEVICE_TYPE_MISMATCH);
 	f(DEVICE_ERROR);
+	f(INVALID_MESH_CONSTRUCTION);
     case CAIRO_STATUS_LAST_STATUS:
 	break;
     }
commit 19b840a9044f873657f0c0dbb3ccf82a13d43888
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Wed Nov 17 14:56:08 2010 +0100

    Keep makefiles in alphabetical order
    
    Recording surfaces were at first called meta surfaces. When the name
    was changed, makefiles were not updated to keep alphabetical order.

diff --git a/src/Makefile.sources b/src/Makefile.sources
index 0f0b922..cc846b4 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -74,7 +74,6 @@ cairo_private = \
 	cairo-image-info-private.h \
 	cairo-list-private.h \
 	cairo-malloc-private.h \
-	cairo-recording-surface-private.h \
 	cairo-mutex-impl-private.h \
 	cairo-mutex-list-private.h \
 	cairo-mutex-private.h \
@@ -85,6 +84,7 @@ cairo_private = \
 	cairo-path-fixed-private.h \
 	cairo-path-private.h \
 	cairo-private.h \
+	cairo-recording-surface-private.h \
 	cairo-reference-count-private.h \
 	cairo-region-private.h \
 	cairo-rtree-private.h \
@@ -136,7 +136,6 @@ cairo_sources = \
 	cairo-image-surface.c \
 	cairo-lzw.c \
 	cairo-matrix.c \
-	cairo-recording-surface.c \
 	cairo-misc.c \
 	cairo-mutex.c \
 	cairo-observer.c \
@@ -151,6 +150,7 @@ cairo_sources = \
 	cairo-pattern.c \
 	cairo-pen.c \
 	cairo-polygon.c \
+	cairo-recording-surface.c \
 	cairo-rectangle.c \
 	cairo-rectangular-scan-converter.c \
 	cairo-region.c \
diff --git a/test/Makefile.am b/test/Makefile.am
index 236fc4f..8932e3e 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -853,20 +853,6 @@ REFERENCE_IMAGES = \
 	mask.svg.rgb24.xfail.png \
 	mask.xlib.ref.png \
 	mask.xlib.rgb24.ref.png \
-	recording-surface-pattern.image16.ref.png \
-	recording-surface-pattern.gl.argb32.ref.png \
-	recording-surface-pattern.pdf.argb32.ref.png \
-	recording-surface-pattern.pdf.rgb24.ref.png \
-	recording-surface-pattern.ps.argb32.ref.png \
-	recording-surface-pattern.ps.rgb24.ref.png \
-	recording-surface-pattern.quartz.argb32.ref.png \
-	recording-surface-pattern.quartz.rgb24.ref.png \
-	recording-surface-pattern.ref.png \
-	recording-surface-pattern.rgb24.ref.png \
-	recording-surface-pattern.svg.argb32.ref.png \
-	recording-surface-pattern.svg.rgb24.ref.png \
-	recording-surface-pattern.xlib.argb32.ref.png \
-	recording-surface-pattern.xlib.rgb24.ref.png \
 	mime-data.pdf.ref.png \
 	mime-data.ps.ref.png \
 	mime-data.ref.png \
@@ -1100,6 +1086,20 @@ REFERENCE_IMAGES = \
 	random-intersections-curves-nz.ref.png \
 	random-intersections-curves-nz.xlib.ref.png \
 	random-intersections-curves-nz.xlib-fallback.ref.png \
+	recording-surface-pattern.image16.ref.png \
+	recording-surface-pattern.gl.argb32.ref.png \
+	recording-surface-pattern.pdf.argb32.ref.png \
+	recording-surface-pattern.pdf.rgb24.ref.png \
+	recording-surface-pattern.ps.argb32.ref.png \
+	recording-surface-pattern.ps.rgb24.ref.png \
+	recording-surface-pattern.quartz.argb32.ref.png \
+	recording-surface-pattern.quartz.rgb24.ref.png \
+	recording-surface-pattern.ref.png \
+	recording-surface-pattern.rgb24.ref.png \
+	recording-surface-pattern.svg.argb32.ref.png \
+	recording-surface-pattern.svg.rgb24.ref.png \
+	recording-surface-pattern.xlib.argb32.ref.png \
+	recording-surface-pattern.xlib.rgb24.ref.png \
 	rectangle-rounding-error.ref.png \
 	rectilinear-dash.quartz.xfail.png \
 	rectilinear-dash.ref.png \
diff --git a/test/Makefile.sources b/test/Makefile.sources
index 6a862f6..2026a08 100644
--- a/test/Makefile.sources
+++ b/test/Makefile.sources
@@ -166,7 +166,6 @@ test_sources = \
 	mask-surface-ctm.c				\
 	mask-transformed-image.c			\
 	mask-transformed-similar.c			\
-	recording-surface-pattern.c			\
 	mime-data.c					\
 	miter-precision.c				\
 	move-to-show-surface.c				\
@@ -208,6 +207,7 @@ test_sources = \
 	random-intersections-nonzero.c			\
 	random-intersections-curves-eo.c		\
 	random-intersections-curves-nz.c		\
+	recording-surface-pattern.c			\
 	rectangle-rounding-error.c			\
 	rectilinear-fill.c				\
 	rectilinear-grid.c				\


More information about the cairo-commit mailing list