diff --git a/doc/pycairo_c_api.rst b/doc/pycairo_c_api.rst index 5f2becd..d84a762 100644 --- a/doc/pycairo_c_api.rst +++ b/doc/pycairo_c_api.rst @@ -46,6 +46,8 @@ Objects:: PycairoMatrix PycairoPath PycairoPattern + PycairoRegion + PycairoRectangleInt PycairoSolidPattern PycairoSurfacePattern PycairoGradient @@ -74,6 +76,8 @@ Types:: PyTypeObject *Matrix_Type; PyTypeObject *Path_Type; PyTypeObject *Pattern_Type; + PyTypeObject *Region_Type; + PyTypeObject *RectangleInt_Type; PyTypeObject *SolidPattern_Type; PyTypeObject *SurfacePattern_Type; PyTypeObject *Gradient_Type; @@ -117,6 +121,12 @@ Functions .. c:function:: PyObject * PycairoPattern_FromPattern(cairo_pattern_t *pattern, PyObject *base) +.. c:function:: PyObject * PycairoRegion_FromRegion(const cairo_region_t *region) + + +.. c:function:: PyObject * PycairoRectangleInt_FromRectangleInt(const cairo_rectangle_int_t *rectangle_int) + + .. c:function:: PyObject * PycairoScaledFont_FromScaledFont(cairo_scaled_font_t *scaled_font) diff --git a/doc/reference/index.rst b/doc/reference/index.rst index 2b1b9ac..81787cc 100644 --- a/doc/reference/index.rst +++ b/doc/reference/index.rst @@ -15,5 +15,6 @@ Reference matrix paths patterns + region surfaces text diff --git a/doc/reference/region.rst b/doc/reference/region.rst new file mode 100644 index 0000000..3a815e7 --- /dev/null +++ b/doc/reference/region.rst @@ -0,0 +1,53 @@ +.. _region: + +****** +Region +****** +Region — Representing a pixel-aligned area + +.. currentmodule:: cairo + + +class Region() +============== +*Region* is a simple graphical data type representing an area of +integer-aligned rectangles. They are often used on raster surfaces to track +areas of interest, such as change or clip areas. + + +.. class:: Region([rectangle_int|rectangle_ints]) + + :param rectangle_int: a rectangle or a list of rectangle + :type rectangle_int: :class:`RectangleInt` or [:class:`RectangleInt`] + + Allocates a new empty region object or a region object with the containing + rectangle(s). + + + .. method:: copy() + + :returns: A newly allocated :class:`Region`. + :raises: :exc:`NoMemory` if memory cannot be allocated. + + Allocates a new *Region* object copying the area from original. + +class RectangleInt() +==================== +*RectangleInt* is a data structure for holding a rectangle with integer +coordinates. + + +.. class:: RectangleInt(x=0, y=0, width=0, height=0) + + :param x: X coordinate of the left side of the rectangle + :type x: int + :param y: Y coordinate of the the top side of the rectangle + :type y: int + :param width: width of the rectangle + :type width: int + :param height: height of the rectangle + :type height: int + + Allocates a new *RectangleInt* object. + + diff --git a/setup.py b/setup.py index 808e34a..7fb4241 100755 --- a/setup.py +++ b/setup.py @@ -76,7 +76,7 @@ def createConfigFile(ConfigFile): if sys.version_info < python_version_required: - raise SystemExit('Error: Python >= %s is required' %s (python_version_required,)) + raise SystemExit('Error: Python >= %s is required' % (python_version_required,)) pkg_config_version_check ('cairo', cairo_version_required) if sys.platform == 'win32': @@ -96,6 +96,7 @@ cairo = dic.Extension( 'src/matrix.c', 'src/path.c', 'src/pattern.c', + 'src/region.c', 'src/surface.c', ], include_dirs = pkg_config_parse('--cflags-only-I', 'cairo'), diff --git a/src/cairomodule.c b/src/cairomodule.c index 886c6d5..907f113 100644 --- a/src/cairomodule.c +++ b/src/cairomodule.c @@ -147,6 +147,12 @@ static Pycairo_CAPI_t CAPI = { PycairoSurface_FromSurface, Pycairo_Check_Status, + + &PycairoRectangleInt_Type, + PycairoRectangleInt_FromRectangleInt, + + &PycairoRegion_Type, + PycairoRegion_FromRegion, }; static PyObject * @@ -236,6 +242,12 @@ PyInit__cairo(void) if (PyType_Ready(&PycairoRadialGradient_Type) < 0) return NULL; + if (PyType_Ready(&PycairoRectangleInt_Type) < 0) + return NULL; + + if (PyType_Ready(&PycairoRegion_Type) < 0) + return NULL; + if (PyType_Ready(&PycairoScaledFont_Type) < 0) return NULL; @@ -325,6 +337,12 @@ PyInit__cairo(void) PyModule_AddObject(m, "RadialGradient", (PyObject *)&PycairoRadialGradient_Type); + Py_INCREF(&PycairoRectangleInt_Type); + PyModule_AddObject(m, "RectangleInt", (PyObject *)&PycairoRectangleInt_Type); + + Py_INCREF(&PycairoRegion_Type); + PyModule_AddObject(m, "Region", (PyObject *)&PycairoRegion_Type); + Py_INCREF(&PycairoScaledFont_Type); PyModule_AddObject(m, "ScaledFont", (PyObject *)&PycairoScaledFont_Type); @@ -535,6 +553,10 @@ PyInit__cairo(void) CONSTANT(PATH_CURVE_TO); CONSTANT(PATH_CLOSE_PATH); + CONSTANT(REGION_OVERLAP_IN); + CONSTANT(REGION_OVERLAP_OUT); + CONSTANT(REGION_OVERLAP_PART); + #ifdef CAIRO_HAS_PS_SURFACE CONSTANT(PS_LEVEL_2); CONSTANT(PS_LEVEL_3); diff --git a/src/private.h b/src/private.h index 197f935..a16730a 100644 --- a/src/private.h +++ b/src/private.h @@ -62,6 +62,13 @@ extern PyTypeObject PycairoRadialGradient_Type; PyObject *PycairoPattern_FromPattern (cairo_pattern_t *pattern, PyObject *base); +extern PyTypeObject PycairoRectangleInt_Type; +PyObject *PycairoRectangleInt_FromRectangleInt ( + cairo_rectangle_int_t *rectangle_int); + +extern PyTypeObject PycairoRegion_Type; +PyObject *PycairoRegion_FromRegion (cairo_region_t *region); + extern PyTypeObject PycairoScaledFont_Type; PyObject *PycairoScaledFont_FromScaledFont (cairo_scaled_font_t *scaled_font); @@ -134,6 +141,15 @@ int Pycairo_Check_Status (cairo_status_t status); } \ } while (0) +#define RETURN_NULL_IF_CAIRO_REGION_ERROR(region) \ + do { \ + cairo_status_t status = cairo_region_status (region); \ + if (status != CAIRO_STATUS_SUCCESS) { \ + Pycairo_Check_Status (status); \ + return NULL; \ + } \ + } while (0) + #define RETURN_NULL_IF_CAIRO_SURFACE_ERROR(surface) \ do { \ cairo_status_t status = cairo_surface_status (surface); \ diff --git a/src/py3cairo.h b/src/py3cairo.h index 853c1d8..41e9f8c 100644 --- a/src/py3cairo.h +++ b/src/py3cairo.h @@ -60,6 +60,16 @@ typedef struct { PyObject *base; /* base object used to create pattern, or NULL */ } PycairoPattern; +typedef struct { + PyObject_HEAD + cairo_rectangle_int_t rectangle_int; +} PycairoRectangleInt; + +typedef struct { + PyObject_HEAD + cairo_region_t *region; +} PycairoRegion; + #define PycairoSolidPattern PycairoPattern #define PycairoSurfacePattern PycairoPattern #define PycairoGradient PycairoPattern @@ -133,6 +143,14 @@ typedef struct { /* misc functions */ int (*Check_Status)(cairo_status_t status); + + PyTypeObject *RectangleInt_Type; + PyObject *(*RectangleInt_FromRectangleInt)( + const cairo_rectangle_int_t *rectangle_int); + + PyTypeObject *Region_Type; + PyObject *(*Region_FromRegion)(const cairo_region_t *region); + } Pycairo_CAPI_t; @@ -160,6 +178,13 @@ typedef struct { #define PycairoRadialGradient_Type *(Pycairo_CAPI->RadialGradient_Type) #define PycairoPattern_FromPattern (Pycairo_CAPI->Pattern_FromPattern) +#define PycairoRectangleInt_Type *(Pycairo_CAPI->RectangleInt_Type) +#define PycairoRectangleInt_FromRectangleInt \ + (Pycairo_CAPI->RectangleInt_FromRectangleInt) + +#define PycairoRegion_Type *(Pycairo_CAPI->Region_Type) +#define PycairoRegion_FromRegion (Pycairo_CAPI->Region_FromRegion) + #define PycairoScaledFont_Type *(Pycairo_CAPI->ScaledFont_Type) #define PycairoScaledFont_FromScaledFont \ (Pycairo_CAPI->ScaledFont_FromScaledFont) diff --git a/src/region.c b/src/region.c new file mode 100644 index 0000000..3b0d34c --- /dev/null +++ b/src/region.c @@ -0,0 +1,603 @@ +/* -*- mode: C; c-basic-offset: 2 -*- + * + * Copyright © 2005,2010 Steve Chaplin + * + * This file is part of pycairo. + * + * Pycairo is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3 as published + * by the Free Software Foundation. + * + * Pycairo is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with pycairo. If not, see . + */ + +#define PY_SSIZE_T_CLEAN +#include +#include "structmember.h" + +#include "config.h" +#include "private.h" + +/* PycairoRectangleInt_FromRectangleInt + * Create a new PycairoRectangleInt from a cairo_rectangle_int_t + * rectangle_int - a cairo_rectangle_int_t to 'wrap' into a Python object. + * rectangle_int is unreferenced if the PycairoRectangleInt creation + * fails. + * Return value: New reference or NULL on failure + */ +PyObject * +PycairoRectangleInt_FromRectangleInt (cairo_rectangle_int_t *rectangle_int) { + PyObject *o; + + assert (rectangle_int != NULL); + + o = PycairoRectangleInt_Type.tp_alloc (&PycairoRectangleInt_Type, 0); + if (o) + ((PycairoRectangleInt *)o)->rectangle_int = *rectangle_int; + return o; +} + +static void +rectangle_int_dealloc(PycairoRectangleInt *o) { +#ifdef DEBUG + printf("rectangle_int_dealloc start\n"); +#endif + //o->ob_type->tp_free((PyObject *)o); + Py_TYPE(o)->tp_free(o); +#ifdef DEBUG + printf("rectangle_int_dealloc end\n"); +#endif +} + +static PyObject * +rectangle_int_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { + static char *kwlist[] = { "x", "y", "width", "height", NULL }; + int x, y, w, h; + x = y = w = h = 0; + cairo_rectangle_int_t rect; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, + "|iiii:RectangleInt.__new__", kwlist, + &x, &y, &w, &h)) + return NULL; + + rect.x = x; + rect.y = y; + rect.width = w; + rect.height = h; + + return PycairoRectangleInt_FromRectangleInt(&rect); +} + +static PyObject * +rectangle_int_str(PycairoRectangleInt *rect_o) { + PyObject *s; + cairo_rectangle_int_t *rect = &(rect_o->rectangle_int); + char buf[80]; + PyOS_snprintf(buf, sizeof(buf), "cairo.RectangleInt(%d, %d, %d, %d)", + rect->x, rect->y, rect->width, rect->height); + s = PyUnicode_FromString(buf); + return s; +} + +static PyObject * +rectangle_int_richcompare(PycairoRectangleInt *self, + PycairoRectangleInt *other, int op) { + int res = 0; + PyObject *b; + + if (op != Py_EQ && op != Py_NE) { + PyErr_SetString(PyExc_TypeError, "Only support testing for == or !="); + return NULL; + } + if (!PyObject_IsInstance((PyObject*)other, + (PyObject*)&PycairoRectangleInt_Type)) { + res = 0; + } + else if ( + self->rectangle_int.x == other->rectangle_int.x && + self->rectangle_int.y == other->rectangle_int.y && + self->rectangle_int.width == other->rectangle_int.width && + self->rectangle_int.height == other->rectangle_int.height + ) + res = 1; + res = op == Py_NE ? !res : res; + b = res ? Py_True : Py_False; + Py_INCREF(b); + + return b; +} + +static PyMemberDef RectangleInt_members[] = { + {"x", T_INT, sizeof(PyObject), 0, + "X coordinate of the left side of the rectangle"}, + {"y", T_INT, sizeof(PyObject)+sizeof(int), 0, + "Y coordinate of the the top side of the rectangle"}, + {"width", T_INT, sizeof(PyObject)+sizeof(int)*2, 0, + "width of the rectangle"}, + {"height", T_INT, sizeof(PyObject)+sizeof(int)*3, 0, + "height of the rectangle"}, + {NULL} +}; + +PyTypeObject PycairoRectangleInt_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + //PyObject_HEAD_INIT(NULL) + //0, /* ob_size */ + "cairo.RectangleInt", /* tp_name */ + sizeof(PycairoRectangleInt), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)rectangle_int_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + (reprfunc)rectangle_int_str, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + (richcmpfunc)rectangle_int_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + RectangleInt_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + (newfunc)rectangle_int_new, /* tp_new */ +}; + +/* PycairoRegion_FromRegion + * Create a new PycairoRegion from a cairo_region_t + * region - a cairo_region_t to 'wrap' into a Python object. + * region is unreferenced if the PycairoRegion creation fails, or if + * region is in an error status. + * Return value: New reference or NULL on failure + */ +PyObject * +PycairoRegion_FromRegion (cairo_region_t *region) { + PyObject *o; + + assert (region != NULL); + + if (Pycairo_Check_Status (cairo_region_status(region))) { + cairo_region_destroy (region); + return NULL; + } + + o = PycairoRegion_Type.tp_alloc (&PycairoRegion_Type, 0); + if (o) + ((PycairoRegion *)o)->region = region; + else + cairo_region_destroy (region); + return o; +} + +static void +region_dealloc(PycairoRegion *o) { +#ifdef DEBUG + printf("region_dealloc start\n"); +#endif + if (o->region) { + cairo_region_destroy(o->region); + o->region = NULL; + } + //o->ob_type->tp_free((PyObject *)o); + Py_TYPE(o)->tp_free(o); +#ifdef DEBUG + printf("region_dealloc end\n"); +#endif +} + +static PyObject * +region_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { + PyObject *s = NULL; + PycairoRectangleInt *rect_obj = NULL; + cairo_region_t *region = NULL; + cairo_rectangle_int_t *rect = NULL; + + if (PyArg_ParseTuple(args, "|O!:Region.__new__", + &PycairoRectangleInt_Type, &rect_obj)) { + if (rect_obj != NULL) { + region = cairo_region_create_rectangle(&(rect_obj->rectangle_int)); + } + } else if (!PyArg_ParseTuple(args, "|O:Region.__new__", &s)) { + PyErr_SetString(PyExc_TypeError, + "argument must be a RectangleInt or a sequence of RectangleInt."); + return NULL; + } + PyErr_Clear(); /* Clear possible err in the 1st arg parser. */ + + /* list of rectangle_int or no args */ + if (s != NULL) { + int i; + int rect_size; + PyObject *seq = NULL; + seq = PySequence_Fast (s, + "argument must be a RectangleInt or a sequence of RectangleInt."); + if (seq == NULL) { + return NULL; + } + rect_size = PySequence_Fast_GET_SIZE(seq); + rect = PyMem_Malloc (rect_size * sizeof(cairo_rectangle_int_t)); + if (rect == NULL) { + Py_DECREF(seq); + return PyErr_NoMemory(); + } + + for(i=0; irectangle_int; + } + + region = cairo_region_create_rectangles(rect, rect_size); + + Py_DECREF(seq); + PyMem_Free(rect); + } + + if (region == NULL) { + region = cairo_region_create(); + } + + RETURN_NULL_IF_CAIRO_REGION_ERROR(region); + return PycairoRegion_FromRegion(region); +} + +PyObject * +region_copy (PycairoRegion *o) { + cairo_region_t *res; + Py_BEGIN_ALLOW_THREADS; + res = cairo_region_copy (o->region); + Py_END_ALLOW_THREADS; + RETURN_NULL_IF_CAIRO_REGION_ERROR(res); + return PycairoRegion_FromRegion(res); +} + + +PyObject * +region_get_extents (PycairoRegion *o) { + cairo_rectangle_int_t rect; + Py_BEGIN_ALLOW_THREADS; + cairo_region_get_extents(o->region, &rect); + Py_END_ALLOW_THREADS; + + return PycairoRectangleInt_FromRectangleInt(&rect); +} + + +PyObject * +region_num_rectangles (PycairoRegion *o) { + int res; + Py_BEGIN_ALLOW_THREADS; + res = cairo_region_num_rectangles(o->region); + Py_END_ALLOW_THREADS; + return Py_BuildValue("i", res); +} + + +PyObject * +region_get_rectangle (PycairoRegion *o, PyObject *args) { + cairo_rectangle_int_t rect; + int i; + int total; + if (!PyArg_ParseTuple (args, "i:Region.get_rectangle", &i)) + return NULL; + total = cairo_region_num_rectangles(o->region); + if (i >= total || i < 0) { + if ( i < 0) + PyErr_SetString(PyExc_ValueError, "index must be a positive number"); + else + PyErr_SetString(PyExc_ValueError, "index is to big for the region"); + return NULL; + } + Py_BEGIN_ALLOW_THREADS; + cairo_region_get_rectangle(o->region, i, &rect); + Py_END_ALLOW_THREADS; + return PycairoRectangleInt_FromRectangleInt(&rect); +} + + +PyObject * +region_is_empty (PycairoRegion *o) { + cairo_bool_t res; + PyObject *b; + Py_BEGIN_ALLOW_THREADS; + res = cairo_region_is_empty(o->region); + Py_END_ALLOW_THREADS; + b = res ? Py_True : Py_False; + Py_INCREF(b); + return b; +} + + +PyObject * +region_contains_point (PycairoRegion *o, PyObject *args) { + int x, y; + cairo_bool_t res; + PyObject *b; + if (!PyArg_ParseTuple (args, "ii:Region.contains_point", &x, &y)) + return NULL; + Py_BEGIN_ALLOW_THREADS; + res = cairo_region_contains_point(o->region, x, y); + Py_END_ALLOW_THREADS; + b = res ? Py_True : Py_False; + Py_INCREF(b); + return b; +} + + +PyObject * +region_contains_rectangle (PycairoRegion *o, PyObject *args) { + cairo_region_overlap_t res; + PycairoRectangleInt *rect_int; + if (!PyArg_ParseTuple (args, "O!:Region.contains_rectangle", + &PycairoRectangleInt_Type, &rect_int)) + return NULL; + Py_BEGIN_ALLOW_THREADS; + res = cairo_region_contains_rectangle(o->region, &(rect_int->rectangle_int)); + Py_END_ALLOW_THREADS; + return Py_BuildValue("i", res); +} + + +PyObject * +region_equal (PycairoRegion *o, PyObject *args) { + cairo_bool_t res; + PyObject *b; + PycairoRegion *region_obj; + if (!PyArg_ParseTuple (args, "O!:Region.equal", + &PycairoRegion_Type, ®ion_obj)) + return NULL; + Py_BEGIN_ALLOW_THREADS; + res = cairo_region_equal (o->region, region_obj->region); + Py_END_ALLOW_THREADS; + b = res ? Py_True : Py_False; + Py_INCREF(b); + return b; +} + +static PyObject * +region_richcompare(PycairoRegion *self, PycairoRegion *other, int op) { + int res = 0; + PyObject *b; + + if (op != Py_EQ && op != Py_NE) { + PyErr_SetString(PyExc_TypeError, "Only support testing for == or !="); + return NULL; + } + if (!PyObject_IsInstance((PyObject*)other, (PyObject*)&PycairoRegion_Type)) { + res = 0; + } else { + res = cairo_region_equal (self->region, other->region); + } + + res = op == Py_NE ? !res : res; + b = res ? Py_True : Py_False; + Py_INCREF(b); + + return b; +} + +PyObject * +region_translate (PycairoRegion *o, PyObject *args) { + int x, y; + if (!PyArg_ParseTuple (args, "ii:Region.translate", &x, &y)) + return NULL; + Py_BEGIN_ALLOW_THREADS; + cairo_region_translate (o->region, x, y); + Py_END_ALLOW_THREADS; + Py_RETURN_NONE; +} + + +PyObject * +region_intersect (PycairoRegion *o, PyObject *args) { + cairo_status_t res; + PyObject *other; + if (!PyArg_ParseTuple (args, "O:Region.intersect", &other)) + return NULL; + + if (PyObject_IsInstance(other, (PyObject*)&PycairoRegion_Type) == 1) { + Py_BEGIN_ALLOW_THREADS; + res = cairo_region_intersect(o->region, + ((PycairoRegion *)other)->region); + Py_END_ALLOW_THREADS; + } else if (PyObject_IsInstance(other, + (PyObject*)&PycairoRectangleInt_Type) == 1) { + Py_BEGIN_ALLOW_THREADS; + res = cairo_region_intersect_rectangle(o->region, + &(((PycairoRectangleInt *)other)->rectangle_int)); + Py_END_ALLOW_THREADS; + } else { + PyErr_SetString(PyExc_TypeError, + "argument must be a Region or a RectangleInt."); + return NULL; + } + + RETURN_NULL_IF_CAIRO_ERROR(res); + Py_RETURN_NONE; +} + +PyObject * +region_subtract (PycairoRegion *o, PyObject *args) { + cairo_status_t res; + PyObject *other; + if (!PyArg_ParseTuple (args, "O:Region.subtract", &other)) + return NULL; + + if (PyObject_IsInstance(other, (PyObject*)&PycairoRegion_Type) == 1) { + Py_BEGIN_ALLOW_THREADS; + res = cairo_region_subtract(o->region, + ((PycairoRegion *)other)->region); + Py_END_ALLOW_THREADS; + } else if (PyObject_IsInstance(other, + (PyObject*)&PycairoRectangleInt_Type) == 1) { + Py_BEGIN_ALLOW_THREADS; + res = cairo_region_subtract_rectangle(o->region, + &(((PycairoRectangleInt *)other)->rectangle_int)); + Py_END_ALLOW_THREADS; + } else { + PyErr_SetString(PyExc_TypeError, + "argument must be a Region or a RectangleInt."); + return NULL; + } + RETURN_NULL_IF_CAIRO_ERROR(res); + Py_RETURN_NONE; +} + +PyObject * +region_union (PycairoRegion *o, PyObject *args) { + cairo_status_t res; + PyObject *other; + if (!PyArg_ParseTuple (args, "O:Region.union", &other)) + return NULL; + + if (PyObject_IsInstance(other, (PyObject*)&PycairoRegion_Type) == 1) { + Py_BEGIN_ALLOW_THREADS; + res = cairo_region_union(o->region, + ((PycairoRegion *)other)->region); + Py_END_ALLOW_THREADS; + } else if (PyObject_IsInstance(other, + (PyObject*)&PycairoRectangleInt_Type) == 1) { + Py_BEGIN_ALLOW_THREADS; + res = cairo_region_union_rectangle(o->region, + &(((PycairoRectangleInt *)other)->rectangle_int)); + Py_END_ALLOW_THREADS; + } else { + PyErr_SetString(PyExc_TypeError, + "argument must be a Region or a RectangleInt."); + return NULL; + } + RETURN_NULL_IF_CAIRO_ERROR(res); + Py_RETURN_NONE; +} + +PyObject * +region_xor (PycairoRegion *o, PyObject *args) { + cairo_status_t res; + PyObject *other; + if (!PyArg_ParseTuple (args, "O:Region.xorg", &other)) + return NULL; + + if (PyObject_IsInstance(other, (PyObject*)&PycairoRegion_Type) == 1) { + Py_BEGIN_ALLOW_THREADS; + res = cairo_region_xor(o->region, + ((PycairoRegion *)other)->region); + Py_END_ALLOW_THREADS; + } else if (PyObject_IsInstance(other, + (PyObject*)&PycairoRectangleInt_Type) == 1) { + Py_BEGIN_ALLOW_THREADS; + res = cairo_region_xor_rectangle(o->region, + &(((PycairoRectangleInt *)other)->rectangle_int)); + Py_END_ALLOW_THREADS; + } else { + PyErr_SetString(PyExc_TypeError, + "argument must be a Region or a RectangleInt."); + return NULL; + } + RETURN_NULL_IF_CAIRO_ERROR(res); + Py_RETURN_NONE; +} + +static PyMethodDef region_methods[] = { + /* methods never exposed in a language binding: + * cairo_region_destroy() + * cairo_region_get_type() + * cairo_region_reference() + * + * cairo_region_status() + * - not needed since Pycairo handles status checking + * + * _(intersect/subtract/union/xor)_rectangle are merged with the region + * ones. + */ + {"copy", (PyCFunction)region_copy, METH_NOARGS }, + {"get_extents", (PyCFunction)region_get_extents, METH_NOARGS }, + {"num_rectangles", (PyCFunction)region_num_rectangles, METH_NOARGS }, + {"get_rectangle", (PyCFunction)region_get_rectangle, METH_VARARGS }, + {"is_empty", (PyCFunction)region_is_empty, METH_NOARGS }, + {"contains_point", (PyCFunction)region_contains_point, METH_VARARGS }, + {"contains_rectangle", (PyCFunction)region_contains_rectangle, + METH_VARARGS }, + {"equal", (PyCFunction)region_equal, METH_VARARGS }, + {"translate", (PyCFunction)region_translate, METH_VARARGS }, + {"intersect", (PyCFunction)region_intersect, METH_VARARGS }, + {"subtract", (PyCFunction)region_subtract, METH_VARARGS }, + {"union", (PyCFunction)region_union, METH_VARARGS }, + {"xor", (PyCFunction)region_xor, METH_VARARGS }, + {NULL, NULL, 0, NULL}, +}; + +PyTypeObject PycairoRegion_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + //PyObject_HEAD_INIT(NULL) + //0, /* ob_size */ + "cairo.Region", /* tp_name */ + sizeof(PycairoRegion), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)region_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + (richcmpfunc)region_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + region_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + (newfunc)region_new, /* tp_new */ +}; + diff --git a/src/wscript b/src/wscript index 0ece7db..d6af709 100644 --- a/src/wscript +++ b/src/wscript @@ -25,6 +25,7 @@ def build(ctx): 'font.c', 'path.c', 'pattern.c', + 'region.c', 'matrix.c', 'surface.c', ], diff --git a/test/api_test.py b/test/api_test.py index 0fe0772..59ca2e6 100644 --- a/test/api_test.py +++ b/test/api_test.py @@ -88,3 +88,65 @@ def test_surface(): def test_text(): pass + +def test_region(): + a = cairo.Region() + assert a.is_empty() == True + assert a.num_rectangles() == 0 + + b = cairo.RectangleInt(1, 2, 10, 12) + d = cairo.RectangleInt(1, 1, 10, 12) + e = cairo.RectangleInt(1, 3, 8, 12) + assert (b.x, b.y, b.width, b.height) == (1, 2, 10, 12) + c = cairo.Region((b, e)) + assert not c.is_empty() + assert c.num_rectangles() == 2 + assert c.get_rectangle(1).y == 14 + + ex = c.get_extents() + assert ex == cairo.RectangleInt(1, 2, 10, 13) + assert c.contains_rectangle(d) == cairo.REGION_OVERLAP_PART + + c.translate(10, 20) + assert c.contains_rectangle(d) == cairo.REGION_OVERLAP_OUT + assert c.get_rectangle(1) == cairo.RectangleInt(11, 34, 8, 1) + + cp = c.copy() + assert c.num_rectangles() == cp.num_rectangles() + assert c.get_rectangle(0) == cp.get_rectangle(0) + assert c == cp + assert 3 != c + assert c != "test" + + c = cairo.Region((b, e)) + c.intersect(d) + assert c.num_rectangles() == 1 + assert c.get_rectangle(0) == cairo.RectangleInt(1, 2, 10, 11) + + c = cairo.Region((b, e)) + c.subtract(d) + assert c.num_rectangles() == 2 + assert c == cairo.Region([ + cairo.RectangleInt(1, 13, 10, 1), cairo.RectangleInt(1, 14, 8, 1) ]) + + d = cairo.Region(d) + c = cairo.Region((b, e)) + c.subtract(d) + assert c.num_rectangles() == 2 + assert c.get_rectangle(0) == cairo.RectangleInt(1, 13, 10, 1) + + c = cairo.Region((b, e)) + c.union(d) + assert c.num_rectangles() == 2 + assert c == cairo.Region([ + cairo.RectangleInt(1, 1, 10, 13), cairo.RectangleInt(1, 14, 8, 1) ]) + + c = cairo.Region((b, e)) + c.xor(d) + assert c.num_rectangles() == 3 + assert c == cairo.Region([ + cairo.RectangleInt(1, 1, 10, 1), + cairo.RectangleInt(1, 14, 8, 1), + cairo.RectangleInt(1, 13, 10, 1), + ]) +