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.
This commit is contained in:
Robert O'Callahan 2006-09-25 23:22:45 -07:00 committed by Vladimir Vukicevic
parent a8ca155f83
commit 191e108b93
10 changed files with 753 additions and 2 deletions

View file

@ -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 *clip,
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 */

View file

@ -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 (cairo_clip_t *clip,
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 *clip,
}
}
}
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);
}

View file

@ -1148,6 +1148,40 @@ _cairo_gstate_clip (cairo_gstate_t *gstate, cairo_path_fixed_t *path)
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)
{

View file

@ -2208,6 +2208,67 @@ cairo_reset_clip (cairo_t *cr)
_cairo_set_error (cr, cr->status);
}
/**
* 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
@ -3097,6 +3158,8 @@ cairo_status_to_string (cairo_status_t status)
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>";

View file

@ -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 */
/**

View file

@ -1338,6 +1338,16 @@ _cairo_gstate_clip (cairo_gstate_t *gstate, cairo_path_fixed_t *path);
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,

View file

@ -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 \

View file

@ -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 \

277
test/get-clip.c Normal file
View file

@ -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@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);
}

199
test/get-path-extents.c Normal file
View file

@ -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@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);
}