summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVladimir Vukicevic <vladimir@pobox.com>2007-12-04 13:49:59 -0800
committerVladimir Vukicevic <vladimir@h-232.office.mozilla.org>2007-12-04 13:50:49 -0800
commit2c25033e14d7d9f705c27683dfb093318d67910b (patch)
tree3c82f80192dc92ab1ccb3222f34b4f7728c9f6fe
parent407ed0a6e7183c4fc6da1e988fada19c82ccef8a (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.c109
-rw-r--r--test/.gitignore1
-rw-r--r--test/Makefile.am1
-rw-r--r--test/clip-zero.c80
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);
+}