Pillow/src/encode.c
eyedav 9527ce7f8c change mode structs to enums
Structs have better type safety, but they make allocation more difficult, especially when we have multiple Python modules trying to share the same code.
2025-07-19 16:54:32 +02:00

1453 lines
42 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 ModeID mode, const RawModeID 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",
getModeData(mode)->name,
getRawModeData(rawmode)->name
);
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_name;
char *rawmode_name;
Py_ssize_t bits = 8;
Py_ssize_t interlace = 0;
if (!PyArg_ParseTuple(args, "ss|nn", &mode_name, &rawmode_name, &bits, &interlace)) {
return NULL;
}
encoder = PyImaging_EncoderNew(sizeof(GIFENCODERSTATE));
if (encoder == NULL) {
return NULL;
}
const ModeID mode = findModeID(mode_name);
const RawModeID rawmode = findRawModeID(rawmode_name);
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_name;
char *rawmode_name;
Py_ssize_t bits = 8;
if (!PyArg_ParseTuple(args, "ss|n", &mode_name, &rawmode_name, &bits)) {
return NULL;
}
encoder = PyImaging_EncoderNew(0);
if (encoder == NULL) {
return NULL;
}
const ModeID mode = findModeID(mode_name);
const RawModeID rawmode = findRawModeID(rawmode_name);
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_name;
char *rawmode_name;
Py_ssize_t stride = 0;
Py_ssize_t ystep = 1;
if (!PyArg_ParseTuple(args, "ss|nn", &mode_name, &rawmode_name, &stride, &ystep)) {
return NULL;
}
encoder = PyImaging_EncoderNew(0);
if (encoder == NULL) {
return NULL;
}
const ModeID mode = findModeID(mode_name);
const RawModeID rawmode = findRawModeID(rawmode_name);
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_name;
char *rawmode_name;
Py_ssize_t ystep = 1;
if (!PyArg_ParseTuple(args, "ss|n", &mode_name, &rawmode_name, &ystep)) {
return NULL;
}
encoder = PyImaging_EncoderNew(0);
if (encoder == NULL) {
return NULL;
}
const ModeID mode = findModeID(mode_name);
const RawModeID rawmode = findRawModeID(rawmode_name);
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, IMAGING_MODE_1, IMAGING_RAWMODE_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_name;
char *rawmode_name;
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_name,
&rawmode_name,
&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;
}
const ModeID mode = findModeID(mode_name);
const RawModeID rawmode = findRawModeID(rawmode_name);
if (get_packer(encoder, mode, rawmode) < 0) {
free(dictionary);
return NULL;
}
encoder->encode = ImagingZipEncode;
encoder->cleanup = ImagingZipEncodeCleanup;
if (rawmode == IMAGING_RAWMODE_P || rawmode == IMAGING_RAWMODE_PA) {
/* 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_name;
char *rawmode_name;
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_name,
&rawmode_name,
&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;
}
const ModeID mode = findModeID(mode_name);
const RawModeID rawmode = findRawModeID(rawmode_name);
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;
}
encoder->cleanup = ImagingLibTiffEncodeCleanup;
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_name;
char *rawmode_name;
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_name,
&rawmode_name,
&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;
}
const ModeID mode = findModeID(mode_name);
RawModeID rawmode = findRawModeID(rawmode_name);
// 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() && rawmode == IMAGING_RAWMODE_RGB) {
rawmode = IMAGING_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;
jpeg_encoder_state->rawmode = rawmode;
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