aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/Pillow/py3/_imaging.c
diff options
context:
space:
mode:
authorshumkovnd <shumkovnd@yandex-team.com>2023-11-10 14:39:34 +0300
committershumkovnd <shumkovnd@yandex-team.com>2023-11-10 16:42:24 +0300
commit77eb2d3fdcec5c978c64e025ced2764c57c00285 (patch)
treec51edb0748ca8d4a08d7c7323312c27ba1a8b79a /contrib/python/Pillow/py3/_imaging.c
parentdd6d20cadb65582270ac23f4b3b14ae189704b9d (diff)
downloadydb-77eb2d3fdcec5c978c64e025ced2764c57c00285.tar.gz
KIKIMR-19287: add task_stats_drawing script
Diffstat (limited to 'contrib/python/Pillow/py3/_imaging.c')
-rw-r--r--contrib/python/Pillow/py3/_imaging.c4406
1 files changed, 4406 insertions, 0 deletions
diff --git a/contrib/python/Pillow/py3/_imaging.c b/contrib/python/Pillow/py3/_imaging.c
new file mode 100644
index 0000000000..2270c77fe7
--- /dev/null
+++ b/contrib/python/Pillow/py3/_imaging.c
@@ -0,0 +1,4406 @@
+/*
+ * The Python Imaging Library.
+ *
+ * the imaging library bindings
+ *
+ * history:
+ * 1995-09-24 fl Created
+ * 1996-03-24 fl Ready for first public release (release 0.0)
+ * 1996-03-25 fl Added fromstring (for Jack's "img" library)
+ * 1996-03-28 fl Added channel operations
+ * 1996-03-31 fl Added point operation
+ * 1996-04-08 fl Added new/new_block/new_array factories
+ * 1996-04-13 fl Added decoders
+ * 1996-05-04 fl Added palette hack
+ * 1996-05-12 fl Compile cleanly as C++
+ * 1996-05-19 fl Added matrix conversions, gradient fills
+ * 1996-05-27 fl Added display_mode
+ * 1996-07-22 fl Added getbbox, offset
+ * 1996-07-23 fl Added sequence semantics
+ * 1996-08-13 fl Added logical operators, point mode
+ * 1996-08-16 fl Modified paste interface
+ * 1996-09-06 fl Added putdata methods, use abstract interface
+ * 1996-11-01 fl Added xbm encoder
+ * 1996-11-04 fl Added experimental path stuff, draw_lines, etc
+ * 1996-12-10 fl Added zip decoder, crc32 interface
+ * 1996-12-14 fl Added modulo arithmetics
+ * 1996-12-29 fl Added zip encoder
+ * 1997-01-03 fl Added fli and msp decoders
+ * 1997-01-04 fl Added experimental sun_rle and tga_rle decoders
+ * 1997-01-05 fl Added gif encoder, getpalette hack
+ * 1997-02-23 fl Added histogram mask
+ * 1997-05-12 fl Minor tweaks to match the IFUNC95 interface
+ * 1997-05-21 fl Added noise generator, spread effect
+ * 1997-06-05 fl Added mandelbrot generator
+ * 1997-08-02 fl Modified putpalette to coerce image mode if necessary
+ * 1998-01-11 fl Added INT32 support
+ * 1998-01-22 fl Fixed draw_points to draw the last point too
+ * 1998-06-28 fl Added getpixel, getink, draw_ink
+ * 1998-07-12 fl Added getextrema
+ * 1998-07-17 fl Added point conversion to arbitrary formats
+ * 1998-09-21 fl Added support for resampling filters
+ * 1998-09-22 fl Added support for quad transform
+ * 1998-12-29 fl Added support for arcs, chords, and pieslices
+ * 1999-01-10 fl Added some experimental arrow graphics stuff
+ * 1999-02-06 fl Added draw_bitmap, font acceleration stuff
+ * 2001-04-17 fl Fixed some egcs compiler nits
+ * 2001-09-17 fl Added screen grab primitives (win32)
+ * 2002-03-09 fl Added stretch primitive
+ * 2002-03-10 fl Fixed filter handling in rotate
+ * 2002-06-06 fl Added I, F, and RGB support to putdata
+ * 2002-06-08 fl Added rankfilter
+ * 2002-06-09 fl Added support for user-defined filter kernels
+ * 2002-11-19 fl Added clipboard grab primitives (win32)
+ * 2002-12-11 fl Added draw context
+ * 2003-04-26 fl Tweaks for Python 2.3 beta 1
+ * 2003-05-21 fl Added createwindow primitive (win32)
+ * 2003-09-13 fl Added thread section hooks
+ * 2003-09-15 fl Added expand helper
+ * 2003-09-26 fl Added experimental LA support
+ * 2004-02-21 fl Handle zero-size images in quantize
+ * 2004-06-05 fl Added ptr attribute (used to access Imaging objects)
+ * 2004-06-05 fl Don't crash when fetching pixels from zero-wide images
+ * 2004-09-17 fl Added getcolors
+ * 2004-10-04 fl Added modefilter
+ * 2005-10-02 fl Added access proxy
+ * 2006-06-18 fl Always draw last point in polyline
+ *
+ * Copyright (c) 1997-2006 by Secret Labs AB
+ * Copyright (c) 1995-2006 by Fredrik Lundh
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+#define PY_SSIZE_T_CLEAN
+#include "Python.h"
+
+#ifdef HAVE_LIBJPEG
+#include "jconfig.h"
+#endif
+
+#ifdef HAVE_LIBZ
+#include "zlib.h"
+#endif
+
+#ifdef HAVE_LIBTIFF
+#ifndef _TIFFIO_
+#include <tiffio.h>
+#endif
+#endif
+
+#include "libImaging/Imaging.h"
+
+#define _USE_MATH_DEFINES
+#include <math.h>
+
+/* Configuration stuff. Feel free to undef things you don't need. */
+#define WITH_IMAGECHOPS /* ImageChops support */
+#define WITH_IMAGEDRAW /* ImageDraw support */
+#define WITH_MAPPING /* use memory mapping to read some file formats */
+#define WITH_IMAGEPATH /* ImagePath stuff */
+#define WITH_ARROW /* arrow graphics stuff (experimental) */
+#define WITH_EFFECTS /* special effects */
+#define WITH_QUANTIZE /* quantization support */
+#define WITH_RANKFILTER /* rank filter */
+#define WITH_MODEFILTER /* mode filter */
+#define WITH_THREADING /* "friendly" threading support */
+#define WITH_UNSHARPMASK /* Kevin Cazabon's unsharpmask module */
+
+#undef VERBOSE
+
+#define B16(p, i) ((((int)p[(i)]) << 8) + p[(i) + 1])
+#define L16(p, i) ((((int)p[(i) + 1]) << 8) + p[(i)])
+#define S16(v) ((v) < 32768 ? (v) : ((v)-65536))
+
+/* -------------------------------------------------------------------- */
+/* OBJECT ADMINISTRATION */
+/* -------------------------------------------------------------------- */
+
+typedef struct {
+ PyObject_HEAD Imaging image;
+ ImagingAccess access;
+} ImagingObject;
+
+static PyTypeObject Imaging_Type;
+
+#ifdef WITH_IMAGEDRAW
+
+typedef struct {
+ /* to write a character, cut out sxy from glyph data, place
+ at current position plus dxy, and advance by (dx, dy) */
+ int dx, dy;
+ int dx0, dy0, dx1, dy1;
+ int sx0, sy0, sx1, sy1;
+} Glyph;
+
+typedef struct {
+ PyObject_HEAD ImagingObject *ref;
+ Imaging bitmap;
+ int ysize;
+ int baseline;
+ Glyph glyphs[256];
+} ImagingFontObject;
+
+static PyTypeObject ImagingFont_Type;
+
+typedef struct {
+ PyObject_HEAD ImagingObject *image;
+ UINT8 ink[4];
+ int blend;
+} ImagingDrawObject;
+
+static PyTypeObject ImagingDraw_Type;
+
+#endif
+
+typedef struct {
+ PyObject_HEAD ImagingObject *image;
+ int readonly;
+} PixelAccessObject;
+
+static PyTypeObject PixelAccess_Type;
+
+PyObject *
+PyImagingNew(Imaging imOut) {
+ ImagingObject *imagep;
+
+ if (!imOut) {
+ return NULL;
+ }
+
+ imagep = PyObject_New(ImagingObject, &Imaging_Type);
+ if (imagep == NULL) {
+ ImagingDelete(imOut);
+ return NULL;
+ }
+
+#ifdef VERBOSE
+ printf("imaging %p allocated\n", imagep);
+#endif
+
+ imagep->image = imOut;
+ imagep->access = ImagingAccessNew(imOut);
+
+ return (PyObject *)imagep;
+}
+
+static void
+_dealloc(ImagingObject *imagep) {
+#ifdef VERBOSE
+ printf("imaging %p deleted\n", imagep);
+#endif
+
+ if (imagep->access) {
+ ImagingAccessDelete(imagep->image, imagep->access);
+ }
+ ImagingDelete(imagep->image);
+ PyObject_Del(imagep);
+}
+
+#define PyImaging_Check(op) (Py_TYPE(op) == &Imaging_Type)
+
+Imaging
+PyImaging_AsImaging(PyObject *op) {
+ if (!PyImaging_Check(op)) {
+ PyErr_BadInternalCall();
+ return NULL;
+ }
+
+ return ((ImagingObject *)op)->image;
+}
+
+/* -------------------------------------------------------------------- */
+/* THREAD HANDLING */
+/* -------------------------------------------------------------------- */
+
+void
+ImagingSectionEnter(ImagingSectionCookie *cookie) {
+#ifdef WITH_THREADING
+ *cookie = (PyThreadState *)PyEval_SaveThread();
+#endif
+}
+
+void
+ImagingSectionLeave(ImagingSectionCookie *cookie) {
+#ifdef WITH_THREADING
+ PyEval_RestoreThread((PyThreadState *)*cookie);
+#endif
+}
+
+/* -------------------------------------------------------------------- */
+/* BUFFER HANDLING */
+/* -------------------------------------------------------------------- */
+/* Python compatibility API */
+
+int
+PyImaging_CheckBuffer(PyObject *buffer) {
+ return PyObject_CheckBuffer(buffer);
+}
+
+int
+PyImaging_GetBuffer(PyObject *buffer, Py_buffer *view) {
+ /* must call check_buffer first! */
+ return PyObject_GetBuffer(buffer, view, PyBUF_SIMPLE);
+}
+
+/* -------------------------------------------------------------------- */
+/* EXCEPTION REROUTING */
+/* -------------------------------------------------------------------- */
+
+/* error messages */
+static const char *must_be_sequence = "argument must be a sequence";
+static const char *must_be_two_coordinates =
+ "coordinate list must contain exactly 2 coordinates";
+static const char *incorrectly_ordered_x_coordinate =
+ "x1 must be greater than or equal to x0";
+static const char *incorrectly_ordered_y_coordinate =
+ "y1 must be greater than or equal to y0";
+static const char *wrong_mode = "unrecognized image mode";
+static const char *wrong_raw_mode = "unrecognized raw mode";
+static const char *outside_image = "image index out of range";
+static const char *outside_palette = "palette index out of range";
+static const char *wrong_palette_size = "invalid palette size";
+static const char *no_palette = "image has no palette";
+static const char *readonly = "image is readonly";
+/* static const char* no_content = "image has no content"; */
+
+void *
+ImagingError_OSError(void) {
+ PyErr_SetString(PyExc_OSError, "error when accessing file");
+ return NULL;
+}
+
+void *
+ImagingError_MemoryError(void) {
+ return PyErr_NoMemory();
+}
+
+void *
+ImagingError_Mismatch(void) {
+ PyErr_SetString(PyExc_ValueError, "images do not match");
+ return NULL;
+}
+
+void *
+ImagingError_ModeError(void) {
+ PyErr_SetString(PyExc_ValueError, "image has wrong mode");
+ return NULL;
+}
+
+void *
+ImagingError_ValueError(const char *message) {
+ PyErr_SetString(
+ PyExc_ValueError, (message) ? (char *)message : "unrecognized argument value");
+ return NULL;
+}
+
+void
+ImagingError_Clear(void) {
+ PyErr_Clear();
+}
+
+/* -------------------------------------------------------------------- */
+/* HELPERS */
+/* -------------------------------------------------------------------- */
+
+static int
+getbands(const char *mode) {
+ Imaging im;
+ int bands;
+
+ /* FIXME: add primitive to libImaging to avoid extra allocation */
+ im = ImagingNew(mode, 0, 0);
+ if (!im) {
+ return -1;
+ }
+
+ bands = im->bands;
+
+ ImagingDelete(im);
+
+ return bands;
+}
+
+#define TYPE_UINT8 (0x100 | sizeof(UINT8))
+#define TYPE_INT32 (0x200 | sizeof(INT32))
+#define TYPE_FLOAT16 (0x500 | sizeof(FLOAT16))
+#define TYPE_FLOAT32 (0x300 | sizeof(FLOAT32))
+#define TYPE_DOUBLE (0x400 | sizeof(double))
+
+static void *
+getlist(PyObject *arg, Py_ssize_t *length, const char *wrong_length, int type) {
+ /* - allocates and returns a c array of the items in the
+ python sequence arg.
+ - the size of the returned array is in length
+ - all of the arg items must be numeric items of the type
+ specified in type
+ - sequence length is checked against the length parameter IF
+ an error parameter is passed in wrong_length
+ - caller is responsible for freeing the memory
+ */
+
+ Py_ssize_t i, n;
+ int itemp;
+ double dtemp;
+ FLOAT32 ftemp;
+ UINT8 *list;
+ PyObject *seq;
+ PyObject *op;
+
+ if (!PySequence_Check(arg)) {
+ PyErr_SetString(PyExc_TypeError, must_be_sequence);
+ return NULL;
+ }
+
+ n = PySequence_Size(arg);
+ if (length && wrong_length && n != *length) {
+ PyErr_SetString(PyExc_ValueError, wrong_length);
+ return NULL;
+ }
+
+ /* malloc check ok, type & ff is just a sizeof(something)
+ calloc checks for overflow */
+ list = calloc(n, type & 0xff);
+ if (!list) {
+ return ImagingError_MemoryError();
+ }
+
+ seq = PySequence_Fast(arg, must_be_sequence);
+ if (!seq) {
+ free(list);
+ return NULL;
+ }
+
+ for (i = 0; i < n; i++) {
+ op = PySequence_Fast_GET_ITEM(seq, i);
+ // DRY, branch prediction is going to work _really_ well
+ // on this switch. And 3 fewer loops to copy/paste.
+ switch (type) {
+ case TYPE_UINT8:
+ itemp = PyLong_AsLong(op);
+ list[i] = CLIP8(itemp);
+ break;
+ case TYPE_INT32:
+ itemp = PyLong_AsLong(op);
+ memcpy(list + i * sizeof(INT32), &itemp, sizeof(itemp));
+ break;
+ case TYPE_FLOAT32:
+ ftemp = (FLOAT32)PyFloat_AsDouble(op);
+ memcpy(list + i * sizeof(ftemp), &ftemp, sizeof(ftemp));
+ break;
+ case TYPE_DOUBLE:
+ dtemp = PyFloat_AsDouble(op);
+ memcpy(list + i * sizeof(dtemp), &dtemp, sizeof(dtemp));
+ break;
+ }
+ }
+
+ Py_DECREF(seq);
+
+ if (PyErr_Occurred()) {
+ free(list);
+ return NULL;
+ }
+
+ if (length) {
+ *length = n;
+ }
+
+ return list;
+}
+
+FLOAT32
+float16tofloat32(const FLOAT16 in) {
+ UINT32 t1;
+ UINT32 t2;
+ UINT32 t3;
+ FLOAT32 out[1] = {0};
+
+ t1 = in & 0x7fff; // Non-sign bits
+ t2 = in & 0x8000; // Sign bit
+ t3 = in & 0x7c00; // Exponent
+
+ t1 <<= 13; // Align mantissa on MSB
+ t2 <<= 16; // Shift sign bit into position
+
+ t1 += 0x38000000; // Adjust bias
+
+ t1 = (t3 == 0 ? 0 : t1); // Denormals-as-zero
+
+ t1 |= t2; // Re-insert sign bit
+
+ memcpy(out, &t1, 4);
+ return out[0];
+}
+
+static inline PyObject *
+getpixel(Imaging im, ImagingAccess access, int x, int y) {
+ union {
+ UINT8 b[4];
+ UINT16 h;
+ INT32 i;
+ FLOAT32 f;
+ } pixel;
+
+ if (x < 0) {
+ x = im->xsize + x;
+ }
+ if (y < 0) {
+ y = im->ysize + y;
+ }
+
+ if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) {
+ PyErr_SetString(PyExc_IndexError, outside_image);
+ return NULL;
+ }
+
+ access->get_pixel(im, x, y, &pixel);
+
+ switch (im->type) {
+ case IMAGING_TYPE_UINT8:
+ switch (im->bands) {
+ case 1:
+ return PyLong_FromLong(pixel.b[0]);
+ case 2:
+ return Py_BuildValue("BB", pixel.b[0], pixel.b[1]);
+ case 3:
+ return Py_BuildValue("BBB", pixel.b[0], pixel.b[1], pixel.b[2]);
+ case 4:
+ return Py_BuildValue(
+ "BBBB", pixel.b[0], pixel.b[1], pixel.b[2], pixel.b[3]);
+ }
+ break;
+ case IMAGING_TYPE_INT32:
+ return PyLong_FromLong(pixel.i);
+ case IMAGING_TYPE_FLOAT32:
+ return PyFloat_FromDouble(pixel.f);
+ case IMAGING_TYPE_SPECIAL:
+ if (im->bands == 1) {
+ return PyLong_FromLong(pixel.h);
+ } else {
+ return Py_BuildValue("BBB", pixel.b[0], pixel.b[1], pixel.b[2]);
+ }
+ break;
+ }
+
+ /* unknown type */
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static char *
+getink(PyObject *color, Imaging im, char *ink) {
+ int g = 0, b = 0, a = 0;
+ double f = 0;
+ /* Windows 64 bit longs are 32 bits, and 0xFFFFFFFF (white) is a
+ Python long (not int) that raises an overflow error when trying
+ to return it into a 32 bit C long
+ */
+ PY_LONG_LONG r = 0;
+ FLOAT32 ftmp;
+ INT32 itmp;
+
+ /* fill ink buffer (four bytes) with something that can
+ be cast to either UINT8 or INT32 */
+
+ int rIsInt = 0;
+ int tupleSize = PyTuple_Check(color) ? PyTuple_GET_SIZE(color) : -1;
+ if (tupleSize == 1) {
+ color = PyTuple_GetItem(color, 0);
+ }
+ if (im->type == IMAGING_TYPE_UINT8 || im->type == IMAGING_TYPE_INT32 ||
+ im->type == IMAGING_TYPE_SPECIAL) {
+ if (PyLong_Check(color)) {
+ r = PyLong_AsLongLong(color);
+ if (r == -1 && PyErr_Occurred()) {
+ return NULL;
+ }
+ rIsInt = 1;
+ } else if (im->bands == 1) {
+ PyErr_SetString(
+ PyExc_TypeError, "color must be int or single-element tuple");
+ return NULL;
+ } else if (tupleSize == -1) {
+ PyErr_SetString(PyExc_TypeError, "color must be int or tuple");
+ return NULL;
+ }
+ }
+
+ switch (im->type) {
+ case IMAGING_TYPE_UINT8:
+ /* unsigned integer */
+ if (im->bands == 1) {
+ /* unsigned integer, single layer */
+ if (rIsInt != 1) {
+ if (tupleSize != 1) {
+ PyErr_SetString(PyExc_TypeError, "color must be int or single-element tuple");
+ return NULL;
+ } else if (!PyArg_ParseTuple(color, "L", &r)) {
+ return NULL;
+ }
+ }
+ ink[0] = (char)CLIP8(r);
+ ink[1] = ink[2] = ink[3] = 0;
+ } else {
+ if (rIsInt) {
+ /* compatibility: ABGR */
+ a = (UINT8)(r >> 24);
+ b = (UINT8)(r >> 16);
+ g = (UINT8)(r >> 8);
+ r = (UINT8)r;
+ } else {
+ a = 255;
+ if (im->bands == 2) {
+ if (tupleSize != 1 && tupleSize != 2) {
+ PyErr_SetString(PyExc_TypeError, "color must be int, or tuple of one or two elements");
+ return NULL;
+ } else if (!PyArg_ParseTuple(color, "L|i", &r, &a)) {
+ return NULL;
+ }
+ g = b = r;
+ } else {
+ if (tupleSize != 3 && tupleSize != 4) {
+ PyErr_SetString(PyExc_TypeError, "color must be int, or tuple of one, three or four elements");
+ return NULL;
+ } else if (!PyArg_ParseTuple(color, "Lii|i", &r, &g, &b, &a)) {
+ return NULL;
+ }
+ }
+ }
+ ink[0] = (char)CLIP8(r);
+ ink[1] = (char)CLIP8(g);
+ ink[2] = (char)CLIP8(b);
+ ink[3] = (char)CLIP8(a);
+ }
+ return ink;
+ case IMAGING_TYPE_INT32:
+ /* signed integer */
+ itmp = r;
+ memcpy(ink, &itmp, sizeof(itmp));
+ return ink;
+ case IMAGING_TYPE_FLOAT32:
+ /* floating point */
+ f = PyFloat_AsDouble(color);
+ if (f == -1.0 && PyErr_Occurred()) {
+ return NULL;
+ }
+ ftmp = f;
+ memcpy(ink, &ftmp, sizeof(ftmp));
+ return ink;
+ case IMAGING_TYPE_SPECIAL:
+ if (strncmp(im->mode, "I;16", 4) == 0) {
+ ink[0] = (UINT8)r;
+ ink[1] = (UINT8)(r >> 8);
+ ink[2] = ink[3] = 0;
+ return ink;
+ } else {
+ if (rIsInt) {
+ b = (UINT8)(r >> 16);
+ g = (UINT8)(r >> 8);
+ r = (UINT8)r;
+ } else if (tupleSize != 3) {
+ PyErr_SetString(PyExc_TypeError, "color must be int, or tuple of one or three elements");
+ return NULL;
+ } else if (!PyArg_ParseTuple(color, "iiL", &b, &g, &r)) {
+ return NULL;
+ }
+ if (!strcmp(im->mode, "BGR;15")) {
+ UINT16 v = ((((UINT16)r) << 7) & 0x7c00) +
+ ((((UINT16)g) << 2) & 0x03e0) +
+ ((((UINT16)b) >> 3) & 0x001f);
+
+ ink[0] = (UINT8)v;
+ ink[1] = (UINT8)(v >> 8);
+ ink[2] = ink[3] = 0;
+ return ink;
+ } else if (!strcmp(im->mode, "BGR;16")) {
+ UINT16 v = ((((UINT16)r) << 8) & 0xf800) +
+ ((((UINT16)g) << 3) & 0x07e0) +
+ ((((UINT16)b) >> 3) & 0x001f);
+ ink[0] = (UINT8)v;
+ ink[1] = (UINT8)(v >> 8);
+ ink[2] = ink[3] = 0;
+ return ink;
+ } else if (!strcmp(im->mode, "BGR;24")) {
+ ink[0] = (UINT8)b;
+ ink[1] = (UINT8)g;
+ ink[2] = (UINT8)r;
+ ink[3] = 0;
+ return ink;
+ }
+ }
+ }
+
+ PyErr_SetString(PyExc_ValueError, wrong_mode);
+ return NULL;
+}
+
+/* -------------------------------------------------------------------- */
+/* FACTORIES */
+/* -------------------------------------------------------------------- */
+
+static PyObject *
+_fill(PyObject *self, PyObject *args) {
+ char *mode;
+ int xsize, ysize;
+ PyObject *color;
+ char buffer[4];
+ Imaging im;
+
+ xsize = ysize = 256;
+ color = NULL;
+
+ if (!PyArg_ParseTuple(args, "s|(ii)O", &mode, &xsize, &ysize, &color)) {
+ return NULL;
+ }
+
+ im = ImagingNewDirty(mode, xsize, ysize);
+ if (!im) {
+ return NULL;
+ }
+
+ buffer[0] = buffer[1] = buffer[2] = buffer[3] = 0;
+ if (color) {
+ if (!getink(color, im, buffer)) {
+ ImagingDelete(im);
+ return NULL;
+ }
+ }
+
+ (void)ImagingFill(im, buffer);
+
+ return PyImagingNew(im);
+}
+
+static PyObject *
+_new(PyObject *self, PyObject *args) {
+ char *mode;
+ int xsize, ysize;
+
+ if (!PyArg_ParseTuple(args, "s(ii)", &mode, &xsize, &ysize)) {
+ return NULL;
+ }
+
+ return PyImagingNew(ImagingNew(mode, xsize, ysize));
+}
+
+static PyObject *
+_new_block(PyObject *self, PyObject *args) {
+ char *mode;
+ int xsize, ysize;
+
+ if (!PyArg_ParseTuple(args, "s(ii)", &mode, &xsize, &ysize)) {
+ return NULL;
+ }
+
+ return PyImagingNew(ImagingNewBlock(mode, xsize, ysize));
+}
+
+static PyObject *
+_linear_gradient(PyObject *self, PyObject *args) {
+ char *mode;
+
+ if (!PyArg_ParseTuple(args, "s", &mode)) {
+ return NULL;
+ }
+
+ return PyImagingNew(ImagingFillLinearGradient(mode));
+}
+
+static PyObject *
+_radial_gradient(PyObject *self, PyObject *args) {
+ char *mode;
+
+ if (!PyArg_ParseTuple(args, "s", &mode)) {
+ return NULL;
+ }
+
+ return PyImagingNew(ImagingFillRadialGradient(mode));
+}
+
+static PyObject *
+_alpha_composite(ImagingObject *self, PyObject *args) {
+ ImagingObject *imagep1;
+ ImagingObject *imagep2;
+
+ if (!PyArg_ParseTuple(
+ args, "O!O!", &Imaging_Type, &imagep1, &Imaging_Type, &imagep2)) {
+ return NULL;
+ }
+
+ return PyImagingNew(ImagingAlphaComposite(imagep1->image, imagep2->image));
+}
+
+static PyObject *
+_blend(ImagingObject *self, PyObject *args) {
+ ImagingObject *imagep1;
+ ImagingObject *imagep2;
+ double alpha;
+
+ alpha = 0.5;
+ if (!PyArg_ParseTuple(
+ args, "O!O!|d", &Imaging_Type, &imagep1, &Imaging_Type, &imagep2, &alpha)) {
+ return NULL;
+ }
+
+ return PyImagingNew(ImagingBlend(imagep1->image, imagep2->image, (float)alpha));
+}
+
+/* -------------------------------------------------------------------- */
+/* METHODS */
+/* -------------------------------------------------------------------- */
+
+static INT16 *
+_prepare_lut_table(PyObject *table, Py_ssize_t table_size) {
+ int i;
+ Py_buffer buffer_info;
+ INT32 data_type = TYPE_FLOAT32;
+ float item = 0;
+ void *table_data = NULL;
+ int free_table_data = 0;
+ INT16 *prepared;
+
+/* NOTE: This value should be the same as in ColorLUT.c */
+#define PRECISION_BITS (16 - 8 - 2)
+
+ const char *wrong_size =
+ ("The table should have table_channels * "
+ "size1D * size2D * size3D float items.");
+
+ if (PyObject_CheckBuffer(table)) {
+ if (!PyObject_GetBuffer(table, &buffer_info, PyBUF_CONTIG_RO | PyBUF_FORMAT)) {
+ if (buffer_info.ndim == 1 && buffer_info.shape[0] == table_size) {
+ if (strlen(buffer_info.format) == 1) {
+ switch (buffer_info.format[0]) {
+ case 'e':
+ data_type = TYPE_FLOAT16;
+ table_data = buffer_info.buf;
+ break;
+ case 'f':
+ data_type = TYPE_FLOAT32;
+ table_data = buffer_info.buf;
+ break;
+ case 'd':
+ data_type = TYPE_DOUBLE;
+ table_data = buffer_info.buf;
+ break;
+ }
+ }
+ }
+ PyBuffer_Release(&buffer_info);
+ }
+ }
+
+ if (!table_data) {
+ free_table_data = 1;
+ table_data = getlist(table, &table_size, wrong_size, TYPE_FLOAT32);
+ if (!table_data) {
+ return NULL;
+ }
+ }
+
+ /* malloc check ok, max is 2 * 4 * 65**3 = 2197000 */
+ prepared = (INT16 *)malloc(sizeof(INT16) * table_size);
+ if (!prepared) {
+ if (free_table_data) {
+ free(table_data);
+ }
+ return (INT16 *)ImagingError_MemoryError();
+ }
+
+ for (i = 0; i < table_size; i++) {
+ FLOAT16 htmp;
+ double dtmp;
+ switch (data_type) {
+ case TYPE_FLOAT16:
+ memcpy(&htmp, ((char *)table_data) + i * sizeof(htmp), sizeof(htmp));
+ item = float16tofloat32(htmp);
+ break;
+ case TYPE_FLOAT32:
+ memcpy(
+ &item, ((char *)table_data) + i * sizeof(FLOAT32), sizeof(FLOAT32));
+ break;
+ case TYPE_DOUBLE:
+ memcpy(&dtmp, ((char *)table_data) + i * sizeof(dtmp), sizeof(dtmp));
+ item = (FLOAT32)dtmp;
+ break;
+ }
+ /* Max value for INT16 */
+ if (item >= (0x7fff - 0.5) / (255 << PRECISION_BITS)) {
+ prepared[i] = 0x7fff;
+ continue;
+ }
+ /* Min value for INT16 */
+ if (item <= (-0x8000 + 0.5) / (255 << PRECISION_BITS)) {
+ prepared[i] = -0x8000;
+ continue;
+ }
+ if (item < 0) {
+ prepared[i] = item * (255 << PRECISION_BITS) - 0.5;
+ } else {
+ prepared[i] = item * (255 << PRECISION_BITS) + 0.5;
+ }
+ }
+
+#undef PRECISION_BITS
+ if (free_table_data) {
+ free(table_data);
+ }
+ return prepared;
+}
+
+static PyObject *
+_color_lut_3d(ImagingObject *self, PyObject *args) {
+ char *mode;
+ int filter;
+ int table_channels;
+ int size1D, size2D, size3D;
+ PyObject *table;
+
+ INT16 *prepared_table;
+ Imaging imOut;
+
+ if (!PyArg_ParseTuple(
+ args,
+ "siiiiiO:color_lut_3d",
+ &mode,
+ &filter,
+ &table_channels,
+ &size1D,
+ &size2D,
+ &size3D,
+ &table)) {
+ return NULL;
+ }
+
+ /* actually, it is trilinear */
+ if (filter != IMAGING_TRANSFORM_BILINEAR) {
+ PyErr_SetString(PyExc_ValueError, "Only LINEAR filter is supported.");
+ return NULL;
+ }
+
+ if (1 > table_channels || table_channels > 4) {
+ PyErr_SetString(PyExc_ValueError, "table_channels should be from 1 to 4");
+ return NULL;
+ }
+
+ if (2 > size1D || size1D > 65 || 2 > size2D || size2D > 65 || 2 > size3D ||
+ size3D > 65) {
+ PyErr_SetString(
+ PyExc_ValueError, "Table size in any dimension should be from 2 to 65");
+ return NULL;
+ }
+
+ prepared_table =
+ _prepare_lut_table(table, table_channels * size1D * size2D * size3D);
+ if (!prepared_table) {
+ return NULL;
+ }
+
+ imOut = ImagingNewDirty(mode, self->image->xsize, self->image->ysize);
+ if (!imOut) {
+ free(prepared_table);
+ return NULL;
+ }
+
+ if (!ImagingColorLUT3D_linear(
+ imOut,
+ self->image,
+ table_channels,
+ size1D,
+ size2D,
+ size3D,
+ prepared_table)) {
+ free(prepared_table);
+ ImagingDelete(imOut);
+ return NULL;
+ }
+
+ free(prepared_table);
+
+ return PyImagingNew(imOut);
+}
+
+static PyObject *
+_convert(ImagingObject *self, PyObject *args) {
+ char *mode;
+ int dither = 0;
+ ImagingObject *paletteimage = NULL;
+
+ if (!PyArg_ParseTuple(args, "s|iO", &mode, &dither, &paletteimage)) {
+ return NULL;
+ }
+ if (paletteimage != NULL) {
+ if (!PyImaging_Check(paletteimage)) {
+ PyObject_Print((PyObject *)paletteimage, stderr, 0);
+ PyErr_SetString(
+ PyExc_ValueError, "palette argument must be image with mode 'P'");
+ return NULL;
+ }
+ if (paletteimage->image->palette == NULL) {
+ PyErr_SetString(PyExc_ValueError, "null palette");
+ return NULL;
+ }
+ }
+
+ return PyImagingNew(ImagingConvert(
+ self->image, mode, paletteimage ? paletteimage->image->palette : NULL, dither));
+}
+
+static PyObject *
+_convert2(ImagingObject *self, PyObject *args) {
+ ImagingObject *imagep1;
+ ImagingObject *imagep2;
+ if (!PyArg_ParseTuple(
+ args, "O!O!", &Imaging_Type, &imagep1, &Imaging_Type, &imagep2)) {
+ return NULL;
+ }
+
+ if (!ImagingConvert2(imagep1->image, imagep2->image)) {
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_convert_matrix(ImagingObject *self, PyObject *args) {
+ char *mode;
+ float m[12];
+ if (!PyArg_ParseTuple(args, "s(ffff)", &mode, m + 0, m + 1, m + 2, m + 3)) {
+ PyErr_Clear();
+ if (!PyArg_ParseTuple(
+ args,
+ "s(ffffffffffff)",
+ &mode,
+ m + 0,
+ m + 1,
+ m + 2,
+ m + 3,
+ m + 4,
+ m + 5,
+ m + 6,
+ m + 7,
+ m + 8,
+ m + 9,
+ m + 10,
+ m + 11)) {
+ return NULL;
+ }
+ }
+
+ return PyImagingNew(ImagingConvertMatrix(self->image, mode, m));
+}
+
+static PyObject *
+_convert_transparent(ImagingObject *self, PyObject *args) {
+ char *mode;
+ int r, g, b;
+ if (PyArg_ParseTuple(args, "s(iii)", &mode, &r, &g, &b)) {
+ return PyImagingNew(ImagingConvertTransparent(self->image, mode, r, g, b));
+ }
+ PyErr_Clear();
+ if (PyArg_ParseTuple(args, "si", &mode, &r)) {
+ return PyImagingNew(ImagingConvertTransparent(self->image, mode, r, 0, 0));
+ }
+ return NULL;
+}
+
+static PyObject *
+_copy(ImagingObject *self, PyObject *args) {
+ if (!PyArg_ParseTuple(args, "")) {
+ return NULL;
+ }
+
+ return PyImagingNew(ImagingCopy(self->image));
+}
+
+static PyObject *
+_crop(ImagingObject *self, PyObject *args) {
+ int x0, y0, x1, y1;
+ if (!PyArg_ParseTuple(args, "(iiii)", &x0, &y0, &x1, &y1)) {
+ return NULL;
+ }
+
+ return PyImagingNew(ImagingCrop(self->image, x0, y0, x1, y1));
+}
+
+static PyObject *
+_expand_image(ImagingObject *self, PyObject *args) {
+ int x, y;
+ if (!PyArg_ParseTuple(args, "ii", &x, &y)) {
+ return NULL;
+ }
+
+ return PyImagingNew(ImagingExpand(self->image, x, y));
+}
+
+static PyObject *
+_filter(ImagingObject *self, PyObject *args) {
+ PyObject *imOut;
+ Py_ssize_t kernelsize;
+ FLOAT32 *kerneldata;
+
+ int xsize, ysize, i;
+ float divisor, offset;
+ PyObject *kernel = NULL;
+ if (!PyArg_ParseTuple(
+ args, "(ii)ffO", &xsize, &ysize, &divisor, &offset, &kernel)) {
+ return NULL;
+ }
+
+ /* get user-defined kernel */
+ kerneldata = getlist(kernel, &kernelsize, NULL, TYPE_FLOAT32);
+ if (!kerneldata) {
+ return NULL;
+ }
+ if (kernelsize != (Py_ssize_t)xsize * (Py_ssize_t)ysize) {
+ free(kerneldata);
+ return ImagingError_ValueError("bad kernel size");
+ }
+
+ for (i = 0; i < kernelsize; ++i) {
+ kerneldata[i] /= divisor;
+ }
+
+ imOut = PyImagingNew(ImagingFilter(self->image, xsize, ysize, kerneldata, offset));
+
+ free(kerneldata);
+
+ return imOut;
+}
+
+#ifdef WITH_UNSHARPMASK
+static PyObject *
+_gaussian_blur(ImagingObject *self, PyObject *args) {
+ Imaging imIn;
+ Imaging imOut;
+
+ float xradius, yradius;
+ int passes = 3;
+ if (!PyArg_ParseTuple(args, "(ff)|i", &xradius, &yradius, &passes)) {
+ return NULL;
+ }
+
+ imIn = self->image;
+ imOut = ImagingNewDirty(imIn->mode, imIn->xsize, imIn->ysize);
+ if (!imOut) {
+ return NULL;
+ }
+
+ if (!ImagingGaussianBlur(imOut, imIn, xradius, yradius, passes)) {
+ ImagingDelete(imOut);
+ return NULL;
+ }
+
+ return PyImagingNew(imOut);
+}
+#endif
+
+static PyObject *
+_getpalette(ImagingObject *self, PyObject *args) {
+ PyObject *palette;
+ int palettesize;
+ int bits;
+ ImagingShuffler pack;
+
+ char *mode = "RGB";
+ char *rawmode = "RGB";
+ if (!PyArg_ParseTuple(args, "|ss", &mode, &rawmode)) {
+ return NULL;
+ }
+
+ if (!self->image->palette) {
+ PyErr_SetString(PyExc_ValueError, no_palette);
+ return NULL;
+ }
+
+ pack = ImagingFindPacker(mode, rawmode, &bits);
+ if (!pack) {
+ PyErr_SetString(PyExc_ValueError, wrong_raw_mode);
+ return NULL;
+ }
+
+ palettesize = self->image->palette->size;
+ palette = PyBytes_FromStringAndSize(NULL, palettesize * bits / 8);
+ if (!palette) {
+ return NULL;
+ }
+
+ pack(
+ (UINT8 *)PyBytes_AsString(palette), self->image->palette->palette, palettesize);
+
+ return palette;
+}
+
+static PyObject *
+_getpalettemode(ImagingObject *self) {
+ if (!self->image->palette) {
+ PyErr_SetString(PyExc_ValueError, no_palette);
+ return NULL;
+ }
+
+ return PyUnicode_FromString(self->image->palette->mode);
+}
+
+static inline int
+_getxy(PyObject *xy, int *x, int *y) {
+ PyObject *value;
+
+ if (!PyTuple_Check(xy) || PyTuple_GET_SIZE(xy) != 2) {
+ goto badarg;
+ }
+
+ value = PyTuple_GET_ITEM(xy, 0);
+ if (PyLong_Check(value)) {
+ *x = PyLong_AS_LONG(value);
+ } else if (PyFloat_Check(value)) {
+ *x = (int)PyFloat_AS_DOUBLE(value);
+ } else {
+ PyObject *int_value = PyObject_CallMethod(value, "__int__", NULL);
+ if (int_value != NULL && PyLong_Check(int_value)) {
+ *x = PyLong_AS_LONG(int_value);
+ } else {
+ goto badval;
+ }
+ }
+
+ value = PyTuple_GET_ITEM(xy, 1);
+ if (PyLong_Check(value)) {
+ *y = PyLong_AS_LONG(value);
+ } else if (PyFloat_Check(value)) {
+ *y = (int)PyFloat_AS_DOUBLE(value);
+ } else {
+ PyObject *int_value = PyObject_CallMethod(value, "__int__", NULL);
+ if (int_value != NULL && PyLong_Check(int_value)) {
+ *y = PyLong_AS_LONG(int_value);
+ } else {
+ goto badval;
+ }
+ }
+
+ return 0;
+
+badarg:
+ PyErr_SetString(PyExc_TypeError, "argument must be sequence of length 2");
+ return -1;
+
+badval:
+ PyErr_SetString(PyExc_TypeError, "an integer is required");
+ return -1;
+}
+
+static PyObject *
+_getpixel(ImagingObject *self, PyObject *args) {
+ PyObject *xy;
+ int x, y;
+
+ if (PyTuple_GET_SIZE(args) != 1) {
+ PyErr_SetString(PyExc_TypeError, "argument 1 must be sequence of length 2");
+ return NULL;
+ }
+
+ xy = PyTuple_GET_ITEM(args, 0);
+
+ if (_getxy(xy, &x, &y)) {
+ return NULL;
+ }
+
+ if (self->access == NULL) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ return getpixel(self->image, self->access, x, y);
+}
+
+union hist_extrema {
+ UINT8 u[2];
+ INT32 i[2];
+ FLOAT32 f[2];
+};
+
+static union hist_extrema *
+parse_histogram_extremap(
+ ImagingObject *self, PyObject *extremap, union hist_extrema *ep) {
+ int i0, i1;
+ double f0, f1;
+
+ if (extremap) {
+ switch (self->image->type) {
+ case IMAGING_TYPE_UINT8:
+ if (!PyArg_ParseTuple(extremap, "ii", &i0, &i1)) {
+ return NULL;
+ }
+ ep->u[0] = CLIP8(i0);
+ ep->u[1] = CLIP8(i1);
+ break;
+ case IMAGING_TYPE_INT32:
+ if (!PyArg_ParseTuple(extremap, "ii", &i0, &i1)) {
+ return NULL;
+ }
+ ep->i[0] = i0;
+ ep->i[1] = i1;
+ break;
+ case IMAGING_TYPE_FLOAT32:
+ if (!PyArg_ParseTuple(extremap, "dd", &f0, &f1)) {
+ return NULL;
+ }
+ ep->f[0] = (FLOAT32)f0;
+ ep->f[1] = (FLOAT32)f1;
+ break;
+ default:
+ return NULL;
+ }
+ } else {
+ return NULL;
+ }
+ return ep;
+}
+
+static PyObject *
+_histogram(ImagingObject *self, PyObject *args) {
+ ImagingHistogram h;
+ PyObject *list;
+ int i;
+ union hist_extrema extrema;
+ union hist_extrema *ep;
+
+ PyObject *extremap = NULL;
+ ImagingObject *maskp = NULL;
+ if (!PyArg_ParseTuple(args, "|OO!", &extremap, &Imaging_Type, &maskp)) {
+ return NULL;
+ }
+
+ /* Using a var to avoid allocations. */
+ ep = parse_histogram_extremap(self, extremap, &extrema);
+ h = ImagingGetHistogram(self->image, (maskp) ? maskp->image : NULL, ep);
+
+ if (!h) {
+ return NULL;
+ }
+
+ /* Build an integer list containing the histogram */
+ list = PyList_New(h->bands * 256);
+ if (list == NULL) {
+ ImagingHistogramDelete(h);
+ return NULL;
+ }
+ for (i = 0; i < h->bands * 256; i++) {
+ PyObject *item;
+ item = PyLong_FromLong(h->histogram[i]);
+ if (item == NULL) {
+ Py_DECREF(list);
+ list = NULL;
+ break;
+ }
+ PyList_SetItem(list, i, item);
+ }
+
+ /* Destroy the histogram structure */
+ ImagingHistogramDelete(h);
+
+ return list;
+}
+
+static PyObject *
+_entropy(ImagingObject *self, PyObject *args) {
+ ImagingHistogram h;
+ int idx, length;
+ long sum;
+ double entropy, fsum, p;
+ union hist_extrema extrema;
+ union hist_extrema *ep;
+
+ PyObject *extremap = NULL;
+ ImagingObject *maskp = NULL;
+ if (!PyArg_ParseTuple(args, "|OO!", &extremap, &Imaging_Type, &maskp)) {
+ return NULL;
+ }
+
+ /* Using a local var to avoid allocations. */
+ ep = parse_histogram_extremap(self, extremap, &extrema);
+ h = ImagingGetHistogram(self->image, (maskp) ? maskp->image : NULL, ep);
+
+ if (!h) {
+ return NULL;
+ }
+
+ /* Calculate the histogram entropy */
+ /* First, sum the histogram data */
+ length = h->bands * 256;
+ sum = 0;
+ for (idx = 0; idx < length; idx++) {
+ sum += h->histogram[idx];
+ }
+
+ /* Next, normalize the histogram data, */
+ /* using the histogram sum value */
+ fsum = (double)sum;
+ entropy = 0.0;
+ for (idx = 0; idx < length; idx++) {
+ p = (double)h->histogram[idx] / fsum;
+ if (p != 0.0) {
+ entropy += p * log(p) * M_LOG2E;
+ }
+ }
+
+ /* Destroy the histogram structure */
+ ImagingHistogramDelete(h);
+
+ return PyFloat_FromDouble(-entropy);
+}
+
+#ifdef WITH_MODEFILTER
+static PyObject *
+_modefilter(ImagingObject *self, PyObject *args) {
+ int size;
+ if (!PyArg_ParseTuple(args, "i", &size)) {
+ return NULL;
+ }
+
+ return PyImagingNew(ImagingModeFilter(self->image, size));
+}
+#endif
+
+static PyObject *
+_offset(ImagingObject *self, PyObject *args) {
+ int xoffset, yoffset;
+ if (!PyArg_ParseTuple(args, "ii", &xoffset, &yoffset)) {
+ return NULL;
+ }
+
+ return PyImagingNew(ImagingOffset(self->image, xoffset, yoffset));
+}
+
+static PyObject *
+_paste(ImagingObject *self, PyObject *args) {
+ int status;
+ char ink[4];
+
+ PyObject *source;
+ int x0, y0, x1, y1;
+ ImagingObject *maskp = NULL;
+ if (!PyArg_ParseTuple(
+ args, "O(iiii)|O!", &source, &x0, &y0, &x1, &y1, &Imaging_Type, &maskp)) {
+ return NULL;
+ }
+
+ if (PyImaging_Check(source)) {
+ status = ImagingPaste(
+ self->image,
+ PyImaging_AsImaging(source),
+ (maskp) ? maskp->image : NULL,
+ x0,
+ y0,
+ x1,
+ y1);
+
+ } else {
+ if (!getink(source, self->image, ink)) {
+ return NULL;
+ }
+ status = ImagingFill2(
+ self->image, ink, (maskp) ? maskp->image : NULL, x0, y0, x1, y1);
+ }
+
+ if (status < 0) {
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_point(ImagingObject *self, PyObject *args) {
+ static const char *wrong_number = "wrong number of lut entries";
+
+ Py_ssize_t n;
+ int i, bands;
+ Imaging im;
+
+ PyObject *list;
+ char *mode;
+ if (!PyArg_ParseTuple(args, "Oz", &list, &mode)) {
+ return NULL;
+ }
+
+ if (mode && !strcmp(mode, "F")) {
+ FLOAT32 *data;
+
+ /* map from 8-bit data to floating point */
+ n = 256;
+ data = getlist(list, &n, wrong_number, TYPE_FLOAT32);
+ if (!data) {
+ return NULL;
+ }
+ im = ImagingPoint(self->image, mode, (void *)data);
+ free(data);
+
+ } else if (!strcmp(self->image->mode, "I") && mode && !strcmp(mode, "L")) {
+ UINT8 *data;
+
+ /* map from 16-bit subset of 32-bit data to 8-bit */
+ /* FIXME: support arbitrary number of entries (requires API change) */
+ n = 65536;
+ data = getlist(list, &n, wrong_number, TYPE_UINT8);
+ if (!data) {
+ return NULL;
+ }
+ im = ImagingPoint(self->image, mode, (void *)data);
+ free(data);
+
+ } else {
+ INT32 *data;
+ UINT8 lut[1024];
+
+ if (mode) {
+ bands = getbands(mode);
+ if (bands < 0) {
+ return NULL;
+ }
+ } else {
+ bands = self->image->bands;
+ }
+
+ /* map to integer data */
+ n = 256 * bands;
+ data = getlist(list, &n, wrong_number, TYPE_INT32);
+ if (!data) {
+ return NULL;
+ }
+
+ if (mode && !strcmp(mode, "I")) {
+ im = ImagingPoint(self->image, mode, (void *)data);
+ } else if (mode && bands > 1) {
+ for (i = 0; i < 256; i++) {
+ lut[i * 4] = CLIP8(data[i]);
+ lut[i * 4 + 1] = CLIP8(data[i + 256]);
+ lut[i * 4 + 2] = CLIP8(data[i + 512]);
+ if (n > 768) {
+ lut[i * 4 + 3] = CLIP8(data[i + 768]);
+ }
+ }
+ im = ImagingPoint(self->image, mode, (void *)lut);
+ } else {
+ /* map individual bands */
+ for (i = 0; i < n; i++) {
+ lut[i] = CLIP8(data[i]);
+ }
+ im = ImagingPoint(self->image, mode, (void *)lut);
+ }
+ free(data);
+ }
+
+ return PyImagingNew(im);
+}
+
+static PyObject *
+_point_transform(ImagingObject *self, PyObject *args) {
+ double scale = 1.0;
+ double offset = 0.0;
+ if (!PyArg_ParseTuple(args, "|dd", &scale, &offset)) {
+ return NULL;
+ }
+
+ return PyImagingNew(ImagingPointTransform(self->image, scale, offset));
+}
+
+static PyObject *
+_putdata(ImagingObject *self, PyObject *args) {
+ Imaging image;
+ // i & n are # pixels, require py_ssize_t. x can be as large as n. y, just because.
+ Py_ssize_t n, i, x, y;
+
+ PyObject *data;
+ PyObject *seq = NULL;
+ PyObject *op;
+ double scale = 1.0;
+ double offset = 0.0;
+
+ if (!PyArg_ParseTuple(args, "O|dd", &data, &scale, &offset)) {
+ return NULL;
+ }
+
+ if (!PySequence_Check(data)) {
+ PyErr_SetString(PyExc_TypeError, must_be_sequence);
+ return NULL;
+ }
+
+ image = self->image;
+
+ n = PyObject_Length(data);
+ if (n > (Py_ssize_t)image->xsize * (Py_ssize_t)image->ysize) {
+ PyErr_SetString(PyExc_TypeError, "too many data entries");
+ return NULL;
+ }
+
+#define set_value_to_item(seq, i) \
+op = PySequence_Fast_GET_ITEM(seq, i); \
+if (PySequence_Check(op)) { \
+ PyErr_SetString(PyExc_TypeError, "sequence must be flattened"); \
+ return NULL; \
+} else { \
+ value = PyFloat_AsDouble(op); \
+}
+ if (image->image8) {
+ if (PyBytes_Check(data)) {
+ unsigned char *p;
+ p = (unsigned char *)PyBytes_AS_STRING(data);
+ if (scale == 1.0 && offset == 0.0) {
+ /* Plain string data */
+ for (i = y = 0; i < n; i += image->xsize, y++) {
+ x = n - i;
+ if (x > (int)image->xsize) {
+ x = image->xsize;
+ }
+ memcpy(image->image8[y], p + i, x);
+ }
+ } else {
+ /* Scaled and clipped string data */
+ for (i = x = y = 0; i < n; i++) {
+ image->image8[y][x] = CLIP8((int)(p[i] * scale + offset));
+ if (++x >= (int)image->xsize) {
+ x = 0, y++;
+ }
+ }
+ }
+ } else {
+ seq = PySequence_Fast(data, must_be_sequence);
+ if (!seq) {
+ PyErr_SetString(PyExc_TypeError, must_be_sequence);
+ return NULL;
+ }
+ double value;
+ if (image->bands == 1) {
+ int bigendian = 0;
+ if (image->type == IMAGING_TYPE_SPECIAL) {
+ // I;16*
+ bigendian = strcmp(image->mode, "I;16B") == 0;
+ }
+ for (i = x = y = 0; i < n; i++) {
+ set_value_to_item(seq, i);
+ if (scale != 1.0 || offset != 0.0) {
+ value = value * scale + offset;
+ }
+ if (image->type == IMAGING_TYPE_SPECIAL) {
+ image->image8[y][x * 2 + (bigendian ? 1 : 0)] = CLIP8((int)value % 256);
+ image->image8[y][x * 2 + (bigendian ? 0 : 1)] = CLIP8((int)value >> 8);
+ } else {
+ image->image8[y][x] = (UINT8)CLIP8(value);
+ }
+ if (++x >= (int)image->xsize) {
+ x = 0, y++;
+ }
+ }
+ } else {
+ // BGR;*
+ int b;
+ for (i = x = y = 0; i < n; i++) {
+ char ink[4];
+
+ op = PySequence_Fast_GET_ITEM(seq, i);
+ if (!op || !getink(op, image, ink)) {
+ Py_DECREF(seq);
+ return NULL;
+ }
+ /* FIXME: what about scale and offset? */
+ for (b = 0; b < image->pixelsize; b++) {
+ image->image8[y][x * image->pixelsize + b] = ink[b];
+ }
+ if (++x >= (int)image->xsize) {
+ x = 0, y++;
+ }
+ }
+ }
+ PyErr_Clear(); /* Avoid weird exceptions */
+ }
+ } else {
+ /* 32-bit images */
+ seq = PySequence_Fast(data, must_be_sequence);
+ if (!seq) {
+ PyErr_SetString(PyExc_TypeError, must_be_sequence);
+ return NULL;
+ }
+ switch (image->type) {
+ case IMAGING_TYPE_INT32:
+ for (i = x = y = 0; i < n; i++) {
+ double value;
+ set_value_to_item(seq, i);
+ IMAGING_PIXEL_INT32(image, x, y) =
+ (INT32)(value * scale + offset);
+ if (++x >= (int)image->xsize) {
+ x = 0, y++;
+ }
+ }
+ PyErr_Clear(); /* Avoid weird exceptions */
+ break;
+ case IMAGING_TYPE_FLOAT32:
+ for (i = x = y = 0; i < n; i++) {
+ double value;
+ set_value_to_item(seq, i);
+ IMAGING_PIXEL_FLOAT32(image, x, y) =
+ (FLOAT32)(value * scale + offset);
+ if (++x >= (int)image->xsize) {
+ x = 0, y++;
+ }
+ }
+ PyErr_Clear(); /* Avoid weird exceptions */
+ break;
+ default:
+ for (i = x = y = 0; i < n; i++) {
+ union {
+ char ink[4];
+ INT32 inkint;
+ } u;
+
+ u.inkint = 0;
+
+ op = PySequence_Fast_GET_ITEM(seq, i);
+ if (!op || !getink(op, image, u.ink)) {
+ Py_DECREF(seq);
+ return NULL;
+ }
+ /* FIXME: what about scale and offset? */
+ image->image32[y][x] = u.inkint;
+ if (++x >= (int)image->xsize) {
+ x = 0, y++;
+ }
+ }
+ PyErr_Clear(); /* Avoid weird exceptions */
+ break;
+ }
+ }
+
+ Py_XDECREF(seq);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+#ifdef WITH_QUANTIZE
+
+static PyObject *
+_quantize(ImagingObject *self, PyObject *args) {
+ int colours = 256;
+ int method = 0;
+ int kmeans = 0;
+ if (!PyArg_ParseTuple(args, "|iii", &colours, &method, &kmeans)) {
+ return NULL;
+ }
+
+ if (!self->image->xsize || !self->image->ysize) {
+ /* no content; return an empty image */
+ return PyImagingNew(ImagingNew("P", self->image->xsize, self->image->ysize));
+ }
+
+ return PyImagingNew(ImagingQuantize(self->image, colours, method, kmeans));
+}
+#endif
+
+static PyObject *
+_putpalette(ImagingObject *self, PyObject *args) {
+ ImagingShuffler unpack;
+ int bits;
+
+ char *rawmode, *palette_mode;
+ UINT8 *palette;
+ Py_ssize_t palettesize;
+ if (!PyArg_ParseTuple(args, "sy#", &rawmode, &palette, &palettesize)) {
+ return NULL;
+ }
+
+ if (strcmp(self->image->mode, "L") && strcmp(self->image->mode, "LA") &&
+ strcmp(self->image->mode, "P") && strcmp(self->image->mode, "PA")) {
+ PyErr_SetString(PyExc_ValueError, wrong_mode);
+ return NULL;
+ }
+
+ palette_mode = strncmp("RGBA", rawmode, 4) == 0 ? "RGBA" : "RGB";
+ unpack = ImagingFindUnpacker(palette_mode, rawmode, &bits);
+ if (!unpack) {
+ PyErr_SetString(PyExc_ValueError, wrong_raw_mode);
+ return NULL;
+ }
+
+ if (palettesize * 8 / bits > 256) {
+ PyErr_SetString(PyExc_ValueError, wrong_palette_size);
+ return NULL;
+ }
+
+ ImagingPaletteDelete(self->image->palette);
+
+ strcpy(self->image->mode, strlen(self->image->mode) == 2 ? "PA" : "P");
+
+ self->image->palette = ImagingPaletteNew(palette_mode);
+
+ self->image->palette->size = palettesize * 8 / bits;
+ unpack(self->image->palette->palette, palette, self->image->palette->size);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_putpalettealpha(ImagingObject *self, PyObject *args) {
+ int index;
+ int alpha = 0;
+ if (!PyArg_ParseTuple(args, "i|i", &index, &alpha)) {
+ return NULL;
+ }
+
+ if (!self->image->palette) {
+ PyErr_SetString(PyExc_ValueError, no_palette);
+ return NULL;
+ }
+
+ if (index < 0 || index >= 256) {
+ PyErr_SetString(PyExc_ValueError, outside_palette);
+ return NULL;
+ }
+
+ strcpy(self->image->palette->mode, "RGBA");
+ self->image->palette->palette[index * 4 + 3] = (UINT8)alpha;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_putpalettealphas(ImagingObject *self, PyObject *args) {
+ int i;
+ UINT8 *values;
+ Py_ssize_t length;
+ if (!PyArg_ParseTuple(args, "y#", &values, &length)) {
+ return NULL;
+ }
+
+ if (!self->image->palette) {
+ PyErr_SetString(PyExc_ValueError, no_palette);
+ return NULL;
+ }
+
+ if (length > 256) {
+ PyErr_SetString(PyExc_ValueError, outside_palette);
+ return NULL;
+ }
+
+ strcpy(self->image->palette->mode, "RGBA");
+ for (i = 0; i < length; i++) {
+ self->image->palette->palette[i * 4 + 3] = (UINT8)values[i];
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_putpixel(ImagingObject *self, PyObject *args) {
+ Imaging im;
+ char ink[4];
+
+ int x, y;
+ PyObject *color;
+ if (!PyArg_ParseTuple(args, "(ii)O", &x, &y, &color)) {
+ return NULL;
+ }
+
+ im = self->image;
+
+ if (x < 0) {
+ x = im->xsize + x;
+ }
+ if (y < 0) {
+ y = im->ysize + y;
+ }
+
+ if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) {
+ PyErr_SetString(PyExc_IndexError, outside_image);
+ return NULL;
+ }
+
+ if (!getink(color, im, ink)) {
+ return NULL;
+ }
+
+ if (self->access) {
+ self->access->put_pixel(im, x, y, ink);
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+#ifdef WITH_RANKFILTER
+static PyObject *
+_rankfilter(ImagingObject *self, PyObject *args) {
+ int size, rank;
+ if (!PyArg_ParseTuple(args, "ii", &size, &rank)) {
+ return NULL;
+ }
+
+ return PyImagingNew(ImagingRankFilter(self->image, size, rank));
+}
+#endif
+
+static PyObject *
+_resize(ImagingObject *self, PyObject *args) {
+ Imaging imIn;
+ Imaging imOut;
+
+ int xsize, ysize;
+ int filter = IMAGING_TRANSFORM_NEAREST;
+ float box[4] = {0, 0, 0, 0};
+
+ imIn = self->image;
+ box[2] = imIn->xsize;
+ box[3] = imIn->ysize;
+
+ if (!PyArg_ParseTuple(
+ args,
+ "(ii)|i(ffff)",
+ &xsize,
+ &ysize,
+ &filter,
+ &box[0],
+ &box[1],
+ &box[2],
+ &box[3])) {
+ return NULL;
+ }
+
+ if (xsize < 1 || ysize < 1) {
+ return ImagingError_ValueError("height and width must be > 0");
+ }
+
+ if (box[0] < 0 || box[1] < 0) {
+ return ImagingError_ValueError("box offset can't be negative");
+ }
+
+ if (box[2] > imIn->xsize || box[3] > imIn->ysize) {
+ return ImagingError_ValueError("box can't exceed original image size");
+ }
+
+ if (box[2] - box[0] < 0 || box[3] - box[1] < 0) {
+ return ImagingError_ValueError("box can't be empty");
+ }
+
+ // If box's coordinates are int and box size matches requested size
+ if (box[0] - (int)box[0] == 0 && box[2] - box[0] == xsize &&
+ box[1] - (int)box[1] == 0 && box[3] - box[1] == ysize) {
+ imOut = ImagingCrop(imIn, box[0], box[1], box[2], box[3]);
+ } else if (filter == IMAGING_TRANSFORM_NEAREST) {
+ double a[8];
+
+ memset(a, 0, sizeof a);
+ a[0] = (double)(box[2] - box[0]) / xsize;
+ a[4] = (double)(box[3] - box[1]) / ysize;
+ a[2] = box[0];
+ a[5] = box[1];
+
+ imOut = ImagingNewDirty(imIn->mode, xsize, ysize);
+
+ imOut = ImagingTransform(
+ imOut, imIn, IMAGING_TRANSFORM_AFFINE, 0, 0, xsize, ysize, a, filter, 1);
+ } else {
+ imOut = ImagingResample(imIn, xsize, ysize, filter, box);
+ }
+
+ return PyImagingNew(imOut);
+}
+
+static PyObject *
+_reduce(ImagingObject *self, PyObject *args) {
+ Imaging imIn;
+ Imaging imOut;
+
+ int xscale, yscale;
+ int box[4] = {0, 0, 0, 0};
+
+ imIn = self->image;
+ box[2] = imIn->xsize;
+ box[3] = imIn->ysize;
+
+ if (!PyArg_ParseTuple(
+ args,
+ "(ii)|(iiii)",
+ &xscale,
+ &yscale,
+ &box[0],
+ &box[1],
+ &box[2],
+ &box[3])) {
+ return NULL;
+ }
+
+ if (xscale < 1 || yscale < 1) {
+ return ImagingError_ValueError("scale must be > 0");
+ }
+
+ if (box[0] < 0 || box[1] < 0) {
+ return ImagingError_ValueError("box offset can't be negative");
+ }
+
+ if (box[2] > imIn->xsize || box[3] > imIn->ysize) {
+ return ImagingError_ValueError("box can't exceed original image size");
+ }
+
+ if (box[2] <= box[0] || box[3] <= box[1]) {
+ return ImagingError_ValueError("box can't be empty");
+ }
+
+ if (xscale == 1 && yscale == 1) {
+ imOut = ImagingCrop(imIn, box[0], box[1], box[2], box[3]);
+ } else {
+ // Change box format: (left, top, width, height)
+ box[2] -= box[0];
+ box[3] -= box[1];
+ imOut = ImagingReduce(imIn, xscale, yscale, box);
+ }
+
+ return PyImagingNew(imOut);
+}
+
+#define IS_RGB(mode) \
+ (!strcmp(mode, "RGB") || !strcmp(mode, "RGBA") || !strcmp(mode, "RGBX"))
+
+static PyObject *
+im_setmode(ImagingObject *self, PyObject *args) {
+ /* attempt to modify the mode of an image in place */
+
+ Imaging im;
+
+ char *mode;
+ Py_ssize_t modelen;
+ if (!PyArg_ParseTuple(args, "s#:setmode", &mode, &modelen)) {
+ return NULL;
+ }
+
+ im = self->image;
+
+ /* move all logic in here to the libImaging primitive */
+
+ if (!strcmp(im->mode, mode)) {
+ ; /* same mode; always succeeds */
+ } else if (IS_RGB(im->mode) && IS_RGB(mode)) {
+ /* color to color */
+ strcpy(im->mode, mode);
+ im->bands = modelen;
+ if (!strcmp(mode, "RGBA")) {
+ (void)ImagingFillBand(im, 3, 255);
+ }
+ } else {
+ /* trying doing an in-place conversion */
+ if (!ImagingConvertInPlace(im, mode)) {
+ return NULL;
+ }
+ }
+
+ if (self->access) {
+ ImagingAccessDelete(im, self->access);
+ }
+ self->access = ImagingAccessNew(im);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_transform2(ImagingObject *self, PyObject *args) {
+ static const char *wrong_number = "wrong number of matrix entries";
+
+ Imaging imOut;
+ Py_ssize_t n;
+ double *a;
+
+ ImagingObject *imagep;
+ int x0, y0, x1, y1;
+ int method;
+ PyObject *data;
+ int filter = IMAGING_TRANSFORM_NEAREST;
+ int fill = 1;
+ if (!PyArg_ParseTuple(
+ args,
+ "(iiii)O!iO|ii",
+ &x0,
+ &y0,
+ &x1,
+ &y1,
+ &Imaging_Type,
+ &imagep,
+ &method,
+ &data,
+ &filter,
+ &fill)) {
+ return NULL;
+ }
+
+ switch (method) {
+ case IMAGING_TRANSFORM_AFFINE:
+ n = 6;
+ break;
+ case IMAGING_TRANSFORM_PERSPECTIVE:
+ n = 8;
+ break;
+ case IMAGING_TRANSFORM_QUAD:
+ n = 8;
+ break;
+ default:
+ n = -1; /* force error */
+ }
+
+ a = getlist(data, &n, wrong_number, TYPE_DOUBLE);
+ if (!a) {
+ return NULL;
+ }
+
+ imOut = ImagingTransform(
+ self->image, imagep->image, method, x0, y0, x1, y1, a, filter, fill);
+
+ free(a);
+
+ if (!imOut) {
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_transpose(ImagingObject *self, PyObject *args) {
+ Imaging imIn;
+ Imaging imOut;
+
+ int op;
+ if (!PyArg_ParseTuple(args, "i", &op)) {
+ return NULL;
+ }
+
+ imIn = self->image;
+
+ switch (op) {
+ case 0: /* flip left right */
+ case 1: /* flip top bottom */
+ case 3: /* rotate 180 */
+ imOut = ImagingNewDirty(imIn->mode, imIn->xsize, imIn->ysize);
+ break;
+ case 2: /* rotate 90 */
+ case 4: /* rotate 270 */
+ case 5: /* transpose */
+ case 6: /* transverse */
+ imOut = ImagingNewDirty(imIn->mode, imIn->ysize, imIn->xsize);
+ break;
+ default:
+ PyErr_SetString(PyExc_ValueError, "No such transpose operation");
+ return NULL;
+ }
+
+ if (imOut) {
+ switch (op) {
+ case 0:
+ (void)ImagingFlipLeftRight(imOut, imIn);
+ break;
+ case 1:
+ (void)ImagingFlipTopBottom(imOut, imIn);
+ break;
+ case 2:
+ (void)ImagingRotate90(imOut, imIn);
+ break;
+ case 3:
+ (void)ImagingRotate180(imOut, imIn);
+ break;
+ case 4:
+ (void)ImagingRotate270(imOut, imIn);
+ break;
+ case 5:
+ (void)ImagingTranspose(imOut, imIn);
+ break;
+ case 6:
+ (void)ImagingTransverse(imOut, imIn);
+ break;
+ }
+ }
+
+ return PyImagingNew(imOut);
+}
+
+#ifdef WITH_UNSHARPMASK
+static PyObject *
+_unsharp_mask(ImagingObject *self, PyObject *args) {
+ Imaging imIn;
+ Imaging imOut;
+
+ float radius;
+ int percent, threshold;
+ if (!PyArg_ParseTuple(args, "fii", &radius, &percent, &threshold)) {
+ return NULL;
+ }
+
+ imIn = self->image;
+ imOut = ImagingNewDirty(imIn->mode, imIn->xsize, imIn->ysize);
+ if (!imOut) {
+ return NULL;
+ }
+
+ if (!ImagingUnsharpMask(imOut, imIn, radius, percent, threshold)) {
+ return NULL;
+ }
+
+ return PyImagingNew(imOut);
+}
+#endif
+
+static PyObject *
+_box_blur(ImagingObject *self, PyObject *args) {
+ Imaging imIn;
+ Imaging imOut;
+
+ float xradius, yradius;
+ int n = 1;
+ if (!PyArg_ParseTuple(args, "(ff)|i", &xradius, &yradius, &n)) {
+ return NULL;
+ }
+
+ imIn = self->image;
+ imOut = ImagingNewDirty(imIn->mode, imIn->xsize, imIn->ysize);
+ if (!imOut) {
+ return NULL;
+ }
+
+ if (!ImagingBoxBlur(imOut, imIn, xradius, yradius, n)) {
+ ImagingDelete(imOut);
+ return NULL;
+ }
+
+ return PyImagingNew(imOut);
+}
+
+/* -------------------------------------------------------------------- */
+
+static PyObject *
+_isblock(ImagingObject *self) {
+ return PyBool_FromLong(self->image->block != NULL);
+}
+
+static PyObject *
+_getbbox(ImagingObject *self, PyObject *args) {
+ int bbox[4];
+
+ int alpha_only = 1;
+ if (!PyArg_ParseTuple(args, "|i", &alpha_only)) {
+ return NULL;
+ }
+
+ if (!ImagingGetBBox(self->image, bbox, alpha_only)) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ return Py_BuildValue("iiii", bbox[0], bbox[1], bbox[2], bbox[3]);
+}
+
+static PyObject *
+_getcolors(ImagingObject *self, PyObject *args) {
+ ImagingColorItem *items;
+ int i, colors;
+ PyObject *out;
+
+ int maxcolors = 256;
+ if (!PyArg_ParseTuple(args, "i:getcolors", &maxcolors)) {
+ return NULL;
+ }
+
+ items = ImagingGetColors(self->image, maxcolors, &colors);
+ if (!items) {
+ return NULL;
+ }
+
+ if (colors > maxcolors) {
+ out = Py_None;
+ Py_INCREF(out);
+ } else {
+ out = PyList_New(colors);
+ if (out == NULL) {
+ free(items);
+ return NULL;
+ }
+ for (i = 0; i < colors; i++) {
+ ImagingColorItem *v = &items[i];
+ PyObject *item = Py_BuildValue(
+ "iN", v->count, getpixel(self->image, self->access, v->x, v->y));
+ PyList_SetItem(out, i, item);
+ }
+ }
+
+ free(items);
+
+ return out;
+}
+
+static PyObject *
+_getextrema(ImagingObject *self) {
+ union {
+ UINT8 u[2];
+ INT32 i[2];
+ FLOAT32 f[2];
+ UINT16 s[2];
+ } extrema;
+ int status;
+
+ status = ImagingGetExtrema(self->image, &extrema);
+ if (status < 0) {
+ return NULL;
+ }
+
+ if (status) {
+ switch (self->image->type) {
+ case IMAGING_TYPE_UINT8:
+ return Py_BuildValue("BB", extrema.u[0], extrema.u[1]);
+ case IMAGING_TYPE_INT32:
+ return Py_BuildValue("ii", extrema.i[0], extrema.i[1]);
+ case IMAGING_TYPE_FLOAT32:
+ return Py_BuildValue("dd", extrema.f[0], extrema.f[1]);
+ case IMAGING_TYPE_SPECIAL:
+ if (strcmp(self->image->mode, "I;16") == 0) {
+ return Py_BuildValue("HH", extrema.s[0], extrema.s[1]);
+ }
+ }
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_getprojection(ImagingObject *self) {
+ unsigned char *xprofile;
+ unsigned char *yprofile;
+ PyObject *result;
+
+ /* malloc check ok */
+ xprofile = malloc(self->image->xsize);
+ yprofile = malloc(self->image->ysize);
+
+ if (xprofile == NULL || yprofile == NULL) {
+ free(xprofile);
+ free(yprofile);
+ return ImagingError_MemoryError();
+ }
+
+ ImagingGetProjection(
+ self->image, (unsigned char *)xprofile, (unsigned char *)yprofile);
+
+ result = Py_BuildValue(
+ "y#y#",
+ xprofile,
+ (Py_ssize_t)self->image->xsize,
+ yprofile,
+ (Py_ssize_t)self->image->ysize);
+
+ free(xprofile);
+ free(yprofile);
+
+ return result;
+}
+
+/* -------------------------------------------------------------------- */
+
+static PyObject *
+_getband(ImagingObject *self, PyObject *args) {
+ int band;
+
+ if (!PyArg_ParseTuple(args, "i", &band)) {
+ return NULL;
+ }
+
+ return PyImagingNew(ImagingGetBand(self->image, band));
+}
+
+static PyObject *
+_fillband(ImagingObject *self, PyObject *args) {
+ int band;
+ int color;
+
+ if (!PyArg_ParseTuple(args, "ii", &band, &color)) {
+ return NULL;
+ }
+
+ if (!ImagingFillBand(self->image, band, color)) {
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_putband(ImagingObject *self, PyObject *args) {
+ ImagingObject *imagep;
+ int band;
+ if (!PyArg_ParseTuple(args, "O!i", &Imaging_Type, &imagep, &band)) {
+ return NULL;
+ }
+
+ if (!ImagingPutBand(self->image, imagep->image, band)) {
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_merge(PyObject *self, PyObject *args) {
+ char *mode;
+ ImagingObject *band0 = NULL;
+ ImagingObject *band1 = NULL;
+ ImagingObject *band2 = NULL;
+ ImagingObject *band3 = NULL;
+ Imaging bands[4] = {NULL, NULL, NULL, NULL};
+
+ if (!PyArg_ParseTuple(
+ args,
+ "sO!|O!O!O!",
+ &mode,
+ &Imaging_Type,
+ &band0,
+ &Imaging_Type,
+ &band1,
+ &Imaging_Type,
+ &band2,
+ &Imaging_Type,
+ &band3)) {
+ return NULL;
+ }
+
+ if (band0) {
+ bands[0] = band0->image;
+ }
+ if (band1) {
+ bands[1] = band1->image;
+ }
+ if (band2) {
+ bands[2] = band2->image;
+ }
+ if (band3) {
+ bands[3] = band3->image;
+ }
+
+ return PyImagingNew(ImagingMerge(mode, bands));
+}
+
+static PyObject *
+_split(ImagingObject *self) {
+ int fails = 0;
+ Py_ssize_t i;
+ PyObject *list;
+ PyObject *imaging_object;
+ Imaging bands[4] = {NULL, NULL, NULL, NULL};
+
+ if (!ImagingSplit(self->image, bands)) {
+ return NULL;
+ }
+
+ list = PyTuple_New(self->image->bands);
+ for (i = 0; i < self->image->bands; i++) {
+ imaging_object = PyImagingNew(bands[i]);
+ if (!imaging_object) {
+ fails += 1;
+ }
+ PyTuple_SET_ITEM(list, i, imaging_object);
+ }
+ if (fails) {
+ Py_DECREF(list);
+ list = NULL;
+ }
+ return list;
+}
+
+/* -------------------------------------------------------------------- */
+
+#ifdef WITH_IMAGECHOPS
+
+static PyObject *
+_chop_invert(ImagingObject *self) {
+ return PyImagingNew(ImagingNegative(self->image));
+}
+
+static PyObject *
+_chop_lighter(ImagingObject *self, PyObject *args) {
+ ImagingObject *imagep;
+
+ if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) {
+ return NULL;
+ }
+
+ return PyImagingNew(ImagingChopLighter(self->image, imagep->image));
+}
+
+static PyObject *
+_chop_darker(ImagingObject *self, PyObject *args) {
+ ImagingObject *imagep;
+
+ if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) {
+ return NULL;
+ }
+
+ return PyImagingNew(ImagingChopDarker(self->image, imagep->image));
+}
+
+static PyObject *
+_chop_difference(ImagingObject *self, PyObject *args) {
+ ImagingObject *imagep;
+
+ if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) {
+ return NULL;
+ }
+
+ return PyImagingNew(ImagingChopDifference(self->image, imagep->image));
+}
+
+static PyObject *
+_chop_multiply(ImagingObject *self, PyObject *args) {
+ ImagingObject *imagep;
+
+ if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) {
+ return NULL;
+ }
+
+ return PyImagingNew(ImagingChopMultiply(self->image, imagep->image));
+}
+
+static PyObject *
+_chop_screen(ImagingObject *self, PyObject *args) {
+ ImagingObject *imagep;
+
+ if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) {
+ return NULL;
+ }
+
+ return PyImagingNew(ImagingChopScreen(self->image, imagep->image));
+}
+
+static PyObject *
+_chop_add(ImagingObject *self, PyObject *args) {
+ ImagingObject *imagep;
+ float scale;
+ int offset;
+
+ scale = 1.0;
+ offset = 0;
+
+ if (!PyArg_ParseTuple(args, "O!|fi", &Imaging_Type, &imagep, &scale, &offset)) {
+ return NULL;
+ }
+
+ return PyImagingNew(ImagingChopAdd(self->image, imagep->image, scale, offset));
+}
+
+static PyObject *
+_chop_subtract(ImagingObject *self, PyObject *args) {
+ ImagingObject *imagep;
+ float scale;
+ int offset;
+
+ scale = 1.0;
+ offset = 0;
+
+ if (!PyArg_ParseTuple(args, "O!|fi", &Imaging_Type, &imagep, &scale, &offset)) {
+ return NULL;
+ }
+
+ return PyImagingNew(ImagingChopSubtract(self->image, imagep->image, scale, offset));
+}
+
+static PyObject *
+_chop_and(ImagingObject *self, PyObject *args) {
+ ImagingObject *imagep;
+
+ if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) {
+ return NULL;
+ }
+
+ return PyImagingNew(ImagingChopAnd(self->image, imagep->image));
+}
+
+static PyObject *
+_chop_or(ImagingObject *self, PyObject *args) {
+ ImagingObject *imagep;
+
+ if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) {
+ return NULL;
+ }
+
+ return PyImagingNew(ImagingChopOr(self->image, imagep->image));
+}
+
+static PyObject *
+_chop_xor(ImagingObject *self, PyObject *args) {
+ ImagingObject *imagep;
+
+ if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) {
+ return NULL;
+ }
+
+ return PyImagingNew(ImagingChopXor(self->image, imagep->image));
+}
+
+static PyObject *
+_chop_add_modulo(ImagingObject *self, PyObject *args) {
+ ImagingObject *imagep;
+
+ if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) {
+ return NULL;
+ }
+
+ return PyImagingNew(ImagingChopAddModulo(self->image, imagep->image));
+}
+
+static PyObject *
+_chop_subtract_modulo(ImagingObject *self, PyObject *args) {
+ ImagingObject *imagep;
+
+ if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) {
+ return NULL;
+ }
+
+ return PyImagingNew(ImagingChopSubtractModulo(self->image, imagep->image));
+}
+
+static PyObject *
+_chop_soft_light(ImagingObject *self, PyObject *args) {
+ ImagingObject *imagep;
+
+ if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) {
+ return NULL;
+ }
+
+ return PyImagingNew(ImagingChopSoftLight(self->image, imagep->image));
+}
+
+static PyObject *
+_chop_hard_light(ImagingObject *self, PyObject *args) {
+ ImagingObject *imagep;
+
+ if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) {
+ return NULL;
+ }
+
+ return PyImagingNew(ImagingChopHardLight(self->image, imagep->image));
+}
+
+static PyObject *
+_chop_overlay(ImagingObject *self, PyObject *args) {
+ ImagingObject *imagep;
+
+ if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) {
+ return NULL;
+ }
+
+ return PyImagingNew(ImagingOverlay(self->image, imagep->image));
+}
+#endif
+
+/* -------------------------------------------------------------------- */
+
+#ifdef WITH_IMAGEDRAW
+
+static PyObject *
+_font_new(PyObject *self_, PyObject *args) {
+ ImagingFontObject *self;
+ int i, y0, y1;
+ static const char *wrong_length = "descriptor table has wrong size";
+
+ ImagingObject *imagep;
+ unsigned char *glyphdata;
+ Py_ssize_t glyphdata_length;
+ if (!PyArg_ParseTuple(
+ args, "O!y#", &Imaging_Type, &imagep, &glyphdata, &glyphdata_length)) {
+ return NULL;
+ }
+
+ if (glyphdata_length != 256 * 20) {
+ PyErr_SetString(PyExc_ValueError, wrong_length);
+ return NULL;
+ }
+
+ self = PyObject_New(ImagingFontObject, &ImagingFont_Type);
+ if (self == NULL) {
+ return NULL;
+ }
+
+ /* glyph bitmap */
+ self->bitmap = imagep->image;
+
+ y0 = y1 = 0;
+
+ /* glyph glyphs */
+ for (i = 0; i < 256; i++) {
+ self->glyphs[i].dx = S16(B16(glyphdata, 0));
+ self->glyphs[i].dy = S16(B16(glyphdata, 2));
+ self->glyphs[i].dx0 = S16(B16(glyphdata, 4));
+ self->glyphs[i].dy0 = S16(B16(glyphdata, 6));
+ self->glyphs[i].dx1 = S16(B16(glyphdata, 8));
+ self->glyphs[i].dy1 = S16(B16(glyphdata, 10));
+ self->glyphs[i].sx0 = S16(B16(glyphdata, 12));
+ self->glyphs[i].sy0 = S16(B16(glyphdata, 14));
+ self->glyphs[i].sx1 = S16(B16(glyphdata, 16));
+ self->glyphs[i].sy1 = S16(B16(glyphdata, 18));
+ if (self->glyphs[i].dy0 < y0) {
+ y0 = self->glyphs[i].dy0;
+ }
+ if (self->glyphs[i].dy1 > y1) {
+ y1 = self->glyphs[i].dy1;
+ }
+ glyphdata += 20;
+ }
+
+ self->baseline = -y0;
+ self->ysize = y1 - y0;
+
+ /* keep a reference to the bitmap object */
+ Py_INCREF(imagep);
+ self->ref = imagep;
+
+ return (PyObject *)self;
+}
+
+static void
+_font_dealloc(ImagingFontObject *self) {
+ Py_XDECREF(self->ref);
+ PyObject_Del(self);
+}
+
+static inline int
+textwidth(ImagingFontObject *self, const unsigned char *text) {
+ int xsize;
+
+ for (xsize = 0; *text; text++) {
+ xsize += self->glyphs[*text].dx;
+ }
+
+ return xsize;
+}
+
+void
+_font_text_asBytes(PyObject *encoded_string, unsigned char **text) {
+ /* Allocates *text, returns a 'new reference'. Caller is required to free */
+
+ PyObject *bytes = NULL;
+ Py_ssize_t len = 0;
+ char *buffer;
+
+ *text = NULL;
+
+ if (PyUnicode_CheckExact(encoded_string)) {
+ bytes = PyUnicode_AsLatin1String(encoded_string);
+ if (!bytes) {
+ return;
+ }
+ PyBytes_AsStringAndSize(bytes, &buffer, &len);
+ } else if (PyBytes_Check(encoded_string)) {
+ PyBytes_AsStringAndSize(encoded_string, &buffer, &len);
+ }
+
+ *text = calloc(len + 1, 1);
+ if (*text) {
+ memcpy(*text, buffer, len);
+ } else {
+ ImagingError_MemoryError();
+ }
+ if (bytes) {
+ Py_DECREF(bytes);
+ }
+
+ return;
+}
+
+static PyObject *
+_font_getmask(ImagingFontObject *self, PyObject *args) {
+ Imaging im;
+ Imaging bitmap;
+ int x, b;
+ int i = 0;
+ int status;
+ Glyph *glyph;
+
+ PyObject *encoded_string;
+
+ unsigned char *text;
+ char *mode = "";
+
+ if (!PyArg_ParseTuple(args, "O|s:getmask", &encoded_string, &mode)) {
+ return NULL;
+ }
+
+ _font_text_asBytes(encoded_string, &text);
+ if (!text) {
+ return NULL;
+ }
+
+ im = ImagingNew(self->bitmap->mode, textwidth(self, text), self->ysize);
+ if (!im) {
+ free(text);
+ return ImagingError_MemoryError();
+ }
+
+ b = 0;
+ (void)ImagingFill(im, &b);
+
+ b = self->baseline;
+ for (x = 0; text[i]; i++) {
+ glyph = &self->glyphs[text[i]];
+ bitmap =
+ ImagingCrop(self->bitmap, glyph->sx0, glyph->sy0, glyph->sx1, glyph->sy1);
+ if (!bitmap) {
+ goto failed;
+ }
+ status = ImagingPaste(
+ im,
+ bitmap,
+ NULL,
+ glyph->dx0 + x,
+ glyph->dy0 + b,
+ glyph->dx1 + x,
+ glyph->dy1 + b);
+ ImagingDelete(bitmap);
+ if (status < 0) {
+ goto failed;
+ }
+ x = x + glyph->dx;
+ b = b + glyph->dy;
+ }
+ free(text);
+ return PyImagingNew(im);
+
+failed:
+ free(text);
+ ImagingDelete(im);
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+_font_getsize(ImagingFontObject *self, PyObject *args) {
+ unsigned char *text;
+ PyObject *encoded_string;
+ PyObject *val;
+
+ if (!PyArg_ParseTuple(args, "O:getsize", &encoded_string)) {
+ return NULL;
+ }
+
+ _font_text_asBytes(encoded_string, &text);
+ if (!text) {
+ return NULL;
+ }
+
+ val = Py_BuildValue("ii", textwidth(self, text), self->ysize);
+ free(text);
+ return val;
+}
+
+static struct PyMethodDef _font_methods[] = {
+ {"getmask", (PyCFunction)_font_getmask, METH_VARARGS},
+ {"getsize", (PyCFunction)_font_getsize, METH_VARARGS},
+ {NULL, NULL} /* sentinel */
+};
+
+/* -------------------------------------------------------------------- */
+
+static PyObject *
+_draw_new(PyObject *self_, PyObject *args) {
+ ImagingDrawObject *self;
+
+ ImagingObject *imagep;
+ int blend = 0;
+ if (!PyArg_ParseTuple(args, "O!|i", &Imaging_Type, &imagep, &blend)) {
+ return NULL;
+ }
+
+ self = PyObject_New(ImagingDrawObject, &ImagingDraw_Type);
+ if (self == NULL) {
+ return NULL;
+ }
+
+ /* keep a reference to the image object */
+ Py_INCREF(imagep);
+ self->image = imagep;
+
+ self->ink[0] = self->ink[1] = self->ink[2] = self->ink[3] = 0;
+
+ self->blend = blend;
+
+ return (PyObject *)self;
+}
+
+static void
+_draw_dealloc(ImagingDrawObject *self) {
+ Py_XDECREF(self->image);
+ PyObject_Del(self);
+}
+
+extern Py_ssize_t
+PyPath_Flatten(PyObject *data, double **xy);
+
+static PyObject *
+_draw_ink(ImagingDrawObject *self, PyObject *args) {
+ INT32 ink = 0;
+ PyObject *color;
+ if (!PyArg_ParseTuple(args, "O", &color)) {
+ return NULL;
+ }
+
+ if (!getink(color, self->image->image, (char *)&ink)) {
+ return NULL;
+ }
+
+ return PyLong_FromLong((int)ink);
+}
+
+static PyObject *
+_draw_arc(ImagingDrawObject *self, PyObject *args) {
+ double *xy;
+ Py_ssize_t n;
+
+ PyObject *data;
+ int ink;
+ int width = 0;
+ float start, end;
+ if (!PyArg_ParseTuple(args, "Offi|i", &data, &start, &end, &ink, &width)) {
+ return NULL;
+ }
+
+ n = PyPath_Flatten(data, &xy);
+ if (n < 0) {
+ return NULL;
+ }
+ if (n != 2) {
+ PyErr_SetString(PyExc_TypeError, must_be_two_coordinates);
+ free(xy);
+ return NULL;
+ }
+ if (xy[2] < xy[0]) {
+ PyErr_SetString(PyExc_ValueError, incorrectly_ordered_x_coordinate);
+ free(xy);
+ return NULL;
+ }
+ if (xy[3] < xy[1]) {
+ PyErr_SetString(PyExc_ValueError, incorrectly_ordered_y_coordinate);
+ free(xy);
+ return NULL;
+ }
+
+ n = ImagingDrawArc(
+ self->image->image,
+ (int)xy[0],
+ (int)xy[1],
+ (int)xy[2],
+ (int)xy[3],
+ start,
+ end,
+ &ink,
+ width,
+ self->blend);
+
+ free(xy);
+
+ if (n < 0) {
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_draw_bitmap(ImagingDrawObject *self, PyObject *args) {
+ double *xy;
+ Py_ssize_t n;
+
+ PyObject *data;
+ ImagingObject *bitmap;
+ int ink;
+ if (!PyArg_ParseTuple(args, "OO!i", &data, &Imaging_Type, &bitmap, &ink)) {
+ return NULL;
+ }
+
+ n = PyPath_Flatten(data, &xy);
+ if (n < 0) {
+ return NULL;
+ }
+ if (n != 1) {
+ PyErr_SetString(
+ PyExc_TypeError, "coordinate list must contain exactly 1 coordinate");
+ free(xy);
+ return NULL;
+ }
+
+ n = ImagingDrawBitmap(
+ self->image->image, (int)xy[0], (int)xy[1], bitmap->image, &ink, self->blend);
+
+ free(xy);
+
+ if (n < 0) {
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_draw_chord(ImagingDrawObject *self, PyObject *args) {
+ double *xy;
+ Py_ssize_t n;
+
+ PyObject *data;
+ int ink, fill;
+ int width = 0;
+ float start, end;
+ if (!PyArg_ParseTuple(args, "Offii|i", &data, &start, &end, &ink, &fill, &width)) {
+ return NULL;
+ }
+
+ n = PyPath_Flatten(data, &xy);
+ if (n < 0) {
+ return NULL;
+ }
+ if (n != 2) {
+ PyErr_SetString(PyExc_TypeError, must_be_two_coordinates);
+ free(xy);
+ return NULL;
+ }
+ if (xy[2] < xy[0]) {
+ PyErr_SetString(PyExc_ValueError, incorrectly_ordered_x_coordinate);
+ free(xy);
+ return NULL;
+ }
+ if (xy[3] < xy[1]) {
+ PyErr_SetString(PyExc_ValueError, incorrectly_ordered_y_coordinate);
+ free(xy);
+ return NULL;
+ }
+
+ n = ImagingDrawChord(
+ self->image->image,
+ (int)xy[0],
+ (int)xy[1],
+ (int)xy[2],
+ (int)xy[3],
+ start,
+ end,
+ &ink,
+ fill,
+ width,
+ self->blend);
+
+ free(xy);
+
+ if (n < 0) {
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_draw_ellipse(ImagingDrawObject *self, PyObject *args) {
+ double *xy;
+ Py_ssize_t n;
+
+ PyObject *data;
+ int ink;
+ int fill = 0;
+ int width = 0;
+ if (!PyArg_ParseTuple(args, "Oi|ii", &data, &ink, &fill, &width)) {
+ return NULL;
+ }
+
+ n = PyPath_Flatten(data, &xy);
+ if (n < 0) {
+ return NULL;
+ }
+ if (n != 2) {
+ PyErr_SetString(PyExc_TypeError, must_be_two_coordinates);
+ free(xy);
+ return NULL;
+ }
+ if (xy[2] < xy[0]) {
+ PyErr_SetString(PyExc_ValueError, incorrectly_ordered_x_coordinate);
+ free(xy);
+ return NULL;
+ }
+ if (xy[3] < xy[1]) {
+ PyErr_SetString(PyExc_ValueError, incorrectly_ordered_y_coordinate);
+ free(xy);
+ return NULL;
+ }
+
+ n = ImagingDrawEllipse(
+ self->image->image,
+ (int)xy[0],
+ (int)xy[1],
+ (int)xy[2],
+ (int)xy[3],
+ &ink,
+ fill,
+ width,
+ self->blend);
+
+ free(xy);
+
+ if (n < 0) {
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_draw_lines(ImagingDrawObject *self, PyObject *args) {
+ double *xy;
+ Py_ssize_t i, n;
+
+ PyObject *data;
+ int ink;
+ int width = 0;
+ if (!PyArg_ParseTuple(args, "Oi|i", &data, &ink, &width)) {
+ return NULL;
+ }
+
+ n = PyPath_Flatten(data, &xy);
+ if (n < 0) {
+ return NULL;
+ }
+
+ if (width <= 1) {
+ double *p = NULL;
+ for (i = 0; i < n - 1; i++) {
+ p = &xy[i + i];
+ if (ImagingDrawLine(
+ self->image->image,
+ (int)p[0],
+ (int)p[1],
+ (int)p[2],
+ (int)p[3],
+ &ink,
+ self->blend) < 0) {
+ free(xy);
+ return NULL;
+ }
+ }
+ if (p) { /* draw last point */
+ ImagingDrawPoint(
+ self->image->image, (int)p[2], (int)p[3], &ink, self->blend);
+ }
+ } else {
+ for (i = 0; i < n - 1; i++) {
+ double *p = &xy[i + i];
+ if (ImagingDrawWideLine(
+ self->image->image,
+ (int)p[0],
+ (int)p[1],
+ (int)p[2],
+ (int)p[3],
+ &ink,
+ width,
+ self->blend) < 0) {
+ free(xy);
+ return NULL;
+ }
+ }
+ }
+
+ free(xy);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_draw_points(ImagingDrawObject *self, PyObject *args) {
+ double *xy;
+ Py_ssize_t i, n;
+
+ PyObject *data;
+ int ink;
+ if (!PyArg_ParseTuple(args, "Oi", &data, &ink)) {
+ return NULL;
+ }
+
+ n = PyPath_Flatten(data, &xy);
+ if (n < 0) {
+ return NULL;
+ }
+
+ for (i = 0; i < n; i++) {
+ double *p = &xy[i + i];
+ if (ImagingDrawPoint(
+ self->image->image, (int)p[0], (int)p[1], &ink, self->blend) < 0) {
+ free(xy);
+ return NULL;
+ }
+ }
+
+ free(xy);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+#ifdef WITH_ARROW
+
+/* from outline.c */
+extern ImagingOutline
+PyOutline_AsOutline(PyObject *outline);
+
+static PyObject *
+_draw_outline(ImagingDrawObject *self, PyObject *args) {
+ ImagingOutline outline;
+
+ PyObject *outline_;
+ int ink;
+ int fill = 0;
+ if (!PyArg_ParseTuple(args, "Oi|i", &outline_, &ink, &fill)) {
+ return NULL;
+ }
+
+ outline = PyOutline_AsOutline(outline_);
+ if (!outline) {
+ PyErr_SetString(PyExc_TypeError, "expected outline object");
+ return NULL;
+ }
+
+ if (ImagingDrawOutline(self->image->image, outline, &ink, fill, self->blend) < 0) {
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+#endif
+
+static PyObject *
+_draw_pieslice(ImagingDrawObject *self, PyObject *args) {
+ double *xy;
+ Py_ssize_t n;
+
+ PyObject *data;
+ int ink, fill;
+ int width = 0;
+ float start, end;
+ if (!PyArg_ParseTuple(args, "Offii|i", &data, &start, &end, &ink, &fill, &width)) {
+ return NULL;
+ }
+
+ n = PyPath_Flatten(data, &xy);
+ if (n < 0) {
+ return NULL;
+ }
+ if (n != 2) {
+ PyErr_SetString(PyExc_TypeError, must_be_two_coordinates);
+ free(xy);
+ return NULL;
+ }
+ if (xy[2] < xy[0]) {
+ PyErr_SetString(PyExc_ValueError, incorrectly_ordered_x_coordinate);
+ free(xy);
+ return NULL;
+ }
+ if (xy[3] < xy[1]) {
+ PyErr_SetString(PyExc_ValueError, incorrectly_ordered_y_coordinate);
+ free(xy);
+ return NULL;
+ }
+
+ n = ImagingDrawPieslice(
+ self->image->image,
+ (int)xy[0],
+ (int)xy[1],
+ (int)xy[2],
+ (int)xy[3],
+ start,
+ end,
+ &ink,
+ fill,
+ width,
+ self->blend);
+
+ free(xy);
+
+ if (n < 0) {
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_draw_polygon(ImagingDrawObject *self, PyObject *args) {
+ double *xy;
+ int *ixy;
+ Py_ssize_t n, i;
+
+ PyObject *data;
+ int ink;
+ int fill = 0;
+ int width = 0;
+ if (!PyArg_ParseTuple(args, "Oi|ii", &data, &ink, &fill, &width)) {
+ return NULL;
+ }
+
+ n = PyPath_Flatten(data, &xy);
+ if (n < 0) {
+ return NULL;
+ }
+ if (n < 2) {
+ PyErr_SetString(
+ PyExc_TypeError, "coordinate list must contain at least 2 coordinates");
+ free(xy);
+ return NULL;
+ }
+
+ /* Copy list of vertices to array */
+ ixy = (int *)calloc(n, 2 * sizeof(int));
+ if (ixy == NULL) {
+ free(xy);
+ return ImagingError_MemoryError();
+ }
+
+ for (i = 0; i < n; i++) {
+ ixy[i + i] = (int)xy[i + i];
+ ixy[i + i + 1] = (int)xy[i + i + 1];
+ }
+
+ free(xy);
+
+ if (ImagingDrawPolygon(self->image->image, n, ixy, &ink, fill, width, self->blend) < 0) {
+ free(ixy);
+ return NULL;
+ }
+
+ free(ixy);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_draw_rectangle(ImagingDrawObject *self, PyObject *args) {
+ double *xy;
+ Py_ssize_t n;
+
+ PyObject *data;
+ int ink;
+ int fill = 0;
+ int width = 0;
+ if (!PyArg_ParseTuple(args, "Oi|ii", &data, &ink, &fill, &width)) {
+ return NULL;
+ }
+
+ n = PyPath_Flatten(data, &xy);
+ if (n < 0) {
+ return NULL;
+ }
+ if (n != 2) {
+ PyErr_SetString(PyExc_TypeError, must_be_two_coordinates);
+ free(xy);
+ return NULL;
+ }
+ if (xy[2] < xy[0]) {
+ PyErr_SetString(PyExc_ValueError, incorrectly_ordered_x_coordinate);
+ free(xy);
+ return NULL;
+ }
+ if (xy[3] < xy[1]) {
+ PyErr_SetString(PyExc_ValueError, incorrectly_ordered_y_coordinate);
+ free(xy);
+ return NULL;
+ }
+
+ n = ImagingDrawRectangle(
+ self->image->image,
+ (int)xy[0],
+ (int)xy[1],
+ (int)xy[2],
+ (int)xy[3],
+ &ink,
+ fill,
+ width,
+ self->blend);
+
+ free(xy);
+
+ if (n < 0) {
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static struct PyMethodDef _draw_methods[] = {
+#ifdef WITH_IMAGEDRAW
+ /* Graphics (ImageDraw) */
+ {"draw_lines", (PyCFunction)_draw_lines, METH_VARARGS},
+#ifdef WITH_ARROW
+ {"draw_outline", (PyCFunction)_draw_outline, METH_VARARGS},
+#endif
+ {"draw_polygon", (PyCFunction)_draw_polygon, METH_VARARGS},
+ {"draw_rectangle", (PyCFunction)_draw_rectangle, METH_VARARGS},
+ {"draw_points", (PyCFunction)_draw_points, METH_VARARGS},
+ {"draw_arc", (PyCFunction)_draw_arc, METH_VARARGS},
+ {"draw_bitmap", (PyCFunction)_draw_bitmap, METH_VARARGS},
+ {"draw_chord", (PyCFunction)_draw_chord, METH_VARARGS},
+ {"draw_ellipse", (PyCFunction)_draw_ellipse, METH_VARARGS},
+ {"draw_pieslice", (PyCFunction)_draw_pieslice, METH_VARARGS},
+ {"draw_ink", (PyCFunction)_draw_ink, METH_VARARGS},
+#endif
+ {NULL, NULL} /* sentinel */
+};
+
+#endif
+
+static PyObject *
+pixel_access_new(ImagingObject *imagep, PyObject *args) {
+ PixelAccessObject *self;
+
+ int readonly = 0;
+ if (!PyArg_ParseTuple(args, "|i", &readonly)) {
+ return NULL;
+ }
+
+ self = PyObject_New(PixelAccessObject, &PixelAccess_Type);
+ if (self == NULL) {
+ return NULL;
+ }
+
+ /* keep a reference to the image object */
+ Py_INCREF(imagep);
+ self->image = imagep;
+
+ self->readonly = readonly;
+
+ return (PyObject *)self;
+}
+
+static void
+pixel_access_dealloc(PixelAccessObject *self) {
+ Py_XDECREF(self->image);
+ PyObject_Del(self);
+}
+
+static PyObject *
+pixel_access_getitem(PixelAccessObject *self, PyObject *xy) {
+ int x, y;
+ if (_getxy(xy, &x, &y)) {
+ return NULL;
+ }
+
+ return getpixel(self->image->image, self->image->access, x, y);
+}
+
+static int
+pixel_access_setitem(PixelAccessObject *self, PyObject *xy, PyObject *color) {
+ Imaging im = self->image->image;
+ char ink[4];
+ int x, y;
+
+ if (self->readonly) {
+ (void)ImagingError_ValueError(readonly);
+ return -1;
+ }
+
+ if (_getxy(xy, &x, &y)) {
+ return -1;
+ }
+
+ if (x < 0) {
+ x = im->xsize + x;
+ }
+ if (y < 0) {
+ y = im->ysize + y;
+ }
+
+ if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) {
+ PyErr_SetString(PyExc_IndexError, outside_image);
+ return -1;
+ }
+
+ if (!color) { /* FIXME: raise exception? */
+ return 0;
+ }
+
+ if (!getink(color, im, ink)) {
+ return -1;
+ }
+
+ self->image->access->put_pixel(im, x, y, ink);
+
+ return 0;
+}
+
+/* -------------------------------------------------------------------- */
+/* EFFECTS (experimental) */
+/* -------------------------------------------------------------------- */
+
+#ifdef WITH_EFFECTS
+
+static PyObject *
+_effect_mandelbrot(ImagingObject *self, PyObject *args) {
+ int xsize = 512;
+ int ysize = 512;
+ double extent[4];
+ int quality = 100;
+
+ extent[0] = -3;
+ extent[1] = -2.5;
+ extent[2] = 2;
+ extent[3] = 2.5;
+
+ if (!PyArg_ParseTuple(
+ args,
+ "|(ii)(dddd)i",
+ &xsize,
+ &ysize,
+ &extent[0],
+ &extent[1],
+ &extent[2],
+ &extent[3],
+ &quality)) {
+ return NULL;
+ }
+
+ return PyImagingNew(ImagingEffectMandelbrot(xsize, ysize, extent, quality));
+}
+
+static PyObject *
+_effect_noise(ImagingObject *self, PyObject *args) {
+ int xsize, ysize;
+ float sigma = 128;
+ if (!PyArg_ParseTuple(args, "(ii)|f", &xsize, &ysize, &sigma)) {
+ return NULL;
+ }
+
+ return PyImagingNew(ImagingEffectNoise(xsize, ysize, sigma));
+}
+
+static PyObject *
+_effect_spread(ImagingObject *self, PyObject *args) {
+ int dist;
+
+ if (!PyArg_ParseTuple(args, "i", &dist)) {
+ return NULL;
+ }
+
+ return PyImagingNew(ImagingEffectSpread(self->image, dist));
+}
+
+#endif
+
+/* -------------------------------------------------------------------- */
+/* UTILITIES */
+/* -------------------------------------------------------------------- */
+
+static PyObject *
+_getcodecstatus(PyObject *self, PyObject *args) {
+ int status;
+ char *msg;
+
+ if (!PyArg_ParseTuple(args, "i", &status)) {
+ return NULL;
+ }
+
+ switch (status) {
+ case IMAGING_CODEC_OVERRUN:
+ msg = "buffer overrun";
+ break;
+ case IMAGING_CODEC_BROKEN:
+ msg = "broken data stream";
+ break;
+ case IMAGING_CODEC_UNKNOWN:
+ msg = "unrecognized data stream contents";
+ break;
+ case IMAGING_CODEC_CONFIG:
+ msg = "codec configuration error";
+ break;
+ case IMAGING_CODEC_MEMORY:
+ msg = "out of memory";
+ break;
+ default:
+ Py_RETURN_NONE;
+ }
+
+ return PyUnicode_FromString(msg);
+}
+
+/* -------------------------------------------------------------------- */
+/* DEBUGGING HELPERS */
+/* -------------------------------------------------------------------- */
+
+static PyObject *
+_save_ppm(ImagingObject *self, PyObject *args) {
+ char *filename;
+
+ if (!PyArg_ParseTuple(args, "s", &filename)) {
+ return NULL;
+ }
+
+ if (!ImagingSavePPM(self->image, filename)) {
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* -------------------------------------------------------------------- */
+
+/* methods */
+
+static struct PyMethodDef methods[] = {
+
+ /* Put commonly used methods first */
+ {"getpixel", (PyCFunction)_getpixel, METH_VARARGS},
+ {"putpixel", (PyCFunction)_putpixel, METH_VARARGS},
+
+ {"pixel_access", (PyCFunction)pixel_access_new, METH_VARARGS},
+
+ /* Standard processing methods (Image) */
+ {"color_lut_3d", (PyCFunction)_color_lut_3d, METH_VARARGS},
+ {"convert", (PyCFunction)_convert, METH_VARARGS},
+ {"convert2", (PyCFunction)_convert2, METH_VARARGS},
+ {"convert_matrix", (PyCFunction)_convert_matrix, METH_VARARGS},
+ {"convert_transparent", (PyCFunction)_convert_transparent, METH_VARARGS},
+ {"copy", (PyCFunction)_copy, METH_VARARGS},
+ {"crop", (PyCFunction)_crop, METH_VARARGS},
+ {"expand", (PyCFunction)_expand_image, METH_VARARGS},
+ {"filter", (PyCFunction)_filter, METH_VARARGS},
+ {"histogram", (PyCFunction)_histogram, METH_VARARGS},
+ {"entropy", (PyCFunction)_entropy, METH_VARARGS},
+#ifdef WITH_MODEFILTER
+ {"modefilter", (PyCFunction)_modefilter, METH_VARARGS},
+#endif
+ {"offset", (PyCFunction)_offset, METH_VARARGS},
+ {"paste", (PyCFunction)_paste, METH_VARARGS},
+ {"point", (PyCFunction)_point, METH_VARARGS},
+ {"point_transform", (PyCFunction)_point_transform, METH_VARARGS},
+ {"putdata", (PyCFunction)_putdata, METH_VARARGS},
+#ifdef WITH_QUANTIZE
+ {"quantize", (PyCFunction)_quantize, METH_VARARGS},
+#endif
+#ifdef WITH_RANKFILTER
+ {"rankfilter", (PyCFunction)_rankfilter, METH_VARARGS},
+#endif
+ {"resize", (PyCFunction)_resize, METH_VARARGS},
+ {"reduce", (PyCFunction)_reduce, METH_VARARGS},
+ {"transpose", (PyCFunction)_transpose, METH_VARARGS},
+ {"transform2", (PyCFunction)_transform2, METH_VARARGS},
+
+ {"isblock", (PyCFunction)_isblock, METH_NOARGS},
+
+ {"getbbox", (PyCFunction)_getbbox, METH_VARARGS},
+ {"getcolors", (PyCFunction)_getcolors, METH_VARARGS},
+ {"getextrema", (PyCFunction)_getextrema, METH_NOARGS},
+ {"getprojection", (PyCFunction)_getprojection, METH_NOARGS},
+
+ {"getband", (PyCFunction)_getband, METH_VARARGS},
+ {"putband", (PyCFunction)_putband, METH_VARARGS},
+ {"split", (PyCFunction)_split, METH_NOARGS},
+ {"fillband", (PyCFunction)_fillband, METH_VARARGS},
+
+ {"setmode", (PyCFunction)im_setmode, METH_VARARGS},
+
+ {"getpalette", (PyCFunction)_getpalette, METH_VARARGS},
+ {"getpalettemode", (PyCFunction)_getpalettemode, METH_NOARGS},
+ {"putpalette", (PyCFunction)_putpalette, METH_VARARGS},
+ {"putpalettealpha", (PyCFunction)_putpalettealpha, METH_VARARGS},
+ {"putpalettealphas", (PyCFunction)_putpalettealphas, METH_VARARGS},
+
+#ifdef WITH_IMAGECHOPS
+ /* Channel operations (ImageChops) */
+ {"chop_invert", (PyCFunction)_chop_invert, METH_NOARGS},
+ {"chop_lighter", (PyCFunction)_chop_lighter, METH_VARARGS},
+ {"chop_darker", (PyCFunction)_chop_darker, METH_VARARGS},
+ {"chop_difference", (PyCFunction)_chop_difference, METH_VARARGS},
+ {"chop_multiply", (PyCFunction)_chop_multiply, METH_VARARGS},
+ {"chop_screen", (PyCFunction)_chop_screen, METH_VARARGS},
+ {"chop_add", (PyCFunction)_chop_add, METH_VARARGS},
+ {"chop_subtract", (PyCFunction)_chop_subtract, METH_VARARGS},
+ {"chop_add_modulo", (PyCFunction)_chop_add_modulo, METH_VARARGS},
+ {"chop_subtract_modulo", (PyCFunction)_chop_subtract_modulo, METH_VARARGS},
+ {"chop_and", (PyCFunction)_chop_and, METH_VARARGS},
+ {"chop_or", (PyCFunction)_chop_or, METH_VARARGS},
+ {"chop_xor", (PyCFunction)_chop_xor, METH_VARARGS},
+ {"chop_soft_light", (PyCFunction)_chop_soft_light, METH_VARARGS},
+ {"chop_hard_light", (PyCFunction)_chop_hard_light, METH_VARARGS},
+ {"chop_overlay", (PyCFunction)_chop_overlay, METH_VARARGS},
+
+#endif
+
+#ifdef WITH_UNSHARPMASK
+ /* Kevin Cazabon's unsharpmask extension */
+ {"gaussian_blur", (PyCFunction)_gaussian_blur, METH_VARARGS},
+ {"unsharp_mask", (PyCFunction)_unsharp_mask, METH_VARARGS},
+#endif
+
+ {"box_blur", (PyCFunction)_box_blur, METH_VARARGS},
+
+#ifdef WITH_EFFECTS
+ /* Special effects */
+ {"effect_spread", (PyCFunction)_effect_spread, METH_VARARGS},
+#endif
+
+ /* Misc. */
+ {"new_block", (PyCFunction)_new_block, METH_VARARGS},
+
+ {"save_ppm", (PyCFunction)_save_ppm, METH_VARARGS},
+
+ {NULL, NULL} /* sentinel */
+};
+
+/* attributes */
+
+static PyObject *
+_getattr_mode(ImagingObject *self, void *closure) {
+ return PyUnicode_FromString(self->image->mode);
+}
+
+static PyObject *
+_getattr_size(ImagingObject *self, void *closure) {
+ return Py_BuildValue("ii", self->image->xsize, self->image->ysize);
+}
+
+static PyObject *
+_getattr_bands(ImagingObject *self, void *closure) {
+ return PyLong_FromLong(self->image->bands);
+}
+
+static PyObject *
+_getattr_id(ImagingObject *self, void *closure) {
+ return PyLong_FromSsize_t((Py_ssize_t)self->image);
+}
+
+static PyObject *
+_getattr_ptr(ImagingObject *self, void *closure) {
+ return PyCapsule_New(self->image, IMAGING_MAGIC, NULL);
+}
+
+static PyObject *
+_getattr_unsafe_ptrs(ImagingObject *self, void *closure) {
+ return Py_BuildValue(
+ "(sn)(sn)(sn)",
+ "image8",
+ self->image->image8,
+ "image32",
+ self->image->image32,
+ "image",
+ self->image->image);
+};
+
+static struct PyGetSetDef getsetters[] = {
+ {"mode", (getter)_getattr_mode},
+ {"size", (getter)_getattr_size},
+ {"bands", (getter)_getattr_bands},
+ {"id", (getter)_getattr_id},
+ {"ptr", (getter)_getattr_ptr},
+ {"unsafe_ptrs", (getter)_getattr_unsafe_ptrs},
+ {NULL}};
+
+/* basic sequence semantics */
+
+static Py_ssize_t
+image_length(ImagingObject *self) {
+ Imaging im = self->image;
+
+ return (Py_ssize_t)im->xsize * im->ysize;
+}
+
+static PyObject *
+image_item(ImagingObject *self, Py_ssize_t i) {
+ int x, y;
+ Imaging im = self->image;
+
+ if (im->xsize > 0) {
+ x = i % im->xsize;
+ y = i / im->xsize;
+ } else {
+ x = y = 0; /* leave it to getpixel to raise an exception */
+ }
+
+ return getpixel(im, self->access, x, y);
+}
+
+static PySequenceMethods image_as_sequence = {
+ (lenfunc)image_length, /*sq_length*/
+ (binaryfunc)NULL, /*sq_concat*/
+ (ssizeargfunc)NULL, /*sq_repeat*/
+ (ssizeargfunc)image_item, /*sq_item*/
+ (ssizessizeargfunc)NULL, /*sq_slice*/
+ (ssizeobjargproc)NULL, /*sq_ass_item*/
+ (ssizessizeobjargproc)NULL, /*sq_ass_slice*/
+};
+
+/* type description */
+
+static PyTypeObject Imaging_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0) "ImagingCore", /*tp_name*/
+ sizeof(ImagingObject), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ /* methods */
+ (destructor)_dealloc, /*tp_dealloc*/
+ 0, /*tp_vectorcall_offset*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_as_async*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ &image_as_sequence, /*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*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ methods, /*tp_methods*/
+ 0, /*tp_members*/
+ getsetters, /*tp_getset*/
+};
+
+#ifdef WITH_IMAGEDRAW
+
+static PyTypeObject ImagingFont_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0) "ImagingFont", /*tp_name*/
+ sizeof(ImagingFontObject), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ /* methods */
+ (destructor)_font_dealloc, /*tp_dealloc*/
+ 0, /*tp_vectorcall_offset*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_as_async*/
+ 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*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ _font_methods, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
+};
+
+static PyTypeObject ImagingDraw_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0) "ImagingDraw", /*tp_name*/
+ sizeof(ImagingDrawObject), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ /* methods */
+ (destructor)_draw_dealloc, /*tp_dealloc*/
+ 0, /*tp_vectorcall_offset*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_as_async*/
+ 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*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ _draw_methods, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
+};
+
+#endif
+
+static PyMappingMethods pixel_access_as_mapping = {
+ (lenfunc)NULL, /*mp_length*/
+ (binaryfunc)pixel_access_getitem, /*mp_subscript*/
+ (objobjargproc)pixel_access_setitem, /*mp_ass_subscript*/
+};
+
+/* type description */
+
+static PyTypeObject PixelAccess_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0) "PixelAccess", /*tp_name*/
+ sizeof(PixelAccessObject), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ /* methods */
+ (destructor)pixel_access_dealloc, /*tp_dealloc*/
+ 0, /*tp_vectorcall_offset*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_as_async*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ &pixel_access_as_mapping, /*tp_as_mapping*/
+ 0 /*tp_hash*/
+};
+
+/* -------------------------------------------------------------------- */
+
+static PyObject *
+_get_stats(PyObject *self, PyObject *args) {
+ PyObject *d;
+ PyObject *v;
+ ImagingMemoryArena arena = &ImagingDefaultArena;
+
+ if (!PyArg_ParseTuple(args, ":get_stats")) {
+ return NULL;
+ }
+
+ d = PyDict_New();
+ if (!d) {
+ return NULL;
+ }
+ v = PyLong_FromLong(arena->stats_new_count);
+ PyDict_SetItemString(d, "new_count", v ? v : Py_None);
+ Py_XDECREF(v);
+
+ v = PyLong_FromLong(arena->stats_allocated_blocks);
+ PyDict_SetItemString(d, "allocated_blocks", v ? v : Py_None);
+ Py_XDECREF(v);
+
+ v = PyLong_FromLong(arena->stats_reused_blocks);
+ PyDict_SetItemString(d, "reused_blocks", v ? v : Py_None);
+ Py_XDECREF(v);
+
+ v = PyLong_FromLong(arena->stats_reallocated_blocks);
+ PyDict_SetItemString(d, "reallocated_blocks", v ? v : Py_None);
+ Py_XDECREF(v);
+
+ v = PyLong_FromLong(arena->stats_freed_blocks);
+ PyDict_SetItemString(d, "freed_blocks", v ? v : Py_None);
+ Py_XDECREF(v);
+
+ v = PyLong_FromLong(arena->blocks_cached);
+ PyDict_SetItemString(d, "blocks_cached", v ? v : Py_None);
+ Py_XDECREF(v);
+ return d;
+}
+
+static PyObject *
+_reset_stats(PyObject *self, PyObject *args) {
+ ImagingMemoryArena arena = &ImagingDefaultArena;
+
+ if (!PyArg_ParseTuple(args, ":reset_stats")) {
+ return NULL;
+ }
+
+ arena->stats_new_count = 0;
+ arena->stats_allocated_blocks = 0;
+ arena->stats_reused_blocks = 0;
+ arena->stats_reallocated_blocks = 0;
+ arena->stats_freed_blocks = 0;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_get_alignment(PyObject *self, PyObject *args) {
+ if (!PyArg_ParseTuple(args, ":get_alignment")) {
+ return NULL;
+ }
+
+ return PyLong_FromLong(ImagingDefaultArena.alignment);
+}
+
+static PyObject *
+_get_block_size(PyObject *self, PyObject *args) {
+ if (!PyArg_ParseTuple(args, ":get_block_size")) {
+ return NULL;
+ }
+
+ return PyLong_FromLong(ImagingDefaultArena.block_size);
+}
+
+static PyObject *
+_get_blocks_max(PyObject *self, PyObject *args) {
+ if (!PyArg_ParseTuple(args, ":get_blocks_max")) {
+ return NULL;
+ }
+
+ return PyLong_FromLong(ImagingDefaultArena.blocks_max);
+}
+
+static PyObject *
+_set_alignment(PyObject *self, PyObject *args) {
+ int alignment;
+ if (!PyArg_ParseTuple(args, "i:set_alignment", &alignment)) {
+ return NULL;
+ }
+
+ if (alignment < 1 || alignment > 128) {
+ PyErr_SetString(PyExc_ValueError, "alignment should be from 1 to 128");
+ return NULL;
+ }
+ /* Is power of two */
+ if (alignment & (alignment - 1)) {
+ PyErr_SetString(PyExc_ValueError, "alignment should be power of two");
+ return NULL;
+ }
+
+ ImagingDefaultArena.alignment = alignment;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_set_block_size(PyObject *self, PyObject *args) {
+ int block_size;
+ if (!PyArg_ParseTuple(args, "i:set_block_size", &block_size)) {
+ return NULL;
+ }
+
+ if (block_size <= 0) {
+ PyErr_SetString(PyExc_ValueError, "block_size should be greater than 0");
+ return NULL;
+ }
+
+ if (block_size & 0xfff) {
+ PyErr_SetString(PyExc_ValueError, "block_size should be multiple of 4096");
+ return NULL;
+ }
+
+ ImagingDefaultArena.block_size = block_size;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_set_blocks_max(PyObject *self, PyObject *args) {
+ int blocks_max;
+ if (!PyArg_ParseTuple(args, "i:set_blocks_max", &blocks_max)) {
+ return NULL;
+ }
+
+ if (blocks_max < 0) {
+ PyErr_SetString(PyExc_ValueError, "blocks_max should be greater than 0");
+ return NULL;
+ } else if (
+ (unsigned long)blocks_max >
+ SIZE_MAX / sizeof(ImagingDefaultArena.blocks_pool[0])) {
+ PyErr_SetString(PyExc_ValueError, "blocks_max is too large");
+ return NULL;
+ }
+
+ if (!ImagingMemorySetBlocksMax(&ImagingDefaultArena, blocks_max)) {
+ return ImagingError_MemoryError();
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_clear_cache(PyObject *self, PyObject *args) {
+ int i = 0;
+
+ if (!PyArg_ParseTuple(args, "|i:clear_cache", &i)) {
+ return NULL;
+ }
+
+ ImagingMemoryClearCache(&ImagingDefaultArena, i);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* -------------------------------------------------------------------- */
+
+/* FIXME: this is something of a mess. Should replace this with
+ pluggable codecs, but not before PIL 1.2 */
+
+/* Decoders (in decode.c) */
+extern PyObject *
+PyImaging_BcnDecoderNew(PyObject *self, PyObject *args);
+extern PyObject *
+PyImaging_BitDecoderNew(PyObject *self, PyObject *args);
+extern PyObject *
+PyImaging_FliDecoderNew(PyObject *self, PyObject *args);
+extern PyObject *
+PyImaging_GifDecoderNew(PyObject *self, PyObject *args);
+extern PyObject *
+PyImaging_HexDecoderNew(PyObject *self, PyObject *args);
+extern PyObject *
+PyImaging_JpegDecoderNew(PyObject *self, PyObject *args);
+extern PyObject *
+PyImaging_Jpeg2KDecoderNew(PyObject *self, PyObject *args);
+extern PyObject *
+PyImaging_LibTiffDecoderNew(PyObject *self, PyObject *args);
+extern PyObject *
+PyImaging_PackbitsDecoderNew(PyObject *self, PyObject *args);
+extern PyObject *
+PyImaging_PcdDecoderNew(PyObject *self, PyObject *args);
+extern PyObject *
+PyImaging_PcxDecoderNew(PyObject *self, PyObject *args);
+extern PyObject *
+PyImaging_RawDecoderNew(PyObject *self, PyObject *args);
+extern PyObject *
+PyImaging_SgiRleDecoderNew(PyObject *self, PyObject *args);
+extern PyObject *
+PyImaging_SunRleDecoderNew(PyObject *self, PyObject *args);
+extern PyObject *
+PyImaging_TgaRleDecoderNew(PyObject *self, PyObject *args);
+extern PyObject *
+PyImaging_XbmDecoderNew(PyObject *self, PyObject *args);
+extern PyObject *
+PyImaging_ZipDecoderNew(PyObject *self, PyObject *args);
+
+/* Encoders (in encode.c) */
+extern PyObject *
+PyImaging_EpsEncoderNew(PyObject *self, PyObject *args);
+extern PyObject *
+PyImaging_GifEncoderNew(PyObject *self, PyObject *args);
+extern PyObject *
+PyImaging_JpegEncoderNew(PyObject *self, PyObject *args);
+extern PyObject *
+PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args);
+extern PyObject *
+PyImaging_PcxEncoderNew(PyObject *self, PyObject *args);
+extern PyObject *
+PyImaging_RawEncoderNew(PyObject *self, PyObject *args);
+extern PyObject *
+PyImaging_TgaRleEncoderNew(PyObject *self, PyObject *args);
+extern PyObject *
+PyImaging_XbmEncoderNew(PyObject *self, PyObject *args);
+extern PyObject *
+PyImaging_ZipEncoderNew(PyObject *self, PyObject *args);
+extern PyObject *
+PyImaging_LibTiffEncoderNew(PyObject *self, PyObject *args);
+
+/* Display support etc (in display.c) */
+#ifdef _WIN32
+extern PyObject *
+PyImaging_CreateWindowWin32(PyObject *self, PyObject *args);
+extern PyObject *
+PyImaging_DisplayWin32(PyObject *self, PyObject *args);
+extern PyObject *
+PyImaging_DisplayModeWin32(PyObject *self, PyObject *args);
+extern PyObject *
+PyImaging_GrabScreenWin32(PyObject *self, PyObject *args);
+extern PyObject *
+PyImaging_GrabClipboardWin32(PyObject *self, PyObject *args);
+extern PyObject *
+PyImaging_EventLoopWin32(PyObject *self, PyObject *args);
+extern PyObject *
+PyImaging_DrawWmf(PyObject *self, PyObject *args);
+#endif
+#ifdef HAVE_XCB
+extern PyObject *
+PyImaging_GrabScreenX11(PyObject *self, PyObject *args);
+#endif
+
+/* Experimental path stuff (in path.c) */
+extern PyObject *
+PyPath_Create(ImagingObject *self, PyObject *args);
+
+/* Experimental outline stuff (in outline.c) */
+extern PyObject *
+PyOutline_Create(ImagingObject *self, PyObject *args);
+
+extern PyObject *
+PyImaging_MapBuffer(PyObject *self, PyObject *args);
+
+static PyMethodDef functions[] = {
+
+ /* Object factories */
+ {"alpha_composite", (PyCFunction)_alpha_composite, METH_VARARGS},
+ {"blend", (PyCFunction)_blend, METH_VARARGS},
+ {"fill", (PyCFunction)_fill, METH_VARARGS},
+ {"new", (PyCFunction)_new, METH_VARARGS},
+ {"merge", (PyCFunction)_merge, METH_VARARGS},
+
+ /* Functions */
+ {"convert", (PyCFunction)_convert2, METH_VARARGS},
+
+ /* Codecs */
+ {"bcn_decoder", (PyCFunction)PyImaging_BcnDecoderNew, METH_VARARGS},
+ {"bit_decoder", (PyCFunction)PyImaging_BitDecoderNew, METH_VARARGS},
+ {"eps_encoder", (PyCFunction)PyImaging_EpsEncoderNew, METH_VARARGS},
+ {"fli_decoder", (PyCFunction)PyImaging_FliDecoderNew, METH_VARARGS},
+ {"gif_decoder", (PyCFunction)PyImaging_GifDecoderNew, METH_VARARGS},
+ {"gif_encoder", (PyCFunction)PyImaging_GifEncoderNew, METH_VARARGS},
+ {"hex_decoder", (PyCFunction)PyImaging_HexDecoderNew, METH_VARARGS},
+ {"hex_encoder", (PyCFunction)PyImaging_EpsEncoderNew, METH_VARARGS}, /* EPS=HEX! */
+#ifdef HAVE_LIBJPEG
+ {"jpeg_decoder", (PyCFunction)PyImaging_JpegDecoderNew, METH_VARARGS},
+ {"jpeg_encoder", (PyCFunction)PyImaging_JpegEncoderNew, METH_VARARGS},
+#endif
+#ifdef HAVE_OPENJPEG
+ {"jpeg2k_decoder", (PyCFunction)PyImaging_Jpeg2KDecoderNew, METH_VARARGS},
+ {"jpeg2k_encoder", (PyCFunction)PyImaging_Jpeg2KEncoderNew, METH_VARARGS},
+#endif
+#ifdef HAVE_LIBTIFF
+ {"libtiff_decoder", (PyCFunction)PyImaging_LibTiffDecoderNew, METH_VARARGS},
+ {"libtiff_encoder", (PyCFunction)PyImaging_LibTiffEncoderNew, METH_VARARGS},
+#endif
+ {"packbits_decoder", (PyCFunction)PyImaging_PackbitsDecoderNew, METH_VARARGS},
+ {"pcd_decoder", (PyCFunction)PyImaging_PcdDecoderNew, METH_VARARGS},
+ {"pcx_decoder", (PyCFunction)PyImaging_PcxDecoderNew, METH_VARARGS},
+ {"pcx_encoder", (PyCFunction)PyImaging_PcxEncoderNew, METH_VARARGS},
+ {"raw_decoder", (PyCFunction)PyImaging_RawDecoderNew, METH_VARARGS},
+ {"raw_encoder", (PyCFunction)PyImaging_RawEncoderNew, METH_VARARGS},
+ {"sgi_rle_decoder", (PyCFunction)PyImaging_SgiRleDecoderNew, METH_VARARGS},
+ {"sun_rle_decoder", (PyCFunction)PyImaging_SunRleDecoderNew, METH_VARARGS},
+ {"tga_rle_decoder", (PyCFunction)PyImaging_TgaRleDecoderNew, METH_VARARGS},
+ {"tga_rle_encoder", (PyCFunction)PyImaging_TgaRleEncoderNew, METH_VARARGS},
+ {"xbm_decoder", (PyCFunction)PyImaging_XbmDecoderNew, METH_VARARGS},
+ {"xbm_encoder", (PyCFunction)PyImaging_XbmEncoderNew, METH_VARARGS},
+#ifdef HAVE_LIBZ
+ {"zip_decoder", (PyCFunction)PyImaging_ZipDecoderNew, METH_VARARGS},
+ {"zip_encoder", (PyCFunction)PyImaging_ZipEncoderNew, METH_VARARGS},
+#endif
+
+/* Memory mapping */
+#ifdef WITH_MAPPING
+ {"map_buffer", (PyCFunction)PyImaging_MapBuffer, METH_VARARGS},
+#endif
+
+/* Display support */
+#ifdef _WIN32
+ {"display", (PyCFunction)PyImaging_DisplayWin32, METH_VARARGS},
+ {"display_mode", (PyCFunction)PyImaging_DisplayModeWin32, METH_VARARGS},
+ {"grabscreen_win32", (PyCFunction)PyImaging_GrabScreenWin32, METH_VARARGS},
+ {"grabclipboard_win32", (PyCFunction)PyImaging_GrabClipboardWin32, METH_VARARGS},
+ {"createwindow", (PyCFunction)PyImaging_CreateWindowWin32, METH_VARARGS},
+ {"eventloop", (PyCFunction)PyImaging_EventLoopWin32, METH_VARARGS},
+ {"drawwmf", (PyCFunction)PyImaging_DrawWmf, METH_VARARGS},
+#endif
+#ifdef HAVE_XCB
+ {"grabscreen_x11", (PyCFunction)PyImaging_GrabScreenX11, METH_VARARGS},
+#endif
+
+ /* Utilities */
+ {"getcodecstatus", (PyCFunction)_getcodecstatus, METH_VARARGS},
+
+/* Special effects (experimental) */
+#ifdef WITH_EFFECTS
+ {"effect_mandelbrot", (PyCFunction)_effect_mandelbrot, METH_VARARGS},
+ {"effect_noise", (PyCFunction)_effect_noise, METH_VARARGS},
+ {"linear_gradient", (PyCFunction)_linear_gradient, METH_VARARGS},
+ {"radial_gradient", (PyCFunction)_radial_gradient, METH_VARARGS},
+ {"wedge", (PyCFunction)_linear_gradient, METH_VARARGS}, /* Compatibility */
+#endif
+
+/* Drawing support stuff */
+#ifdef WITH_IMAGEDRAW
+ {"font", (PyCFunction)_font_new, METH_VARARGS},
+ {"draw", (PyCFunction)_draw_new, METH_VARARGS},
+#endif
+
+/* Experimental path stuff */
+#ifdef WITH_IMAGEPATH
+ {"path", (PyCFunction)PyPath_Create, METH_VARARGS},
+#endif
+
+/* Experimental arrow graphics stuff */
+#ifdef WITH_ARROW
+ {"outline", (PyCFunction)PyOutline_Create, METH_VARARGS},
+#endif
+
+ /* Resource management */
+ {"get_stats", (PyCFunction)_get_stats, METH_VARARGS},
+ {"reset_stats", (PyCFunction)_reset_stats, METH_VARARGS},
+ {"get_alignment", (PyCFunction)_get_alignment, METH_VARARGS},
+ {"get_block_size", (PyCFunction)_get_block_size, METH_VARARGS},
+ {"get_blocks_max", (PyCFunction)_get_blocks_max, METH_VARARGS},
+ {"set_alignment", (PyCFunction)_set_alignment, METH_VARARGS},
+ {"set_block_size", (PyCFunction)_set_block_size, METH_VARARGS},
+ {"set_blocks_max", (PyCFunction)_set_blocks_max, METH_VARARGS},
+ {"clear_cache", (PyCFunction)_clear_cache, METH_VARARGS},
+
+ {NULL, NULL} /* sentinel */
+};
+
+static int
+setup_module(PyObject *m) {
+ PyObject *d = PyModule_GetDict(m);
+ const char *version = (char *)PILLOW_VERSION;
+
+ /* Ready object types */
+ if (PyType_Ready(&Imaging_Type) < 0) {
+ return -1;
+ }
+
+#ifdef WITH_IMAGEDRAW
+ if (PyType_Ready(&ImagingFont_Type) < 0) {
+ return -1;
+ }
+
+ if (PyType_Ready(&ImagingDraw_Type) < 0) {
+ return -1;
+ }
+#endif
+ if (PyType_Ready(&PixelAccess_Type) < 0) {
+ return -1;
+ }
+
+ ImagingAccessInit();
+
+#ifdef HAVE_LIBJPEG
+ {
+ extern const char *ImagingJpegVersion(void);
+ PyObject *v = PyUnicode_FromString(ImagingJpegVersion());
+ PyDict_SetItemString(d, "jpeglib_version", v ? v : Py_None);
+ Py_XDECREF(v);
+ }
+#endif
+
+#ifdef HAVE_OPENJPEG
+ {
+ extern const char *ImagingJpeg2KVersion(void);
+ PyObject *v = PyUnicode_FromString(ImagingJpeg2KVersion());
+ PyDict_SetItemString(d, "jp2klib_version", v ? v : Py_None);
+ Py_XDECREF(v);
+ }
+#endif
+
+ PyObject *have_libjpegturbo;
+#ifdef LIBJPEG_TURBO_VERSION
+ have_libjpegturbo = Py_True;
+ {
+#define tostr1(a) #a
+#define tostr(a) tostr1(a)
+ PyObject *v = PyUnicode_FromString(tostr(LIBJPEG_TURBO_VERSION));
+ PyDict_SetItemString(d, "libjpeg_turbo_version", v ? v : Py_None);
+ Py_XDECREF(v);
+#undef tostr
+#undef tostr1
+ }
+#else
+ have_libjpegturbo = Py_False;
+#endif
+ Py_INCREF(have_libjpegturbo);
+ PyModule_AddObject(m, "HAVE_LIBJPEGTURBO", have_libjpegturbo);
+
+ PyObject *have_libimagequant;
+#ifdef HAVE_LIBIMAGEQUANT
+ have_libimagequant = Py_True;
+ {
+ extern const char *ImagingImageQuantVersion(void);
+ PyObject *v = PyUnicode_FromString(ImagingImageQuantVersion());
+ PyDict_SetItemString(d, "imagequant_version", v ? v : Py_None);
+ Py_XDECREF(v);
+ }
+#else
+ have_libimagequant = Py_False;
+#endif
+ Py_INCREF(have_libimagequant);
+ PyModule_AddObject(m, "HAVE_LIBIMAGEQUANT", have_libimagequant);
+
+#ifdef HAVE_LIBZ
+ /* zip encoding strategies */
+ PyModule_AddIntConstant(m, "DEFAULT_STRATEGY", Z_DEFAULT_STRATEGY);
+ PyModule_AddIntConstant(m, "FILTERED", Z_FILTERED);
+ PyModule_AddIntConstant(m, "HUFFMAN_ONLY", Z_HUFFMAN_ONLY);
+ PyModule_AddIntConstant(m, "RLE", Z_RLE);
+ PyModule_AddIntConstant(m, "FIXED", Z_FIXED);
+ {
+ extern const char *ImagingZipVersion(void);
+ PyObject *v = PyUnicode_FromString(ImagingZipVersion());
+ PyDict_SetItemString(d, "zlib_version", v ? v : Py_None);
+ Py_XDECREF(v);
+ }
+#endif
+
+#ifdef HAVE_LIBTIFF
+ {
+ extern const char *ImagingTiffVersion(void);
+ PyObject *v = PyUnicode_FromString(ImagingTiffVersion());
+ PyDict_SetItemString(d, "libtiff_version", v ? v : Py_None);
+ Py_XDECREF(v);
+
+ // Test for libtiff 4.0 or later, excluding libtiff 3.9.6 and 3.9.7
+ PyObject *support_custom_tags;
+#if TIFFLIB_VERSION >= 20111221 && TIFFLIB_VERSION != 20120218 && \
+ TIFFLIB_VERSION != 20120922
+ support_custom_tags = Py_True;
+#else
+ support_custom_tags = Py_False;
+#endif
+ PyDict_SetItemString(d, "libtiff_support_custom_tags", support_custom_tags);
+ }
+#endif
+
+ PyObject *have_xcb;
+#ifdef HAVE_XCB
+ have_xcb = Py_True;
+#else
+ have_xcb = Py_False;
+#endif
+ Py_INCREF(have_xcb);
+ PyModule_AddObject(m, "HAVE_XCB", have_xcb);
+
+ PyObject *pillow_version = PyUnicode_FromString(version);
+ PyDict_SetItemString(d, "PILLOW_VERSION", pillow_version ? pillow_version : Py_None);
+ Py_XDECREF(pillow_version);
+
+ return 0;
+}
+
+PyMODINIT_FUNC
+PyInit__imaging(void) {
+ PyObject *m;
+
+ static PyModuleDef module_def = {
+ PyModuleDef_HEAD_INIT,
+ "_imaging", /* m_name */
+ NULL, /* m_doc */
+ -1, /* m_size */
+ functions, /* m_methods */
+ };
+
+ m = PyModule_Create(&module_def);
+
+ if (setup_module(m) < 0) {
+ Py_DECREF(m);
+ return NULL;
+ }
+
+ return m;
+}