[cairo-commit] 8 commits - src/cairo.h src/cairoint.h src/cairo-path-fill.c src/cairo-path-fixed.c src/cairo-quartz.h src/cairo-quartz-image-surface.c src/cairo-quartz-private.h src/cairo-quartz-surface.c src/Makefile.am test/buffer-diff.c test/dash-scale-quartz-ref.png test/degenerate-pen.c test/degenerate-pen-ps-argb32-ref.png test/degenerate-pen-ps-ref.png test/degenerate-pen-quartz-ref.png test/degenerate-pen-ref.png test/leaky-dash-quartz-ref.png test/line-width-scale-quartz-ref.png test/mask-alpha-quartz-argb32-ref.png test/mask-quartz-ref.png test/mask-quartz-rgb24-ref.png test/meta-surface-pattern-quartz-ref.png test/meta-surface-pattern-quartz-rgb24-ref.png test/new-sub-path-quartz-ref.png test/over-above-source-quartz-ref.png test/over-above-source-quartz-rgb24-ref.png test/over-around-source-quartz-ref.png test/over-around-source-quartz-rgb24-ref.png test/over-between-source-quartz-ref.png test/over-between-source-quartz-rgb24-ref.png test/rotate-image-surface-paint-quartz-ref.png test/trap-clip-quartz-ref.png

Vladimir Vukicevic vladimir at kemper.freedesktop.org
Mon Feb 25 18:45:52 PST 2008


 dev/null                                       |binary
 src/Makefile.am                                |    2 
 src/cairo-path-fill.c                          |   66 --
 src/cairo-path-fixed.c                         |   81 +++
 src/cairo-quartz-image-surface.c               |  370 +++++++++++++++
 src/cairo-quartz-private.h                     |   31 +
 src/cairo-quartz-surface.c                     |  596 ++++++++++++-------------
 src/cairo-quartz.h                             |    6 
 src/cairo.h                                    |    4 
 src/cairoint.h                                 |    9 
 test/buffer-diff.c                             |   24 -
 test/dash-scale-quartz-ref.png                 |binary
 test/degenerate-pen-ps-ref.png                 |binary
 test/degenerate-pen-quartz-ref.png             |binary
 test/degenerate-pen-ref.png                    |binary
 test/degenerate-pen.c                          |    4 
 test/leaky-dash-quartz-ref.png                 |binary
 test/line-width-scale-quartz-ref.png           |binary
 test/mask-alpha-quartz-argb32-ref.png          |binary
 test/mask-quartz-ref.png                       |binary
 test/mask-quartz-rgb24-ref.png                 |binary
 test/meta-surface-pattern-quartz-ref.png       |binary
 test/meta-surface-pattern-quartz-rgb24-ref.png |binary
 test/new-sub-path-quartz-ref.png               |binary
 test/over-above-source-quartz-ref.png          |binary
 test/over-above-source-quartz-rgb24-ref.png    |binary
 test/over-around-source-quartz-ref.png         |binary
 test/over-around-source-quartz-rgb24-ref.png   |binary
 test/over-between-source-quartz-ref.png        |binary
 test/over-between-source-quartz-rgb24-ref.png  |binary
 test/rotate-image-surface-paint-quartz-ref.png |binary
 test/trap-clip-quartz-ref.png                  |binary
 32 files changed, 822 insertions(+), 371 deletions(-)

New commits:
commit 7acfee38b1ac6ef2292d754c7103bd65d58f72d8
Author: Vladimir Vukicevic <vladimir at pobox.com>
Date:   Mon Feb 25 21:44:04 2008 -0500

    [atsui] Make default font 'Helvetica'
    
    The previous default font was Monaco, which is a fixed-pitch font; Helvetica
    is more inline with the other platform defaults.

diff --git a/src/cairoint.h b/src/cairoint.h
index d24dfa8..099d0d5 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -840,7 +840,7 @@ typedef struct _cairo_traps {
 #define CAIRO_FONT_WEIGHT_DEFAULT  CAIRO_FONT_WEIGHT_NORMAL
 
 #define CAIRO_WIN32_FONT_FAMILY_DEFAULT "Arial"
-#define CAIRO_ATSUI_FONT_FAMILY_DEFAULT  "Monaco"
+#define CAIRO_ATSUI_FONT_FAMILY_DEFAULT  "Helvetica"
 #define CAIRO_FT_FONT_FAMILY_DEFAULT     ""
 
 #if   CAIRO_HAS_WIN32_FONT
commit 9979f786acaf0ebba1bf97e40aeba66287a3c743
Author: Vladimir Vukicevic <vladimir at pobox.com>
Date:   Mon Feb 25 21:27:33 2008 -0500

    [test] update degenerate-pen test
    
    pdiff was hiding a rgb24 failure here, as the test was drawing using
    black ink on the default black background.  Instead, explicitly fill
    the surface with white first.

diff --git a/test/degenerate-pen-ps-argb32-ref.png b/test/degenerate-pen-ps-argb32-ref.png
deleted file mode 100644
index 2c23cd4..0000000
Binary files a/test/degenerate-pen-ps-argb32-ref.png and /dev/null differ
diff --git a/test/degenerate-pen-ps-ref.png b/test/degenerate-pen-ps-ref.png
new file mode 100644
index 0000000..2d0f07b
Binary files /dev/null and b/test/degenerate-pen-ps-ref.png differ
diff --git a/test/degenerate-pen-quartz-ref.png b/test/degenerate-pen-quartz-ref.png
new file mode 100644
index 0000000..cc25b88
Binary files /dev/null and b/test/degenerate-pen-quartz-ref.png differ
diff --git a/test/degenerate-pen-ref.png b/test/degenerate-pen-ref.png
index 539a325..5961ddd 100644
Binary files a/test/degenerate-pen-ref.png and b/test/degenerate-pen-ref.png differ
diff --git a/test/degenerate-pen.c b/test/degenerate-pen.c
index 858949f..4ff50f8 100644
--- a/test/degenerate-pen.c
+++ b/test/degenerate-pen.c
@@ -60,6 +60,10 @@ cairo_test_t test = {
 static cairo_test_status_t
 draw (cairo_t *cr, int width, int height)
 {
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
     cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
 
     cairo_translate (cr, PAD, PAD);
commit baec928a69b5b763b30766cddfb1473e4e05fc3c
Author: Vladimir Vukicevic <vladimir at pobox.com>
Date:   Mon Feb 25 21:06:36 2008 -0500

    [quartz] More mask fixes -- handle all types via fallback mask image

diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c
index f421347..83c2b81 100644
--- a/src/cairo-quartz-surface.c
+++ b/src/cairo-quartz-surface.c
@@ -1654,6 +1654,52 @@ _cairo_quartz_surface_mask_with_surface (cairo_quartz_surface_t *surface,
     return status;
 }
 
+/* This is somewhat less than ideal, but it gets the job done;
+ * it would be better to avoid calling back into cairo.  This
+ * creates a temporary surface to use as the mask.
+ */
+static cairo_int_status_t
+_cairo_quartz_surface_mask_with_generic (cairo_quartz_surface_t *surface,
+					 cairo_operator_t op,
+					 cairo_pattern_t *source,
+					 cairo_pattern_t *mask)
+{
+    int width = surface->extents.width - surface->extents.x;
+    int height = surface->extents.height - surface->extents.y;
+
+    cairo_surface_t *gradient_surf = NULL;
+    cairo_t *gradient_surf_cr = NULL;
+
+    cairo_pattern_union_t surface_pattern;
+    cairo_int_status_t status;
+
+    /* Render the gradient to a surface */
+    gradient_surf = cairo_quartz_surface_create (CAIRO_FORMAT_ARGB32,
+						 width,
+						 height);
+    gradient_surf_cr = cairo_create(gradient_surf);
+    cairo_set_source (gradient_surf_cr, mask);
+    cairo_set_operator (gradient_surf_cr, CAIRO_OPERATOR_SOURCE);
+    cairo_paint (gradient_surf_cr);
+    status = cairo_status (gradient_surf_cr);
+    cairo_destroy (gradient_surf_cr);
+
+    if (status)
+	goto BAIL;
+
+    _cairo_pattern_init_for_surface (&surface_pattern.surface, gradient_surf);
+
+    status = _cairo_quartz_surface_mask_with_surface (surface, op, source, &surface_pattern.surface);
+
+    _cairo_pattern_fini (&surface_pattern.base);
+
+  BAIL:
+    if (gradient_surf)
+	cairo_surface_destroy (gradient_surf);
+
+    return status;
+}
+
 static cairo_int_status_t
 _cairo_quartz_surface_mask (void *abstract_surface,
 			    cairo_operator_t op,
@@ -1673,30 +1719,29 @@ _cairo_quartz_surface_mask (void *abstract_surface,
 	cairo_solid_pattern_t *solid_mask = (cairo_solid_pattern_t *) mask;
 
 	CGContextSetAlpha (surface->cgContext, solid_mask->color.alpha);
-    } else if (CGContextClipToMaskPtr &&
-               mask->type == CAIRO_PATTERN_TYPE_SURFACE &&
-	       mask->extend == CAIRO_EXTEND_NONE) {
-	return _cairo_quartz_surface_mask_with_surface (surface, op, source, (cairo_surface_pattern_t *) mask);
-    } else {
-	/* So, CGContextClipToMask is not present in 10.3.9, so we're
-	 * doomed; if we have imageData, we can do fallback, otherwise
-	 * just pretend success.
-	 */
-	if (surface->imageData)
-	    return CAIRO_INT_STATUS_UNSUPPORTED;
+	rv = _cairo_quartz_surface_paint (surface, op, source);
+	CGContextSetAlpha (surface->cgContext, 1.0);
 
-	return CAIRO_STATUS_SUCCESS;
+	return rv;
     }
 
-    rv = _cairo_quartz_surface_paint (surface, op, source);
+    /* If we have CGContextClipToMask, we can do more complex masks */
+    if (CGContextClipToMaskPtr) {
+	/* For these, we can skip creating a temporary surface, since we already have one */
+	if (mask->type == CAIRO_PATTERN_TYPE_SURFACE && mask->extend == CAIRO_EXTEND_NONE)
+	    return _cairo_quartz_surface_mask_with_surface (surface, op, source, (cairo_surface_pattern_t *) mask);
 
-    if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
-	CGContextSetAlpha (surface->cgContext, 1.0);
+	return _cairo_quartz_surface_mask_with_generic (surface, op, source, mask);
     }
 
-    ND((stderr, "-- mask\n"));
+    /* So, CGContextClipToMask is not present in 10.3.9, so we're
+     * doomed; if we have imageData, we can do fallback, otherwise
+     * just pretend success.
+     */
+    if (surface->imageData)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
 
-    return rv;
+    return CAIRO_STATUS_SUCCESS;
 }
 
 static cairo_int_status_t
diff --git a/test/mask-quartz-ref.png b/test/mask-quartz-ref.png
new file mode 100644
index 0000000..3925740
Binary files /dev/null and b/test/mask-quartz-ref.png differ
diff --git a/test/mask-quartz-rgb24-ref.png b/test/mask-quartz-rgb24-ref.png
new file mode 100644
index 0000000..b9c57c7
Binary files /dev/null and b/test/mask-quartz-rgb24-ref.png differ
commit 3fcd0be52215e1d8a59560d6b0919fb3f53b7a28
Author: Vladimir Vukicevic <vladimir at pobox.com>
Date:   Mon Feb 25 21:06:35 2008 -0500

    Use pdiff only if the pixel difference is less than a limit

diff --git a/test/buffer-diff.c b/test/buffer-diff.c
index 24fdad3..d1b5863 100644
--- a/test/buffer-diff.c
+++ b/test/buffer-diff.c
@@ -42,6 +42,10 @@
 #include "buffer-diff.h"
 #include "xmalloc.h"
 
+/* Don't allow any differences greater than this value, even if pdiff
+ * claims that the images are identical */
+#define PERCEPTUAL_DIFF_THRESHOLD 25
+
 static void
 xunlink (const char *pathname)
 {
@@ -152,13 +156,19 @@ compare_surfaces (cairo_surface_t	*surface_a,
     /* Then, if there are any different pixels, we give the pdiff code
      * a crack at the images. If it decides that there are no visually
      * discernible differences in any pixels, then we accept this
-     * result as good enough. */
-    discernible_pixels_changed = pdiff_compare (surface_a, surface_b,
-						gamma, luminance, field_of_view);
-    if (discernible_pixels_changed == 0) {
-	result->pixels_changed = 0;
-	cairo_test_log ("But perceptual diff finds no visually discernible difference.\n"
-			"Accepting result.\n");
+     * result as good enough.
+     * 
+     * Only let pdiff have a crack at the comparison if the max difference
+     * is lower than a threshold, otherwise some problems could be masked.
+     */
+    if (result->max_diff < PERCEPTUAL_DIFF_THRESHOLD) {
+        discernible_pixels_changed = pdiff_compare (surface_a, surface_b,
+                                                    gamma, luminance, field_of_view);
+        if (discernible_pixels_changed == 0) {
+            result->pixels_changed = 0;
+            cairo_test_log ("But perceptual diff finds no visually discernible difference.\n"
+                            "Accepting result.\n");
+        }
     }
 }
 
commit 1dfb1bd45fbe08392e233af67f464b2776de9f19
Author: Vladimir Vukicevic <vladimir at pobox.com>
Date:   Mon Feb 25 21:06:28 2008 -0500

    [quartz] update test suite images for quartz

diff --git a/test/dash-scale-quartz-ref.png b/test/dash-scale-quartz-ref.png
new file mode 100644
index 0000000..8fc897b
Binary files /dev/null and b/test/dash-scale-quartz-ref.png differ
diff --git a/test/leaky-dash-quartz-ref.png b/test/leaky-dash-quartz-ref.png
new file mode 100644
index 0000000..8343359
Binary files /dev/null and b/test/leaky-dash-quartz-ref.png differ
diff --git a/test/line-width-scale-quartz-ref.png b/test/line-width-scale-quartz-ref.png
new file mode 100644
index 0000000..7a31539
Binary files /dev/null and b/test/line-width-scale-quartz-ref.png differ
diff --git a/test/mask-alpha-quartz-argb32-ref.png b/test/mask-alpha-quartz-argb32-ref.png
new file mode 100644
index 0000000..a7fdc5f
Binary files /dev/null and b/test/mask-alpha-quartz-argb32-ref.png differ
diff --git a/test/meta-surface-pattern-quartz-ref.png b/test/meta-surface-pattern-quartz-ref.png
new file mode 100644
index 0000000..755f281
Binary files /dev/null and b/test/meta-surface-pattern-quartz-ref.png differ
diff --git a/test/meta-surface-pattern-quartz-rgb24-ref.png b/test/meta-surface-pattern-quartz-rgb24-ref.png
new file mode 100644
index 0000000..b71891e
Binary files /dev/null and b/test/meta-surface-pattern-quartz-rgb24-ref.png differ
diff --git a/test/new-sub-path-quartz-ref.png b/test/new-sub-path-quartz-ref.png
index e9c40b9..4278017 100644
Binary files a/test/new-sub-path-quartz-ref.png and b/test/new-sub-path-quartz-ref.png differ
diff --git a/test/over-above-source-quartz-ref.png b/test/over-above-source-quartz-ref.png
new file mode 100644
index 0000000..79d3c93
Binary files /dev/null and b/test/over-above-source-quartz-ref.png differ
diff --git a/test/over-above-source-quartz-rgb24-ref.png b/test/over-above-source-quartz-rgb24-ref.png
new file mode 100644
index 0000000..38e823e
Binary files /dev/null and b/test/over-above-source-quartz-rgb24-ref.png differ
diff --git a/test/over-around-source-quartz-ref.png b/test/over-around-source-quartz-ref.png
new file mode 100644
index 0000000..417c8a7
Binary files /dev/null and b/test/over-around-source-quartz-ref.png differ
diff --git a/test/over-around-source-quartz-rgb24-ref.png b/test/over-around-source-quartz-rgb24-ref.png
new file mode 100644
index 0000000..3e4d764
Binary files /dev/null and b/test/over-around-source-quartz-rgb24-ref.png differ
diff --git a/test/over-between-source-quartz-ref.png b/test/over-between-source-quartz-ref.png
new file mode 100644
index 0000000..de954d5
Binary files /dev/null and b/test/over-between-source-quartz-ref.png differ
diff --git a/test/over-between-source-quartz-rgb24-ref.png b/test/over-between-source-quartz-rgb24-ref.png
new file mode 100644
index 0000000..97dae07
Binary files /dev/null and b/test/over-between-source-quartz-rgb24-ref.png differ
diff --git a/test/rotate-image-surface-paint-quartz-ref.png b/test/rotate-image-surface-paint-quartz-ref.png
new file mode 100644
index 0000000..0f2f626
Binary files /dev/null and b/test/rotate-image-surface-paint-quartz-ref.png differ
diff --git a/test/trap-clip-quartz-ref.png b/test/trap-clip-quartz-ref.png
index 28f5977..70e5b17 100644
Binary files a/test/trap-clip-quartz-ref.png and b/test/trap-clip-quartz-ref.png differ
commit b439e638087d6e76d14ca42cd59dba3915dcc8e8
Author: Vladimir Vukicevic <vladimir at pobox.com>
Date:   Mon Feb 25 21:06:25 2008 -0500

    [quartz] fix mask to correctly take CTM into account

diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c
index 917752e..f421347 100644
--- a/src/cairo-quartz-surface.c
+++ b/src/cairo-quartz-surface.c
@@ -331,10 +331,9 @@ _cairo_quartz_filter_to_quartz (cairo_filter_t filter)
 	    return kCGInterpolationLow;
 
 	case CAIRO_FILTER_BEST:
-	    return kCGInterpolationHigh;
-
 	case CAIRO_FILTER_GOOD:
 	case CAIRO_FILTER_BILINEAR:
+	case CAIRO_FILTER_GAUSSIAN:
 	    return kCGInterpolationDefault;
     }
 
@@ -343,7 +342,7 @@ _cairo_quartz_filter_to_quartz (cairo_filter_t filter)
 
 static inline void
 _cairo_quartz_cairo_matrix_to_quartz (const cairo_matrix_t *src,
-				       CGAffineTransform *dst)
+				      CGAffineTransform *dst)
 {
     dst->a = src->xx;
     dst->b = src->yx;
@@ -515,33 +514,6 @@ SurfacePatternReleaseInfoFunc (void *ainfo)
     free (info);
 }
 
-/* Borrowed from cairo-meta-surface */
-static cairo_status_t
-_init_pattern_with_snapshot (cairo_pattern_t *pattern,
-			     const cairo_pattern_t *other)
-{
-    cairo_status_t status;
-
-    status = _cairo_pattern_init_copy (pattern, other);
-    if (status)
-	return status;
-
-    if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
-	cairo_surface_pattern_t *surface_pattern =
-	    (cairo_surface_pattern_t *) pattern;
-	cairo_surface_t *surface = surface_pattern->surface;
-
-	surface_pattern->surface = _cairo_surface_snapshot (surface);
-
-	cairo_surface_destroy (surface);
-
-	if (surface_pattern->surface->status)
-	    return surface_pattern->surface->status;
-    }
-
-    return CAIRO_STATUS_SUCCESS;
-}
-
 static cairo_int_status_t
 _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t *dest,
 							 cairo_pattern_t *apattern,
@@ -1641,6 +1613,7 @@ _cairo_quartz_surface_mask_with_surface (cairo_quartz_surface_t *surface,
     CGImageRef img;
     cairo_surface_t *pat_surf = mask->surface;
     cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    CGAffineTransform ctm, mask_matrix;
 
     status = _cairo_surface_get_extents (pat_surf, &extents);
     if (status)
@@ -1656,13 +1629,16 @@ _cairo_quartz_surface_mask_with_surface (cairo_quartz_surface_t *surface,
 	goto BAIL;
     }
 
-    rect = CGRectMake (-mask->base.matrix.x0, -mask->base.matrix.y0, extents.width, extents.height);
+    rect = CGRectMake (0.0f, 0.0f, extents.width, extents.height);
 
     CGContextSaveGState (surface->cgContext);
 
     /* ClipToMask is essentially drawing an image, so we need to flip the CTM
      * to get the image to appear oriented the right way */
-    CGAffineTransform ctm = CGContextGetCTM (surface->cgContext);
+    ctm = CGContextGetCTM (surface->cgContext);
+
+    _cairo_quartz_cairo_matrix_to_quartz (&mask->base.matrix, &mask_matrix);
+    CGContextConcatCTM (surface->cgContext, CGAffineTransformInvert(mask_matrix));
     CGContextTranslateCTM (surface->cgContext, 0.0f, rect.size.height);
     CGContextScaleCTM (surface->cgContext, 1.0f, -1.0f);
 
commit a4975ab1173957a293aad8ccac51d1e43cab86da
Author: Vladimir Vukicevic <vladimir at pobox.com>
Date:   Mon Feb 25 21:06:23 2008 -0500

    [quartz] Optimize path handling where possible

diff --git a/src/cairo-path-fill.c b/src/cairo-path-fill.c
index 8e4daca..ba33114 100644
--- a/src/cairo-path-fill.c
+++ b/src/cairo-path-fill.c
@@ -214,71 +214,9 @@ static cairo_int_status_t
 _cairo_path_fixed_fill_rectangle (cairo_path_fixed_t	*path,
 				  cairo_traps_t		*traps)
 {
-    cairo_path_buf_t *buf = &path->buf_head.base;
-    int final;
-
-    /* Ensure the path has the operators we expect for a rectangular path.
-     */
-    if (buf == NULL || buf->num_ops < 5)
-	return CAIRO_INT_STATUS_UNSUPPORTED;
-
-    if (buf->op[0] != CAIRO_PATH_OP_MOVE_TO ||
-	buf->op[1] != CAIRO_PATH_OP_LINE_TO ||
-	buf->op[2] != CAIRO_PATH_OP_LINE_TO ||
-	buf->op[3] != CAIRO_PATH_OP_LINE_TO)
-    {
-	return CAIRO_INT_STATUS_UNSUPPORTED;
-    }
-
-    /* Now, there are choices. The rectangle might end with a LINE_TO
-     * (to the original point), but this isn't required. If it
-     * doesn't, then it must end with a CLOSE_PATH. */
-    if (buf->op[4] == CAIRO_PATH_OP_LINE_TO) {
-	if (buf->points[4].x != buf->points[0].x ||
-	    buf->points[4].y != buf->points[0].y)
-	{
-	    return CAIRO_INT_STATUS_UNSUPPORTED;
-	}
-    } else if (buf->op[4] != CAIRO_PATH_OP_CLOSE_PATH) {
-	return CAIRO_INT_STATUS_UNSUPPORTED;
-    }
-
-    /* Finally, a trailing CLOSE_PATH or MOVE_TO after the rectangle
-     * is fine. But anything more than that means we must return
-     * unsupported. */
-    final = 5;
-    if (final < buf->num_ops &&
-	buf->op[final] == CAIRO_PATH_OP_CLOSE_PATH)
-    {
-	final++;
-    }
-    if (final < buf->num_ops &&
-	buf->op[final] == CAIRO_PATH_OP_MOVE_TO)
-    {
-	final++;
-    }
-    if (final < buf->num_ops)
-	return CAIRO_INT_STATUS_UNSUPPORTED;
-
-    /* Now that we've verified the operators, we must ensure that the
-     * path coordinates are consistent with a rectangle. There are two
-     * choices here. */
-    if (buf->points[0].y == buf->points[1].y &&
-	buf->points[1].x == buf->points[2].x &&
-	buf->points[2].y == buf->points[3].y &&
-	buf->points[3].x == buf->points[0].x)
-    {
-	return _cairo_traps_tessellate_convex_quad (traps,
-						    buf->points);
-    }
-
-    if (buf->points[0].x == buf->points[1].x &&
-	buf->points[1].y == buf->points[2].y &&
-	buf->points[2].x == buf->points[3].x &&
-	buf->points[3].y == buf->points[0].y)
-    {
+    if (_cairo_path_fixed_is_box (path, NULL)) {
 	return _cairo_traps_tessellate_convex_quad (traps,
-						    buf->points);
+                                                    path->buf_head.base.points);
     }
 
     return CAIRO_INT_STATUS_UNSUPPORTED;
diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index 91b8c0e..5c10d65 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -721,3 +721,84 @@ _cairo_path_fixed_interpret_flat (cairo_path_fixed_t			*path,
 					_cpf_close_path,
 					&flattener);
 }
+
+cairo_bool_t
+_cairo_path_fixed_is_empty (cairo_path_fixed_t *path)
+{
+    if (path->buf_head.base.num_ops == 0)
+	return TRUE;
+
+    return FALSE;
+}
+
+/**
+ * Check whether the given path contains a single rectangle.
+ */
+cairo_bool_t
+_cairo_path_fixed_is_box (cairo_path_fixed_t *path,
+			  cairo_box_t *box)
+{
+    cairo_path_buf_t *buf = &path->buf_head.base;
+
+    /* We can't have more than one buf for this check */
+    if (buf->next != NULL)
+	return FALSE;
+
+    /* Do we have the right number of ops? */
+    if (buf->num_ops != 5 && buf->num_ops != 6)
+	return FALSE;
+
+    /* Check whether the ops are those that would be used for a rectangle */
+    if (buf->op[0] != CAIRO_PATH_OP_MOVE_TO ||
+	buf->op[1] != CAIRO_PATH_OP_LINE_TO ||
+	buf->op[2] != CAIRO_PATH_OP_LINE_TO ||
+	buf->op[3] != CAIRO_PATH_OP_LINE_TO)
+    {
+	return FALSE;
+    }
+
+    /* Now, there are choices. The rectangle might end with a LINE_TO
+     * (to the original point), but this isn't required. If it
+     * doesn't, then it must end with a CLOSE_PATH. */
+    if (buf->op[4] == CAIRO_PATH_OP_LINE_TO) {
+	if (buf->points[4].x != buf->points[0].x ||
+	    buf->points[4].y != buf->points[0].y)
+	    return FALSE;
+    } else if (buf->op[4] != CAIRO_PATH_OP_CLOSE_PATH) {
+	return FALSE;
+    }
+
+    if (buf->num_ops == 6) {
+	/* A trailing CLOSE_PATH or MOVE_TO is ok */
+	if (buf->op[5] != CAIRO_PATH_OP_MOVE_TO &&
+	    buf->op[5] != CAIRO_PATH_OP_CLOSE_PATH)
+	    return FALSE;
+    }
+
+    /* Ok, we may have a box, if the points line up */
+    if (buf->points[0].y == buf->points[1].y &&
+	buf->points[1].x == buf->points[2].x &&
+	buf->points[2].y == buf->points[3].y &&
+	buf->points[3].x == buf->points[0].x)
+    {
+	if (box) {
+	    box->p1 = buf->points[0];
+	    box->p2 = buf->points[2];
+	}
+	return TRUE;
+    }
+
+    if (buf->points[0].x == buf->points[1].x &&
+	buf->points[1].y == buf->points[2].y &&
+	buf->points[2].x == buf->points[3].x &&
+	buf->points[3].y == buf->points[0].y)
+    {
+	if (box) {
+	    box->p1 = buf->points[0];
+	    box->p2 = buf->points[2];
+	}
+	return TRUE;
+    }
+
+    return FALSE;
+}
diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c
index e918ec0..917752e 100644
--- a/src/cairo-quartz-surface.c
+++ b/src/cairo-quartz-surface.c
@@ -1297,6 +1297,7 @@ _cairo_quartz_surface_fill (void *abstract_surface,
     cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
     cairo_quartz_action_t action;
     quartz_stroke_t stroke;
+    cairo_box_t box;
 
     ND((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n", surface, op, source->type));
 
@@ -1306,6 +1307,16 @@ _cairo_quartz_surface_fill (void *abstract_surface,
     if (op == CAIRO_OPERATOR_DEST)
 	return CAIRO_STATUS_SUCCESS;
 
+    /* Check whether the path would be a no-op */
+    /* XXX handle unbounded ops */
+    if (_cairo_path_fixed_is_empty(path) ||
+	(_cairo_path_fixed_is_box(path, &box) &&
+	 box.p1.x == box.p2.x &&
+	 box.p1.y == box.p2.y))
+    {
+	return CAIRO_STATUS_SUCCESS;
+    }
+
     CGContextSaveGState (surface->cgContext);
 
     CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
diff --git a/src/cairoint.h b/src/cairoint.h
index 489cf40..d24dfa8 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1392,6 +1392,13 @@ cairo_private void
 _cairo_path_fixed_device_transform (cairo_path_fixed_t	*path,
 				    cairo_matrix_t	*device_transform);
 
+cairo_private cairo_bool_t
+_cairo_path_fixed_is_empty (cairo_path_fixed_t *path);
+
+cairo_private cairo_bool_t
+_cairo_path_fixed_is_box (cairo_path_fixed_t *path,
+                          cairo_box_t *box);
+
 /* cairo_path_fill.c */
 cairo_private cairo_status_t
 _cairo_path_fixed_fill_to_traps (cairo_path_fixed_t *path,
commit 63711b1d4a61f21db070f30b9c153d0923cc24bb
Author: Vladimir Vukicevic <vladimir at pobox.com>
Date:   Mon Feb 25 21:06:21 2008 -0500

    [quartz] Add quartz-image-surface type

diff --git a/src/Makefile.am b/src/Makefile.am
index 525ca66..3656f05 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -178,7 +178,7 @@ backend_pkgconfigs += cairo-xcb.pc
 endif
 
 quartz_headers = cairo-quartz.h
-quartz_sources = cairo-quartz-surface.c cairo-quartz-private.h
+quartz_sources = cairo-quartz-surface.c cairo-quartz-image-surface.c cairo-quartz-private.h
 cairo_all_sources += $(quartz_headers) $(quartz_sources)
 if CAIRO_HAS_QUARTZ_SURFACE
 cairo_headers += $(quartz_headers)
diff --git a/src/cairo-quartz-image-surface.c b/src/cairo-quartz-image-surface.c
new file mode 100644
index 0000000..7a6d07d
--- /dev/null
+++ b/src/cairo-quartz-image-surface.c
@@ -0,0 +1,370 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright � 2008 Mozilla Corporation
+ *
+ * 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 Mozilla Corporation.
+ *
+ * Contributor(s):
+ *	Vladimir Vukicevic <vladimir at mozilla.com>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-quartz-private.h"
+
+#define SURFACE_ERROR_NO_MEMORY (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_MEMORY)))
+#define SURFACE_ERROR_INVALID_FORMAT (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_INVALID_FORMAT)))
+
+static void
+DataProviderReleaseCallback (void *info, const void *data, size_t size)
+{
+    cairo_surface_t *surface = (cairo_surface_t *) info;
+    cairo_surface_destroy (surface);
+}
+
+CGImageRef
+_cairo_quartz_create_cgimage (cairo_format_t format,
+			      unsigned int width,
+			      unsigned int height,
+			      unsigned int stride,
+			      void *data,
+			      cairo_bool_t interpolate,
+			      CGColorSpaceRef colorSpaceOverride,
+			      CGDataProviderReleaseDataCallback releaseCallback,
+			      void *releaseInfo)
+{
+    CGImageRef image = NULL;
+    CGDataProviderRef dataProvider = NULL;
+    CGColorSpaceRef colorSpace = colorSpaceOverride;
+    CGBitmapInfo bitinfo;
+    int bitsPerComponent, bitsPerPixel;
+
+    switch (format) {
+	case CAIRO_FORMAT_ARGB32:
+	    if (colorSpace == NULL)
+		colorSpace = CGColorSpaceCreateDeviceRGB();
+	    bitinfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
+	    bitsPerComponent = 8;
+	    bitsPerPixel = 32;
+	    break;
+
+	case CAIRO_FORMAT_RGB24:
+	    if (colorSpace == NULL)
+		colorSpace = CGColorSpaceCreateDeviceRGB();
+	    bitinfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
+	    bitsPerComponent = 8;
+	    bitsPerPixel = 32;
+	    break;
+
+	/* XXX -- should use CGImageMaskCreate! */
+	case CAIRO_FORMAT_A8:
+	    if (colorSpace == NULL)
+		colorSpace = CGColorSpaceCreateDeviceGray();
+	    bitinfo = kCGImageAlphaNone;
+	    bitsPerComponent = 8;
+	    bitsPerPixel = 8;
+	    break;
+
+	case CAIRO_FORMAT_A1:
+	default:
+	    return NULL;
+    }
+
+    dataProvider = CGDataProviderCreateWithData (releaseInfo,
+						 data,
+						 height * stride,
+						 releaseCallback);
+
+    if (!dataProvider) {
+	// manually release
+	if (releaseCallback)
+	    releaseCallback (releaseInfo, data, height * stride);
+	goto FINISH;
+    }
+
+    image = CGImageCreate (width, height,
+			   bitsPerComponent,
+			   bitsPerPixel,
+			   stride,
+			   colorSpace,
+			   bitinfo,
+			   dataProvider,
+			   NULL,
+			   interpolate,
+			   kCGRenderingIntentDefault);
+
+FINISH:
+
+    CGDataProviderRelease (dataProvider);
+
+    if (colorSpace != colorSpaceOverride)
+	CGColorSpaceRelease (colorSpace);
+
+    return image;
+}
+
+
+static cairo_surface_t *
+_cairo_quartz_image_surface_create_similar (void *asurface,
+					    cairo_content_t content,
+					    int width,
+					    int height)
+{
+    cairo_surface_t *result;
+    cairo_surface_t *isurf = cairo_image_surface_create (_cairo_format_from_content (content),
+							 width,
+							 height);
+    if (cairo_surface_status(isurf))
+	return isurf;
+
+    result = cairo_quartz_image_surface_create (isurf);
+    cairo_surface_destroy (isurf);
+
+    return result;
+}
+
+static cairo_status_t
+_cairo_quartz_image_surface_finish (void *asurface)
+{
+    cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface;
+
+    /* the imageSurface will be destroyed by the data provider's release callback */
+    CGImageRelease (surface->image);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_quartz_image_surface_acquire_source_image (void *asurface,
+						  cairo_image_surface_t **image_out,
+						  void **image_extra)
+{
+    cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface;
+
+    *image_out = surface->imageSurface;
+    *image_extra = NULL;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_quartz_image_surface_acquire_dest_image (void *asurface,
+						cairo_rectangle_int_t *interest_rect,
+						cairo_image_surface_t **image_out,
+						cairo_rectangle_int_t *image_rect,
+						void **image_extra)
+{
+    cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface;
+
+    *image_out = surface->imageSurface;
+    *image_rect = surface->extents;
+    *image_extra = NULL;
+
+    return CAIRO_STATUS_SUCCESS;
+   
+}
+
+static cairo_int_status_t
+_cairo_quartz_image_surface_get_extents (void *asurface,
+					 cairo_rectangle_int_t *extents)
+{
+    cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface;
+
+    *extents = surface->extents;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* we assume some drawing happened to the image buffer; make sure it's
+ * represented in the CGImage on flush()
+ */
+
+static cairo_status_t
+_cairo_quartz_image_surface_flush (void *asurface)
+{
+    cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface;
+    CGImageRef oldImage = surface->image;
+    CGImageRef newImage = NULL;
+
+    /* To be released by the ReleaseCallback */
+    cairo_surface_reference ((cairo_surface_t*) surface->imageSurface);
+
+    newImage = _cairo_quartz_create_cgimage (surface->imageSurface->format,
+					     surface->imageSurface->width,
+					     surface->imageSurface->height,
+					     surface->imageSurface->stride,
+					     surface->imageSurface->data,
+					     FALSE,
+					     NULL,
+					     DataProviderReleaseCallback,
+					     surface->imageSurface);
+
+    surface->image = newImage;
+    CGImageRelease (oldImage);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t cairo_quartz_image_surface_backend = {
+    CAIRO_SURFACE_TYPE_QUARTZ_IMAGE,
+    _cairo_quartz_image_surface_create_similar,
+    _cairo_quartz_image_surface_finish,
+    _cairo_quartz_image_surface_acquire_source_image,
+    NULL, /* release_source_image */
+    _cairo_quartz_image_surface_acquire_dest_image,
+    NULL, /* release_dest_image */
+    NULL, /* clone_similar */
+    NULL, /* composite */
+    NULL, /* fill_rectangles */
+    NULL, /* composite_trapezoids */
+    NULL, /* copy_page */
+    NULL, /* show_page */
+    NULL, /* set_clip_region */
+    NULL, /* intersect_clip_path */
+    _cairo_quartz_image_surface_get_extents,
+    NULL, /* old_show_glyphs */
+    NULL, /* get_font_options */
+    _cairo_quartz_image_surface_flush,
+    NULL, /* mark_dirty_rectangle */
+    NULL, /* scaled_font_fini */
+    NULL, /* scaled_glyph_fini */
+
+    NULL, /* paint */
+    NULL, /* mask */
+    NULL, /* stroke */
+    NULL, /* fill */
+    NULL, /* surface_show_glyphs */
+    NULL, /* snapshot */
+    NULL, /* is_similar */
+    NULL, /* reset */
+    NULL  /* fill_stroke */
+
+};
+
+/**
+ * cairo_quartz_image_surface_create
+ * @surface: a cairo image surface to wrap with a quartz image surface
+ *
+ * Creates a Quartz surface backed by a CGImageRef that references the
+ * given image surface. The resulting surface can be rendered quickly
+ * when used as a source when rendering to a #cairo_quartz_surface.  If
+ * the data in the image surface is every updated, cairo_surface_flush()
+ * must be called on the #cairo_quartz_image_surface to ensure that the
+ * CGImageRef refers to the updated data.
+ *
+ * Return value: the newly created surface.
+ *
+ * Since: 1.6
+ */
+cairo_surface_t *
+cairo_quartz_image_surface_create (cairo_surface_t *surface)
+{
+    cairo_quartz_image_surface_t *qisurf;
+
+    CGImageRef image;
+
+    CGContextRef cgContext;
+    CGColorSpaceRef cgColorspace;
+    CGBitmapInfo bitinfo;
+
+    cairo_image_surface_t *image_surface;
+    int width, height, stride;
+    cairo_format_t format;
+    unsigned char *data;
+
+    if (cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_IMAGE)
+	return SURFACE_ERROR_NO_MEMORY;
+
+    image_surface = (cairo_image_surface_t*) surface;
+    width = image_surface->width;
+    height = image_surface->height;
+    stride = image_surface->stride;
+    format = image_surface->format;
+    data = image_surface->data;
+
+    if (!_cairo_quartz_verify_surface_size(width, height))
+	return SURFACE_ERROR_NO_MEMORY;
+
+    if (width == 0 || height == 0)
+	return SURFACE_ERROR_NO_MEMORY;
+
+    if (format != CAIRO_FORMAT_ARGB32 && format != CAIRO_FORMAT_RGB24)
+	return SURFACE_ERROR_INVALID_FORMAT;
+
+    qisurf = malloc(sizeof(cairo_quartz_image_surface_t));
+    if (qisurf == NULL)
+	return SURFACE_ERROR_NO_MEMORY;
+
+    memset (qisurf, 0, sizeof(cairo_quartz_image_surface_t));
+
+    /* In case the create_cgimage fails, this ref will
+     * be released via the callback (which will be called in
+     * case of failure.)
+     */
+    cairo_surface_reference (surface);
+
+    image = _cairo_quartz_create_cgimage (format,
+					  width, height,
+					  stride,
+					  data,
+					  FALSE,
+					  NULL,
+					  DataProviderReleaseCallback,
+					  surface);
+
+    if (!image) {
+	free (qisurf);
+	return SURFACE_ERROR_NO_MEMORY;
+    }
+
+    _cairo_surface_init (&qisurf->base,
+			 &cairo_quartz_image_surface_backend,
+			 _cairo_content_from_format (format));
+
+    qisurf->extents.x = qisurf->extents.y = 0;
+    qisurf->extents.width = width;
+    qisurf->extents.height = height;
+
+    qisurf->image = image;
+    qisurf->imageSurface = image_surface;
+
+    return &qisurf->base;
+}
+
+
+cairo_surface_t *
+cairo_quartz_image_surface_get_image (cairo_surface_t *asurface)
+{
+    cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t*) asurface;
+
+    if (cairo_surface_get_type(asurface) != CAIRO_SURFACE_TYPE_QUARTZ_IMAGE)
+	return NULL;
+
+    return (cairo_surface_t*) surface->imageSurface;
+}
diff --git a/src/cairo-quartz-private.h b/src/cairo-quartz-private.h
index 52a3d7f..cf26f4f 100644
--- a/src/cairo-quartz-private.h
+++ b/src/cairo-quartz-private.h
@@ -46,11 +46,12 @@
 typedef struct cairo_quartz_surface {
     cairo_surface_t base;
 
-    void *imageData;
-
     CGContextRef cgContext;
     CGAffineTransform cgContextBaseCTM;
 
+    void *imageData;
+    cairo_surface_t *imageSurfaceEquiv;
+
     cairo_rectangle_int_t extents;
 
     /* These are stored while drawing operations are in place, set up
@@ -63,7 +64,33 @@ typedef struct cairo_quartz_surface {
 
     CGShadingRef sourceShading;
     CGPatternRef sourcePattern;
+
+    CGInterpolationQuality oldInterpolationQuality;
 } cairo_quartz_surface_t;
+
+typedef struct cairo_quartz_image_surface {
+    cairo_surface_t base;
+
+    cairo_rectangle_int_t extents;
+
+    CGImageRef image;
+    cairo_image_surface_t *imageSurface;
+} cairo_quartz_image_surface_t;
+
+cairo_bool_t
+_cairo_quartz_verify_surface_size(int width, int height);
+
+CGImageRef
+_cairo_quartz_create_cgimage (cairo_format_t format,
+			      unsigned int width,
+			      unsigned int height,
+			      unsigned int stride,
+			      void *data,
+			      cairo_bool_t interpolate,
+			      CGColorSpaceRef colorSpaceOverride,
+			      CGDataProviderReleaseDataCallback releaseCallback,
+			      void *releaseInfo);
+
 #endif /* CAIRO_HAS_QUARTZ_SURFACE */
 
 #if CAIRO_HAS_ATSUI_FONT
diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c
index 9aff1c3..e918ec0 100644
--- a/src/cairo-quartz-surface.c
+++ b/src/cairo-quartz-surface.c
@@ -34,12 +34,12 @@
  *	Vladimir Vukicevic <vladimir at mozilla.com>
  */
 
+#include <dlfcn.h>
+
 #include "cairoint.h"
 
 #include "cairo-quartz-private.h"
 
-#include <dlfcn.h>
-
 /* The 10.5 SDK includes a funky new definition of FloatToFixed which
  * causes all sorts of breakage; so reset to old-style definition
  */
@@ -140,7 +140,8 @@ static void quartz_ensure_symbols(void)
 #define CG_MAX_WIDTH    USHRT_MAX
 
 /* is the desired size of the surface within bounds? */
-static cairo_bool_t verify_surface_size(int width, int height)
+cairo_bool_t
+_cairo_quartz_verify_surface_size(int width, int height)
 {
     /* hmmm, allow width, height == 0 ? */
     if (width < 0 || height < 0) {
@@ -295,7 +296,7 @@ _cairo_quartz_cairo_operator_to_quartz (cairo_operator_t op)
     return kPrivateCGCompositeCopy;
 }
 
-static CGLineCap
+static inline CGLineCap
 _cairo_quartz_cairo_line_cap_to_quartz (cairo_line_cap_t ccap)
 {
     switch (ccap) {
@@ -307,7 +308,7 @@ _cairo_quartz_cairo_line_cap_to_quartz (cairo_line_cap_t ccap)
     return kCGLineCapButt;
 }
 
-static CGLineJoin
+static inline CGLineJoin
 _cairo_quartz_cairo_line_join_to_quartz (cairo_line_join_t cjoin)
 {
     switch (cjoin) {
@@ -319,7 +320,28 @@ _cairo_quartz_cairo_line_join_to_quartz (cairo_line_join_t cjoin)
     return kCGLineJoinMiter;
 }
 
-static void
+static inline CGInterpolationQuality
+_cairo_quartz_filter_to_quartz (cairo_filter_t filter)
+{
+    switch (filter) {
+	case CAIRO_FILTER_NEAREST:
+	    return kCGInterpolationNone;
+
+	case CAIRO_FILTER_FAST:
+	    return kCGInterpolationLow;
+
+	case CAIRO_FILTER_BEST:
+	    return kCGInterpolationHigh;
+
+	case CAIRO_FILTER_GOOD:
+	case CAIRO_FILTER_BILINEAR:
+	    return kCGInterpolationDefault;
+    }
+
+    return kCGInterpolationDefault;
+}
+
+static inline void
 _cairo_quartz_cairo_matrix_to_quartz (const cairo_matrix_t *src,
 				       CGAffineTransform *dst)
 {
@@ -393,114 +415,104 @@ CreateGradientFunction (cairo_gradient_pattern_t *gpat)
 			     &callbacks);
 }
 
-/* generic cairo surface -> #cairo_quartz_surface_t function */
-static cairo_int_status_t
-_cairo_quartz_surface_to_quartz (cairo_surface_t *target,
-				 cairo_surface_t *pat_surf,
-				 cairo_quartz_surface_t **quartz_surf)
-{
+/* Obtain a CGImageRef from a cairo_surface_t * */
 
-    if (cairo_surface_get_type(pat_surf) != CAIRO_SURFACE_TYPE_QUARTZ) {
-	/* XXXtodo/perf don't use clone if the source surface is an image surface!  Instead,
-	 * just create the CGImage directly!
-	 */
+static CGImageRef
+_cairo_surface_to_cgimage (cairo_surface_t *target,
+			   cairo_surface_t *source)
+{
+    cairo_surface_type_t stype = cairo_surface_get_type (source);
+    cairo_image_surface_t *isurf;
+    CGImageRef image, image2;
+    void *image_extra;
 
-	cairo_surface_t *ref_type = target;
-	cairo_surface_t *new_surf = NULL;
-	cairo_rectangle_int_t rect;
-	cairo_status_t status;
+    if (stype == CAIRO_SURFACE_TYPE_QUARTZ_IMAGE) {
+	cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) source;
+	return CGImageRetain (surface->image);
+    }
 
-	if (ref_type == NULL)
-	    ref_type = cairo_quartz_surface_create (CAIRO_FORMAT_ARGB32, 1, 1);
+    if (stype == CAIRO_SURFACE_TYPE_QUARTZ) {
+	cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) source;
+	image = CGBitmapContextCreateImage (surface->cgContext);
+	if (image)
+	    return image;
+    }
 
-	status = _cairo_surface_get_extents (pat_surf, &rect);
+    if (stype != CAIRO_SURFACE_TYPE_IMAGE) {
+	cairo_status_t status =
+	    _cairo_surface_acquire_source_image (source, &isurf, &image_extra);
 	if (status)
-	    return status;
-
-	status = _cairo_surface_clone_similar (ref_type, pat_surf, rect.x, rect.y,
-				      rect.width, rect.height, &new_surf);
-	if (target == NULL)
-	    cairo_surface_destroy(ref_type);
+	    return NULL;
+    } else {
+	isurf = (cairo_image_surface_t *) source;
+    }
 
-        if (status)
-	    return status;
+    image2 = _cairo_quartz_create_cgimage (isurf->format,
+					   isurf->width,
+					   isurf->height,
+					   isurf->stride,
+					   isurf->data,
+					   FALSE,
+					   NULL, NULL, NULL);
 
- 	if (new_surf &&
-	    cairo_surface_get_type (new_surf) != CAIRO_SURFACE_TYPE_QUARTZ)
-	{
-	    ND((stderr, "got a non-quartz surface, format=%d width=%u height=%u type=%d\n", cairo_surface_get_type (pat_surf), rect.width, rect.height, cairo_surface_get_type (new_surf)));
-	    cairo_surface_destroy (new_surf);
-	    return CAIRO_INT_STATUS_UNSUPPORTED;
-	}
+    image = CGImageCreateCopy (image2);
+    CGImageRelease (image2);
 
-	*quartz_surf = (cairo_quartz_surface_t *) new_surf;
-    } else {
-	/* If it's a quartz surface, we can try to see if it's a CGBitmapContext;
-	 * we do this when we call CGBitmapContextCreateImage below.
-	 */
-	cairo_surface_reference (pat_surf);
-	*quartz_surf = (cairo_quartz_surface_t*) pat_surf;
-    }
+    if ((cairo_surface_t*) isurf != source)
+	_cairo_surface_release_source_image (source, isurf, image_extra);
 
-    return CAIRO_STATUS_SUCCESS;
+    return image;
 }
 
 /* Generic #cairo_pattern_t -> CGPattern function */
-static void
-SurfacePatternDrawFunc (void *info, CGContextRef context)
-{
-    cairo_surface_pattern_t *spat = (cairo_surface_pattern_t *) info;
-    cairo_surface_t *pat_surf = spat->surface;
-    cairo_int_status_t status;
 
-    cairo_quartz_surface_t *quartz_surf;
-    CGImageRef img;
+typedef struct {
+    CGImageRef image;
     CGRect imageBounds;
+    cairo_bool_t do_reflect;
+} SurfacePatternDrawInfo;
 
-    status = _cairo_quartz_surface_to_quartz (NULL, pat_surf, &quartz_surf);
-    if (status)
-	return;
+static void
+SurfacePatternDrawFunc (void *ainfo, CGContextRef context)
+{
+    SurfacePatternDrawInfo *info = (SurfacePatternDrawInfo*) ainfo;
 
-    img = CGBitmapContextCreateImage (quartz_surf->cgContext);
-    if (!img) {
-	// ... give up.
-	ND((stderr, "CGBitmapContextCreateImage failed\n"));
-	_cairo_error (CAIRO_STATUS_NO_MEMORY);
-	cairo_surface_destroy ((cairo_surface_t*)quartz_surf);
-	return;
-    }
+    CGContextTranslateCTM (context, 0, info->imageBounds.size.height);
+    CGContextScaleCTM (context, 1, -1);
 
-    /* XXXtodo WHY does this need to be flipped?  Writing this stuff
-     * to disk shows that in both this path and the path above the source image
-     * has an identical orientation, and the destination context at all times has a Y
-     * flip.  So why do we need to flip in this case?
-     */
-    if (cairo_surface_get_type(pat_surf) == CAIRO_SURFACE_TYPE_QUARTZ) {
-	CGContextTranslateCTM (context, 0, CGImageGetHeight(img));
-	CGContextScaleCTM (context, 1, -1);
-    }
-
-    imageBounds.size = CGSizeMake (CGImageGetWidth(img), CGImageGetHeight(img));
-    imageBounds.origin.x = 0;
-    imageBounds.origin.y = 0;
+    CGContextDrawImage (context, info->imageBounds, info->image);
+    if (info->do_reflect) {
+	/* draw 3 more copies of the image, flipped.
+	 * DrawImage draws the image according to the current Y-direction into the rectangle given
+	 * (imageBounds); at the time of the first DrawImage above, the origin is at the bottom left
+	 * of the base image position, and the Y axis is extending upwards.
+	 */
 
-    CGContextDrawImage (context, imageBounds, img);
-    if (spat->base.extend == CAIRO_EXTEND_REFLECT) {
-	/* draw 3 more copies of the image, flipped. */
-	CGContextTranslateCTM (context, 0, 2 * imageBounds.size.height);
+	/* Make the y axis extend downwards, and draw a flipped image below */
 	CGContextScaleCTM (context, 1, -1);
-	CGContextDrawImage (context, imageBounds, img);
-	CGContextTranslateCTM (context, 2 * imageBounds.size.width, 0);
+	CGContextDrawImage (context, info->imageBounds, info->image);
+
+	/* Shift over to the right, and flip vertically (translation is 2x,
+	 * since we'll be flipping and thus rendering the rectangle "backwards"
+	 */
+	CGContextTranslateCTM (context, 2 * info->imageBounds.size.width, 0);
 	CGContextScaleCTM (context, -1, 1);
-	CGContextDrawImage (context, imageBounds, img);
-	CGContextTranslateCTM (context, 0, 2 * imageBounds.size.height);
+	CGContextDrawImage (context, info->imageBounds, info->image);
+
+	/* Then unflip the Y-axis again, and draw the image above the point. */
 	CGContextScaleCTM (context, 1, -1);
-	CGContextDrawImage (context, imageBounds, img);
+	CGContextDrawImage (context, info->imageBounds, info->image);
+
     }
+}
 
-    CGImageRelease (img);
+static void
+SurfacePatternReleaseInfoFunc (void *ainfo)
+{
+    SurfacePatternDrawInfo *info = (SurfacePatternDrawInfo*) ainfo;
 
-    cairo_surface_destroy ((cairo_surface_t*) quartz_surf);
+    CGImageRelease (info->image);
+    free (info);
 }
 
 /* Borrowed from cairo-meta-surface */
@@ -532,50 +544,64 @@ _init_pattern_with_snapshot (cairo_pattern_t *pattern,
 
 static cairo_int_status_t
 _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t *dest,
-							 cairo_pattern_t *abspat,
+							 cairo_pattern_t *apattern,
 							 CGPatternRef *cgpat)
 {
-    cairo_surface_pattern_t *spat;
+    cairo_surface_pattern_t *spattern;
     cairo_surface_t *pat_surf;
     cairo_rectangle_int_t extents;
 
+    CGImageRef image;
     CGRect pbounds;
     CGAffineTransform ptransform, stransform;
     CGPatternCallbacks cb = { 0,
 			      SurfacePatternDrawFunc,
-			      (CGFunctionReleaseInfoCallback) cairo_pattern_destroy };
+			      SurfacePatternReleaseInfoFunc };
+    SurfacePatternDrawInfo *info;
     float rw, rh;
     cairo_status_t status;
 
-    cairo_pattern_union_t *snap_pattern = NULL;
-    cairo_pattern_t *target_pattern = abspat;
-
     cairo_matrix_t m;
+
     /* SURFACE is the only type we'll handle here */
-    if (abspat->type != CAIRO_PATTERN_TYPE_SURFACE)
+    if (apattern->type != CAIRO_PATTERN_TYPE_SURFACE)
 	return CAIRO_INT_STATUS_UNSUPPORTED;
 
-    spat = (cairo_surface_pattern_t *) abspat;
-    pat_surf = spat->surface;
+    spattern = (cairo_surface_pattern_t *) apattern;
+    pat_surf = spattern->surface;
 
     status = _cairo_surface_get_extents (pat_surf, &extents);
     if (status)
 	return status;
 
+    image = _cairo_surface_to_cgimage ((cairo_surface_t*) dest, pat_surf);
+    if (image == NULL)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    info = malloc(sizeof(SurfacePatternDrawInfo));
+    if (!info)
+	return CAIRO_STATUS_NO_MEMORY;
+
+    /* XXX -- if we're printing, we may need to call CGImageCreateCopy to make sure
+     * that the data will stick around for this image when the printer gets to it.
+     * Otherwise, the underlying data store may disappear from under us!
+     *
+     * _cairo_surface_to_cgimage will copy when it converts non-Quartz surfaces,
+     * since the Quartz surfaces have a higher chance of sticking around.  If the
+     * source is a quartz image surface, then it's set up to retain a ref to the
+     * image surface that it's backed by.
+     */
+    info->image = image;
+
+    info->imageBounds = CGRectMake (0, 0, extents.width, extents.height);
+
     pbounds.origin.x = 0;
     pbounds.origin.y = 0;
 
-    // kjs seems to indicate this should work (setting to 0,0 to avoid
-    // tiling); however, the pattern CTM scaling ends up being NaN in
-    // the pattern draw function if either rw or rh are 0.
-    // XXXtodo get pattern drawing working with extend options
-    // XXXtodo/perf optimize CAIRO_EXTEND_NONE to a single DrawImage instead of a pattern
-    if (spat->base.extend == CAIRO_EXTEND_REFLECT) {
-	/* XXX broken; need to emulate by reflecting the image into 4 quadrants
-	 * and then tiling that
-	 */
-	pbounds.size.width = 2 * extents.width;
-	pbounds.size.height = 2 * extents.height;
+    if (spattern->base.extend == CAIRO_EXTEND_REFLECT) {
+	pbounds.size.width = 2.0 * extents.width;
+	pbounds.size.height = 2.0 * extents.height;
+	info->do_reflect = TRUE;
     } else {
 	pbounds.size.width = extents.width;
 	pbounds.size.height = extents.height;
@@ -583,7 +609,7 @@ _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t
     rw = pbounds.size.width;
     rh = pbounds.size.height;
 
-    m = spat->base.matrix;
+    m = spattern->base.matrix;
     cairo_matrix_invert(&m);
     _cairo_quartz_cairo_matrix_to_quartz (&m, &stransform);
 
@@ -601,25 +627,14 @@ _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t
     ND((stderr, "  context xform: t: %f %f xx: %f xy: %f yx: %f yy: %f\n", xform.tx, xform.ty, xform.a, xform.b, xform.c, xform.d));
 #endif
 
+    *cgpat = CGPatternCreate (info,
+			      pbounds,
+			      ptransform,
+			      rw, rh,
+			      kCGPatternTilingConstantSpacing, /* kCGPatternTilingNoDistortion, */
+			      TRUE,
+			      &cb);
 
-    /* XXX fixme: only do snapshots if the context is for printing, or get rid of the
-       other block if it doesn't fafect performance */
-    if (1 /* context is for printing */) {
-	snap_pattern = (cairo_pattern_union_t*) malloc(sizeof(cairo_pattern_union_t));
-	target_pattern = (cairo_pattern_t*) snap_pattern;
-	_init_pattern_with_snapshot (target_pattern, abspat);
-    } else {
-	cairo_pattern_reference (abspat);
-	target_pattern = abspat;
-    }
-
-    *cgpat = CGPatternCreate (target_pattern,
-			     pbounds,
-			     ptransform,
-			     rw, rh,
-			     kCGPatternTilingConstantSpacing, /* kCGPatternTilingNoDistortion, */
-			     TRUE,
-			     &cb);
     return CAIRO_STATUS_SUCCESS;
 }
 
@@ -751,6 +766,9 @@ _cairo_quartz_setup_source (cairo_quartz_surface_t *surface,
 {
     assert (!(surface->sourceImage || surface->sourceShading || surface->sourcePattern));
 
+    surface->oldInterpolationQuality = CGContextGetInterpolationQuality (surface->cgContext);
+    CGContextSetInterpolationQuality (surface->cgContext, _cairo_quartz_filter_to_quartz (source->filter));
+
     if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
 	cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
 
@@ -785,7 +803,6 @@ _cairo_quartz_setup_source (cairo_quartz_surface_t *surface,
     {
 	cairo_surface_pattern_t *spat = (cairo_surface_pattern_t *) source;
 	cairo_surface_t *pat_surf = spat->surface;
-	cairo_quartz_surface_t *quartz_surf;
 	CGImageRef img;
 	cairo_matrix_t m = spat->base.matrix;
 	cairo_rectangle_int_t extents;
@@ -794,16 +811,7 @@ _cairo_quartz_setup_source (cairo_quartz_surface_t *surface,
 	CGRect srcRect;
 	cairo_fixed_t fw, fh;
 
-	status = _cairo_quartz_surface_to_quartz ((cairo_surface_t *) surface, pat_surf, &quartz_surf);
-	if (status)
-	    return DO_UNSUPPORTED;
-
-	surface->sourceImageSurface = (cairo_surface_t *)quartz_surf;
-
-	if (IS_EMPTY(quartz_surf))
-	    return DO_NOTHING;
-
-	img = CGBitmapContextCreateImage (quartz_surf->cgContext);
+	img = _cairo_surface_to_cgimage ((cairo_surface_t *) surface, pat_surf);
 	if (!img)
 	    return DO_UNSUPPORTED;
 
@@ -898,6 +906,8 @@ static void
 _cairo_quartz_teardown_source (cairo_quartz_surface_t *surface,
 				cairo_pattern_t *source)
 {
+    CGContextSetInterpolationQuality (surface->cgContext, surface->oldInterpolationQuality);
+
     if (surface->sourceImage) {
 	CGImageRelease(surface->sourceImage);
 	surface->sourceImage = NULL;
@@ -937,6 +947,11 @@ _cairo_quartz_get_image (cairo_quartz_surface_t *surface,
 	return CAIRO_STATUS_SUCCESS;
     }
 
+    if (surface->imageSurfaceEquiv) {
+	*image_out = (cairo_image_surface_t*) cairo_surface_reference(surface->imageSurfaceEquiv);
+	return CAIRO_STATUS_SUCCESS;
+    }
+
     if (CGBitmapContextGetBitsPerPixel(surface->cgContext) != 0) {
 	unsigned int stride;
 	unsigned int bitinfo;
@@ -945,6 +960,7 @@ _cairo_quartz_get_image (cairo_quartz_surface_t *surface,
 	unsigned int color_comps;
 
 	imageData = (unsigned char *) CGBitmapContextGetData(surface->cgContext);
+
 #ifdef USE_10_3_WORKAROUNDS
 	bitinfo = CGBitmapContextGetAlphaInfo (surface->cgContext);
 #else
@@ -1029,6 +1045,11 @@ _cairo_quartz_surface_finish (void *abstract_surface)
 
     surface->cgContext = NULL;
 
+    if (surface->imageSurfaceEquiv) {
+	cairo_surface_destroy (surface->imageSurfaceEquiv);
+	surface->imageSurfaceEquiv = NULL;
+    }
+
     if (surface->imageData) {
 	free (surface->imageData);
 	surface->imageData = NULL;
@@ -1121,7 +1142,7 @@ _cairo_quartz_surface_create_similar (void *abstract_surface,
 	return NULL;
 
     // verify width and height of surface
-    if (!verify_surface_size(width, height)) {
+    if (!_cairo_quartz_verify_surface_size(width, height)) {
 	_cairo_error (CAIRO_STATUS_NO_MEMORY);
 	return NULL;
     }
@@ -1131,12 +1152,12 @@ _cairo_quartz_surface_create_similar (void *abstract_surface,
 
 static cairo_status_t
 _cairo_quartz_surface_clone_similar (void *abstract_surface,
-				      cairo_surface_t *src,
-				      int              src_x,
-				      int              src_y,
-				      int              width,
-				      int              height,
-				      cairo_surface_t **clone_out)
+				     cairo_surface_t *src,
+				     int              src_x,
+				     int              src_y,
+				     int              width,
+				     int              height,
+				     cairo_surface_t **clone_out)
 {
     cairo_quartz_surface_t *new_surface = NULL;
     cairo_format_t new_format;
@@ -1145,97 +1166,58 @@ _cairo_quartz_surface_clone_similar (void *abstract_surface,
     *clone_out = NULL;
 
     // verify width and height of surface
-    if (!verify_surface_size(width, height)) {
+    if (!_cairo_quartz_verify_surface_size(width, height)) {
 	return CAIRO_INT_STATUS_UNSUPPORTED;
     }
 
-    if (cairo_surface_get_type(src) == CAIRO_SURFACE_TYPE_QUARTZ) {
+    if (width == 0 || height == 0) {
+	*clone_out = (cairo_surface_t*)
+	    _cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA,
+						   width, height);
+	return CAIRO_STATUS_SUCCESS;
+    }
+
+    if (src->backend->type == CAIRO_SURFACE_TYPE_QUARTZ) {
 	cairo_quartz_surface_t *qsurf = (cairo_quartz_surface_t *) src;
 
 	if (IS_EMPTY(qsurf)) {
-	    *clone_out = (cairo_surface_t*) _cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA, qsurf->extents.width, qsurf->extents.height);
+	    *clone_out = (cairo_surface_t*)
+		_cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA,
+						       qsurf->extents.width, qsurf->extents.height);
 	    return CAIRO_STATUS_SUCCESS;
 	}
-
-	quartz_image = CGBitmapContextCreateImage (qsurf->cgContext);
-	new_format = CAIRO_FORMAT_ARGB32;  /* XXX bogus; recover a real format from the image */
-    } else if (_cairo_surface_is_image (src)) {
-	cairo_image_surface_t *isurf = (cairo_image_surface_t *) src;
-	CGDataProviderRef dataProvider;
-	CGColorSpaceRef cgColorspace;
-	CGBitmapInfo bitinfo;
-	int bitsPerComponent, bitsPerPixel;
-
-	if (isurf->width == 0 || isurf->height == 0) {
-	    *clone_out = (cairo_surface_t*) _cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA, isurf->width, isurf->height);
-	    return CAIRO_STATUS_SUCCESS;
-	}
-
-	if (isurf->format == CAIRO_FORMAT_ARGB32) {
-	    cgColorspace = CGColorSpaceCreateDeviceRGB();
-	    bitinfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
-	    bitsPerComponent = 8;
-	    bitsPerPixel = 32;
-	} else if (isurf->format == CAIRO_FORMAT_RGB24) {
-	    cgColorspace = CGColorSpaceCreateDeviceRGB();
-	    bitinfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
-	    bitsPerComponent = 8;
-	    bitsPerPixel = 32;
-	} else if (isurf->format == CAIRO_FORMAT_A8) {
-	    cgColorspace = CGColorSpaceCreateDeviceGray();
-	    bitinfo = kCGImageAlphaNone;
-	    bitsPerComponent = 8;
-	    bitsPerPixel = 8;
-	} else {
-	    /* SUPPORT A1, maybe */
-	    return CAIRO_INT_STATUS_UNSUPPORTED;
-	}
-
-	new_format = isurf->format;
-
-	dataProvider = CGDataProviderCreateWithData (NULL,
-						     isurf->data,
-						     isurf->height * isurf->stride,
-						     NULL);
-
-	quartz_image = CGImageCreate (isurf->width, isurf->height,
-				      bitsPerComponent,
-				      bitsPerPixel,
-				      isurf->stride,
-				      cgColorspace,
-				      bitinfo,
-				      dataProvider,
-				      NULL,
-				      false,
-				      kCGRenderingIntentDefault);
-	CGDataProviderRelease (dataProvider);
-	CGColorSpaceRelease (cgColorspace);
-    } else {
-	return CAIRO_INT_STATUS_UNSUPPORTED;
     }
 
+    quartz_image = _cairo_surface_to_cgimage ((cairo_surface_t*) abstract_surface, src);
     if (!quartz_image)
 	return CAIRO_INT_STATUS_UNSUPPORTED;
 
+    new_format = CAIRO_FORMAT_ARGB32;  /* assumed */
+    if (_cairo_surface_is_image (src)) {
+	new_format = ((cairo_image_surface_t *) src)->format;
+    }
+
     new_surface = (cairo_quartz_surface_t *)
-	cairo_quartz_surface_create (new_format,
-				     CGImageGetWidth (quartz_image),
-				     CGImageGetHeight (quartz_image));
+	cairo_quartz_surface_create (new_format, width, height);
     if (!new_surface || new_surface->base.status) {
 	CGImageRelease (quartz_image);
 	return CAIRO_INT_STATUS_UNSUPPORTED;
     }
 
+    CGContextSaveGState (new_surface->cgContext);
+
     CGContextSetCompositeOperation (new_surface->cgContext,
 				    kPrivateCGCompositeCopy);
 
-    quartz_image_to_png (quartz_image, NULL);
-
+    CGContextTranslateCTM (new_surface->cgContext, -src_x, -src_y);
     CGContextDrawImage (new_surface->cgContext,
-			CGRectMake (src_x, src_y, width, height),
+			CGRectMake (0, 0, CGImageGetWidth(quartz_image), CGImageGetHeight(quartz_image)),
 			quartz_image);
-    CGImageRelease (quartz_image);
 
+    CGContextRestoreGState (new_surface->cgContext);
+
+    CGImageRelease (quartz_image);
+    
     *clone_out = (cairo_surface_t*) new_surface;
 
     return CAIRO_STATUS_SUCCESS;
@@ -1254,8 +1236,8 @@ _cairo_quartz_surface_get_extents (void *abstract_surface,
 
 static cairo_int_status_t
 _cairo_quartz_surface_paint (void *abstract_surface,
-			      cairo_operator_t op,
-			      cairo_pattern_t *source)
+			     cairo_operator_t op,
+			     cairo_pattern_t *source)
 {
     cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
     cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
@@ -1281,17 +1263,11 @@ _cairo_quartz_surface_paint (void *abstract_surface,
     } else if (action == DO_SHADING) {
 	CGContextDrawShading (surface->cgContext, surface->sourceShading);
     } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
-	cairo_surface_pattern_t *surface_pattern =
-	    (cairo_surface_pattern_t *) source;
-	cairo_surface_t *pat_surf = surface_pattern->surface;
-
 	CGContextSaveGState (surface->cgContext);
 
 	CGContextConcatCTM (surface->cgContext, surface->sourceImageTransform);
-	if (cairo_surface_get_type(pat_surf) == CAIRO_SURFACE_TYPE_QUARTZ) {
-	    CGContextTranslateCTM (surface->cgContext, 0, CGImageGetHeight(surface->sourceImage));
-	    CGContextScaleCTM (surface->cgContext, 1, -1);
-	}
+	CGContextTranslateCTM (surface->cgContext, 0, surface->sourceImageRect.size.height);
+	CGContextScaleCTM (surface->cgContext, 1, -1);
 
 	if (action == DO_IMAGE)
 	    CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
@@ -1361,19 +1337,14 @@ _cairo_quartz_surface_fill (void *abstract_surface,
 
 	CGContextDrawShading (surface->cgContext, surface->sourceShading);
     } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
-	cairo_surface_pattern_t *surface_pattern =
-	    (cairo_surface_pattern_t *) source;
-	cairo_surface_t *pat_surf = surface_pattern->surface;
 	if (fill_rule == CAIRO_FILL_RULE_WINDING)
 	    CGContextClip (surface->cgContext);
 	else
 	    CGContextEOClip (surface->cgContext);
 
 	CGContextConcatCTM (surface->cgContext, surface->sourceImageTransform);
-	if (cairo_surface_get_type(pat_surf) == CAIRO_SURFACE_TYPE_QUARTZ) {
-	    CGContextTranslateCTM (surface->cgContext, 0, CGImageGetHeight(surface->sourceImage));
-	    CGContextScaleCTM (surface->cgContext, 1, -1);
-	}
+	CGContextTranslateCTM (surface->cgContext, 0, surface->sourceImageRect.size.height);
+	CGContextScaleCTM (surface->cgContext, 1, -1);
 
 	if (action == DO_IMAGE)
 	    CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
@@ -1470,10 +1441,8 @@ _cairo_quartz_surface_stroke (void *abstract_surface,
 	CGContextClip (surface->cgContext);
 
 	CGContextConcatCTM (surface->cgContext, surface->sourceImageTransform);
-	if (cairo_surface_get_type(((cairo_surface_pattern_t*)source)->surface) == CAIRO_SURFACE_TYPE_QUARTZ) {
-	    CGContextTranslateCTM (surface->cgContext, 0, CGImageGetHeight(surface->sourceImage));
-	    CGContextScaleCTM (surface->cgContext, 1, -1);
-	}
+	CGContextTranslateCTM (surface->cgContext, 0, surface->sourceImageRect.size.height);
+	CGContextScaleCTM (surface->cgContext, 1, -1);
 
 	if (action == DO_IMAGE)
 	    CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
@@ -1622,10 +1591,8 @@ _cairo_quartz_surface_show_glyphs (void *abstract_surface,
 
     if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
 	CGContextConcatCTM (surface->cgContext, surface->sourceImageTransform);
-	if (cairo_surface_get_type(((cairo_surface_pattern_t*)source)->surface) == CAIRO_SURFACE_TYPE_QUARTZ) {
-	    CGContextTranslateCTM (surface->cgContext, 0, CGImageGetHeight(surface->sourceImage));
-	    CGContextScaleCTM (surface->cgContext, 1, -1);
-	}
+	CGContextTranslateCTM (surface->cgContext, 0, surface->sourceImageRect.size.height);
+	CGContextScaleCTM (surface->cgContext, 1, -1);
 
 	if (action == DO_IMAGE)
 	    CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
@@ -1659,7 +1626,6 @@ _cairo_quartz_surface_mask_with_surface (cairo_quartz_surface_t *surface,
                                          cairo_surface_pattern_t *mask)
 {
     cairo_rectangle_int_t extents;
-    cairo_quartz_surface_t *quartz_surf;
     CGRect rect;
     CGImageRef img;
     cairo_surface_t *pat_surf = mask->surface;
@@ -1669,37 +1635,43 @@ _cairo_quartz_surface_mask_with_surface (cairo_quartz_surface_t *surface,
     if (status)
 	return status;
 
-    status = _cairo_quartz_surface_to_quartz (NULL, pat_surf, &quartz_surf);
-    if (status)
-	return status;
-
     // everything would be masked out, so do nothing
-    if (IS_EMPTY(quartz_surf))
+    if (extents.width == 0 || extents.height == 0)
 	goto BAIL;
 
-    img = CGBitmapContextCreateImage (quartz_surf->cgContext);
+    img = _cairo_surface_to_cgimage ((cairo_surface_t *) surface, pat_surf);
     if (!img) {
 	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
 	goto BAIL;
     }
 
     rect = CGRectMake (-mask->base.matrix.x0, -mask->base.matrix.y0, extents.width, extents.height);
+
     CGContextSaveGState (surface->cgContext);
+
+    /* ClipToMask is essentially drawing an image, so we need to flip the CTM
+     * to get the image to appear oriented the right way */
+    CGAffineTransform ctm = CGContextGetCTM (surface->cgContext);
+    CGContextTranslateCTM (surface->cgContext, 0.0f, rect.size.height);
+    CGContextScaleCTM (surface->cgContext, 1.0f, -1.0f);
+
     CGContextClipToMaskPtr (surface->cgContext, rect, img);
+
+    CGContextSetCTM (surface->cgContext, ctm);
+
     status = _cairo_quartz_surface_paint (surface, op, source);
 
     CGContextRestoreGState (surface->cgContext);
     CGImageRelease (img);
   BAIL:
-    cairo_surface_destroy ((cairo_surface_t*) quartz_surf);
     return status;
 }
 
 static cairo_int_status_t
 _cairo_quartz_surface_mask (void *abstract_surface,
-			     cairo_operator_t op,
-			     cairo_pattern_t *source,
-			     cairo_pattern_t *mask)
+			    cairo_operator_t op,
+			    cairo_pattern_t *source,
+			    cairo_pattern_t *mask)
 {
     cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
     cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
@@ -1871,6 +1843,7 @@ _cairo_quartz_surface_create_internal (CGContextRef cgContext,
     surface->cgContextBaseCTM = CGContextGetCTM (cgContext);
 
     surface->imageData = NULL;
+    surface->imageSurfaceEquiv = NULL;
 
     return surface;
 }
@@ -1953,7 +1926,7 @@ cairo_quartz_surface_create (cairo_format_t format,
     int bitsPerComponent;
 
     // verify width and height of surface
-    if (!verify_surface_size(width, height))
+    if (!_cairo_quartz_verify_surface_size(width, height))
 	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
     if (width == 0 || height == 0) {
@@ -1994,6 +1967,7 @@ cairo_quartz_surface_create (cairo_format_t format,
 	CGColorSpaceRelease (cgColorspace);
 	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
     }
+
     /* zero the memory to match the image surface behaviour */
     memset (imageData, 0, height * stride);
 
@@ -2025,6 +1999,7 @@ cairo_quartz_surface_create (cairo_format_t format,
     }
 
     surf->imageData = imageData;
+    surf->imageSurfaceEquiv = cairo_image_surface_create_for_data (imageData, format, width, height, stride);
 
     return (cairo_surface_t *) surf;
 }
@@ -2151,4 +2126,3 @@ quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest)
     CGImageRelease(imgref);
 #endif
 }
-
diff --git a/src/cairo-quartz.h b/src/cairo-quartz.h
index 5d78d39..c0edb0c 100644
--- a/src/cairo-quartz.h
+++ b/src/cairo-quartz.h
@@ -57,6 +57,12 @@ cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext,
 cairo_public CGContextRef
 cairo_quartz_surface_get_cg_context (cairo_surface_t *surface);
 
+cairo_public cairo_surface_t *
+cairo_quartz_image_surface_create (cairo_surface_t *image_surface);
+
+cairo_public cairo_surface_t *
+cairo_quartz_image_surface_get_image (cairo_surface_t *surface);
+
 CAIRO_END_DECLS
 
 #else  /* CAIRO_HAS_QUARTZ_SURFACE */
diff --git a/src/cairo.h b/src/cairo.h
index 5988f3a..cf14e6f 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -1491,6 +1491,7 @@ cairo_surface_status (cairo_surface_t *surface);
  * @CAIRO_SURFACE_TYPE_SVG: The surface is of type svg
  * @CAIRO_SURFACE_TYPE_OS2: The surface is of type os2
  * @CAIRO_SURFACE_TYPE_WIN32_PRINTING: The surface is a win32 printing surface
+ * @CAIRO_SURFACE_TYPE_QUARTZ_IMAGE: The surface is of type quartz_image
  *
  * #cairo_surface_type_t is used to describe the type of a given
  * surface. The surface types are also known as "backends" or "surface
@@ -1528,7 +1529,8 @@ typedef enum _cairo_surface_type {
     CAIRO_SURFACE_TYPE_DIRECTFB,
     CAIRO_SURFACE_TYPE_SVG,
     CAIRO_SURFACE_TYPE_OS2,
-    CAIRO_SURFACE_TYPE_WIN32_PRINTING
+    CAIRO_SURFACE_TYPE_WIN32_PRINTING,
+    CAIRO_SURFACE_TYPE_QUARTZ_IMAGE
 } cairo_surface_type_t;
 
 cairo_public cairo_surface_type_t


More information about the cairo-commit mailing list