[cairo-commit] 4 commits - src/cairo-quartz-surface.c test/clip-zero.c test/.gitignore test/Makefile.am

Vladimir Vukicevic vladimir at kemper.freedesktop.org
Tue Dec 4 14:05:58 PST 2007


 src/cairo-quartz-surface.c |  227 +++++++++++++++++++++++++++++++--------------
 test/.gitignore            |    1 
 test/Makefile.am           |    1 
 test/clip-zero.c           |   80 +++++++++++++++
 4 files changed, 243 insertions(+), 66 deletions(-)

New commits:
commit 150564c7f8792fa2217fc2574e9e1925c9cd500f
Author: Vladimir Vukicevic <vladimir at pobox.com>
Date:   Tue Dec 4 14:05:47 2007 -0800

    [quartz] use rectangle_int_t, not rectangle_int16_t

diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c
index 6b37dfa..b51a85a 100644
--- a/src/cairo-quartz-surface.c
+++ b/src/cairo-quartz-surface.c
@@ -1669,7 +1669,7 @@ _cairo_quartz_surface_mask_with_surface (cairo_quartz_surface_t *surface,
                                          cairo_pattern_t *source,
                                          cairo_surface_pattern_t *mask)
 {
-    cairo_rectangle_int16_t extents;
+    cairo_rectangle_int_t extents;
     cairo_quartz_surface_t *quartz_surf;
     CGRect rect;
     CGImageRef img;
commit e8574022f949559c069da30f11dee8680fc59586
Author: Vladimir Vukicevic <vladimir at pobox.com>
Date:   Tue Dec 4 13:54:32 2007 -0800

    [quartz] cleanup: remove trailing whitespace

diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c
index d1339db..6b37dfa 100644
--- a/src/cairo-quartz-surface.c
+++ b/src/cairo-quartz-surface.c
@@ -86,7 +86,7 @@ CG_EXTERN CGSize CGContextGetPatternPhase (CGContextRef);
 
 /* We need to work with the 10.3 SDK as well (and 10.3 machines; luckily, 10.3.9
  * has all the stuff we care about, just some of it isn't exported in the SDK.
- */ 
+ */
 #ifndef kCGBitmapByteOrder32Host
 #define USE_10_3_WORKAROUNDS
 #define kCGBitmapAlphaInfoMask 0x1F
@@ -171,7 +171,7 @@ _cairo_path_to_quartz_context_line_to (void *closure, cairo_point_t *point)
     quartz_stroke_t *stroke = (quartz_stroke_t *)closure;
     double x = _cairo_fixed_to_double (point->x);
     double y = _cairo_fixed_to_double (point->y);
-    
+
     if (stroke->ctm_inverse)
 	cairo_matrix_transform_point (stroke->ctm_inverse, &x, &y);
 
@@ -390,12 +390,12 @@ _cairo_quartz_cairo_gradient_pattern_to_quartz (cairo_pattern_t *abspat,
 	return CAIRO_INT_STATUS_UNSUPPORTED;
 
     /* bandaid for mozilla bug 379321, also visible in the
-     * linear-gradient-reflect test. 
+     * linear-gradient-reflect test.
      */
     if (abspat->extend == CAIRO_EXTEND_REFLECT ||
 	abspat->extend == CAIRO_EXTEND_REPEAT)
 	return CAIRO_INT_STATUS_UNSUPPORTED;
-	
+
     /* We can only do this if we have an identity pattern matrix;
      * otherwise fall back through to the generic pattern case.
      * XXXperf we could optimize this by creating a pattern with the shading;
@@ -1093,7 +1093,7 @@ _cairo_quartz_surface_create_similar (void *abstract_surface,
 	format = CAIRO_FORMAT_A8;
     else
 	return NULL;
-	
+
     // verify width and height of surface
     if (!verify_surface_size(width, height)) {
 	_cairo_error (CAIRO_STATUS_NO_MEMORY);
@@ -1115,7 +1115,7 @@ _cairo_quartz_surface_clone_similar (void *abstract_surface,
     cairo_quartz_surface_t *new_surface = NULL;
     cairo_format_t new_format;
     CGImageRef quartz_image = NULL;
-    
+
     *clone_out = NULL;
 
     // verify width and height of surface
@@ -1435,7 +1435,7 @@ _cairo_quartz_surface_stroke (void *abstract_surface,
 
 	for (k = 0; k < max_dashes; k++)
 	    fdash[k] = (float) style->dash[k % style->num_dashes];
-	
+
 	CGContextSetLineDash (surface->cgContext, style->dash_offset, fdash, max_dashes);
 	if (fdash != sdash)
 	    free (fdash);
@@ -1561,9 +1561,9 @@ _cairo_quartz_surface_show_glyphs (void *abstract_surface,
      * text matrix?
      */
     //ND((stderr, "show_glyphs: glyph 0 at: %f, %f\n", glyphs[0].x, glyphs[0].y));
-    cairoTextTransform = CGAffineTransformMake (scaled_font->font_matrix.xx, 
+    cairoTextTransform = CGAffineTransformMake (scaled_font->font_matrix.xx,
 						scaled_font->font_matrix.yx,
-						scaled_font->font_matrix.xy, 
+						scaled_font->font_matrix.xy,
 						scaled_font->font_matrix.yy,
 						0., 0.);
 
@@ -1656,7 +1656,7 @@ BAIL:
     }
 
     _cairo_quartz_teardown_source (surface, source);
-    
+
     CGContextRestoreGState (surface->cgContext);
 
     return rv;
@@ -1885,7 +1885,7 @@ _cairo_quartz_surface_create_internal (CGContextRef cgContext,
 
     return surface;
 }
-					 
+
 /**
  * cairo_quartz_surface_create_for_cg_context
  * @cgContext: the existing CGContext for which to create the surface
commit ee01a94726b3c0782fa182c9e874b2ed57e71aca
Author: Vladimir Vukicevic <vladimir at pobox.com>
Date:   Tue Dec 4 13:53:03 2007 -0800

    [quartz] Use CGContextDrawTiledImage if available
    
    Use this 10.5-only method if available, as it greatly speeds up
    tiled image rendering (EXTEND_REPEAT).

diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c
index 977c71f..d1339db 100644
--- a/src/cairo-quartz-surface.c
+++ b/src/cairo-quartz-surface.c
@@ -103,6 +103,9 @@ CG_EXTERN CGImageRef CGBitmapContextCreateImage (CGContextRef);
 /* missing in 10.3.9 */
 extern void CGContextClipToMask (CGContextRef, CGRect, CGImageRef) __attribute__((weak_import));
 
+/* 10.5-only optimization */
+extern void CGContextDrawTiledImage (CGContextRef, CGRect, CGImageRef) __attribute__((weak_import));
+
 /*
  * Utility functions
  */
@@ -699,7 +702,8 @@ typedef enum {
     DO_PATTERN,
     DO_IMAGE,
     DO_UNSUPPORTED,
-    DO_NOTHING
+    DO_NOTHING,
+    DO_TILED_IMAGE
 } cairo_quartz_action_t;
 
 static cairo_quartz_action_t
@@ -737,7 +741,7 @@ _cairo_quartz_setup_source (cairo_quartz_surface_t *surface,
 
 	return DO_SHADING;
     } else if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
-	       source->extend == CAIRO_EXTEND_NONE)
+	       (source->extend == CAIRO_EXTEND_NONE || (CGContextDrawTiledImage && source->extend == CAIRO_EXTEND_REPEAT)))
     {
 	    cairo_surface_pattern_t *spat = (cairo_surface_pattern_t *) source;
 	    cairo_surface_t *pat_surf = spat->surface;
@@ -771,7 +775,10 @@ _cairo_quartz_setup_source (cairo_quartz_surface_t *surface,
 
 	    surface->sourceImageRect = CGRectMake (0, 0, extents.width, extents.height);
 
-	    return DO_IMAGE;
+	    if (source->extend == CAIRO_EXTEND_NONE)
+		return DO_IMAGE;
+	    else
+		return DO_TILED_IMAGE;
     } else if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
 	float patternAlpha = 1.0f;
 	CGColorSpaceRef patternSpace;
@@ -1247,24 +1254,31 @@ _cairo_quartz_surface_paint (void *abstract_surface,
 							  surface->extents.height));
     } else if (action == DO_SHADING) {
 	CGContextDrawShading (surface->cgContext, surface->sourceShading);
-    } else if (action == DO_IMAGE) {
+    } 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);
-	CGContextSetRGBFillColor (surface->cgContext, 0.0, 0.0, 0.0, 0.0);
-	CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x,
-							  surface->extents.y,
-							  surface->extents.width,
-							  surface->extents.height));
+
+	if (action == DO_IMAGE && op != CAIRO_OPERATOR_OVER) {
+	    CGContextSetRGBFillColor (surface->cgContext, 0.0, 0.0, 0.0, 0.0);
+	    CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x,
+							      surface->extents.y,
+							      surface->extents.width,
+							      surface->extents.height));
+	}
+
 	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);
 	}
 
-	CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
+	if (action == DO_IMAGE)
+	    CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
+	else
+	    CGContextDrawTiledImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
 	CGContextRestoreGState (surface->cgContext);
     } else if (action != DO_NOTHING) {
 	rv = CAIRO_INT_STATUS_UNSUPPORTED;
@@ -1328,7 +1342,7 @@ _cairo_quartz_surface_fill (void *abstract_surface,
 	    CGContextEOClip (surface->cgContext);
 
 	CGContextDrawShading (surface->cgContext, surface->sourceShading);
-    } else if (action == DO_IMAGE) {
+    } 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;
@@ -1337,18 +1351,24 @@ _cairo_quartz_surface_fill (void *abstract_surface,
 	else
 	    CGContextEOClip (surface->cgContext);
 
-	CGContextSetRGBFillColor (surface->cgContext, 0.0, 0.0, 0.0, 0.0);
-	CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x,
-							  surface->extents.y,
-							  surface->extents.width,
-							  surface->extents.height));
+	if (action == DO_IMAGE && op != CAIRO_OPERATOR_OVER) {
+	    CGContextSetRGBFillColor (surface->cgContext, 0.0, 0.0, 0.0, 0.0);
+	    CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x,
+							      surface->extents.y,
+							      surface->extents.width,
+							      surface->extents.height));
+	}
+
 	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);
 	}
 
-	CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
+	if (action == DO_IMAGE)
+	    CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
+	else
+	    CGContextDrawTiledImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
     } else if (action != DO_NOTHING) {
 	rv = CAIRO_INT_STATUS_UNSUPPORTED;
     }
@@ -1435,22 +1455,28 @@ _cairo_quartz_surface_stroke (void *abstract_surface,
 
     if (action == DO_SOLID || action == DO_PATTERN) {
 	CGContextStrokePath (surface->cgContext);
-    } else if (action == DO_IMAGE) {
+    } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
 	CGContextReplacePathWithStrokedPath (surface->cgContext);
 	CGContextClip (surface->cgContext);
 
-	CGContextSetRGBFillColor (surface->cgContext, 0.0, 0.0, 0.0, 0.0);
-	CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x,
-							  surface->extents.y,
-							  surface->extents.width,
-							  surface->extents.height));
+	if (action == DO_IMAGE && op != CAIRO_OPERATOR_OVER) {
+	    CGContextSetRGBFillColor (surface->cgContext, 0.0, 0.0, 0.0, 0.0);
+	    CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x,
+							      surface->extents.y,
+							      surface->extents.width,
+							      surface->extents.height));
+	}
+
 	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);
 	}
 
-	CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
+	if (action == DO_IMAGE)
+	    CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
+	else
+	    CGContextDrawTiledImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
     } else if (action == DO_SHADING) {
 	CGContextReplacePathWithStrokedPath (surface->cgContext);
 	CGContextClip (surface->cgContext);
@@ -1512,7 +1538,7 @@ _cairo_quartz_surface_show_glyphs (void *abstract_surface,
     action = _cairo_quartz_setup_source (surface, source);
     if (action == DO_SOLID || action == DO_PATTERN) {
 	CGContextSetTextDrawingMode (surface->cgContext, kCGTextFill);
-    } else if (action == DO_IMAGE || action == DO_SHADING) {
+    } else if (action == DO_IMAGE || action == DO_TILED_IMAGE || action == DO_SHADING) {
 	CGContextSetTextDrawingMode (surface->cgContext, kCGTextClip);
     } else {
 	if (action != DO_NOTHING)
@@ -1597,19 +1623,25 @@ _cairo_quartz_surface_show_glyphs (void *abstract_surface,
 				     cg_advances,
 				     num_glyphs);
 
-    if (action == DO_IMAGE) {
-	CGContextSetRGBFillColor (surface->cgContext, 0.0, 0.0, 0.0, 0.0);
-	CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x,
-							  surface->extents.y,
-							  surface->extents.width,
-							  surface->extents.height));
+    if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
+	if (action == DO_IMAGE && op != CAIRO_OPERATOR_OVER) {
+	    CGContextSetRGBFillColor (surface->cgContext, 0.0, 0.0, 0.0, 0.0);
+	    CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x,
+							      surface->extents.y,
+							      surface->extents.width,
+							      surface->extents.height));
+	}
+
 	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);
 	}
 
-	CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
+	if (action == DO_IMAGE)
+	    CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
+	else
+	    CGContextDrawTiledImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
     } else if (action == DO_SHADING) {
 	CGContextDrawShading (surface->cgContext, surface->sourceShading);
     }
commit 2c25033e14d7d9f705c27683dfb093318d67910b
Author: Vladimir Vukicevic <vladimir at pobox.com>
Date:   Tue Dec 4 13:49:59 2007 -0800

    [quartz] Handle creating 0x0 surfaces
    
    Make all 0x0 surfaces be no-ops when used in a rendering operation.

diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c
index b3d0634..977c71f 100644
--- a/src/cairo-quartz-surface.c
+++ b/src/cairo-quartz-surface.c
@@ -57,6 +57,8 @@
 #define ND(_x)	do {} while(0)
 #endif
 
+#define IS_EMPTY(s) ((s)->extents.width == 0 || (s)->extents.height == 0)
+
 /* This method is private, but it exists.  Its params are are exposed
  * as args to the NS* method, but not as CG.
  */
@@ -108,6 +110,12 @@ extern void CGContextClipToMask (CGContextRef, CGRect, CGImageRef) __attribute__
 static void quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest);
 static void quartz_image_to_png (CGImageRef, char *dest);
 
+static cairo_quartz_surface_t *
+_cairo_quartz_surface_create_internal (CGContextRef cgContext,
+				       cairo_content_t content,
+				       unsigned int width,
+				       unsigned int height);
+
 /* CoreGraphics limitation with flipped CTM surfaces: height must be less than signed 16-bit max */
 
 #define CG_MAX_HEIGHT   SHRT_MAX
@@ -690,7 +698,8 @@ typedef enum {
     DO_SHADING,
     DO_PATTERN,
     DO_IMAGE,
-    DO_UNSUPPORTED
+    DO_UNSUPPORTED,
+    DO_NOTHING
 } cairo_quartz_action_t;
 
 static cairo_quartz_action_t
@@ -742,6 +751,11 @@ _cairo_quartz_setup_source (cairo_quartz_surface_t *surface,
 	    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);
 	    if (!img)
 		return DO_UNSUPPORTED;
@@ -757,8 +771,6 @@ _cairo_quartz_setup_source (cairo_quartz_surface_t *surface,
 
 	    surface->sourceImageRect = CGRectMake (0, 0, extents.width, extents.height);
 
-	    surface->sourceImageSurface = (cairo_surface_t *)quartz_surf;
-
 	    return DO_IMAGE;
     } else if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
 	float patternAlpha = 1.0f;
@@ -845,6 +857,13 @@ _cairo_quartz_get_image (cairo_quartz_surface_t *surface,
     unsigned char *imageData;
     cairo_image_surface_t *isurf;
 
+    if (IS_EMPTY(surface)) {
+	*image_out = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
+	if (data_out)
+	    *data_out = NULL;
+	return CAIRO_STATUS_SUCCESS;
+    }
+
     if (CGBitmapContextGetBitsPerPixel(surface->cgContext) != 0) {
 	unsigned int stride;
 	unsigned int bitinfo;
@@ -927,6 +946,9 @@ _cairo_quartz_surface_finish (void *abstract_surface)
 
     ND((stderr, "_cairo_quartz_surface_finish[%p] cgc: %p\n", surface, surface->cgContext));
 
+    if (IS_EMPTY(surface))
+	return CAIRO_STATUS_SUCCESS;
+
     /* Restore our saved gstate that we use to reset clipping */
     CGContextRestoreGState (surface->cgContext);
 
@@ -1006,6 +1028,11 @@ _cairo_quartz_surface_release_dest_image (void *abstract_surface,
 
     //ND((stderr, "%p _cairo_quartz_surface_release_dest_image\n", surface));
 
+    if (IS_EMPTY(surface)) {
+	cairo_surface_destroy ((cairo_surface_t*) image);
+	return;
+    }
+
     if (!CGBitmapContextGetData (surface->cgContext)) {
 	CGDataProviderRef dataProvider;
 	CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
@@ -1080,6 +1107,7 @@ _cairo_quartz_surface_clone_similar (void *abstract_surface,
 {
     cairo_quartz_surface_t *new_surface = NULL;
     cairo_format_t new_format;
+    CGImageRef quartz_image = NULL;
     
     *clone_out = NULL;
 
@@ -1088,10 +1116,14 @@ _cairo_quartz_surface_clone_similar (void *abstract_surface,
 	return CAIRO_INT_STATUS_UNSUPPORTED;
     }
 
-    CGImageRef quartz_image = NULL;
-
     if (cairo_surface_get_type(src) == 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);
+	    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)) {
@@ -1101,6 +1133,11 @@ _cairo_quartz_surface_clone_similar (void *abstract_surface,
 	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;
@@ -1193,6 +1230,9 @@ _cairo_quartz_surface_paint (void *abstract_surface,
 
     ND((stderr, "%p _cairo_quartz_surface_paint op %d source->type %d\n", surface, op, source->type));
 
+    if (IS_EMPTY(surface))
+	return CAIRO_STATUS_SUCCESS;
+
     if (op == CAIRO_OPERATOR_DEST)
 	return CAIRO_STATUS_SUCCESS;
 
@@ -1226,7 +1266,7 @@ _cairo_quartz_surface_paint (void *abstract_surface,
 
 	CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
 	CGContextRestoreGState (surface->cgContext);
-    } else {
+    } else if (action != DO_NOTHING) {
 	rv = CAIRO_INT_STATUS_UNSUPPORTED;
     }
 
@@ -1252,6 +1292,9 @@ _cairo_quartz_surface_fill (void *abstract_surface,
 
     ND((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n", surface, op, source->type));
 
+    if (IS_EMPTY(surface))
+	return CAIRO_STATUS_SUCCESS;
+
     if (op == CAIRO_OPERATOR_DEST)
 	return CAIRO_STATUS_SUCCESS;
 
@@ -1261,10 +1304,6 @@ _cairo_quartz_surface_fill (void *abstract_surface,
     CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz (op));
 
     action = _cairo_quartz_setup_source (surface, source);
-    if (action == DO_UNSUPPORTED) {
-	CGContextRestoreGState (surface->cgContext);
-	return CAIRO_INT_STATUS_UNSUPPORTED;
-    }
 
     CGContextBeginPath (surface->cgContext);
 
@@ -1310,7 +1349,7 @@ _cairo_quartz_surface_fill (void *abstract_surface,
 	}
 
 	CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
-    } else {
+    } else if (action != DO_NOTHING) {
 	rv = CAIRO_INT_STATUS_UNSUPPORTED;
     }
 
@@ -1342,6 +1381,9 @@ _cairo_quartz_surface_stroke (void *abstract_surface,
 
     ND((stderr, "%p _cairo_quartz_surface_stroke op %d source->type %d\n", surface, op, source->type));
 
+    if (IS_EMPTY(surface))
+	return CAIRO_STATUS_SUCCESS;
+
     if (op == CAIRO_OPERATOR_DEST)
 	return CAIRO_STATUS_SUCCESS;
 
@@ -1382,10 +1424,6 @@ _cairo_quartz_surface_stroke (void *abstract_surface,
     CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz (op));
 
     action = _cairo_quartz_setup_source (surface, source);
-    if (action == DO_UNSUPPORTED) {
-	CGContextRestoreGState (surface->cgContext);
-	return CAIRO_INT_STATUS_UNSUPPORTED;
-    }
 
     CGContextBeginPath (surface->cgContext);
 
@@ -1418,7 +1456,7 @@ _cairo_quartz_surface_stroke (void *abstract_surface,
 	CGContextClip (surface->cgContext);
 
 	CGContextDrawShading (surface->cgContext, surface->sourceShading);
-    } else {
+    } else if (action != DO_NOTHING) {
 	rv = CAIRO_INT_STATUS_UNSUPPORTED;
     }
 
@@ -1457,6 +1495,9 @@ _cairo_quartz_surface_show_glyphs (void *abstract_surface,
     float xprev, yprev;
     int i;
 
+    if (IS_EMPTY(surface))
+	return CAIRO_STATUS_SUCCESS;
+
     if (num_glyphs <= 0)
 	return CAIRO_STATUS_SUCCESS;
 
@@ -1474,8 +1515,8 @@ _cairo_quartz_surface_show_glyphs (void *abstract_surface,
     } else if (action == DO_IMAGE || action == DO_SHADING) {
 	CGContextSetTextDrawingMode (surface->cgContext, kCGTextClip);
     } else {
-	/* Unsupported */
-	rv = CAIRO_INT_STATUS_UNSUPPORTED;
+	if (action != DO_NOTHING)
+	    rv = CAIRO_INT_STATUS_UNSUPPORTED;
 	goto BAIL;
     }
 
@@ -1611,6 +1652,10 @@ _cairo_quartz_surface_mask_with_surface (cairo_quartz_surface_t *surface,
     if (status)
 	return status;
 
+    // everything would be masked out, so do nothing
+    if (IS_EMPTY(quartz_surf))
+	goto BAIL;
+
     img = CGBitmapContextCreateImage (quartz_surf->cgContext);
     if (!img) {
 	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
@@ -1640,6 +1685,9 @@ _cairo_quartz_surface_mask (void *abstract_surface,
 
     ND((stderr, "%p _cairo_quartz_surface_mask op %d source->type %d mask->type %d\n", surface, op, source->type, mask->type));
 
+    if (IS_EMPTY(surface))
+	return CAIRO_STATUS_SUCCESS;
+
     if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
 	/* This is easy; we just need to paint with the alpha. */
 	cairo_solid_pattern_t *solid_mask = (cairo_solid_pattern_t *) mask;
@@ -1684,6 +1732,9 @@ _cairo_quartz_surface_intersect_clip_path (void *abstract_surface,
 
     ND((stderr, "%p _cairo_quartz_surface_intersect_clip_path path: %p\n", surface, path));
 
+    if (IS_EMPTY(surface))
+	return CAIRO_STATUS_SUCCESS;
+
     if (path == NULL) {
 	/* If we're being asked to reset the clip, we can only do it
 	 * by restoring the gstate to our previous saved one, and
@@ -1700,7 +1751,7 @@ _cairo_quartz_surface_intersect_clip_path (void *abstract_surface,
 	stroke.ctm_inverse = NULL;
 
 	/* path must not be empty. */
-	CGContextMoveToPoint (surface->cgContext, 0, 0);	    
+	CGContextMoveToPoint (surface->cgContext, 0, 0);
 	status = _cairo_quartz_cairo_path_to_quartz_context (path, &stroke);
 	if (status)
 	    return status;
@@ -1748,7 +1799,7 @@ static const struct _cairo_surface_backend cairo_quartz_surface_backend = {
     _cairo_quartz_surface_fill,
 #if CAIRO_HAS_ATSUI_FONT
     _cairo_quartz_surface_show_glyphs,
-#else 
+#else
     NULL, /* surface_show_glyphs */
 #endif /* CAIRO_HAS_ATSUI_FONT */
 
@@ -1758,7 +1809,7 @@ static const struct _cairo_surface_backend cairo_quartz_surface_backend = {
     NULL  /* fill_stroke */
 };
 
-static cairo_quartz_surface_t *
+cairo_quartz_surface_t *
 _cairo_quartz_surface_create_internal (CGContextRef cgContext,
 					cairo_content_t content,
 					unsigned int width,
@@ -1783,6 +1834,13 @@ _cairo_quartz_surface_create_internal (CGContextRef cgContext,
     surface->extents.width = width;
     surface->extents.height = height;
 
+    if (IS_EMPTY(surface)) {
+	surface->cgContext = NULL;
+	surface->cgContextBaseCTM = CGAffineTransformIdentity;
+	surface->imageData = NULL;
+	return surface;
+    }
+
     /* Save so we can always get back to a known-good CGContext -- this is
      * required for proper behaviour of intersect_clip_path(NULL)
      */
@@ -1835,7 +1893,7 @@ cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext,
     CGContextRetain (cgContext);
 
     surf = _cairo_quartz_surface_create_internal (cgContext, CAIRO_CONTENT_COLOR_ALPHA,
-						   width, height);
+						  width, height);
     if (!surf) {
 	CGContextRelease (cgContext);
 	// create_internal will have set an error
@@ -1879,6 +1937,11 @@ cairo_quartz_surface_create (cairo_format_t format,
 	return (cairo_surface_t*) &_cairo_surface_nil;
     }
 
+    if (width == 0 || height == 0) {
+	return (cairo_surface_t*) _cairo_quartz_surface_create_internal (NULL, _cairo_content_from_format (format),
+									 width, height);
+    }
+
     if (format == CAIRO_FORMAT_ARGB32) {
 	cgColorspace = CGColorSpaceCreateDeviceRGB();
 	stride = width * 4;
@@ -1938,7 +2001,7 @@ cairo_quartz_surface_create (cairo_format_t format,
     CGContextScaleCTM (cgc, 1.0, -1.0);
 
     surf = _cairo_quartz_surface_create_internal (cgc, _cairo_content_from_format (format),
-						   width, height);
+						  width, height);
     if (!surf) {
 	CGContextRelease (cgc);
 	free (imageData);
diff --git a/test/.gitignore b/test/.gitignore
index b75c7f2..91d5096 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -17,6 +17,7 @@ clip-nesting
 clip-operator
 clip-push-group
 clip-twice
+clip-zero
 close-path
 composite-integer-translate-source
 composite-integer-translate-over
diff --git a/test/Makefile.am b/test/Makefile.am
index 3cb0708..ec2d028 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -15,6 +15,7 @@ clip-nesting			\
 clip-operator			\
 clip-push-group			\
 clip-twice			\
+clip-zero \
 close-path			\
 composite-integer-translate-source \
 composite-integer-translate-over \
diff --git a/test/clip-zero.c b/test/clip-zero.c
new file mode 100644
index 0000000..ec2dcc8
--- /dev/null
+++ b/test/clip-zero.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright © 2007 Mozilla Corporation
+ *
+ * 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: Vladimir Vukicevic <vladimir at pobox.com>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_draw_function_t draw;
+
+cairo_test_t test = {
+    "clip-zero",
+    "Verifies that 0x0 surfaces or clips don't cause problems.",
+    0, 0,
+    draw
+};
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *pat;
+    cairo_surface_t *surf;
+
+    cairo_new_path (cr);
+    cairo_rectangle (cr, 0, 0, 0, 0);
+    cairo_clip (cr);
+
+    cairo_push_group (cr);
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+
+    cairo_new_path (cr);
+    cairo_rectangle (cr, -10, 10, 20, 20);
+    cairo_fill_preserve (cr);
+    cairo_stroke_preserve (cr);
+    cairo_paint (cr);
+
+    cairo_select_font_face (cr, "sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_show_text (cr, "ABC");
+
+    cairo_mask (cr, cairo_get_source (cr));
+
+    surf = cairo_surface_create_similar (cairo_get_target (cr), CAIRO_CONTENT_COLOR_ALPHA, 0, 0);
+    pat = cairo_pattern_create_for_surface (surf);
+    cairo_surface_destroy (surf);
+
+    cairo_mask (cr, pat);
+    cairo_pattern_destroy (pat);
+
+    cairo_pop_group_to_source (cr);
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+int
+main (void)
+{
+    return cairo_test (&test);
+}


More information about the cairo-commit mailing list