mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-10-31 07:57:27 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			1417 lines
		
	
	
		
			41 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1417 lines
		
	
	
		
			41 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * The Python Imaging Library.
 | |
|  *
 | |
|  * standard encoder interfaces for the Imaging library
 | |
|  *
 | |
|  * History:
 | |
|  * 1996-04-19 fl   Based on decoders.c
 | |
|  * 1996-05-12 fl   Compile cleanly as C++
 | |
|  * 1996-12-30 fl   Plugged potential memory leak for tiled images
 | |
|  * 1997-01-03 fl   Added GIF encoder
 | |
|  * 1997-01-05 fl   Plugged encoder buffer leaks
 | |
|  * 1997-01-11 fl   Added encode_to_file method
 | |
|  * 1998-03-09 fl   Added mode/rawmode argument to encoders
 | |
|  * 1998-07-09 fl   Added interlace argument to GIF encoder
 | |
|  * 1999-02-07 fl   Added PCX encoder
 | |
|  *
 | |
|  * Copyright (c) 1997-2001 by Secret Labs AB
 | |
|  * Copyright (c) 1996-1997 by Fredrik Lundh
 | |
|  *
 | |
|  * See the README file for information on usage and redistribution.
 | |
|  */
 | |
| 
 | |
| /* FIXME: make these pluggable! */
 | |
| 
 | |
| #define PY_SSIZE_T_CLEAN
 | |
| #include "Python.h"
 | |
| 
 | |
| #include "thirdparty/pythoncapi_compat.h"
 | |
| #include "libImaging/Imaging.h"
 | |
| #include "libImaging/Bcn.h"
 | |
| #include "libImaging/Gif.h"
 | |
| 
 | |
| #ifdef HAVE_UNISTD_H
 | |
| #include <unistd.h> /* write */
 | |
| #endif
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /* Common                                                               */
 | |
| /* -------------------------------------------------------------------- */
 | |
| 
 | |
| typedef struct {
 | |
|     PyObject_HEAD int (*encode)(
 | |
|         Imaging im, ImagingCodecState state, UINT8 *buffer, int bytes
 | |
|     );
 | |
|     int (*cleanup)(ImagingCodecState state);
 | |
|     struct ImagingCodecStateInstance state;
 | |
|     Imaging im;
 | |
|     PyObject *lock;
 | |
|     int pushes_fd;
 | |
| } ImagingEncoderObject;
 | |
| 
 | |
| static PyTypeObject ImagingEncoderType;
 | |
| 
 | |
| static ImagingEncoderObject *
 | |
| PyImaging_EncoderNew(int contextsize) {
 | |
|     ImagingEncoderObject *encoder;
 | |
|     void *context;
 | |
| 
 | |
|     if (PyType_Ready(&ImagingEncoderType) < 0) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     encoder = PyObject_New(ImagingEncoderObject, &ImagingEncoderType);
 | |
|     if (encoder == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     /* Clear the encoder state */
 | |
|     memset(&encoder->state, 0, sizeof(encoder->state));
 | |
| 
 | |
|     /* Allocate encoder context */
 | |
|     if (contextsize > 0) {
 | |
|         context = (void *)calloc(1, contextsize);
 | |
|         if (!context) {
 | |
|             Py_DECREF(encoder);
 | |
|             (void)ImagingError_MemoryError();
 | |
|             return NULL;
 | |
|         }
 | |
|     } else {
 | |
|         context = 0;
 | |
|     }
 | |
| 
 | |
|     /* Initialize encoder context */
 | |
|     encoder->state.context = context;
 | |
| 
 | |
|     /* Most encoders don't need this */
 | |
|     encoder->cleanup = NULL;
 | |
| 
 | |
|     /* Target image */
 | |
|     encoder->lock = NULL;
 | |
|     encoder->im = NULL;
 | |
|     encoder->pushes_fd = 0;
 | |
| 
 | |
|     return encoder;
 | |
| }
 | |
| 
 | |
| static void
 | |
| _dealloc(ImagingEncoderObject *encoder) {
 | |
|     if (encoder->cleanup) {
 | |
|         encoder->cleanup(&encoder->state);
 | |
|     }
 | |
|     free(encoder->state.buffer);
 | |
|     free(encoder->state.context);
 | |
|     Py_XDECREF(encoder->lock);
 | |
|     Py_XDECREF(encoder->state.fd);
 | |
|     PyObject_Del(encoder);
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| _encode_cleanup(ImagingEncoderObject *encoder, PyObject *args) {
 | |
|     int status = 0;
 | |
| 
 | |
|     if (encoder->cleanup) {
 | |
|         status = encoder->cleanup(&encoder->state);
 | |
|     }
 | |
| 
 | |
|     return Py_BuildValue("i", status);
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| _encode(ImagingEncoderObject *encoder, PyObject *args) {
 | |
|     PyObject *buf;
 | |
|     PyObject *result;
 | |
|     int status;
 | |
| 
 | |
|     /* Encode to a Python string (allocated by this method) */
 | |
| 
 | |
|     Py_ssize_t bufsize = 16384;
 | |
| 
 | |
|     if (!PyArg_ParseTuple(args, "|n", &bufsize)) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     buf = PyBytes_FromStringAndSize(NULL, bufsize);
 | |
|     if (!buf) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     status = encoder->encode(
 | |
|         encoder->im, &encoder->state, (UINT8 *)PyBytes_AsString(buf), bufsize
 | |
|     );
 | |
| 
 | |
|     /* adjust string length to avoid slicing in encoder */
 | |
|     if (_PyBytes_Resize(&buf, (status > 0) ? status : 0) < 0) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     result = Py_BuildValue("iiO", status, encoder->state.errcode, buf);
 | |
| 
 | |
|     Py_DECREF(buf); /* must release buffer!!! */
 | |
| 
 | |
|     return result;
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| _encode_to_pyfd(ImagingEncoderObject *encoder) {
 | |
|     PyObject *result;
 | |
|     int status;
 | |
| 
 | |
|     if (!encoder->pushes_fd) {
 | |
|         // UNDONE, appropriate errcode???
 | |
|         result = Py_BuildValue("ii", 0, IMAGING_CODEC_CONFIG);
 | |
|         return result;
 | |
|     }
 | |
| 
 | |
|     status = encoder->encode(encoder->im, &encoder->state, (UINT8 *)NULL, 0);
 | |
| 
 | |
|     result = Py_BuildValue("ii", status, encoder->state.errcode);
 | |
| 
 | |
|     return result;
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| _encode_to_file(ImagingEncoderObject *encoder, PyObject *args) {
 | |
|     UINT8 *buf;
 | |
|     int status;
 | |
|     ImagingSectionCookie cookie;
 | |
| 
 | |
|     /* Encode to a file handle */
 | |
| 
 | |
|     Py_ssize_t fh;
 | |
|     Py_ssize_t bufsize = 16384;
 | |
| 
 | |
|     if (!PyArg_ParseTuple(args, "n|n", &fh, &bufsize)) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     /* Allocate an encoder buffer */
 | |
|     /* malloc check ok, either constant int, or checked by PyArg_ParseTuple */
 | |
|     buf = (UINT8 *)malloc(bufsize);
 | |
|     if (!buf) {
 | |
|         return ImagingError_MemoryError();
 | |
|     }
 | |
| 
 | |
|     ImagingSectionEnter(&cookie);
 | |
| 
 | |
|     do {
 | |
|         /* This replaces the inner loop in the ImageFile _save
 | |
|            function. */
 | |
| 
 | |
|         status = encoder->encode(encoder->im, &encoder->state, buf, bufsize);
 | |
| 
 | |
|         if (status > 0) {
 | |
|             if (write(fh, buf, status) < 0) {
 | |
|                 ImagingSectionLeave(&cookie);
 | |
|                 free(buf);
 | |
|                 return PyErr_SetFromErrno(PyExc_OSError);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|     } while (encoder->state.errcode == 0);
 | |
| 
 | |
|     ImagingSectionLeave(&cookie);
 | |
| 
 | |
|     free(buf);
 | |
| 
 | |
|     return Py_BuildValue("i", encoder->state.errcode);
 | |
| }
 | |
| 
 | |
| extern Imaging
 | |
| PyImaging_AsImaging(PyObject *op);
 | |
| 
 | |
| static PyObject *
 | |
| _setimage(ImagingEncoderObject *encoder, PyObject *args) {
 | |
|     PyObject *op;
 | |
|     Imaging im;
 | |
|     ImagingCodecState state;
 | |
|     Py_ssize_t x0, y0, x1, y1;
 | |
| 
 | |
|     /* Define where image data should be stored */
 | |
| 
 | |
|     x0 = y0 = x1 = y1 = 0;
 | |
| 
 | |
|     /* FIXME: should publish the ImagingType descriptor */
 | |
|     if (!PyArg_ParseTuple(args, "O|(nnnn)", &op, &x0, &y0, &x1, &y1)) {
 | |
|         return NULL;
 | |
|     }
 | |
|     im = PyImaging_AsImaging(op);
 | |
|     if (!im) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     encoder->im = im;
 | |
| 
 | |
|     state = &encoder->state;
 | |
| 
 | |
|     if (x0 == 0 && x1 == 0) {
 | |
|         state->xsize = im->xsize;
 | |
|         state->ysize = im->ysize;
 | |
|     } else {
 | |
|         state->xoff = x0;
 | |
|         state->yoff = y0;
 | |
|         state->xsize = x1 - x0;
 | |
|         state->ysize = y1 - y0;
 | |
|     }
 | |
| 
 | |
|     if (state->xsize <= 0 || state->xsize + state->xoff > im->xsize ||
 | |
|         state->ysize <= 0 || state->ysize + state->yoff > im->ysize) {
 | |
|         PyErr_SetString(PyExc_SystemError, "tile cannot extend outside image");
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     /* Allocate memory buffer (if bits field is set) */
 | |
|     if (state->bits > 0) {
 | |
|         if (state->xsize > ((INT_MAX / state->bits) - 7)) {
 | |
|             return ImagingError_MemoryError();
 | |
|         }
 | |
|         state->bytes = (state->bits * state->xsize + 7) / 8;
 | |
|         /* malloc check ok, overflow checked above */
 | |
|         state->buffer = (UINT8 *)calloc(1, state->bytes);
 | |
|         if (!state->buffer) {
 | |
|             return ImagingError_MemoryError();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* Keep a reference to the image object, to make sure it doesn't
 | |
|        go away before we do */
 | |
|     Py_INCREF(op);
 | |
|     Py_XDECREF(encoder->lock);
 | |
|     encoder->lock = op;
 | |
| 
 | |
|     Py_RETURN_NONE;
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| _setfd(ImagingEncoderObject *encoder, PyObject *args) {
 | |
|     PyObject *fd;
 | |
|     ImagingCodecState state;
 | |
| 
 | |
|     if (!PyArg_ParseTuple(args, "O", &fd)) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     state = &encoder->state;
 | |
| 
 | |
|     Py_XINCREF(fd);
 | |
|     state->fd = fd;
 | |
| 
 | |
|     Py_RETURN_NONE;
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| _get_pushes_fd(ImagingEncoderObject *encoder, void *closure) {
 | |
|     return PyBool_FromLong(encoder->pushes_fd);
 | |
| }
 | |
| 
 | |
| static struct PyMethodDef methods[] = {
 | |
|     {"encode", (PyCFunction)_encode, METH_VARARGS},
 | |
|     {"cleanup", (PyCFunction)_encode_cleanup, METH_VARARGS},
 | |
|     {"encode_to_file", (PyCFunction)_encode_to_file, METH_VARARGS},
 | |
|     {"encode_to_pyfd", (PyCFunction)_encode_to_pyfd, METH_NOARGS},
 | |
|     {"setimage", (PyCFunction)_setimage, METH_VARARGS},
 | |
|     {"setfd", (PyCFunction)_setfd, METH_VARARGS},
 | |
|     {NULL, NULL} /* sentinel */
 | |
| };
 | |
| 
 | |
| static struct PyGetSetDef getseters[] = {
 | |
|     {"pushes_fd",
 | |
|      (getter)_get_pushes_fd,
 | |
|      NULL,
 | |
|      "True if this decoder expects to push directly to self.fd",
 | |
|      NULL},
 | |
|     {NULL, NULL, NULL, NULL, NULL} /* sentinel */
 | |
| };
 | |
| 
 | |
| static PyTypeObject ImagingEncoderType = {
 | |
|     PyVarObject_HEAD_INIT(NULL, 0).tp_name = "ImagingEncoder",
 | |
|     .tp_basicsize = sizeof(ImagingEncoderObject),
 | |
|     .tp_dealloc = (destructor)_dealloc,
 | |
|     .tp_methods = methods,
 | |
|     .tp_getset = getseters,
 | |
| };
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| 
 | |
| int
 | |
| get_packer(ImagingEncoderObject *encoder, const char *mode, const char *rawmode) {
 | |
|     int bits;
 | |
|     ImagingShuffler pack;
 | |
| 
 | |
|     pack = ImagingFindPacker(mode, rawmode, &bits);
 | |
|     if (!pack) {
 | |
|         Py_DECREF(encoder);
 | |
|         PyErr_Format(PyExc_ValueError, "No packer found from %s to %s", mode, rawmode);
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     encoder->state.shuffle = pack;
 | |
|     encoder->state.bits = bits;
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /* BCN                                                                  */
 | |
| /* -------------------------------------------------------------------- */
 | |
| 
 | |
| PyObject *
 | |
| PyImaging_BcnEncoderNew(PyObject *self, PyObject *args) {
 | |
|     ImagingEncoderObject *encoder;
 | |
| 
 | |
|     char *mode;
 | |
|     int n;
 | |
|     if (!PyArg_ParseTuple(args, "si", &mode, &n)) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     encoder = PyImaging_EncoderNew(0);
 | |
|     if (encoder == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     encoder->encode = ImagingBcnEncode;
 | |
|     encoder->state.state = n;
 | |
| 
 | |
|     return (PyObject *)encoder;
 | |
| }
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /* EPS                                                                  */
 | |
| /* -------------------------------------------------------------------- */
 | |
| 
 | |
| PyObject *
 | |
| PyImaging_EpsEncoderNew(PyObject *self, PyObject *args) {
 | |
|     ImagingEncoderObject *encoder;
 | |
| 
 | |
|     encoder = PyImaging_EncoderNew(0);
 | |
|     if (encoder == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     encoder->encode = ImagingEpsEncode;
 | |
| 
 | |
|     return (PyObject *)encoder;
 | |
| }
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /* GIF                                                                  */
 | |
| /* -------------------------------------------------------------------- */
 | |
| 
 | |
| PyObject *
 | |
| PyImaging_GifEncoderNew(PyObject *self, PyObject *args) {
 | |
|     ImagingEncoderObject *encoder;
 | |
| 
 | |
|     char *mode;
 | |
|     char *rawmode;
 | |
|     Py_ssize_t bits = 8;
 | |
|     Py_ssize_t interlace = 0;
 | |
|     if (!PyArg_ParseTuple(args, "ss|nn", &mode, &rawmode, &bits, &interlace)) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     encoder = PyImaging_EncoderNew(sizeof(GIFENCODERSTATE));
 | |
|     if (encoder == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     if (get_packer(encoder, mode, rawmode) < 0) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     encoder->encode = ImagingGifEncode;
 | |
| 
 | |
|     ((GIFENCODERSTATE *)encoder->state.context)->bits = bits;
 | |
|     ((GIFENCODERSTATE *)encoder->state.context)->interlace = interlace;
 | |
| 
 | |
|     return (PyObject *)encoder;
 | |
| }
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /* PCX                                                                  */
 | |
| /* -------------------------------------------------------------------- */
 | |
| 
 | |
| PyObject *
 | |
| PyImaging_PcxEncoderNew(PyObject *self, PyObject *args) {
 | |
|     ImagingEncoderObject *encoder;
 | |
| 
 | |
|     char *mode;
 | |
|     char *rawmode;
 | |
|     Py_ssize_t bits = 8;
 | |
| 
 | |
|     if (!PyArg_ParseTuple(args, "ss|n", &mode, &rawmode, &bits)) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     encoder = PyImaging_EncoderNew(0);
 | |
|     if (encoder == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     if (get_packer(encoder, mode, rawmode) < 0) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     encoder->encode = ImagingPcxEncode;
 | |
| 
 | |
|     return (PyObject *)encoder;
 | |
| }
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /* RAW                                                                  */
 | |
| /* -------------------------------------------------------------------- */
 | |
| 
 | |
| PyObject *
 | |
| PyImaging_RawEncoderNew(PyObject *self, PyObject *args) {
 | |
|     ImagingEncoderObject *encoder;
 | |
| 
 | |
|     char *mode;
 | |
|     char *rawmode;
 | |
|     Py_ssize_t stride = 0;
 | |
|     Py_ssize_t ystep = 1;
 | |
| 
 | |
|     if (!PyArg_ParseTuple(args, "ss|nn", &mode, &rawmode, &stride, &ystep)) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     encoder = PyImaging_EncoderNew(0);
 | |
|     if (encoder == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     if (get_packer(encoder, mode, rawmode) < 0) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     encoder->encode = ImagingRawEncode;
 | |
| 
 | |
|     encoder->state.ystep = ystep;
 | |
|     encoder->state.count = stride;
 | |
| 
 | |
|     return (PyObject *)encoder;
 | |
| }
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /* TGA                                                                  */
 | |
| /* -------------------------------------------------------------------- */
 | |
| 
 | |
| PyObject *
 | |
| PyImaging_TgaRleEncoderNew(PyObject *self, PyObject *args) {
 | |
|     ImagingEncoderObject *encoder;
 | |
| 
 | |
|     char *mode;
 | |
|     char *rawmode;
 | |
|     Py_ssize_t ystep = 1;
 | |
| 
 | |
|     if (!PyArg_ParseTuple(args, "ss|n", &mode, &rawmode, &ystep)) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     encoder = PyImaging_EncoderNew(0);
 | |
|     if (encoder == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     if (get_packer(encoder, mode, rawmode) < 0) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     encoder->encode = ImagingTgaRleEncode;
 | |
| 
 | |
|     encoder->state.ystep = ystep;
 | |
| 
 | |
|     return (PyObject *)encoder;
 | |
| }
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /* XBM                                                                  */
 | |
| /* -------------------------------------------------------------------- */
 | |
| 
 | |
| PyObject *
 | |
| PyImaging_XbmEncoderNew(PyObject *self, PyObject *args) {
 | |
|     ImagingEncoderObject *encoder;
 | |
| 
 | |
|     encoder = PyImaging_EncoderNew(0);
 | |
|     if (encoder == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     if (get_packer(encoder, "1", "1;R") < 0) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     encoder->encode = ImagingXbmEncode;
 | |
| 
 | |
|     return (PyObject *)encoder;
 | |
| }
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /* ZIP                                                                  */
 | |
| /* -------------------------------------------------------------------- */
 | |
| 
 | |
| #ifdef HAVE_LIBZ
 | |
| 
 | |
| #include "libImaging/ZipCodecs.h"
 | |
| 
 | |
| PyObject *
 | |
| PyImaging_ZipEncoderNew(PyObject *self, PyObject *args) {
 | |
|     ImagingEncoderObject *encoder;
 | |
| 
 | |
|     char *mode;
 | |
|     char *rawmode;
 | |
|     Py_ssize_t optimize = 0;
 | |
|     Py_ssize_t compress_level = -1;
 | |
|     Py_ssize_t compress_type = -1;
 | |
|     char *dictionary = NULL;
 | |
|     Py_ssize_t dictionary_size = 0;
 | |
|     if (!PyArg_ParseTuple(
 | |
|             args,
 | |
|             "ss|nnny#",
 | |
|             &mode,
 | |
|             &rawmode,
 | |
|             &optimize,
 | |
|             &compress_level,
 | |
|             &compress_type,
 | |
|             &dictionary,
 | |
|             &dictionary_size
 | |
|         )) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     /* Copy to avoid referencing Python's memory */
 | |
|     if (dictionary && dictionary_size > 0) {
 | |
|         /* malloc check ok, size comes from PyArg_ParseTuple */
 | |
|         char *p = malloc(dictionary_size);
 | |
|         if (!p) {
 | |
|             return ImagingError_MemoryError();
 | |
|         }
 | |
|         memcpy(p, dictionary, dictionary_size);
 | |
|         dictionary = p;
 | |
|     } else {
 | |
|         dictionary = NULL;
 | |
|     }
 | |
| 
 | |
|     encoder = PyImaging_EncoderNew(sizeof(ZIPSTATE));
 | |
|     if (encoder == NULL) {
 | |
|         free(dictionary);
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     if (get_packer(encoder, mode, rawmode) < 0) {
 | |
|         free(dictionary);
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     encoder->encode = ImagingZipEncode;
 | |
|     encoder->cleanup = ImagingZipEncodeCleanup;
 | |
| 
 | |
|     if (rawmode[0] == 'P') {
 | |
|         /* disable filtering */
 | |
|         ((ZIPSTATE *)encoder->state.context)->mode = ZIP_PNG_PALETTE;
 | |
|     }
 | |
| 
 | |
|     ((ZIPSTATE *)encoder->state.context)->optimize = optimize;
 | |
|     ((ZIPSTATE *)encoder->state.context)->compress_level = compress_level;
 | |
|     ((ZIPSTATE *)encoder->state.context)->compress_type = compress_type;
 | |
|     ((ZIPSTATE *)encoder->state.context)->dictionary = dictionary;
 | |
|     ((ZIPSTATE *)encoder->state.context)->dictionary_size = dictionary_size;
 | |
| 
 | |
|     return (PyObject *)encoder;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /* LibTiff                                                              */
 | |
| /* -------------------------------------------------------------------- */
 | |
| 
 | |
| #ifdef HAVE_LIBTIFF
 | |
| 
 | |
| #include "libImaging/TiffDecode.h"
 | |
| 
 | |
| #include <string.h>
 | |
| 
 | |
| PyObject *
 | |
| PyImaging_LibTiffEncoderNew(PyObject *self, PyObject *args) {
 | |
|     ImagingEncoderObject *encoder;
 | |
| 
 | |
|     char *mode;
 | |
|     char *rawmode;
 | |
|     char *compname;
 | |
|     char *filename;
 | |
|     Py_ssize_t fp;
 | |
| 
 | |
|     PyObject *tags, *types;
 | |
|     PyObject *key, *value;
 | |
|     Py_ssize_t pos = 0;
 | |
|     int key_int, status, is_core_tag, is_var_length, num_core_tags, i;
 | |
|     TIFFDataType type = TIFF_NOTYPE;
 | |
|     // This list also exists in TiffTags.py
 | |
|     const int core_tags[] = {256,   257, 258,   259, 262, 263, 266,   269,   274,
 | |
|                              277,   278, 280,   281, 340, 341, 282,   283,   284,
 | |
|                              286,   287, 296,   297, 320, 321, 338,   32995, 32998,
 | |
|                              32996, 339, 32997, 330, 531, 530, 65537, 301,   532};
 | |
| 
 | |
|     Py_ssize_t tags_size;
 | |
|     PyObject *item;
 | |
| 
 | |
|     if (!PyArg_ParseTuple(
 | |
|             args, "sssnsOO", &mode, &rawmode, &compname, &fp, &filename, &tags, &types
 | |
|         )) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     if (!PyList_Check(tags)) {
 | |
|         PyErr_SetString(PyExc_ValueError, "Invalid tags list");
 | |
|         return NULL;
 | |
|     } else {
 | |
|         tags_size = PyList_Size(tags);
 | |
|         TRACE(("tags size: %d\n", (int)tags_size));
 | |
|         for (pos = 0; pos < tags_size; pos++) {
 | |
|             item = PyList_GetItemRef(tags, pos);
 | |
|             if (item == NULL) {
 | |
|                 return NULL;
 | |
|             }
 | |
| 
 | |
|             if (!PyTuple_Check(item) || PyTuple_Size(item) != 2) {
 | |
|                 Py_DECREF(item);
 | |
|                 PyErr_SetString(PyExc_ValueError, "Invalid tags list");
 | |
|                 return NULL;
 | |
|             }
 | |
|             Py_DECREF(item);
 | |
|         }
 | |
|         pos = 0;
 | |
|     }
 | |
|     if (!PyDict_Check(types)) {
 | |
|         PyErr_SetString(PyExc_ValueError, "Invalid types dictionary");
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     TRACE(("new tiff encoder %s fp: %d, filename: %s \n", compname, fp, filename));
 | |
| 
 | |
|     encoder = PyImaging_EncoderNew(sizeof(TIFFSTATE));
 | |
|     if (encoder == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     if (get_packer(encoder, mode, rawmode) < 0) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     if (!ImagingLibTiffEncodeInit(&encoder->state, filename, fp)) {
 | |
|         Py_DECREF(encoder);
 | |
|         PyErr_SetString(PyExc_RuntimeError, "tiff codec initialization failed");
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     num_core_tags = sizeof(core_tags) / sizeof(int);
 | |
|     for (pos = 0; pos < tags_size; pos++) {
 | |
|         item = PyList_GetItemRef(tags, pos);
 | |
|         if (item == NULL) {
 | |
|             return NULL;
 | |
|         }
 | |
| 
 | |
|         // We already checked that tags is a 2-tuple list.
 | |
|         key = PyTuple_GET_ITEM(item, 0);
 | |
|         key_int = (int)PyLong_AsLong(key);
 | |
|         value = PyTuple_GET_ITEM(item, 1);
 | |
|         Py_DECREF(item);
 | |
| 
 | |
|         status = 0;
 | |
|         is_core_tag = 0;
 | |
|         is_var_length = 0;
 | |
|         type = TIFF_NOTYPE;
 | |
| 
 | |
|         for (i = 0; i < num_core_tags; i++) {
 | |
|             if (core_tags[i] == key_int) {
 | |
|                 is_core_tag = 1;
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (!is_core_tag) {
 | |
|             PyObject *tag_type;
 | |
|             if (PyDict_GetItemRef(types, key, &tag_type) < 0) {
 | |
|                 return NULL;  // Exception has been already set
 | |
|             }
 | |
|             if (tag_type) {
 | |
|                 int type_int = PyLong_AsLong(tag_type);
 | |
|                 if (type_int >= TIFF_BYTE && type_int <= TIFF_LONG8) {
 | |
|                     type = (TIFFDataType)type_int;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (type == TIFF_NOTYPE) {
 | |
|             // Autodetect type. Types should not be changed for backwards
 | |
|             // compatibility.
 | |
|             if (PyLong_Check(value)) {
 | |
|                 type = TIFF_LONG;
 | |
|             } else if (PyFloat_Check(value)) {
 | |
|                 type = TIFF_DOUBLE;
 | |
|             } else if (PyBytes_Check(value)) {
 | |
|                 type = TIFF_ASCII;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (PyTuple_Check(value)) {
 | |
|             Py_ssize_t len;
 | |
|             len = PyTuple_Size(value);
 | |
| 
 | |
|             is_var_length = 1;
 | |
| 
 | |
|             if (!len) {
 | |
|                 continue;
 | |
|             }
 | |
| 
 | |
|             if (type == TIFF_NOTYPE) {
 | |
|                 // Autodetect type based on first item. Types should not be
 | |
|                 // changed for backwards compatibility.
 | |
|                 if (PyLong_Check(PyTuple_GetItem(value, 0))) {
 | |
|                     type = TIFF_LONG;
 | |
|                 } else if (PyFloat_Check(PyTuple_GetItem(value, 0))) {
 | |
|                     type = TIFF_FLOAT;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (!is_core_tag) {
 | |
|             // Register field for non core tags.
 | |
|             if (type == TIFF_BYTE) {
 | |
|                 is_var_length = 1;
 | |
|             }
 | |
|             if (ImagingLibTiffMergeFieldInfo(
 | |
|                     &encoder->state, type, key_int, is_var_length
 | |
|                 )) {
 | |
|                 continue;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (type == TIFF_BYTE || type == TIFF_UNDEFINED) {
 | |
|             status = ImagingLibTiffSetField(
 | |
|                 &encoder->state,
 | |
|                 (ttag_t)key_int,
 | |
|                 PyBytes_Size(value),
 | |
|                 PyBytes_AsString(value)
 | |
|             );
 | |
|         } else if (is_var_length) {
 | |
|             Py_ssize_t len, i;
 | |
|             TRACE(("Setting from Tuple: %d \n", key_int));
 | |
|             len = PyTuple_Size(value);
 | |
| 
 | |
|             if (key_int == TIFFTAG_COLORMAP) {
 | |
|                 int stride = 256;
 | |
|                 if (len != 768) {
 | |
|                     PyErr_SetString(
 | |
|                         PyExc_ValueError, "Requiring 768 items for Colormap"
 | |
|                     );
 | |
|                     return NULL;
 | |
|                 }
 | |
|                 UINT16 *av;
 | |
|                 /* malloc check ok, calloc checks for overflow */
 | |
|                 av = calloc(len, sizeof(UINT16));
 | |
|                 if (av) {
 | |
|                     for (i = 0; i < len; i++) {
 | |
|                         av[i] = (UINT16)PyLong_AsLong(PyTuple_GetItem(value, i));
 | |
|                     }
 | |
|                     status = ImagingLibTiffSetField(
 | |
|                         &encoder->state,
 | |
|                         (ttag_t)key_int,
 | |
|                         av,
 | |
|                         av + stride,
 | |
|                         av + stride * 2
 | |
|                     );
 | |
|                     free(av);
 | |
|                 }
 | |
|             } else if (key_int == TIFFTAG_YCBCRSUBSAMPLING) {
 | |
|                 status = ImagingLibTiffSetField(
 | |
|                     &encoder->state,
 | |
|                     (ttag_t)key_int,
 | |
|                     (UINT16)PyLong_AsLong(PyTuple_GetItem(value, 0)),
 | |
|                     (UINT16)PyLong_AsLong(PyTuple_GetItem(value, 1))
 | |
|                 );
 | |
|             } else if (type == TIFF_SHORT) {
 | |
|                 UINT16 *av;
 | |
|                 /* malloc check ok, calloc checks for overflow */
 | |
|                 av = calloc(len, sizeof(UINT16));
 | |
|                 if (av) {
 | |
|                     for (i = 0; i < len; i++) {
 | |
|                         av[i] = (UINT16)PyLong_AsLong(PyTuple_GetItem(value, i));
 | |
|                     }
 | |
|                     status = ImagingLibTiffSetField(
 | |
|                         &encoder->state, (ttag_t)key_int, len, av
 | |
|                     );
 | |
|                     free(av);
 | |
|                 }
 | |
|             } else if (type == TIFF_LONG) {
 | |
|                 UINT32 *av;
 | |
|                 /* malloc check ok, calloc checks for overflow */
 | |
|                 av = calloc(len, sizeof(UINT32));
 | |
|                 if (av) {
 | |
|                     for (i = 0; i < len; i++) {
 | |
|                         av[i] = (UINT32)PyLong_AsLong(PyTuple_GetItem(value, i));
 | |
|                     }
 | |
|                     status = ImagingLibTiffSetField(
 | |
|                         &encoder->state, (ttag_t)key_int, len, av
 | |
|                     );
 | |
|                     free(av);
 | |
|                 }
 | |
|             } else if (type == TIFF_SBYTE) {
 | |
|                 INT8 *av;
 | |
|                 /* malloc check ok, calloc checks for overflow */
 | |
|                 av = calloc(len, sizeof(INT8));
 | |
|                 if (av) {
 | |
|                     for (i = 0; i < len; i++) {
 | |
|                         av[i] = (INT8)PyLong_AsLong(PyTuple_GetItem(value, i));
 | |
|                     }
 | |
|                     status = ImagingLibTiffSetField(
 | |
|                         &encoder->state, (ttag_t)key_int, len, av
 | |
|                     );
 | |
|                     free(av);
 | |
|                 }
 | |
|             } else if (type == TIFF_SSHORT) {
 | |
|                 INT16 *av;
 | |
|                 /* malloc check ok, calloc checks for overflow */
 | |
|                 av = calloc(len, sizeof(INT16));
 | |
|                 if (av) {
 | |
|                     for (i = 0; i < len; i++) {
 | |
|                         av[i] = (INT16)PyLong_AsLong(PyTuple_GetItem(value, i));
 | |
|                     }
 | |
|                     status = ImagingLibTiffSetField(
 | |
|                         &encoder->state, (ttag_t)key_int, len, av
 | |
|                     );
 | |
|                     free(av);
 | |
|                 }
 | |
|             } else if (type == TIFF_SLONG) {
 | |
|                 INT32 *av;
 | |
|                 /* malloc check ok, calloc checks for overflow */
 | |
|                 av = calloc(len, sizeof(INT32));
 | |
|                 if (av) {
 | |
|                     for (i = 0; i < len; i++) {
 | |
|                         av[i] = (INT32)PyLong_AsLong(PyTuple_GetItem(value, i));
 | |
|                     }
 | |
|                     status = ImagingLibTiffSetField(
 | |
|                         &encoder->state, (ttag_t)key_int, len, av
 | |
|                     );
 | |
|                     free(av);
 | |
|                 }
 | |
|             } else if (type == TIFF_FLOAT) {
 | |
|                 FLOAT32 *av;
 | |
|                 /* malloc check ok, calloc checks for overflow */
 | |
|                 av = calloc(len, sizeof(FLOAT32));
 | |
|                 if (av) {
 | |
|                     for (i = 0; i < len; i++) {
 | |
|                         av[i] = (FLOAT32)PyFloat_AsDouble(PyTuple_GetItem(value, i));
 | |
|                     }
 | |
|                     status = ImagingLibTiffSetField(
 | |
|                         &encoder->state, (ttag_t)key_int, len, av
 | |
|                     );
 | |
|                     free(av);
 | |
|                 }
 | |
|             } else if (type == TIFF_DOUBLE) {
 | |
|                 FLOAT64 *av;
 | |
|                 /* malloc check ok, calloc checks for overflow */
 | |
|                 av = calloc(len, sizeof(FLOAT64));
 | |
|                 if (av) {
 | |
|                     for (i = 0; i < len; i++) {
 | |
|                         av[i] = PyFloat_AsDouble(PyTuple_GetItem(value, i));
 | |
|                     }
 | |
|                     status = ImagingLibTiffSetField(
 | |
|                         &encoder->state, (ttag_t)key_int, len, av
 | |
|                     );
 | |
|                     free(av);
 | |
|                 }
 | |
|             }
 | |
|         } else {
 | |
|             if (type == TIFF_SHORT) {
 | |
|                 status = ImagingLibTiffSetField(
 | |
|                     &encoder->state, (ttag_t)key_int, (UINT16)PyLong_AsLong(value)
 | |
|                 );
 | |
|             } else if (type == TIFF_LONG) {
 | |
|                 status = ImagingLibTiffSetField(
 | |
|                     &encoder->state, (ttag_t)key_int, (UINT32)PyLong_AsLong(value)
 | |
|                 );
 | |
|             } else if (type == TIFF_SSHORT) {
 | |
|                 status = ImagingLibTiffSetField(
 | |
|                     &encoder->state, (ttag_t)key_int, (INT16)PyLong_AsLong(value)
 | |
|                 );
 | |
|             } else if (type == TIFF_SLONG) {
 | |
|                 status = ImagingLibTiffSetField(
 | |
|                     &encoder->state, (ttag_t)key_int, (INT32)PyLong_AsLong(value)
 | |
|                 );
 | |
|             } else if (type == TIFF_FLOAT) {
 | |
|                 status = ImagingLibTiffSetField(
 | |
|                     &encoder->state, (ttag_t)key_int, (FLOAT32)PyFloat_AsDouble(value)
 | |
|                 );
 | |
|             } else if (type == TIFF_DOUBLE) {
 | |
|                 status = ImagingLibTiffSetField(
 | |
|                     &encoder->state, (ttag_t)key_int, (FLOAT64)PyFloat_AsDouble(value)
 | |
|                 );
 | |
|             } else if (type == TIFF_SBYTE) {
 | |
|                 status = ImagingLibTiffSetField(
 | |
|                     &encoder->state, (ttag_t)key_int, (INT8)PyLong_AsLong(value)
 | |
|                 );
 | |
|             } else if (type == TIFF_ASCII) {
 | |
|                 status = ImagingLibTiffSetField(
 | |
|                     &encoder->state, (ttag_t)key_int, PyBytes_AsString(value)
 | |
|                 );
 | |
|             } else if (type == TIFF_RATIONAL) {
 | |
|                 status = ImagingLibTiffSetField(
 | |
|                     &encoder->state, (ttag_t)key_int, (FLOAT64)PyFloat_AsDouble(value)
 | |
|                 );
 | |
|             } else if (type == TIFF_LONG8) {
 | |
|                 status = ImagingLibTiffSetField(
 | |
|                     &encoder->state, (ttag_t)key_int, (uint64_t)PyLong_AsLongLong(value)
 | |
|                 );
 | |
|             } else {
 | |
|                 TRACE(
 | |
|                     ("Unhandled type for key %d : %s \n",
 | |
|                      key_int,
 | |
|                      PyBytes_AsString(PyObject_Str(value)))
 | |
|                 );
 | |
|             }
 | |
|         }
 | |
|         if (!status) {
 | |
|             TRACE(("Error setting Field\n"));
 | |
|             Py_DECREF(encoder);
 | |
|             PyErr_SetString(PyExc_RuntimeError, "Error setting from dictionary");
 | |
|             return NULL;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     encoder->encode = ImagingLibTiffEncode;
 | |
| 
 | |
|     return (PyObject *)encoder;
 | |
| }
 | |
| 
 | |
| #endif
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /* JPEG                                                                 */
 | |
| /* -------------------------------------------------------------------- */
 | |
| 
 | |
| #ifdef HAVE_LIBJPEG
 | |
| 
 | |
| /* We better define this encoder last in this file, so the following
 | |
|    undef's won't mess things up for the Imaging library proper. */
 | |
| 
 | |
| #undef HAVE_PROTOTYPES
 | |
| #undef HAVE_STDDEF_H
 | |
| #undef HAVE_STDLIB_H
 | |
| #undef UINT8
 | |
| #undef UINT16
 | |
| #undef UINT32
 | |
| #undef INT8
 | |
| #undef INT16
 | |
| #undef INT32
 | |
| 
 | |
| #include "libImaging/Jpeg.h"
 | |
| 
 | |
| static unsigned int *
 | |
| get_qtables_arrays(PyObject *qtables, int *qtablesLen) {
 | |
|     PyObject *tables;
 | |
|     PyObject *table;
 | |
|     PyObject *table_data;
 | |
|     int i, j, num_tables;
 | |
|     unsigned int *qarrays;
 | |
| 
 | |
|     if ((qtables == NULL) || (qtables == Py_None)) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     if (!PySequence_Check(qtables)) {
 | |
|         PyErr_SetString(PyExc_ValueError, "Invalid quantization tables");
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     tables = PySequence_Fast(qtables, "expected a sequence");
 | |
|     num_tables = PySequence_Size(qtables);
 | |
|     if (num_tables < 1 || num_tables > NUM_QUANT_TBLS) {
 | |
|         PyErr_SetString(
 | |
|             PyExc_ValueError,
 | |
|             "Not a valid number of quantization tables. Should be between 1 and 4."
 | |
|         );
 | |
|         Py_DECREF(tables);
 | |
|         return NULL;
 | |
|     }
 | |
|     /* malloc check ok, num_tables <4, DCTSIZE2 == 64 from jpeglib.h */
 | |
|     qarrays = (unsigned int *)malloc(num_tables * DCTSIZE2 * sizeof(unsigned int));
 | |
|     if (!qarrays) {
 | |
|         Py_DECREF(tables);
 | |
|         return ImagingError_MemoryError();
 | |
|     }
 | |
|     for (i = 0; i < num_tables; i++) {
 | |
|         table = PySequence_Fast_GET_ITEM(tables, i);
 | |
|         if (!PySequence_Check(table)) {
 | |
|             PyErr_SetString(PyExc_ValueError, "Invalid quantization tables");
 | |
|             goto JPEG_QTABLES_ERR;
 | |
|         }
 | |
|         if (PySequence_Size(table) != DCTSIZE2) {
 | |
|             PyErr_SetString(PyExc_ValueError, "Invalid quantization table size");
 | |
|             goto JPEG_QTABLES_ERR;
 | |
|         }
 | |
|         table_data = PySequence_Fast(table, "expected a sequence");
 | |
|         for (j = 0; j < DCTSIZE2; j++) {
 | |
|             qarrays[i * DCTSIZE2 + j] =
 | |
|                 PyLong_AS_LONG(PySequence_Fast_GET_ITEM(table_data, j));
 | |
|         }
 | |
|         Py_DECREF(table_data);
 | |
|     }
 | |
| 
 | |
|     *qtablesLen = num_tables;
 | |
| 
 | |
| JPEG_QTABLES_ERR:
 | |
|     Py_DECREF(tables);  // Run on both error and not error
 | |
|     if (PyErr_Occurred()) {
 | |
|         free(qarrays);
 | |
|         qarrays = NULL;
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     return qarrays;
 | |
| }
 | |
| 
 | |
| PyObject *
 | |
| PyImaging_JpegEncoderNew(PyObject *self, PyObject *args) {
 | |
|     ImagingEncoderObject *encoder;
 | |
| 
 | |
|     char *mode;
 | |
|     char *rawmode;
 | |
|     Py_ssize_t quality = 0;
 | |
|     Py_ssize_t progressive = 0;
 | |
|     Py_ssize_t smooth = 0;
 | |
|     Py_ssize_t optimize = 0;
 | |
|     int keep_rgb = 0;
 | |
|     Py_ssize_t streamtype = 0; /* 0=interchange, 1=tables only, 2=image only */
 | |
|     Py_ssize_t xdpi = 0, ydpi = 0;
 | |
|     Py_ssize_t subsampling = -1; /* -1=default, 0=none, 1=medium, 2=high */
 | |
|     Py_ssize_t restart_marker_blocks = 0;
 | |
|     Py_ssize_t restart_marker_rows = 0;
 | |
|     PyObject *qtables = NULL;
 | |
|     unsigned int *qarrays = NULL;
 | |
|     int qtablesLen = 0;
 | |
|     char *comment = NULL;
 | |
|     Py_ssize_t comment_size;
 | |
|     char *extra = NULL;
 | |
|     Py_ssize_t extra_size;
 | |
|     char *rawExif = NULL;
 | |
|     Py_ssize_t rawExifLen = 0;
 | |
| 
 | |
|     if (!PyArg_ParseTuple(
 | |
|             args,
 | |
|             "ss|nnnnpn(nn)nnnOz#y#y#",
 | |
|             &mode,
 | |
|             &rawmode,
 | |
|             &quality,
 | |
|             &progressive,
 | |
|             &smooth,
 | |
|             &optimize,
 | |
|             &keep_rgb,
 | |
|             &streamtype,
 | |
|             &xdpi,
 | |
|             &ydpi,
 | |
|             &subsampling,
 | |
|             &restart_marker_blocks,
 | |
|             &restart_marker_rows,
 | |
|             &qtables,
 | |
|             &comment,
 | |
|             &comment_size,
 | |
|             &extra,
 | |
|             &extra_size,
 | |
|             &rawExif,
 | |
|             &rawExifLen
 | |
|         )) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     encoder = PyImaging_EncoderNew(sizeof(JPEGENCODERSTATE));
 | |
|     if (encoder == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     // libjpeg-turbo supports different output formats.
 | |
|     // We are choosing Pillow's native format (3 color bytes + 1 padding)
 | |
|     // to avoid extra conversion in Pack.c.
 | |
|     if (ImagingJpegUseJCSExtensions() && strcmp(rawmode, "RGB") == 0) {
 | |
|         rawmode = "RGBX";
 | |
|     }
 | |
| 
 | |
|     if (get_packer(encoder, mode, rawmode) < 0) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     // Freed in JpegEncode, Case 6
 | |
|     qarrays = get_qtables_arrays(qtables, &qtablesLen);
 | |
| 
 | |
|     if (comment && comment_size > 0) {
 | |
|         /* malloc check ok, length is from python parsearg */
 | |
|         char *p = malloc(comment_size);  // Freed in JpegEncode, Case 6
 | |
|         if (!p) {
 | |
|             return ImagingError_MemoryError();
 | |
|         }
 | |
|         memcpy(p, comment, comment_size);
 | |
|         comment = p;
 | |
|     } else {
 | |
|         comment = NULL;
 | |
|     }
 | |
| 
 | |
|     if (extra && extra_size > 0) {
 | |
|         /* malloc check ok, length is from python parsearg */
 | |
|         char *p = malloc(extra_size);  // Freed in JpegEncode, Case 6
 | |
|         if (!p) {
 | |
|             if (comment) {
 | |
|                 free(comment);
 | |
|             }
 | |
|             return ImagingError_MemoryError();
 | |
|         }
 | |
|         memcpy(p, extra, extra_size);
 | |
|         extra = p;
 | |
|     } else {
 | |
|         extra = NULL;
 | |
|     }
 | |
| 
 | |
|     if (rawExif && rawExifLen > 0) {
 | |
|         /* malloc check ok, length is from python parsearg */
 | |
|         char *pp = malloc(rawExifLen);  // Freed in JpegEncode, Case 6
 | |
|         if (!pp) {
 | |
|             if (comment) {
 | |
|                 free(comment);
 | |
|             }
 | |
|             if (extra) {
 | |
|                 free(extra);
 | |
|             }
 | |
|             return ImagingError_MemoryError();
 | |
|         }
 | |
|         memcpy(pp, rawExif, rawExifLen);
 | |
|         rawExif = pp;
 | |
|     } else {
 | |
|         rawExif = NULL;
 | |
|     }
 | |
| 
 | |
|     encoder->encode = ImagingJpegEncode;
 | |
| 
 | |
|     JPEGENCODERSTATE *jpeg_encoder_state = (JPEGENCODERSTATE *)encoder->state.context;
 | |
|     strncpy(jpeg_encoder_state->rawmode, rawmode, 8);
 | |
|     jpeg_encoder_state->keep_rgb = keep_rgb;
 | |
|     jpeg_encoder_state->quality = quality;
 | |
|     jpeg_encoder_state->qtables = qarrays;
 | |
|     jpeg_encoder_state->qtablesLen = qtablesLen;
 | |
|     jpeg_encoder_state->subsampling = subsampling;
 | |
|     jpeg_encoder_state->progressive = progressive;
 | |
|     jpeg_encoder_state->smooth = smooth;
 | |
|     jpeg_encoder_state->optimize = optimize;
 | |
|     jpeg_encoder_state->streamtype = streamtype;
 | |
|     jpeg_encoder_state->xdpi = xdpi;
 | |
|     jpeg_encoder_state->ydpi = ydpi;
 | |
|     jpeg_encoder_state->restart_marker_blocks = restart_marker_blocks;
 | |
|     jpeg_encoder_state->restart_marker_rows = restart_marker_rows;
 | |
|     jpeg_encoder_state->comment = comment;
 | |
|     jpeg_encoder_state->comment_size = comment_size;
 | |
|     jpeg_encoder_state->extra = extra;
 | |
|     jpeg_encoder_state->extra_size = extra_size;
 | |
|     jpeg_encoder_state->rawExif = rawExif;
 | |
|     jpeg_encoder_state->rawExifLen = rawExifLen;
 | |
| 
 | |
|     return (PyObject *)encoder;
 | |
| }
 | |
| 
 | |
| #endif
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /* JPEG 2000                                                            */
 | |
| /* -------------------------------------------------------------------- */
 | |
| 
 | |
| #ifdef HAVE_OPENJPEG
 | |
| 
 | |
| #include "libImaging/Jpeg2K.h"
 | |
| 
 | |
| static void
 | |
| j2k_decode_coord_tuple(PyObject *tuple, int *x, int *y) {
 | |
|     *x = *y = 0;
 | |
| 
 | |
|     if (tuple && PyTuple_Check(tuple) && PyTuple_GET_SIZE(tuple) == 2) {
 | |
|         *x = (int)PyLong_AsLong(PyTuple_GET_ITEM(tuple, 0));
 | |
|         *y = (int)PyLong_AsLong(PyTuple_GET_ITEM(tuple, 1));
 | |
| 
 | |
|         if (*x < 0) {
 | |
|             *x = 0;
 | |
|         }
 | |
|         if (*y < 0) {
 | |
|             *y = 0;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| PyObject *
 | |
| PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) {
 | |
|     ImagingEncoderObject *encoder;
 | |
|     JPEG2KENCODESTATE *context;
 | |
| 
 | |
|     char *mode;
 | |
|     char *format;
 | |
|     OPJ_CODEC_FORMAT codec_format;
 | |
|     PyObject *offset = NULL, *tile_offset = NULL, *tile_size = NULL;
 | |
|     char *quality_mode = "rates";
 | |
|     PyObject *quality_layers = NULL;
 | |
|     Py_ssize_t num_resolutions = 0;
 | |
|     PyObject *cblk_size = NULL, *precinct_size = NULL;
 | |
|     int irreversible = 0;
 | |
|     char *progression = "LRCP";
 | |
|     OPJ_PROG_ORDER prog_order;
 | |
|     char *cinema_mode = "no";
 | |
|     OPJ_CINEMA_MODE cine_mode;
 | |
|     char mct = 0;
 | |
|     int sgnd = 0;
 | |
|     Py_ssize_t fd = -1;
 | |
|     char *comment;
 | |
|     Py_ssize_t comment_size;
 | |
|     int plt = 0;
 | |
| 
 | |
|     if (!PyArg_ParseTuple(
 | |
|             args,
 | |
|             "ss|OOOsOnOOpssbbnz#p",
 | |
|             &mode,
 | |
|             &format,
 | |
|             &offset,
 | |
|             &tile_offset,
 | |
|             &tile_size,
 | |
|             &quality_mode,
 | |
|             &quality_layers,
 | |
|             &num_resolutions,
 | |
|             &cblk_size,
 | |
|             &precinct_size,
 | |
|             &irreversible,
 | |
|             &progression,
 | |
|             &cinema_mode,
 | |
|             &mct,
 | |
|             &sgnd,
 | |
|             &fd,
 | |
|             &comment,
 | |
|             &comment_size,
 | |
|             &plt
 | |
|         )) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     if (strcmp(format, "j2k") == 0) {
 | |
|         codec_format = OPJ_CODEC_J2K;
 | |
|     } else if (strcmp(format, "jpt") == 0) {
 | |
|         codec_format = OPJ_CODEC_JPT;
 | |
|     } else if (strcmp(format, "jp2") == 0) {
 | |
|         codec_format = OPJ_CODEC_JP2;
 | |
|     } else {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     if (strcmp(progression, "LRCP") == 0) {
 | |
|         prog_order = OPJ_LRCP;
 | |
|     } else if (strcmp(progression, "RLCP") == 0) {
 | |
|         prog_order = OPJ_RLCP;
 | |
|     } else if (strcmp(progression, "RPCL") == 0) {
 | |
|         prog_order = OPJ_RPCL;
 | |
|     } else if (strcmp(progression, "PCRL") == 0) {
 | |
|         prog_order = OPJ_PCRL;
 | |
|     } else if (strcmp(progression, "CPRL") == 0) {
 | |
|         prog_order = OPJ_CPRL;
 | |
|     } else {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     if (strcmp(cinema_mode, "no") == 0) {
 | |
|         cine_mode = OPJ_OFF;
 | |
|     } else if (strcmp(cinema_mode, "cinema2k-24") == 0) {
 | |
|         cine_mode = OPJ_CINEMA2K_24;
 | |
|     } else if (strcmp(cinema_mode, "cinema2k-48") == 0) {
 | |
|         cine_mode = OPJ_CINEMA2K_48;
 | |
|     } else if (strcmp(cinema_mode, "cinema4k-24") == 0) {
 | |
|         cine_mode = OPJ_CINEMA4K_24;
 | |
|     } else {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     encoder = PyImaging_EncoderNew(sizeof(JPEG2KENCODESTATE));
 | |
|     if (!encoder) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     encoder->encode = ImagingJpeg2KEncode;
 | |
|     encoder->cleanup = ImagingJpeg2KEncodeCleanup;
 | |
|     encoder->pushes_fd = 1;
 | |
| 
 | |
|     context = (JPEG2KENCODESTATE *)encoder->state.context;
 | |
| 
 | |
|     context->fd = fd;
 | |
|     context->format = codec_format;
 | |
|     context->offset_x = context->offset_y = 0;
 | |
| 
 | |
|     j2k_decode_coord_tuple(offset, &context->offset_x, &context->offset_y);
 | |
|     j2k_decode_coord_tuple(
 | |
|         tile_offset, &context->tile_offset_x, &context->tile_offset_y
 | |
|     );
 | |
|     j2k_decode_coord_tuple(tile_size, &context->tile_size_x, &context->tile_size_y);
 | |
| 
 | |
|     /* Error on illegal tile offsets */
 | |
|     if (context->tile_size_x && context->tile_size_y) {
 | |
|         if (context->tile_offset_x <= context->offset_x - context->tile_size_x ||
 | |
|             context->tile_offset_y <= context->offset_y - context->tile_size_y) {
 | |
|             PyErr_SetString(
 | |
|                 PyExc_ValueError,
 | |
|                 "JPEG 2000 tile offset too small; top left tile must "
 | |
|                 "intersect image area"
 | |
|             );
 | |
|             Py_DECREF(encoder);
 | |
|             return NULL;
 | |
|         }
 | |
| 
 | |
|         if (context->tile_offset_x > context->offset_x ||
 | |
|             context->tile_offset_y > context->offset_y) {
 | |
|             PyErr_SetString(
 | |
|                 PyExc_ValueError, "JPEG 2000 tile offset too large to cover image area"
 | |
|             );
 | |
|             Py_DECREF(encoder);
 | |
|             return NULL;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (comment && comment_size > 0) {
 | |
|         /* Size is stored as as an uint16, subtract 4 bytes for the header */
 | |
|         if (comment_size >= 65532) {
 | |
|             PyErr_SetString(PyExc_ValueError, "JPEG 2000 comment is too long");
 | |
|             Py_DECREF(encoder);
 | |
|             return NULL;
 | |
|         }
 | |
| 
 | |
|         char *p = malloc(comment_size + 1);
 | |
|         if (!p) {
 | |
|             Py_DECREF(encoder);
 | |
|             return ImagingError_MemoryError();
 | |
|         }
 | |
|         memcpy(p, comment, comment_size);
 | |
|         p[comment_size] = '\0';
 | |
|         context->comment = p;
 | |
|     }
 | |
| 
 | |
|     if (quality_layers && PySequence_Check(quality_layers)) {
 | |
|         context->quality_is_in_db = strcmp(quality_mode, "dB") == 0;
 | |
|         context->quality_layers = quality_layers;
 | |
|         Py_INCREF(quality_layers);
 | |
|     }
 | |
| 
 | |
|     context->num_resolutions = num_resolutions;
 | |
| 
 | |
|     j2k_decode_coord_tuple(cblk_size, &context->cblk_width, &context->cblk_height);
 | |
|     j2k_decode_coord_tuple(
 | |
|         precinct_size, &context->precinct_width, &context->precinct_height
 | |
|     );
 | |
| 
 | |
|     context->irreversible = irreversible;
 | |
|     context->progression = prog_order;
 | |
|     context->cinema_mode = cine_mode;
 | |
|     context->mct = mct;
 | |
|     context->sgnd = sgnd;
 | |
|     context->plt = plt;
 | |
| 
 | |
|     return (PyObject *)encoder;
 | |
| }
 | |
| 
 | |
| #endif
 |