[cairo-commit] 3 commits - src/cairo.c src/cairo-clip.c
src/cairo-clip-private.h src/cairo-gstate.c src/cairo.h
src/cairoint.h src/cairo-matrix.c test/get-clip.c
test/get-path-extents.c test/Makefile.am test/Makefile.win32
Vladimir Vukicevic
vladimir at kemper.freedesktop.org
Mon Sep 25 23:24:19 PDT 2006
src/cairo-clip-private.h | 5
src/cairo-clip.c | 127 +++++++++++++++++++++
src/cairo-gstate.c | 109 +++++++++++++-----
src/cairo-matrix.c | 18 ++-
src/cairo.c | 89 +++++++++++++++
src/cairo.h | 36 +++++-
src/cairoint.h | 19 +++
test/Makefile.am | 2
test/Makefile.win32 | 2
test/get-clip.c | 277 +++++++++++++++++++++++++++++++++++++++++++++++
test/get-path-extents.c | 199 +++++++++++++++++++++++++++++++++
11 files changed, 852 insertions(+), 31 deletions(-)
New commits:
diff-tree 191e108b93ef6d39832e78323a18cc4c795c7ca3 (from a8ca155f83098c02fb8d3acc57b0492d5b753d54)
Author: Robert O'Callahan <robert at ocallahan.org>
Date: Mon Sep 25 23:22:45 2006 -0700
Add clip getters API + tests
Add new public API methods:
void cairo_clip_extents (cairo_t *cr, double *x1, double *y1,
double *x2, double *y2);
cairo_rectangle_list_t *cairo_copy_clip_rectangles (cairo_t *);
void cairo_rectangle_list_destroy (cairo_rectangle_list_t *);
Also add 'get-clip' and 'get-path-extents' tests.
diff --git a/src/cairo-clip-private.h b/src/cairo-clip-private.h
index b1a243e..d3255fa 100644
--- a/src/cairo-clip-private.h
+++ b/src/cairo-clip-private.h
@@ -38,6 +38,8 @@
#include "cairo-path-fixed-private.h"
+extern cairo_private const cairo_rectangle_list_t _cairo_rectangles_nil;
+
struct _cairo_clip_path {
unsigned int ref_count;
cairo_path_fixed_t path;
@@ -124,4 +126,7 @@ _cairo_clip_translate (cairo_clip_t *cl
cairo_fixed_t tx,
cairo_fixed_t ty);
+cairo_private cairo_rectangle_list_t*
+_cairo_clip_copy_rectangles (cairo_clip_t *clip, cairo_gstate_t *gstate);
+
#endif /* CAIRO_CLIP_PRIVATE_H */
diff --git a/src/cairo-clip.c b/src/cairo-clip.c
index 630ec89..a0f67fa 100644
--- a/src/cairo-clip.c
+++ b/src/cairo-clip.c
@@ -118,6 +118,39 @@ _cairo_clip_reset (cairo_clip_t *clip)
return CAIRO_STATUS_SUCCESS;
}
+static cairo_status_t
+_cairo_clip_path_intersect_to_rectangle (cairo_clip_path_t *clip_path,
+ cairo_rectangle_int16_t *rectangle)
+{
+ while (clip_path) {
+ cairo_status_t status;
+ cairo_traps_t traps;
+ cairo_box_t extents;
+ cairo_rectangle_int16_t extents_rect;
+
+ _cairo_traps_init (&traps);
+
+ status = _cairo_path_fixed_fill_to_traps (&clip_path->path,
+ clip_path->fill_rule,
+ clip_path->tolerance,
+ &traps);
+ if (status) {
+ _cairo_traps_fini (&traps);
+ return status;
+ }
+
+ _cairo_traps_extents (&traps, &extents);
+ _cairo_box_round_to_rectangle (&extents, &extents_rect);
+ _cairo_rectangle_intersect (rectangle, &extents_rect);
+
+ _cairo_traps_fini (&traps);
+
+ clip_path = clip_path->prev;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
cairo_status_t
_cairo_clip_intersect_to_rectangle (cairo_clip_t *clip,
cairo_rectangle_int16_t *rectangle)
@@ -126,7 +159,12 @@ _cairo_clip_intersect_to_rectangle (cair
return CAIRO_STATUS_SUCCESS;
if (clip->path) {
- /* Intersect path extents here. */
+ cairo_status_t status;
+
+ status = _cairo_clip_path_intersect_to_rectangle (clip->path,
+ rectangle);
+ if (status)
+ return status;
}
if (clip->region) {
@@ -534,3 +572,90 @@ _cairo_clip_init_deep_copy (cairo_clip_t
}
}
}
+
+const cairo_rectangle_list_t _cairo_rectangles_nil =
+ { CAIRO_STATUS_NO_MEMORY, NULL, 0 };
+static const cairo_rectangle_list_t _cairo_rectangles_not_representable =
+ { CAIRO_STATUS_CLIP_NOT_REPRESENTABLE, NULL, 0 };
+
+static cairo_bool_t
+_cairo_clip_rect_to_user (cairo_gstate_t *gstate,
+ double x, double y, double width, double height,
+ cairo_rectangle_t *rectangle)
+{
+ double x2 = x + width;
+ double y2 = y + height;
+ cairo_bool_t is_tight;
+
+ _cairo_gstate_backend_to_user_rectangle (gstate, &x, &y, &x2, &y2, &is_tight);
+ rectangle->x = x;
+ rectangle->y = y;
+ rectangle->width = x2 - x;
+ rectangle->height = y2 - y;
+ return is_tight;
+}
+
+cairo_private cairo_rectangle_list_t*
+_cairo_clip_copy_rectangles (cairo_clip_t *clip, cairo_gstate_t *gstate)
+{
+ cairo_rectangle_list_t *list;
+ cairo_rectangle_t *rectangles;
+ int n_boxes;
+
+ if (clip->path || clip->surface)
+ return (cairo_rectangle_list_t*) &_cairo_rectangles_not_representable;
+
+ n_boxes = clip->region ? pixman_region_num_rects (clip->region) : 1;
+ rectangles = malloc (sizeof (cairo_rectangle_t)*n_boxes);
+ if (rectangles == NULL)
+ return (cairo_rectangle_list_t*) &_cairo_rectangles_nil;
+
+ if (clip->region) {
+ pixman_box16_t *boxes;
+ int i;
+
+ boxes = pixman_region_rects (clip->region);
+ for (i = 0; i < n_boxes; ++i) {
+ if (!_cairo_clip_rect_to_user(gstate, boxes[i].x1, boxes[i].y1,
+ boxes[i].x2 - boxes[i].x1,
+ boxes[i].y2 - boxes[i].y1,
+ &rectangles[i])) {
+ free (rectangles);
+ return (cairo_rectangle_list_t*)
+ &_cairo_rectangles_not_representable;
+ }
+ }
+ } else {
+ cairo_rectangle_int16_t extents;
+ _cairo_surface_get_extents (_cairo_gstate_get_target (gstate), &extents);
+ if (!_cairo_clip_rect_to_user(gstate, extents.x, extents.y,
+ extents.width, extents.height,
+ rectangles)) {
+ free (rectangles);
+ return (cairo_rectangle_list_t*)
+ &_cairo_rectangles_not_representable;
+ }
+ }
+
+ list = malloc (sizeof (cairo_rectangle_list_t));
+ if (list == NULL) {
+ free (rectangles);
+ return (cairo_rectangle_list_t*) &_cairo_rectangles_nil;
+ }
+
+ list->status = CAIRO_STATUS_SUCCESS;
+ list->rectangles = rectangles;
+ list->num_rectangles = n_boxes;
+ return list;
+}
+
+void
+cairo_rectangle_list_destroy (cairo_rectangle_list_t *rectangle_list)
+{
+ if (rectangle_list == NULL || rectangle_list == &_cairo_rectangles_nil ||
+ rectangle_list == &_cairo_rectangles_not_representable)
+ return;
+
+ free (rectangle_list->rectangles);
+ free (rectangle_list);
+}
diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c
index 7fee690..8beb8c4 100644
--- a/src/cairo-gstate.c
+++ b/src/cairo-gstate.c
@@ -1148,6 +1148,40 @@ _cairo_gstate_clip (cairo_gstate_t *gsta
gstate->antialias, gstate->target);
}
+cairo_status_t
+_cairo_gstate_clip_extents (cairo_gstate_t *gstate,
+ double *x1,
+ double *y1,
+ double *x2,
+ double *y2)
+{
+ cairo_rectangle_int16_t extents;
+ cairo_status_t status;
+
+ status = _cairo_surface_get_extents (gstate->target, &extents);
+ if (status)
+ return status;
+
+ status = _cairo_clip_intersect_to_rectangle (&gstate->clip, &extents);
+ if (status)
+ return status;
+
+ *x1 = extents.x;
+ *y1 = extents.y;
+ *x2 = extents.x + extents.width;
+ *y2 = extents.y + extents.height;
+
+ _cairo_gstate_backend_to_user_rectangle (gstate, x1, y1, x2, y2, NULL);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_rectangle_list_t*
+_cairo_gstate_copy_clip_rectangles (cairo_gstate_t *gstate)
+{
+ return _cairo_clip_copy_rectangles (&gstate->clip, gstate);
+}
+
static void
_cairo_gstate_unset_scaled_font (cairo_gstate_t *gstate)
{
diff --git a/src/cairo.c b/src/cairo.c
index 11a3190..1a7da53 100644
--- a/src/cairo.c
+++ b/src/cairo.c
@@ -2209,6 +2209,67 @@ cairo_reset_clip (cairo_t *cr)
}
/**
+ * cairo_clip_extents:
+ * @cr: a cairo context
+ * @x1: left of the resulting extents
+ * @y1: top of the resulting extents
+ * @x2: right of the resulting extents
+ * @y2: bottom of the resulting extents
+ *
+ * Computes a bounding box in user coordinates covering the area inside the
+ * current clip.
+ **/
+void
+cairo_clip_extents (cairo_t *cr,
+ double *x1, double *y1,
+ double *x2, double *y2)
+{
+ if (cr->status)
+ return;
+
+ cr->status = _cairo_gstate_clip_extents (cr->gstate, x1, y1, x2, y2);
+ if (cr->status)
+ _cairo_set_error (cr, cr->status);
+}
+
+static cairo_rectangle_list_t *
+_cairo_rectangle_list_create_for_status (cairo_status_t status)
+{
+ cairo_rectangle_list_t *list;
+
+ list = malloc (sizeof (cairo_rectangle_list_t));
+ if (list == NULL)
+ return (cairo_rectangle_list_t*) &_cairo_rectangles_nil;
+ list->status = status;
+ list->rectangles = NULL;
+ list->num_rectangles = 0;
+ return list;
+}
+
+/**
+ * cairo_copy_clip_rectangles:
+ *
+ * Returns the current clip region as a list of rectangles in user coordinates.
+ * Never returns %NULL.
+ *
+ * The status in the list may be CAIRO_STATUS_CLIP_NOT_REPRESENTABLE to
+ * indicate that the clip region cannot be represented as a list of
+ * user-space rectangles. The status may have other values to indicate
+ * other errors.
+ *
+ * The caller must always call cairo_rectangle_list_destroy on the result of
+ * this function.
+ **/
+cairo_rectangle_list_t *
+cairo_copy_clip_rectangles (cairo_t *cr)
+{
+ if (cr->status)
+ return _cairo_rectangle_list_create_for_status (cr->status);
+
+ return _cairo_gstate_copy_clip_rectangles (cr->gstate);
+}
+
+/**
* cairo_select_font_face:
* @cr: a #cairo_t
* @family: a font family name, encoded in UTF-8
@@ -3097,6 +3158,8 @@ cairo_status_to_string (cairo_status_t s
return "invalid value for a DSC comment";
case CAIRO_STATUS_INVALID_INDEX:
return "invalid index passed to getter";
+ case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE:
+ return "clip region not representable in desired format";
}
return "<unknown error status>";
diff --git a/src/cairo.h b/src/cairo.h
index a0c2f54..7a3b653 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -170,6 +170,7 @@ typedef struct _cairo_user_data_key {
* @CAIRO_STATUS_INVALID_DASH: invalid value for a dash setting
* @CAIRO_STATUS_INVALID_DSC_COMMENT: invalid value for a DSC comment (Since 1.2)
* @CAIRO_STATUS_INVALID_INDEX: invalid index passed to getter
+ * @CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: clip region not representable in desired format (Since 1.4)
*
* #cairo_status_t is used to indicate errors that can occur when
* using Cairo. In some cases it is returned directly by functions.
@@ -201,7 +202,8 @@ typedef enum _cairo_status {
CAIRO_STATUS_FILE_NOT_FOUND,
CAIRO_STATUS_INVALID_DASH,
CAIRO_STATUS_INVALID_DSC_COMMENT,
- CAIRO_STATUS_INVALID_INDEX
+ CAIRO_STATUS_INVALID_INDEX,
+ CAIRO_STATUS_CLIP_NOT_REPRESENTABLE
} cairo_status_t;
/**
@@ -589,6 +591,38 @@ cairo_clip (cairo_t *cr);
cairo_public void
cairo_clip_preserve (cairo_t *cr);
+cairo_public void
+cairo_clip_extents (cairo_t *cr,
+ double *x1, double *y1,
+ double *x2, double *y2);
+
+/**
+ * cairo_rectangle_t:
+ *
+ * A data structure for holding a rectangle.
+ */
+typedef struct _cairo_rectangle {
+ double x, y, width, height;
+} cairo_rectangle_t;
+
+/**
+ * cairo_rectangle_list_t:
+ *
+ * A data structure for holding a dynamically allocated
+ * array of rectangles.
+ */
+typedef struct _cairo_rectangle_list {
+ cairo_status_t status;
+ cairo_rectangle_t *rectangles;
+ int num_rectangles;
+} cairo_rectangle_list_t;
+
+cairo_public cairo_rectangle_list_t *
+cairo_copy_clip_rectangles (cairo_t *cr);
+
+cairo_public void
+cairo_rectangle_list_destroy (cairo_rectangle_list_t *rectangle_list);
+
/* Font/Text functions */
/**
diff --git a/src/cairoint.h b/src/cairoint.h
index 23a7750..6a3bc31 100755
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1339,6 +1339,16 @@ cairo_private cairo_status_t
_cairo_gstate_reset_clip (cairo_gstate_t *gstate);
cairo_private cairo_status_t
+_cairo_gstate_clip_extents (cairo_gstate_t *gstate,
+ double *x1,
+ double *y1,
+ double *x2,
+ double *y2);
+
+cairo_private cairo_rectangle_list_t*
+_cairo_gstate_copy_clip_rectangles (cairo_gstate_t *gstate);
+
+cairo_private cairo_status_t
_cairo_gstate_show_surface (cairo_gstate_t *gstate,
cairo_surface_t *surface,
double x,
diff --git a/test/Makefile.am b/test/Makefile.am
index d1706ac..763203b 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -38,7 +38,9 @@ font-face-get-type \
font-matrix-translation \
glyph-cache-pressure \
get-and-set \
+get-clip \
get-group-target \
+get-path-extents \
gradient-alpha \
leaky-dash \
leaky-polygon \
diff --git a/test/Makefile.win32 b/test/Makefile.win32
index e364de8..c2e7338 100644
--- a/test/Makefile.win32
+++ b/test/Makefile.win32
@@ -37,7 +37,9 @@ font-face-get-type \
font-matrix-translation \
glyph-cache-pressure \
get-and-set \
+get-clip \
get-group-target \
+get-path-extents \
gradient-alpha \
leaky-dash \
leaky-polygon \
diff --git a/test/get-clip.c b/test/get-clip.c
new file mode 100644
index 0000000..cfd8141
--- /dev/null
+++ b/test/get-clip.c
@@ -0,0 +1,277 @@
+/*
+ * Copyright © 2006 Novell, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Novell, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Novell, Inc. makes no representations about the
+ * suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Robert O'Callahan <rocallahan at novell.com>
+ */
+
+#include "cairo-test.h"
+#include <stddef.h>
+
+static cairo_test_draw_function_t draw;
+
+cairo_test_t test = {
+ "get-clip",
+ "Test cairo_copy_clip_rectangles and cairo_clip_extents",
+ 0, 0,
+ draw
+};
+
+static cairo_bool_t
+check_count (const char *message, cairo_bool_t uses_clip_rects,
+ cairo_rectangle_list_t *list, int expected)
+{
+ if (!uses_clip_rects) {
+ if (expected == 0 && list->num_rectangles == 0)
+ return 1;
+ cairo_test_log ("Error: %s; cairo_copy_clip_rectangles unexpectedly got %d rectangles\n",
+ message, list->num_rectangles);
+ return 0;
+ }
+
+ if (list->status != CAIRO_STATUS_SUCCESS) {
+ cairo_test_log ("Error: %s; cairo_copy_clip_rectangles failed with \"%s\"\n",
+ message, cairo_status_to_string(list->status));
+ return 0;
+ }
+
+ if (list->num_rectangles == expected)
+ return 1;
+ cairo_test_log ("Error: %s; expected %d rectangles, got %d\n", message,
+ expected, list->num_rectangles);
+ return 0;
+}
+
+static cairo_bool_t
+check_unrepresentable (const char *message, cairo_rectangle_list_t *list)
+{
+ if (list->status != CAIRO_STATUS_CLIP_NOT_REPRESENTABLE) {
+ cairo_test_log ("Error: %s; cairo_copy_clip_rectangles got unexpected result \"%s\"\n"
+ " (we expected CAIRO_STATUS_CLIP_NOT_REPRESENTABLE)",
+ message, cairo_status_to_string(list->status));
+ return 0;
+ }
+ return 1;
+}
+
+static cairo_bool_t
+check_rectangles_contain (const char *message, cairo_bool_t uses_clip_rects,
+ cairo_rectangle_list_t *list,
+ double x, double y, double width, double height)
+{
+ int i;
+
+ if (!uses_clip_rects)
+ return 1;
+
+ for (i = 0; i < list->num_rectangles; ++i) {
+ if (list->rectangles[i].x == x && list->rectangles[i].y == y &&
+ list->rectangles[i].width == width && list->rectangles[i].height == height)
+ return 1;
+ }
+ cairo_test_log ("Error: %s; rectangle list does not contain rectangle %f,%f,%f,%f\n",
+ message, x, y, width, height);
+ return 0;
+}
+
+static cairo_bool_t
+check_clip_extents (const char *message, cairo_t *cr,
+ double x, double y, double width, double height)
+{
+ double ext_x1, ext_y1, ext_x2, ext_y2;
+ cairo_clip_extents (cr, &ext_x1, &ext_y1, &ext_x2, &ext_y2);
+ if (ext_x1 == x && ext_y1 == y && ext_x2 == x + width && ext_y2 == y + height)
+ return 1;
+ cairo_test_log ("Error: %s; clip extents %f,%f,%f,%f should be %f,%f,%f,%f\n",
+ message, ext_x1, ext_y1, ext_x2 - ext_x1, ext_y2 - ext_y1,
+ x, y, width, height);
+ return 0;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+ cairo_surface_t *surface;
+ cairo_t *cr2;
+ cairo_rectangle_list_t *rectangle_list;
+ const char *phase;
+ cairo_bool_t uses_clip_rects;
+
+ surface = cairo_surface_create_similar (cairo_get_target (cr),
+ CAIRO_CONTENT_COLOR, 100, 100);
+ /* don't use cr accidentally */
+ cr = NULL;
+ cr2 = cairo_create (surface);
+ cairo_surface_destroy (surface);
+
+ /* check the surface type so we ignore cairo_copy_clip_rectangles failures
+ on surface types that don't use rectangle lists for clipping */
+ switch (cairo_surface_get_type (surface)) {
+ case CAIRO_SURFACE_TYPE_PDF:
+ case CAIRO_SURFACE_TYPE_PS:
+ case CAIRO_SURFACE_TYPE_SVG:
+ uses_clip_rects = 0;
+ break;
+ case CAIRO_SURFACE_TYPE_IMAGE:
+ case CAIRO_SURFACE_TYPE_XLIB:
+ case CAIRO_SURFACE_TYPE_XCB:
+ case CAIRO_SURFACE_TYPE_GLITZ:
+ case CAIRO_SURFACE_TYPE_QUARTZ:
+ case CAIRO_SURFACE_TYPE_WIN32:
+ case CAIRO_SURFACE_TYPE_BEOS:
+ case CAIRO_SURFACE_TYPE_DIRECTFB:
+ default:
+ uses_clip_rects = 1;
+ break;
+ }
+
+ /* first, test basic stuff. This should not be clipped, it should
+ return the surface rectangle. */
+ phase = "No clip set";
+ rectangle_list = cairo_copy_clip_rectangles (cr2);
+ if (!check_count (phase, uses_clip_rects, rectangle_list, 1) ||
+ !check_clip_extents (phase, cr2, 0, 0, 100, 100) ||
+ !check_rectangles_contain(phase, uses_clip_rects, rectangle_list, 0, 0, 100, 100)) {
+ cairo_rectangle_list_destroy (rectangle_list);
+ return CAIRO_TEST_FAILURE;
+ }
+ cairo_rectangle_list_destroy (rectangle_list);
+
+ /* Test simple clip rect. */
+ phase = "Simple clip rect";
+ cairo_save (cr2);
+ cairo_rectangle (cr2, 10, 10, 80, 80);
+ cairo_clip (cr2);
+ rectangle_list = cairo_copy_clip_rectangles (cr2);
+ if (!check_count (phase, uses_clip_rects, rectangle_list, 1) ||
+ !check_clip_extents (phase, cr2, 10, 10, 80, 80) ||
+ !check_rectangles_contain(phase, uses_clip_rects, rectangle_list, 10, 10, 80, 80)) {
+ cairo_rectangle_list_destroy (rectangle_list);
+ return CAIRO_TEST_FAILURE;
+ }
+ cairo_rectangle_list_destroy (rectangle_list);
+ cairo_restore (cr2);
+
+ /* Test everything clipped out. */
+ phase = "All clipped out";
+ cairo_save (cr2);
+ cairo_clip (cr2);
+ rectangle_list = cairo_copy_clip_rectangles (cr2);
+ if (!check_count (phase, uses_clip_rects, rectangle_list, 0)) {
+ cairo_rectangle_list_destroy (rectangle_list);
+ return CAIRO_TEST_FAILURE;
+ }
+ cairo_rectangle_list_destroy (rectangle_list);
+ cairo_restore (cr2);
+
+ /* test two clip rects */
+ phase = "Two clip rects";
+ cairo_save (cr2);
+ cairo_rectangle (cr2, 10, 10, 10, 10);
+ cairo_rectangle (cr2, 20, 20, 10, 10);
+ cairo_clip (cr2);
+ cairo_rectangle (cr2, 15, 15, 10, 10);
+ cairo_clip (cr2);
+ rectangle_list = cairo_copy_clip_rectangles (cr2);
+ if (!check_count (phase, uses_clip_rects, rectangle_list, 2) ||
+ !check_clip_extents (phase, cr2, 15, 15, 10, 10) ||
+ !check_rectangles_contain(phase, uses_clip_rects, rectangle_list, 15, 15, 5, 5) ||
+ !check_rectangles_contain(phase, uses_clip_rects, rectangle_list, 20, 20, 5, 5)) {
+ cairo_rectangle_list_destroy (rectangle_list);
+ return CAIRO_TEST_FAILURE;
+ }
+ cairo_rectangle_list_destroy (rectangle_list);
+ cairo_restore (cr2);
+
+ /* test non-rectangular clip */
+ phase = "Nonrectangular clip";
+ cairo_save (cr2);
+ cairo_move_to (cr2, 0, 0);
+ cairo_line_to (cr2, 100, 100);
+ cairo_line_to (cr2, 100, 0);
+ cairo_close_path (cr2);
+ cairo_clip (cr2);
+ rectangle_list = cairo_copy_clip_rectangles (cr2);
+ /* can't get this in one tight user-space rectangle */
+ if (!check_unrepresentable (phase, rectangle_list) ||
+ !check_clip_extents (phase, cr2, 0, 0, 100, 100)) {
+ cairo_rectangle_list_destroy (rectangle_list);
+ return CAIRO_TEST_FAILURE;
+ }
+ cairo_rectangle_list_destroy (rectangle_list);
+ cairo_restore (cr2);
+
+ phase = "User space, simple scale, getting clip with same transform";
+ cairo_save (cr2);
+ cairo_scale (cr2, 2, 2);
+ cairo_rectangle (cr2, 5, 5, 40, 40);
+ cairo_clip (cr2);
+ rectangle_list = cairo_copy_clip_rectangles (cr2);
+ if (!check_count (phase, uses_clip_rects, rectangle_list, 1) ||
+ !check_clip_extents (phase, cr2, 5, 5, 40, 40) ||
+ !check_rectangles_contain(phase, uses_clip_rects, rectangle_list, 5, 5, 40, 40)) {
+ cairo_rectangle_list_destroy (rectangle_list);
+ return CAIRO_TEST_FAILURE;
+ }
+ cairo_rectangle_list_destroy (rectangle_list);
+ cairo_restore (cr2);
+
+ phase = "User space, simple scale, getting clip with no transform";
+ cairo_save (cr2);
+ cairo_save (cr2);
+ cairo_scale (cr2, 2, 2);
+ cairo_rectangle (cr2, 5, 5, 40, 40);
+ cairo_restore (cr2);
+ cairo_clip (cr2);
+ rectangle_list = cairo_copy_clip_rectangles (cr2);
+ if (!check_count (phase, uses_clip_rects, rectangle_list, 1) ||
+ !check_clip_extents (phase, cr2, 10, 10, 80, 80) ||
+ !check_rectangles_contain(phase, uses_clip_rects, rectangle_list, 10, 10, 80, 80)) {
+ cairo_rectangle_list_destroy (rectangle_list);
+ return CAIRO_TEST_FAILURE;
+ }
+ cairo_rectangle_list_destroy (rectangle_list);
+ cairo_restore (cr2);
+
+ phase = "User space, rotation, getting clip with no transform";
+ cairo_save (cr2);
+ cairo_save (cr2);
+ cairo_rotate (cr2, 12);
+ cairo_rectangle (cr2, 5, 5, 40, 40);
+ cairo_restore (cr2);
+ cairo_clip (cr2);
+ rectangle_list = cairo_copy_clip_rectangles (cr2);
+ if (!check_unrepresentable (phase, rectangle_list)) {
+ cairo_rectangle_list_destroy (rectangle_list);
+ return CAIRO_TEST_FAILURE;
+ }
+ cairo_rectangle_list_destroy (rectangle_list);
+ cairo_restore (cr2);
+
+ cairo_destroy (cr2);
+ return CAIRO_TEST_SUCCESS;
+}
+
+int
+main (void)
+{
+ return cairo_test (&test);
+}
diff --git a/test/get-path-extents.c b/test/get-path-extents.c
new file mode 100644
index 0000000..e755f84
--- /dev/null
+++ b/test/get-path-extents.c
@@ -0,0 +1,199 @@
+/*
+ * Copyright © 2006 Novell, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Novell, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Novell, Inc. makes no representations about the
+ * suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Robert O'Callahan <rocallahan at novell.com>
+ */
+
+#include "cairo-test.h"
+#include <stddef.h>
+#include <math.h>
+
+static cairo_test_draw_function_t draw;
+
+cairo_test_t test = {
+ "get-path-extents",
+ "Test cairo_fill_extents and cairo_stroke_extents",
+ 0, 0,
+ draw
+};
+
+enum ExtentsType { FILL, STROKE };
+
+enum Relation { EQUALS, CONTAINS };
+
+static cairo_bool_t
+check_extents (const char *message, cairo_t *cr, enum ExtentsType type,
+ enum Relation relation,
+ double x, double y, double width, double height)
+{
+ double ext_x1, ext_y1, ext_x2, ext_y2;
+ const char *type_string;
+ const char *relation_string;
+
+ switch (type) {
+ default:
+ case FILL:
+ type_string = "fill";
+ cairo_fill_extents (cr, &ext_x1, &ext_y1, &ext_x2, &ext_y2);
+ break;
+ case STROKE:
+ type_string = "stroke";
+ cairo_stroke_extents (cr, &ext_x1, &ext_y1, &ext_x2, &ext_y2);
+ break;
+ }
+
+ /* let empty rects match */
+ if ((ext_x1 == ext_x2 || ext_y1 == ext_y2) && (width == 0 || height == 0))
+ return 1;
+
+ switch (relation) {
+ default:
+ case EQUALS:
+ relation_string = "equal";
+ if (ext_x1 == x && ext_y1 == y && ext_x2 == x + width && ext_y2 == y + height)
+ return 1;
+ break;
+ case CONTAINS:
+ relation_string = "contain";
+ if (width == 0 || height == 0) {
+ /* odd test that doesn't really test anything... */
+ return 1;
+ }
+ if (ext_x1 <= x && ext_y1 <= y && ext_x2 >= x + width && ext_y2 >= y + height)
+ return 1;
+ break;
+ }
+
+ cairo_test_log ("Error: %s; %s extents %f,%f,%f,%f should %s %f,%f,%f,%f\n",
+ message, type_string,
+ ext_x1, ext_y1, ext_x2 - ext_x1, ext_y2 - ext_y1,
+ relation_string,
+ x, y, width, height);
+ return 0;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+ cairo_surface_t *surface;
+ cairo_t *cr2;
+ const char *phase;
+
+ surface = cairo_surface_create_similar (cairo_get_target (cr),
+ CAIRO_CONTENT_COLOR, 100, 100);
+ /* don't use cr accidentally */
+ cr = NULL;
+ cr2 = cairo_create (surface);
+ cairo_surface_destroy (surface);
+
+ cairo_set_line_width (cr2, 10);
+ cairo_set_line_join (cr2, CAIRO_LINE_JOIN_MITER);
+ cairo_set_miter_limit (cr2, 100);
+
+ phase = "No path";
+ if (!check_extents (phase, cr2, FILL, EQUALS, 0, 0, 0, 0) ||
+ !check_extents (phase, cr2, STROKE, EQUALS, 0, 0, 0, 0))
+ return CAIRO_TEST_FAILURE;
+
+ phase = "Simple rect";
+ cairo_save (cr2);
+ cairo_rectangle (cr2, 10, 10, 80, 80);
+ if (!check_extents (phase, cr2, FILL, EQUALS, 10, 10, 80, 80) ||
+ !check_extents (phase, cr2, STROKE, EQUALS, 5, 5, 90, 90))
+ return CAIRO_TEST_FAILURE;
+ cairo_new_path (cr2);
+ cairo_restore (cr2);
+
+ phase = "Two rects";
+ cairo_save (cr2);
+ cairo_rectangle (cr2, 10, 10, 10, 10);
+ cairo_rectangle (cr2, 20, 20, 10, 10);
+ if (!check_extents (phase, cr2, FILL, EQUALS, 10, 10, 20, 20) ||
+ !check_extents (phase, cr2, STROKE, EQUALS, 5, 5, 30, 30))
+ return CAIRO_TEST_FAILURE;
+ cairo_new_path (cr2);
+ cairo_restore (cr2);
+
+ phase = "Triangle";
+ cairo_save (cr2);
+ cairo_move_to (cr2, 10, 10);
+ cairo_line_to (cr2, 90, 90);
+ cairo_line_to (cr2, 90, 10);
+ cairo_close_path (cr2);
+ /* miter joins protrude 5*(1+sqrt(2)) above the top-left corner and to
+ the right of the bottom-right corner */
+ if (!check_extents (phase, cr2, FILL, EQUALS, 10, 10, 80, 80) ||
+ !check_extents (phase, cr2, STROKE, CONTAINS, 0, 5, 95, 95))
+ return CAIRO_TEST_FAILURE;
+ cairo_new_path (cr2);
+ cairo_restore (cr2);
+
+ phase = "User space, simple scale, getting extents with same transform";
+ cairo_save (cr2);
+ cairo_scale (cr2, 2, 2);
+ cairo_rectangle (cr2, 5, 5, 40, 40);
+ if (!check_extents (phase, cr2, FILL, EQUALS, 5, 5, 40, 40) ||
+ !check_extents (phase, cr2, STROKE, EQUALS, 0, 0, 50, 50))
+ return CAIRO_TEST_FAILURE;
+ cairo_new_path (cr2);
+ cairo_restore (cr2);
+
+ phase = "User space, simple scale, getting extents with no transform";
+ cairo_save (cr2);
+ cairo_save (cr2);
+ cairo_scale (cr2, 2, 2);
+ cairo_rectangle (cr2, 5, 5, 40, 40);
+ cairo_restore (cr2);
+ if (!check_extents (phase, cr2, FILL, EQUALS, 10, 10, 80, 80) ||
+ !check_extents (phase, cr2, STROKE, EQUALS, 5, 5, 90, 90))
+ return CAIRO_TEST_FAILURE;
+ cairo_new_path (cr2);
+ cairo_restore (cr2);
+
+ phase = "User space, rotation, getting extents with transform";
+ cairo_save (cr2);
+ cairo_rectangle (cr2, -50, -50, 50, 50);
+ cairo_rotate (cr2, -M_PI/4);
+ /* the path in user space is now (nearly) the square rotated by
+ 45 degrees about the origin. Thus its x1 and x2 are both nearly 0.
+ This should show any bugs where we just transform device-space
+ x1,y1 and x2,y2 to get the extents. */
+ /* The largest axis-aligned square inside the rotated path has
+ side lengths 50*sqrt(2), so a bit over 35 on either side of
+ the axes. With the stroke width added to the rotated path,
+ the largest axis-aligned square is a bit over 38 on either side of
+ the axes. */
+ if (!check_extents (phase, cr2, FILL, CONTAINS, -35, -35, 35, 35) ||
+ !check_extents (phase, cr2, STROKE, CONTAINS, -38, -38, 38, 38))
+ return CAIRO_TEST_FAILURE;
+ cairo_new_path (cr2);
+ cairo_restore (cr2);
+
+ cairo_destroy (cr2);
+ return CAIRO_TEST_SUCCESS;
+}
+
+int
+main (void)
+{
+ return cairo_test (&test);
+}
diff-tree a8ca155f83098c02fb8d3acc57b0492d5b753d54 (from 37fa632e59b7325041f689bf1a56e08d04379c96)
Author: Robert O'Callahan <robert at ocallahan.org>
Date: Mon Sep 25 23:16:54 2006 -0700
Fix stroke/fill extents bounding boxes
Correctly return the transformed bounding box for stroke/fill extents,
instead of just transforming the two corners separately.
diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c
index 832c520..7fee690 100644
--- a/src/cairo-gstate.c
+++ b/src/cairo-gstate.c
@@ -706,6 +706,25 @@ _cairo_gstate_backend_to_user (cairo_gst
cairo_matrix_transform_point (&gstate->ctm_inverse, x, y);
}
+void
+_cairo_gstate_backend_to_user_rectangle (cairo_gstate_t *gstate,
+ double *x1, double *y1,
+ double *x2, double *y2,
+ cairo_bool_t *is_tight)
+{
+ double width = *x2 - *x1;
+ double height = *y2 - *y1;
+ cairo_matrix_t matrix_inverse;
+
+ cairo_matrix_multiply (&matrix_inverse, &gstate->ctm_inverse,
+ &gstate->target->device_transform_inverse);
+ _cairo_matrix_transform_bounding_box (
+ &matrix_inverse, x1, y1, &width, &height, is_tight);
+
+ *x2 = *x1 + width;
+ *y2 = *y1 + height;
+}
+
/* XXX: NYI
cairo_status_t
_cairo_gstate_stroke_to_path (cairo_gstate_t *gstate)
@@ -1041,6 +1060,29 @@ _cairo_gstate_show_page (cairo_gstate_t
return status;
}
+static void
+_cairo_gstate_traps_extents_to_user_rectangle (cairo_gstate_t *gstate,
+ cairo_traps_t *traps,
+ double *x1, double *y1,
+ double *x2, double *y2)
+{
+ cairo_box_t extents;
+
+ _cairo_traps_extents (traps, &extents);
+
+ if (extents.p1.x >= extents.p2.x || extents.p1.y >= extents.p2.y) {
+ /* no traps, so we actually won't draw anything */
+ *x1 = *y1 = *x2 = *y2 = 0;
+ } else {
+ *x1 = _cairo_fixed_to_double (extents.p1.x);
+ *y1 = _cairo_fixed_to_double (extents.p1.y);
+ *x2 = _cairo_fixed_to_double (extents.p2.x);
+ *y2 = _cairo_fixed_to_double (extents.p2.y);
+
+ _cairo_gstate_backend_to_user_rectangle (gstate, x1, y1, x2, y2, NULL);
+ }
+}
+
cairo_status_t
_cairo_gstate_stroke_extents (cairo_gstate_t *gstate,
cairo_path_fixed_t *path,
@@ -1049,7 +1091,6 @@ _cairo_gstate_stroke_extents (cairo_gsta
{
cairo_status_t status;
cairo_traps_t traps;
- cairo_box_t extents;
_cairo_traps_init (&traps);
@@ -1059,20 +1100,10 @@ _cairo_gstate_stroke_extents (cairo_gsta
&gstate->ctm_inverse,
gstate->tolerance,
&traps);
- if (status)
- goto BAIL;
-
- _cairo_traps_extents (&traps, &extents);
-
- *x1 = _cairo_fixed_to_double (extents.p1.x);
- *y1 = _cairo_fixed_to_double (extents.p1.y);
- *x2 = _cairo_fixed_to_double (extents.p2.x);
- *y2 = _cairo_fixed_to_double (extents.p2.y);
-
- _cairo_gstate_backend_to_user (gstate, x1, y1);
- _cairo_gstate_backend_to_user (gstate, x2, y2);
+ if (status == CAIRO_STATUS_SUCCESS) {
+ _cairo_gstate_traps_extents_to_user_rectangle(gstate, &traps, x1, y1, x2, y2);
+ }
-BAIL:
_cairo_traps_fini (&traps);
return status;
@@ -1094,20 +1125,10 @@ _cairo_gstate_fill_extents (cairo_gstate
gstate->fill_rule,
gstate->tolerance,
&traps);
- if (status)
- goto BAIL;
-
- _cairo_traps_extents (&traps, &extents);
-
- *x1 = _cairo_fixed_to_double (extents.p1.x);
- *y1 = _cairo_fixed_to_double (extents.p1.y);
- *x2 = _cairo_fixed_to_double (extents.p2.x);
- *y2 = _cairo_fixed_to_double (extents.p2.y);
-
- _cairo_gstate_backend_to_user (gstate, x1, y1);
- _cairo_gstate_backend_to_user (gstate, x2, y2);
+ if (status == CAIRO_STATUS_SUCCESS) {
+ _cairo_gstate_traps_extents_to_user_rectangle(gstate, &traps, x1, y1, x2, y2);
+ }
-BAIL:
_cairo_traps_fini (&traps);
return status;
diff --git a/src/cairo.c b/src/cairo.c
index 0eb75cc..11a3190 100644
--- a/src/cairo.c
+++ b/src/cairo.c
@@ -2063,6 +2063,19 @@ cairo_in_fill (cairo_t *cr, double x, do
return inside;
}
+/**
+ * cairo_stroke_extents:
+ * @cr: a cairo context
+ * @x1: left of the resulting extents
+ * @y1: top of the resulting extents
+ * @x2: right of the resulting extents
+ * @y2: bottom of the resulting extents
+ *
+ * Computes a bounding box in user coordinates covering all area that will
+ * be stroked by the current path with the current stroking parameters. If
+ * the current path is empty, returns an empty rectangle. Surface dimensions
+ * and clipping are not taken into account.
+ **/
void
cairo_stroke_extents (cairo_t *cr,
double *x1, double *y1, double *x2, double *y2)
@@ -2077,6 +2090,19 @@ cairo_stroke_extents (cairo_t *cr,
_cairo_set_error (cr, cr->status);
}
+/**
+ * cairo_fill_extents:
+ * @cr: a cairo context
+ * @x1: left of the resulting extents
+ * @y1: top of the resulting extents
+ * @x2: right of the resulting extents
+ * @y2: bottom of the resulting extents
+ *
+ * Computes a bounding box in user coordinates covering all area that will
+ * be filled by the current path. If the current path is empty, returns an
+ * empty rectangle. Surface dimensions and clipping are not taken into
+ * account.
+ **/
void
cairo_fill_extents (cairo_t *cr,
double *x1, double *y1, double *x2, double *y2)
diff --git a/src/cairoint.h b/src/cairoint.h
index 67227dc..23a7750 100755
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1281,6 +1281,12 @@ _cairo_gstate_user_to_backend (cairo_gst
cairo_private void
_cairo_gstate_backend_to_user (cairo_gstate_t *gstate, double *x, double *y);
+cairo_private void
+_cairo_gstate_backend_to_user_rectangle (cairo_gstate_t *gstate,
+ double *x1, double *y1,
+ double *x2, double *y2,
+ cairo_bool_t *is_tight);
+
cairo_private cairo_status_t
_cairo_gstate_paint (cairo_gstate_t *gstate);
diff-tree 37fa632e59b7325041f689bf1a56e08d04379c96 (from de1915ffd2fe7f973529104a1041b33f2abfdfed)
Author: Robert O'Callahan <robert at ocallahan.org>
Date: Mon Sep 25 23:14:43 2006 -0700
Fix _cairo_matrix_transform_bounding_box to return tightness info
Add return is_tight value to the internal function, indicating whether
the transformed bounds still remain axis-aligned.
diff --git a/src/cairo-matrix.c b/src/cairo-matrix.c
index 60cdca3..339079f 100644
--- a/src/cairo-matrix.c
+++ b/src/cairo-matrix.c
@@ -358,7 +358,8 @@ slim_hidden_def(cairo_matrix_transform_p
void
_cairo_matrix_transform_bounding_box (const cairo_matrix_t *matrix,
double *x, double *y,
- double *width, double *height)
+ double *width, double *height,
+ cairo_bool_t *is_tight)
{
int i;
double quad_x[4], quad_y[4];
@@ -405,6 +406,21 @@ _cairo_matrix_transform_bounding_box (co
*y = min_y;
*width = max_x - min_x;
*height = max_y - min_y;
+
+ if (is_tight) {
+ /* it's tight if and only if the four corner points form an axis-aligned
+ rectangle.
+ And that's true if and only if we can derive corners 0 and 3 from
+ corners 1 and 2 in one of two straightforward ways...
+ We could use a tolerance here but for now we'll fall back to FALSE in the case
+ of floating point error.
+ */
+ *is_tight =
+ (quad_x[1] == quad_x[0] && quad_y[1] == quad_y[3] &&
+ quad_x[2] == quad_x[3] && quad_y[2] == quad_y[0]) ||
+ (quad_x[1] == quad_x[3] && quad_y[1] == quad_y[0] &&
+ quad_x[2] == quad_x[0] && quad_y[2] == quad_y[3]);
+ }
}
static void
diff --git a/src/cairoint.h b/src/cairoint.h
index 2d1d455..67227dc 100755
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -2140,7 +2140,8 @@ _cairo_matrix_get_affine (const cairo_ma
cairo_private void
_cairo_matrix_transform_bounding_box (const cairo_matrix_t *matrix,
double *x, double *y,
- double *width, double *height);
+ double *width, double *height,
+ cairo_bool_t *is_tight);
cairo_private void
_cairo_matrix_compute_determinant (const cairo_matrix_t *matrix, double *det);
More information about the cairo-commit
mailing list