diff options
author | Vladimir Vukicevic <vladimir@pobox.com> | 2007-12-04 13:49:59 -0800 |
---|---|---|
committer | Vladimir Vukicevic <vladimir@h-232.office.mozilla.org> | 2007-12-04 13:50:49 -0800 |
commit | 2c25033e14d7d9f705c27683dfb093318d67910b (patch) | |
tree | 3c82f80192dc92ab1ccb3222f34b4f7728c9f6fe | |
parent | 407ed0a6e7183c4fc6da1e988fada19c82ccef8a (diff) |
[quartz] Handle creating 0x0 surfaces
Make all 0x0 surfaces be no-ops when used in a rendering operation.
-rw-r--r-- | src/cairo-quartz-surface.c | 109 | ||||
-rw-r--r-- | test/.gitignore | 1 | ||||
-rw-r--r-- | test/Makefile.am | 1 | ||||
-rw-r--r-- | test/clip-zero.c | 80 |
4 files changed, 168 insertions, 23 deletions
diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c index b3d06344..977c71fc 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 b75c7f2f..91d5096a 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 3cb0708a..ec2d0288 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 00000000..ec2dcc83 --- /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@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); +} |