accimagemodule.c (262 lines of code) (raw):
#include <Python.h>
#include <structmember.h>
#include "accimage.h"
static struct PyMemberDef Image_members[] = {
{ "channels", T_INT, offsetof(ImageObject, channels), READONLY, "number of channels" },
{ "height", T_INT, offsetof(ImageObject, height), READONLY, "image height in pixels" },
{ "width", T_INT, offsetof(ImageObject, width), READONLY, "image width in pixels" },
{ NULL } /* Sentinel */
};
static PyObject* Image_getsize(ImageObject* self, void* closure) {
return Py_BuildValue("ii", self->width, self->height);
}
static PyGetSetDef Image_getseters[] = {
{ "size", (getter) Image_getsize, (setter) NULL, "Image width x height", NULL },
{ NULL } /* Sentinel */
};
static PyObject* Image_resize(ImageObject* self, PyObject* args, PyObject* kwds) {
static char* argnames[] = { "size", "interpolation", NULL };
PyObject* size = NULL;
int interpolation = 0;
int antialiasing = 1;
int new_height, new_width;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|i", argnames, &size, &interpolation)) {
return NULL;
}
if (!PyArg_ParseTuple(size, "ii", &new_width, &new_height)) {
return NULL;
}
if (new_height <= 0) {
return PyErr_Format(PyExc_ValueError, "positive height expected; got %d", new_height);
}
if (new_width <= 0) {
return PyErr_Format(PyExc_ValueError, "positive width expected; got %d", new_width);
}
// TODO: consider interpolation parameter
image_resize(self, new_height, new_width, antialiasing);
if (PyErr_Occurred()) {
return NULL;
} else {
Py_INCREF(self);
return (PyObject*) self;
}
}
static PyObject* Image_crop(ImageObject* self, PyObject* args, PyObject* kwds) {
static char* argnames[] = { "box", NULL };
PyObject* box_object;
int left, upper, right, lower;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", argnames, &box_object)) {
return NULL;
}
if (!PyArg_ParseTuple(box_object, "iiii", &left, &upper, &right, &lower)) {
return NULL;
}
if (left < 0) {
return PyErr_Format(PyExc_ValueError, "non-negative left offset expected; got %d", left);
}
if (upper < 0) {
return PyErr_Format(PyExc_ValueError, "non-negative upper offset expected; got %d", upper);
}
if (right > self->width) {
return PyErr_Format(PyExc_ValueError, "right coordinate (%d) extends beyond image width (%d)",
right, self->width);
}
if (lower > self->height) {
return PyErr_Format(PyExc_ValueError, "lower coordinate (%d) extends beyond image height (%d)",
lower, self->height);
}
if (right <= left) {
return PyErr_Format(PyExc_ValueError, "right coordinate (%d) does not exceed left coordinate (%d)",
right, left);
}
if (lower <= upper) {
return PyErr_Format(PyExc_ValueError, "lower coordinate (%d) does not exceed upper coordinate (%d)",
lower, upper);
}
self->y_offset += upper;
self->x_offset += left;
self->height = lower - upper;
self->width = right - left;
Py_INCREF(self);
return (PyObject*) self;
}
static PyObject* Image_transpose(ImageObject* self, PyObject* args, PyObject* kwds) {
static char* argnames[] = { "method", NULL };
int method;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", argnames, &method))
return NULL;
switch (method) {
case 0:
/* PIL.Image.FLIP_LEFT_RIGHT */
image_flip_left_right(self);
break;
case 1:
PyErr_SetString(PyExc_ValueError, "unsupported method: PIL.Image.FLIP_TOP_BOTTOM");
return NULL;
case 2:
PyErr_SetString(PyExc_ValueError, "unsupported method: PIL.Image.ROTATE_90");
return NULL;
case 3:
PyErr_SetString(PyExc_ValueError, "unsupported method: PIL.Image.ROTATE_180");
return NULL;
case 4:
PyErr_SetString(PyExc_ValueError, "unsupported method: PIL.Image.ROTATE_270");
return NULL;
case 5:
PyErr_SetString(PyExc_ValueError, "unsupported method: PIL.Image.TRANSPOSE");
return NULL;
default:
return PyErr_Format(PyExc_ValueError, "unknown method (%d)", method);
}
if (PyErr_Occurred()) {
return NULL;
} else {
Py_INCREF(self);
return (PyObject*) self;
}
}
static PyObject* Image_copyto(ImageObject* self, PyObject* args, PyObject* kwds) {
static char* argnames[] = { "buffer", NULL };
static const int FLAGS = PyBUF_CONTIG | PyBUF_FORMAT;
PyObject* buffer_object;
Py_buffer buffer;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", argnames, &buffer_object)) {
return NULL;
}
if (PyObject_GetBuffer(buffer_object, &buffer, FLAGS) < 0) {
return NULL;
}
int expected_size = self->channels * self->height * self->width;
if (strcmp(buffer.format, "f") == 0) {
expected_size *= sizeof(float);
}
if (buffer.len < expected_size) {
PyErr_Format(PyExc_IndexError, "buffer size (%lld) is smaller than image size (%d)",
(long long) buffer.len, expected_size);
goto cleanup;
}
if (buffer.format == NULL || strcmp(buffer.format, "B") == 0) {
image_copy_deinterleave(self, (unsigned char*) buffer.buf);
} else if (strcmp(buffer.format, "f") == 0) {
image_copy_deinterleave_float(self, (float*) buffer.buf);
} else {
PyErr_SetString(PyExc_TypeError, "buffer of unsigned byte or float elements expected");
goto cleanup;
}
cleanup:
PyBuffer_Release(&buffer);
if (PyErr_Occurred()) {
return NULL;
} else {
Py_RETURN_NONE;
}
}
static PyMethodDef Image_methods[] = {
{ "resize", (PyCFunction) Image_resize, METH_VARARGS | METH_KEYWORDS, "Scale image to new size." },
{ "crop", (PyCFunction) Image_crop, METH_VARARGS | METH_KEYWORDS, "Crop image to new size." },
{ "transpose", (PyCFunction) Image_transpose, METH_VARARGS | METH_KEYWORDS, "Transpose/flip/rotate image." },
{ "copyto", (PyCFunction) Image_copyto, METH_VARARGS | METH_KEYWORDS, "Copy data to a buffer." },
{ NULL } /* Sentinel */
};
static PyTypeObject Image_Type = {
/* The ob_type field must be initialized in the module init function
* to be portable to Windows without using C++. */
PyVarObject_HEAD_INIT(NULL, 0)
"accimage.Image", /*tp_name*/
sizeof(ImageObject), /*tp_basicsize*/
0, /*tp_itemsize*/
/* methods */
0, /* see initaccimage */ /*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 | Py_TPFLAGS_BASETYPE, /*tp_flags*/
0, /*tp_doc*/
0, /*tp_traverse*/
0, /*tp_clear*/
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
0, /*tp_iter*/
0, /*tp_iternext*/
Image_methods, /*tp_methods*/
Image_members, /*tp_members*/
Image_getseters, /*tp_getset*/
0, /* see initaccimage */ /*tp_base*/
0, /*tp_dict*/
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
0, /* see initaccimage */ /*tp_init*/
0, /*tp_alloc*/
0, /* see initaccimage */ /*tp_new*/
0, /*tp_free*/
0, /*tp_is_gc*/
};
static int Image_init(ImageObject *self, PyObject *args, PyObject *kwds) {
const char *path;
if (PyArg_ParseTuple(args, "s", &path)) {
image_from_jpeg(self, path);
}
else {
Py_buffer buffer;
PyErr_Clear();
if (PyArg_ParseTuple(args, "y*", &buffer)) {
void* buf = buffer.buf;
Py_ssize_t size = buffer.len;
image_from_buffer(self, buf, size);
PyBuffer_Release(&buffer);
}
}
return PyErr_Occurred() ? -1 : 0;
}
static void Image_dealloc(ImageObject *self) {
if (self->buffer != NULL) {
free(self->buffer);
self->buffer = NULL;
}
}
#if PY_MAJOR_VERSION == 2
PyMODINIT_FUNC initaccimage(void) {
#else
PyMODINIT_FUNC PyInit_accimage(void) {
#endif
PyObject* m;
/*
* Due to cross platform compiler issues the slots must be filled here.
* It's required for portability to Windows without requiring C++.
*/
Image_Type.tp_base = &PyBaseObject_Type;
Image_Type.tp_init = (initproc) Image_init;
Image_Type.tp_dealloc = (destructor) Image_dealloc;
Image_Type.tp_new = PyType_GenericNew;
#if PY_MAJOR_VERSION == 2
m = Py_InitModule("accimage", NULL);
#else
static struct PyModuleDef module_def = {
PyModuleDef_HEAD_INIT,
"accimage",
NULL,
-1,
NULL
};
m = PyModule_Create(&module_def);
#endif
if (m == NULL)
goto err;
if (PyType_Ready(&Image_Type) < 0)
goto err;
PyModule_AddObject(m, "Image", (PyObject*) &Image_Type);
#if PY_MAJOR_VERSION == 2
return;
#else
return m;
#endif
err:
#if PY_MAJOR_VERSION == 2
return;
#else
return NULL;
#endif
}