Pillow/src/path.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

625 lines
17 KiB
C
Raw Normal View History

2010-07-31 06:52:47 +04:00
/*
* The Python Imaging Library.
*
* 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"
2024-07-13 13:34:17 +03:00
#include "thirdparty/pythoncapi_compat.h"
#include "libImaging/Imaging.h"
2010-07-31 06:52:47 +04:00
#include <math.h>
/* compatibility wrappers (defined in _imaging.c) */
extern int
PyImaging_CheckBuffer(PyObject *buffer);
extern int
PyImaging_GetBuffer(PyObject *buffer, Py_buffer *view);
2010-07-31 06:52:47 +04:00
/* -------------------------------------------------------------------- */
2016-03-16 14:23:45 +03:00
/* Class */
2010-07-31 06:52:47 +04:00
/* -------------------------------------------------------------------- */
typedef struct {
PyObject_HEAD Py_ssize_t count;
double *xy;
} PyPathObject;
static PyTypeObject PyPathType;
2010-07-31 06:52:47 +04:00
static double *
2013-03-08 21:51:22 +04:00
alloc_array(Py_ssize_t count) {
2010-07-31 06:52:47 +04:00
double *xy;
if (count < 0) {
return ImagingError_MemoryError();
2010-07-31 06:52:47 +04:00
}
2020-07-01 15:27:31 +03:00
if ((unsigned long long)count > (SIZE_MAX / (2 * sizeof(double))) - 1) {
return ImagingError_MemoryError();
2016-03-15 22:56:40 +03:00
}
2022-01-14 02:16:35 +03:00
xy = calloc(2 * count + 1, sizeof(double));
2020-05-10 12:56:36 +03:00
if (!xy) {
ImagingError_MemoryError();
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
return xy;
}
static PyPathObject *
path_new(Py_ssize_t count, double *xy, int duplicate) {
PyPathObject *path;
if (duplicate) {
/* duplicate path */
double *p = alloc_array(count);
2020-05-10 12:56:36 +03:00
if (!p) {
2010-07-31 06:52:47 +04:00
return NULL;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
memcpy(p, xy, count * 2 * sizeof(double));
xy = p;
}
if (PyType_Ready(&PyPathType) < 0) {
free(xy);
return NULL;
}
2010-07-31 06:52:47 +04:00
path = PyObject_New(PyPathObject, &PyPathType);
if (path == NULL) {
free(xy);
2016-03-16 14:23:45 +03:00
return NULL;
}
2010-07-31 06:52:47 +04:00
path->count = count;
path->xy = xy;
return path;
}
static void
path_dealloc(PyPathObject *path) {
free(path->xy);
PyObject_Del(path);
}
/* -------------------------------------------------------------------- */
2016-03-16 14:23:45 +03:00
/* Helpers */
2010-07-31 06:52:47 +04:00
/* -------------------------------------------------------------------- */
#define PyPath_Check(op) (Py_TYPE(op) == &PyPathType)
2010-07-31 06:52:47 +04:00
Py_ssize_t
2010-07-31 06:52:47 +04:00
PyPath_Flatten(PyObject *data, double **pxy) {
Py_ssize_t i, j, n;
2010-07-31 06:52:47 +04:00
double *xy;
if (PyPath_Check(data)) {
2016-03-16 14:23:45 +03:00
/* This was another path object. */
PyPathObject *path = (PyPathObject *)data;
2010-07-31 06:52:47 +04:00
xy = alloc_array(path->count);
2020-05-10 12:56:36 +03:00
if (!xy) {
2016-03-16 14:23:45 +03:00
return -1;
2020-05-10 12:56:36 +03:00
}
2016-03-16 14:23:45 +03:00
memcpy(xy, path->xy, 2 * path->count * sizeof(double));
*pxy = xy;
return path->count;
2010-07-31 06:52:47 +04:00
}
2010-07-31 06:52:47 +04:00
if (PyImaging_CheckBuffer(data)) {
/* Assume the buffer contains floats */
Py_buffer buffer;
if (PyImaging_GetBuffer(data, &buffer) == 0) {
float *ptr = (float *)buffer.buf;
2019-06-26 03:18:37 +03:00
n = buffer.len / (2 * sizeof(float));
xy = alloc_array(n);
2020-05-10 12:56:36 +03:00
if (!xy) {
return -1;
2020-05-10 12:56:36 +03:00
}
for (i = 0; i < n + n; i++) {
xy[i] = ptr[i];
2020-05-10 12:56:36 +03:00
}
*pxy = xy;
PyBuffer_Release(&buffer);
return n;
}
PyErr_Clear();
2010-07-31 06:52:47 +04:00
}
if (!PySequence_Check(data)) {
2016-03-16 14:23:45 +03:00
PyErr_SetString(PyExc_TypeError, "argument must be sequence");
return -1;
2010-07-31 06:52:47 +04:00
}
j = 0;
n = PyObject_Length(data);
/* Just in case __len__ breaks (or doesn't exist) */
2020-05-10 12:56:36 +03:00
if (PyErr_Occurred()) {
2010-07-31 06:52:47 +04:00
return -1;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
/* Allocate for worst case */
xy = alloc_array(n);
2020-05-10 12:56:36 +03:00
if (!xy) {
2016-03-16 14:23:45 +03:00
return -1;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
#define assign_item_to_array(op, decref) \
if (PyFloat_Check(op)) { \
xy[j++] = PyFloat_AS_DOUBLE(op); \
} else if (PyLong_Check(op)) { \
xy[j++] = (float)PyLong_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 { \
PyErr_SetString(PyExc_ValueError, "incorrect coordinate type"); \
if (decref) { \
Py_DECREF(op); \
} \
free(xy); \
return -1; \
} \
if (decref) { \
Py_DECREF(op); \
}
2010-07-31 06:52:47 +04:00
/* Copy table to path array */
if (PyList_Check(data)) {
for (i = 0; i < n; i++) {
double x, y;
PyObject *op = PyList_GetItemRef(data, i);
if (op == NULL) {
free(xy);
return -1;
}
assign_item_to_array(op, 1);
2010-07-31 06:52:47 +04:00
}
} else if (PyTuple_Check(data)) {
for (i = 0; i < n; i++) {
double x, y;
PyObject *op = PyTuple_GET_ITEM(data, i);
assign_item_to_array(op, 0);
2010-07-31 06:52:47 +04:00
}
} 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;
}
}
assign_item_to_array(op, 1);
2010-07-31 06:52:47 +04:00
}
}
if (j & 1) {
2016-03-16 14:23:45 +03:00
PyErr_SetString(PyExc_ValueError, "wrong number of coordinates");
free(xy);
return -1;
2010-07-31 06:52:47 +04:00
}
*pxy = xy;
return j / 2;
}
/* -------------------------------------------------------------------- */
2016-03-16 14:23:45 +03:00
/* Factories */
2010-07-31 06:52:47 +04:00
/* -------------------------------------------------------------------- */
PyObject *
PyPath_Create(PyObject *self, PyObject *args) {
PyObject *data;
Py_ssize_t count;
double *xy;
if (PyArg_ParseTuple(args, "n:Path", &count)) {
2010-07-31 06:52:47 +04:00
/* number of vertices */
xy = alloc_array(count);
2020-05-10 12:56:36 +03:00
if (!xy) {
2010-07-31 06:52:47 +04:00
return NULL;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
} else {
/* sequence or other path */
PyErr_Clear();
2020-05-10 12:56:36 +03:00
if (!PyArg_ParseTuple(args, "O", &data)) {
2010-07-31 06:52:47 +04:00
return NULL;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
count = PyPath_Flatten(data, &xy);
2020-05-10 12:56:36 +03:00
if (count < 0) {
2010-07-31 06:52:47 +04:00
return NULL;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
}
return (PyObject *)path_new(count, xy, 0);
}
/* -------------------------------------------------------------------- */
2016-03-16 14:23:45 +03:00
/* Methods */
2010-07-31 06:52:47 +04:00
/* -------------------------------------------------------------------- */
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 */
Py_ssize_t i, j;
2010-07-31 06:52:47 +04:00
double *xy;
double cityblock = 2.0;
2020-05-10 12:56:36 +03:00
if (!PyArg_ParseTuple(args, "|d:compact", &cityblock)) {
2016-03-16 14:23:45 +03:00
return NULL;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
xy = self->xy;
/* remove bogus vertices */
for (i = j = 1; i < self->count; i++) {
2016-03-16 14:23:45 +03:00
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++;
}
2010-07-31 06:52:47 +04:00
}
i = self->count - j;
self->count = j;
/* shrink coordinate array */
2016-03-16 19:01:25 +03:00
/* malloc check ok, self->count is smaller than it was before */
2010-07-31 06:52:47 +04:00
self->xy = realloc(self->xy, 2 * self->count * sizeof(double));
return Py_BuildValue("i", i); /* number of removed vertices */
}
static PyObject *
path_getbbox(PyPathObject *self, PyObject *args) {
/* Find bounding box */
Py_ssize_t i;
2010-07-31 06:52:47 +04:00
double *xy;
double x0, y0, x1, y1;
2020-05-10 12:56:36 +03:00
if (!PyArg_ParseTuple(args, ":getbbox")) {
2016-03-16 14:23:45 +03:00
return NULL;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
xy = self->xy;
2021-12-06 14:25:14 +03:00
if (self->count == 0) {
x0 = x1 = 0;
y0 = y1 = 0;
} else {
x0 = x1 = xy[0];
y0 = y1 = xy[1];
2010-07-31 06:52:47 +04:00
2021-12-06 14:25:14 +03:00
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];
}
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
}
return Py_BuildValue("dddd", x0, y0, x1, y1);
}
static PyObject *
path_getitem(PyPathObject *self, Py_ssize_t i) {
2020-05-10 12:56:36 +03:00
if (i < 0) {
i = self->count + i;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
if (i < 0 || i >= self->count) {
2016-03-16 14:23:45 +03:00
PyErr_SetString(PyExc_IndexError, "path index out of range");
return NULL;
2010-07-31 06:52:47 +04:00
}
return Py_BuildValue("dd", self->xy[i + i], self->xy[i + i + 1]);
}
static PyObject *
path_getslice(PyPathObject *self, Py_ssize_t ilow, Py_ssize_t ihigh) {
/* adjust arguments */
2020-05-10 12:56:36 +03:00
if (ilow < 0) {
2010-07-31 06:52:47 +04:00
ilow = 0;
2020-05-10 12:56:36 +03:00
} else if (ilow >= self->count) {
2010-07-31 06:52:47 +04:00
ilow = self->count;
2020-05-10 12:56:36 +03:00
}
if (ihigh < 0) {
2010-07-31 06:52:47 +04:00
ihigh = 0;
2020-05-10 12:56:36 +03:00
}
if (ihigh < ilow) {
2010-07-31 06:52:47 +04:00
ihigh = ilow;
2020-05-10 12:56:36 +03:00
} else if (ihigh > self->count) {
2010-07-31 06:52:47 +04:00
ihigh = self->count;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
return (PyObject *)path_new(ihigh - ilow, self->xy + ilow * 2, 1);
}
static Py_ssize_t
path_len(PyPathObject *self) {
return self->count;
}
static PyObject *
path_map(PyPathObject *self, PyObject *args) {
/* Map coordinate set through function */
Py_ssize_t i;
2010-07-31 06:52:47 +04:00
double *xy;
PyObject *function;
2020-05-10 12:56:36 +03:00
if (!PyArg_ParseTuple(args, "O:map", &function)) {
2016-03-16 14:23:45 +03:00
return NULL;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
xy = self->xy;
/* apply function to coordinate set */
for (i = 0; i < self->count; i++) {
2016-03-16 14:23:45 +03:00
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);
2010-07-31 06:52:47 +04:00
}
Py_INCREF(Py_None);
return Py_None;
}
static int
path_setitem(PyPathObject *self, Py_ssize_t i, PyObject *op) {
2010-07-31 06:52:47 +04:00
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];
2020-05-10 12:56:36 +03:00
if (!PyArg_ParseTuple(op, "dd", &xy[0], &xy[1])) {
2010-07-31 06:52:47 +04:00
return -1;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
return 0;
}
static PyObject *
path_tolist(PyPathObject *self, PyObject *args) {
PyObject *list;
Py_ssize_t i;
2010-07-31 06:52:47 +04:00
int flat = 0;
2020-05-10 12:56:36 +03:00
if (!PyArg_ParseTuple(args, "|i:tolist", &flat)) {
2016-03-16 14:23:45 +03:00
return NULL;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
if (flat) {
list = PyList_New(self->count * 2);
if (list == NULL) {
return NULL;
}
2010-07-31 06:52:47 +04:00
for (i = 0; i < self->count * 2; i++) {
PyObject *item;
item = PyFloat_FromDouble(self->xy[i]);
2020-05-10 12:56:36 +03:00
if (!item) {
2010-07-31 06:52:47 +04:00
goto error;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
PyList_SetItem(list, i, item);
}
} else {
list = PyList_New(self->count);
if (list == NULL) {
return NULL;
}
2010-07-31 06:52:47 +04:00
for (i = 0; i < self->count; i++) {
PyObject *item;
item = Py_BuildValue("dd", self->xy[i + i], self->xy[i + i + 1]);
2020-05-10 12:56:36 +03:00
if (!item) {
2010-07-31 06:52:47 +04:00
goto error;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
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 */
Py_ssize_t i;
2010-07-31 06:52:47 +04:00
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
)) {
2016-03-16 14:23:45 +03:00
return NULL;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
xy = self->xy;
/* transform the coordinate set */
2020-05-10 12:56:36 +03:00
if (b == 0.0 && d == 0.0) {
2016-03-16 14:23:45 +03:00
/* 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;
}
2020-05-10 12:56:36 +03:00
} else {
2016-03-16 14:23:45 +03:00
/* 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;
}
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
/* special treatment of geographical map data */
2020-05-10 12:56:36 +03:00
if (wrap != 0.0) {
for (i = 0; i < self->count; i++) {
2016-03-16 14:23:45 +03:00
xy[i + i] = fmod(xy[i + i], wrap);
2020-05-10 12:56:36 +03:00
}
}
2010-07-31 06:52:47 +04:00
Py_INCREF(Py_None);
return Py_None;
}
static struct PyMethodDef methods[] = {
2021-05-11 13:16:44 +03:00
{"getbbox", (PyCFunction)path_getbbox, METH_VARARGS},
{"tolist", (PyCFunction)path_tolist, METH_VARARGS},
{"compact", (PyCFunction)path_compact, METH_VARARGS},
{"map", (PyCFunction)path_map, METH_VARARGS},
{"transform", (PyCFunction)path_transform, METH_VARARGS},
2010-07-31 06:52:47 +04:00
{NULL, NULL} /* sentinel */
};
static PyObject *
path_getattr_id(PyPathObject *self, void *closure) {
2016-03-16 14:23:45 +03:00
return Py_BuildValue("n", (Py_ssize_t)self->xy);
2010-07-31 06:52:47 +04:00
}
static struct PyGetSetDef getsetters[] = {{"id", (getter)path_getattr_id}, {NULL}};
static PyObject *
path_subscript(PyPathObject *self, PyObject *item) {
if (PyIndex_Check(item)) {
Py_ssize_t i;
i = PyNumber_AsSsize_t(item, PyExc_IndexError);
2020-05-10 12:56:36 +03:00
if (i == -1 && PyErr_Occurred()) {
return NULL;
2020-05-10 12:56:36 +03:00
}
return path_getitem(self, i);
}
if (PySlice_Check(item)) {
int len = 4;
Py_ssize_t start, stop, step, slicelength;
2020-05-10 12:56:36 +03:00
if (PySlice_GetIndicesEx(item, len, &start, &stop, &step, &slicelength) < 0) {
return NULL;
2020-05-10 12:56:36 +03:00
}
if (slicelength <= 0) {
double *xy = alloc_array(0);
return (PyObject *)path_new(0, xy, 0);
} else if (step == 1) {
return path_getslice(self, start, stop);
} else {
PyErr_SetString(PyExc_TypeError, "slice steps not supported");
return NULL;
}
} else {
PyErr_Format(
PyExc_TypeError,
"Path indices must be integers, not %.200s",
Py_TYPE(item)->tp_name
);
return NULL;
}
}
2010-07-31 06:52:47 +04:00
static PySequenceMethods path_as_sequence = {
2016-03-16 14:23:45 +03:00
(lenfunc)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*/
2010-07-31 06:52:47 +04:00
};
static PyMappingMethods path_as_mapping = {
(lenfunc)path_len, (binaryfunc)path_subscript, NULL
};
static PyTypeObject PyPathType = {
2016-03-16 14:23:45 +03:00
PyVarObject_HEAD_INIT(NULL, 0) "Path", /*tp_name*/
sizeof(PyPathObject), /*tp_basicsize*/
2016-03-16 14:23:45 +03:00
0, /*tp_itemsize*/
/* methods */
(destructor)path_dealloc, /*tp_dealloc*/
0, /*tp_vectorcall_offset*/
2016-03-16 14:23:45 +03:00
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_as_async*/
2016-03-16 14:23:45 +03:00
0, /*tp_repr*/
0, /*tp_as_number*/
&path_as_sequence, /*tp_as_sequence*/
&path_as_mapping, /*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*/
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
0, /*tp_iter*/
0, /*tp_iternext*/
methods, /*tp_methods*/
0, /*tp_members*/
getsetters, /*tp_getset*/
2010-07-31 06:52:47 +04:00
};