Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/Imaging/path.c
diff options
context:
space:
mode:
Diffstat (limited to 'Imaging/path.c')
-rw-r--r--Imaging/path.c585
1 files changed, 585 insertions, 0 deletions
diff --git a/Imaging/path.c b/Imaging/path.c
new file mode 100644
index 0000000..2810988
--- /dev/null
+++ b/Imaging/path.c
@@ -0,0 +1,585 @@
+/*
+ * The Python Imaging Library.
+ * $Id: path.c 2935 2006-12-03 12:20:39Z fredrik $
+ *
+ * 2D path utilities
+ *
+ * history:
+ * 1996-11-04 fl Added to PIL (incomplete)
+ * 1996-11-05 fl Added sequence semantics
+ * 1997-02-28 fl Fixed getbbox
+ * 1997-06-12 fl Added id attribute
+ * 1997-06-14 fl Added slicing and setitem
+ * 1998-12-29 fl Improved sequence handling (from Richard Jones)
+ * 1999-01-10 fl Fixed IndexError test for 1.5 (from Fred Drake)
+ * 2000-10-12 fl Added special cases for tuples and lists
+ * 2002-10-27 fl Added clipping boilerplate
+ * 2004-09-19 fl Added tolist(flat) variant
+ * 2005-05-06 fl Added buffer interface support to path constructor
+ *
+ * notes:
+ * FIXME: fill in remaining slots in the sequence api
+ *
+ * Copyright (c) 1997-2005 by Secret Labs AB
+ * Copyright (c) 1997-2005 by Fredrik Lundh
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Python.h"
+
+#include <math.h>
+
+#if PY_VERSION_HEX < 0x01060000
+#define PyObject_New PyObject_NEW
+#define PyObject_Del PyMem_DEL
+#endif
+
+#if PY_VERSION_HEX < 0x02050000
+#define ssizeargfunc intargfunc
+#define ssizessizeargfunc intintargfunc
+#define ssizeobjargproc intobjargproc
+#define ssizessizeobjargproc intintobjargproc
+#endif
+
+/* -------------------------------------------------------------------- */
+/* Class */
+/* -------------------------------------------------------------------- */
+
+typedef struct {
+ PyObject_HEAD
+ int count;
+ double *xy;
+ int index; /* temporary use, e.g. in decimate */
+} PyPathObject;
+
+staticforward PyTypeObject PyPathType;
+
+static double*
+alloc_array(int count)
+{
+ double* xy;
+ if (count < 0) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ xy = malloc(2 * count * sizeof(double) + 1);
+ if (!xy)
+ PyErr_NoMemory();
+ return xy;
+}
+
+static PyPathObject*
+path_new(int count, double* xy, int duplicate)
+{
+ PyPathObject *path;
+
+ if (duplicate) {
+ /* duplicate path */
+ double* p = alloc_array(count);
+ if (!p)
+ return NULL;
+ memcpy(p, xy, count * 2 * sizeof(double));
+ xy = p;
+ }
+
+ path = PyObject_New(PyPathObject, &PyPathType);
+ if (path == NULL)
+ return NULL;
+
+ path->count = count;
+ path->xy = xy;
+
+ return path;
+}
+
+static void
+path_dealloc(PyPathObject* path)
+{
+ free(path->xy);
+ PyObject_Del(path);
+}
+
+/* -------------------------------------------------------------------- */
+/* Helpers */
+/* -------------------------------------------------------------------- */
+
+#define PyPath_Check(op) ((op)->ob_type == &PyPathType)
+
+int
+PyPath_Flatten(PyObject* data, double **pxy)
+{
+ int i, j, n;
+ double *xy;
+ PyBufferProcs *buffer;
+
+ if (PyPath_Check(data)) {
+ /* This was another path object. */
+ PyPathObject *path = (PyPathObject*) data;
+ xy = alloc_array(path->count);
+ if (!xy)
+ return -1;
+ memcpy(xy, path->xy, 2 * path->count * sizeof(double));
+ *pxy = xy;
+ return path->count;
+ }
+
+ buffer = data->ob_type->tp_as_buffer;
+ if (buffer && buffer->bf_getreadbuffer && buffer->bf_getsegcount &&
+ buffer->bf_getsegcount(data, NULL) == 1) {
+ /* Assume the buffer contains floats */
+ float* ptr;
+ int n = buffer->bf_getreadbuffer(data, 0, (void**) &ptr);
+ n /= 2 * sizeof(float);
+ xy = alloc_array(n);
+ if (!xy)
+ return -1;
+ for (i = 0; i < n+n; i++)
+ xy[i] = ptr[i];
+ *pxy = xy;
+ return n;
+ }
+
+ if (!PySequence_Check(data)) {
+ PyErr_SetString(PyExc_TypeError, "argument must be sequence");
+ return -1;
+ }
+
+ j = 0;
+ n = PyObject_Length(data);
+ /* Just in case __len__ breaks (or doesn't exist) */
+ if (PyErr_Occurred())
+ return -1;
+
+ /* Allocate for worst case */
+ xy = alloc_array(n);
+ if (!xy)
+ return -1;
+
+ /* Copy table to path array */
+ if (PyList_Check(data)) {
+ for (i = 0; i < n; i++) {
+ double x, y;
+ PyObject *op = PyList_GET_ITEM(data, i);
+ if (PyFloat_Check(op))
+ xy[j++] = PyFloat_AS_DOUBLE(op);
+ else if (PyInt_Check(op))
+ xy[j++] = (float) PyInt_AS_LONG(op);
+ else if (PyNumber_Check(op))
+ xy[j++] = PyFloat_AsDouble(op);
+ else if (PyArg_ParseTuple(op, "dd", &x, &y)) {
+ xy[j++] = x;
+ xy[j++] = y;
+ } else {
+ free(xy);
+ return -1;
+ }
+ }
+ } else if (PyTuple_Check(data)) {
+ for (i = 0; i < n; i++) {
+ double x, y;
+ PyObject *op = PyTuple_GET_ITEM(data, i);
+ if (PyFloat_Check(op))
+ xy[j++] = PyFloat_AS_DOUBLE(op);
+ else if (PyInt_Check(op))
+ xy[j++] = (float) PyInt_AS_LONG(op);
+ else if (PyNumber_Check(op))
+ xy[j++] = PyFloat_AsDouble(op);
+ else if (PyArg_ParseTuple(op, "dd", &x, &y)) {
+ xy[j++] = x;
+ xy[j++] = y;
+ } else {
+ free(xy);
+ return -1;
+ }
+ }
+ } else {
+ for (i = 0; i < n; i++) {
+ double x, y;
+ PyObject *op = PySequence_GetItem(data, i);
+ if (!op) {
+ /* treat IndexError as end of sequence */
+ if (PyErr_Occurred() &&
+ PyErr_ExceptionMatches(PyExc_IndexError)) {
+ PyErr_Clear();
+ break;
+ } else {
+ free(xy);
+ return -1;
+ }
+ }
+ if (PyFloat_Check(op))
+ xy[j++] = PyFloat_AS_DOUBLE(op);
+ else if (PyInt_Check(op))
+ xy[j++] = (float) PyInt_AS_LONG(op);
+ else if (PyNumber_Check(op))
+ xy[j++] = PyFloat_AsDouble(op);
+ else if (PyArg_ParseTuple(op, "dd", &x, &y)) {
+ xy[j++] = x;
+ xy[j++] = y;
+ } else {
+ Py_DECREF(op);
+ free(xy);
+ return -1;
+ }
+ Py_DECREF(op);
+ }
+ }
+
+ if (j & 1) {
+ PyErr_SetString(PyExc_ValueError, "wrong number of coordinates");
+ free(xy);
+ return -1;
+ }
+
+ *pxy = xy;
+ return j/2;
+}
+
+
+/* -------------------------------------------------------------------- */
+/* Factories */
+/* -------------------------------------------------------------------- */
+
+PyObject*
+PyPath_Create(PyObject* self, PyObject* args)
+{
+ PyObject* data;
+ int count;
+ double *xy;
+
+ if (PyArg_ParseTuple(args, "i:Path", &count)) {
+
+ /* number of vertices */
+ xy = alloc_array(count);
+ if (!xy)
+ return NULL;
+
+ } else {
+
+ /* sequence or other path */
+ PyErr_Clear();
+ if (!PyArg_ParseTuple(args, "O", &data))
+ return NULL;
+
+ count = PyPath_Flatten(data, &xy);
+ if (count < 0)
+ return NULL;
+ }
+
+ return (PyObject*) path_new(count, xy, 0);
+}
+
+
+/* -------------------------------------------------------------------- */
+/* Methods */
+/* -------------------------------------------------------------------- */
+
+static PyObject*
+path_compact(PyPathObject* self, PyObject* args)
+{
+ /* Simple-minded method to shorten path. A point is removed if
+ the city block distance to the previous point is less than the
+ given distance */
+ int i, j;
+ double *xy;
+
+ double cityblock = 2.0;
+
+ if (!PyArg_ParseTuple(args, "|d:compact", &cityblock))
+ return NULL;
+
+ xy = self->xy;
+
+ /* remove bogus vertices */
+ for (i = j = 1; i < self->count; i++) {
+ if (fabs(xy[j+j-2]-xy[i+i]) + fabs(xy[j+j-1]-xy[i+i+1]) >= cityblock) {
+ xy[j+j] = xy[i+i];
+ xy[j+j+1] = xy[i+i+1];
+ j++;
+ }
+ }
+
+ i = self->count - j;
+ self->count = j;
+
+ /* shrink coordinate array */
+ self->xy = realloc(self->xy, 2 * self->count * sizeof(double));
+
+ return Py_BuildValue("i", i); /* number of removed vertices */
+}
+
+static PyObject*
+path_clip_polygon(PyPathObject* self, PyObject* args)
+{
+ /* Clip path representing a single polygon */
+ PyErr_SetString(PyExc_RuntimeError, "not yet implemented");
+ return NULL;
+}
+
+static PyObject*
+path_clip_polyline(PyPathObject* self, PyObject* args)
+{
+ /* Clip path representing a single polyline (outline) */
+ PyErr_SetString(PyExc_RuntimeError, "not yet implemented");
+ return NULL;
+}
+
+static PyObject*
+path_getbbox(PyPathObject* self, PyObject* args)
+{
+ /* Find bounding box */
+ int i;
+ double *xy;
+ double x0, y0, x1, y1;
+
+ if (!PyArg_ParseTuple(args, ":getbbox"))
+ return NULL;
+
+ xy = self->xy;
+
+ x0 = x1 = xy[0];
+ y0 = y1 = xy[1];
+
+ for (i = 1; i < self->count; i++) {
+ if (xy[i+i] < x0)
+ x0 = xy[i+i];
+ if (xy[i+i] > x1)
+ x1 = xy[i+i];
+ if (xy[i+i+1] < y0)
+ y0 = xy[i+i+1];
+ if (xy[i+i+1] > y1)
+ y1 = xy[i+i+1];
+ }
+
+ return Py_BuildValue("dddd", x0, y0, x1, y1);
+}
+
+static PyObject*
+path_getitem(PyPathObject* self, int i)
+{
+ if (i < 0 || i >= self->count) {
+ PyErr_SetString(PyExc_IndexError, "path index out of range");
+ return NULL;
+ }
+
+ return Py_BuildValue("dd", self->xy[i+i], self->xy[i+i+1]);
+}
+
+static PyObject*
+path_getslice(PyPathObject* self, int ilow, int ihigh)
+{
+ /* adjust arguments */
+ if (ilow < 0)
+ ilow = 0;
+ else if (ilow >= self->count)
+ ilow = self->count;
+ if (ihigh < 0)
+ ihigh = 0;
+ if (ihigh < ilow)
+ ihigh = ilow;
+ else if (ihigh > self->count)
+ ihigh = self->count;
+
+ return (PyObject*) path_new(ihigh - ilow, self->xy + ilow * 2, 1);
+}
+
+static int
+path_len(PyPathObject* self)
+{
+ return self->count;
+}
+
+static PyObject*
+path_map(PyPathObject* self, PyObject* args)
+{
+ /* Map coordinate set through function */
+ int i;
+ double *xy;
+ PyObject* function;
+
+ if (!PyArg_ParseTuple(args, "O:map", &function))
+ return NULL;
+
+ xy = self->xy;
+
+ /* apply function to coordinate set */
+ for (i = 0; i < self->count; i++) {
+ double x = xy[i+i];
+ double y = xy[i+i+1];
+ PyObject* item = PyObject_CallFunction(function, "dd", x, y);
+ if (!item || !PyArg_ParseTuple(item, "dd", &x, &y)) {
+ Py_XDECREF(item);
+ return NULL;
+ }
+ xy[i+i] = x;
+ xy[i+i+1] = y;
+ Py_DECREF(item);
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static int
+path_setitem(PyPathObject* self, int i, PyObject* op)
+{
+ double* xy;
+
+ if (i < 0 || i >= self->count) {
+ PyErr_SetString(PyExc_IndexError,
+ "path assignment index out of range");
+ return -1;
+ }
+
+ if (op == NULL) {
+ PyErr_SetString(PyExc_TypeError,
+ "cannot delete from path");
+ return -1;
+ }
+
+ xy = &self->xy[i+i];
+
+ if (!PyArg_ParseTuple(op, "dd", &xy[0], &xy[1]))
+ return -1;
+
+ return 0;
+}
+
+static PyObject*
+path_tolist(PyPathObject* self, PyObject* args)
+{
+ PyObject *list;
+ int i;
+
+ int flat = 0;
+ if (!PyArg_ParseTuple(args, "|i:tolist", &flat))
+ return NULL;
+
+ if (flat) {
+ list = PyList_New(self->count*2);
+ for (i = 0; i < self->count*2; i++) {
+ PyObject* item;
+ item = PyFloat_FromDouble(self->xy[i]);
+ if (!item)
+ goto error;
+ PyList_SetItem(list, i, item);
+ }
+ } else {
+ list = PyList_New(self->count);
+ for (i = 0; i < self->count; i++) {
+ PyObject* item;
+ item = Py_BuildValue("dd", self->xy[i+i], self->xy[i+i+1]);
+ if (!item)
+ goto error;
+ PyList_SetItem(list, i, item);
+ }
+ }
+
+ return list;
+
+error:
+ Py_DECREF(list);
+ return NULL;
+}
+
+static PyObject*
+path_transform(PyPathObject* self, PyObject* args)
+{
+ /* Apply affine transform to coordinate set */
+ int i;
+ double *xy;
+ double a, b, c, d, e, f;
+
+ double wrap = 0.0;
+
+ if (!PyArg_ParseTuple(args, "(dddddd)|d:transform",
+ &a, &b, &c, &d, &e, &f,
+ &wrap))
+ return NULL;
+
+ xy = self->xy;
+
+ /* transform the coordinate set */
+ if (b == 0.0 && d == 0.0)
+ /* scaling */
+ for (i = 0; i < self->count; i++) {
+ xy[i+i] = a*xy[i+i]+c;
+ xy[i+i+1] = e*xy[i+i+1]+f;
+ }
+ else
+ /* affine transform */
+ for (i = 0; i < self->count; i++) {
+ double x = xy[i+i];
+ double y = xy[i+i+1];
+ xy[i+i] = a*x+b*y+c;
+ xy[i+i+1] = d*x+e*y+f;
+ }
+
+ /* special treatment of geographical map data */
+ if (wrap != 0.0)
+ for (i = 0; i < self->count; i++)
+ xy[i+i] = fmod(xy[i+i], wrap);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static struct PyMethodDef methods[] = {
+ {"getbbox", (PyCFunction)path_getbbox, 1},
+ {"tolist", (PyCFunction)path_tolist, 1},
+ {"clip_polygon", (PyCFunction)path_clip_polygon, 1},
+ {"clip_polyline", (PyCFunction)path_clip_polyline, 1},
+ {"compact", (PyCFunction)path_compact, 1},
+ {"map", (PyCFunction)path_map, 1},
+ {"transform", (PyCFunction)path_transform, 1},
+ {NULL, NULL} /* sentinel */
+};
+
+static PyObject*
+path_getattr(PyPathObject* self, char* name)
+{
+ PyObject* res;
+
+ res = Py_FindMethod(methods, (PyObject*) self, name);
+ if (res)
+ return res;
+
+ PyErr_Clear();
+
+ if (strcmp(name, "id") == 0)
+ return Py_BuildValue("l", (long) self->xy);
+
+ PyErr_SetString(PyExc_AttributeError, name);
+ return NULL;
+}
+
+static PySequenceMethods path_as_sequence = {
+ (inquiry)path_len, /*sq_length*/
+ (binaryfunc)0, /*sq_concat*/
+ (ssizeargfunc)0, /*sq_repeat*/
+ (ssizeargfunc)path_getitem, /*sq_item*/
+ (ssizessizeargfunc)path_getslice, /*sq_slice*/
+ (ssizeobjargproc)path_setitem, /*sq_ass_item*/
+ (ssizessizeobjargproc)0, /*sq_ass_slice*/
+};
+
+statichere PyTypeObject PyPathType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Path", /*tp_name*/
+ sizeof(PyPathObject), /*tp_size*/
+ 0, /*tp_itemsize*/
+ /* methods */
+ (destructor)path_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ (getattrfunc)path_getattr, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number */
+ &path_as_sequence, /*tp_as_sequence */
+ 0, /*tp_as_mapping */
+ 0, /*tp_hash*/
+};