mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-26 18:06:18 +03:00
416d8e340e
When saving a JPEG and specifying 'keep' for quality or subsampling, if the source JPEG image is in grayscale mode, don't try to find the subsampling of the source, because grayscale images don't have any subsampling (it's only for color components). For the moment the fix also ignores subsampling of CMYK JPEG because currently Pillow doesn't support encoding JPEG in YCCK mode (and subsampling doesn't make sense in CMYK, but Pillow permits saving CMYK JPEG with subsampling, that's a bug). This fix pass those errors silently, i.e. it doesn't raise an error when 'keep' is used but it's not possible to keep the subsampling (because the image is grayscale or CMYK). I think it's the proper behavior but I'm not sure.
958 lines
28 KiB
C
958 lines
28 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! */
|
|
|
|
#include "Python.h"
|
|
|
|
#include "Imaging.h"
|
|
#include "py3.h"
|
|
#include "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;
|
|
} 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) PyErr_NoMemory();
|
|
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;
|
|
|
|
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);
|
|
PyObject_Del(encoder);
|
|
}
|
|
|
|
static PyObject*
|
|
_encode(ImagingEncoderObject* encoder, PyObject* args)
|
|
{
|
|
PyObject* buf;
|
|
PyObject* result;
|
|
int status;
|
|
|
|
/* Encode to a Python string (allocated by this method) */
|
|
|
|
int bufsize = 16384;
|
|
|
|
if (!PyArg_ParseTuple(args, "|i", &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_file(ImagingEncoderObject* encoder, PyObject* args)
|
|
{
|
|
UINT8* buf;
|
|
int status;
|
|
ImagingSectionCookie cookie;
|
|
|
|
/* Encode to a file handle */
|
|
|
|
int fh;
|
|
int bufsize = 16384;
|
|
|
|
if (!PyArg_ParseTuple(args, "i|i", &fh, &bufsize))
|
|
return NULL;
|
|
|
|
/* Allocate an encoder buffer */
|
|
buf = (UINT8*) malloc(bufsize);
|
|
if (!buf)
|
|
return PyErr_NoMemory();
|
|
|
|
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_IOError);
|
|
}
|
|
|
|
} 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;
|
|
int 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|(iiii)", &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) {
|
|
state->bytes = (state->bits * state->xsize+7)/8;
|
|
state->buffer = (UINT8*) malloc(state->bytes);
|
|
if (!state->buffer)
|
|
return PyErr_NoMemory();
|
|
}
|
|
|
|
/* 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_INCREF(Py_None);
|
|
return Py_None;
|
|
}
|
|
|
|
static struct PyMethodDef methods[] = {
|
|
{"encode", (PyCFunction)_encode, 1},
|
|
{"encode_to_file", (PyCFunction)_encode_to_file, 1},
|
|
{"setimage", (PyCFunction)_setimage, 1},
|
|
{NULL, NULL} /* sentinel */
|
|
};
|
|
|
|
static PyTypeObject ImagingEncoderType = {
|
|
PyVarObject_HEAD_INIT(NULL, 0)
|
|
"ImagingEncoder", /*tp_name*/
|
|
sizeof(ImagingEncoderObject), /*tp_size*/
|
|
0, /*tp_itemsize*/
|
|
/* methods */
|
|
(destructor)_dealloc, /*tp_dealloc*/
|
|
0, /*tp_print*/
|
|
0, /*tp_getattr*/
|
|
0, /*tp_setattr*/
|
|
0, /*tp_compare*/
|
|
0, /*tp_repr*/
|
|
0, /*tp_as_number */
|
|
0, /*tp_as_sequence */
|
|
0, /*tp_as_mapping */
|
|
0, /*tp_hash*/
|
|
0, /*tp_call*/
|
|
0, /*tp_str*/
|
|
0, /*tp_getattro*/
|
|
0, /*tp_setattro*/
|
|
0, /*tp_as_buffer*/
|
|
Py_TPFLAGS_DEFAULT, /*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*/
|
|
0, /*tp_getset*/
|
|
};
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
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_SetString(PyExc_SystemError, "unknown raw mode");
|
|
return -1;
|
|
}
|
|
|
|
encoder->state.shuffle = pack;
|
|
encoder->state.bits = bits;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* 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;
|
|
int bits = 8;
|
|
int interlace = 0;
|
|
if (!PyArg_ParseTuple(args, "ss|ii", &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;
|
|
int bits = 8;
|
|
|
|
if (!PyArg_ParseTuple(args, "ss|ii", &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;
|
|
int stride = 0;
|
|
int ystep = 1;
|
|
|
|
if (!PyArg_ParseTuple(args, "ss|ii", &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;
|
|
}
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* 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 "Zip.h"
|
|
|
|
PyObject*
|
|
PyImaging_ZipEncoderNew(PyObject* self, PyObject* args)
|
|
{
|
|
ImagingEncoderObject* encoder;
|
|
|
|
char* mode;
|
|
char* rawmode;
|
|
int optimize = 0;
|
|
int compress_level = -1;
|
|
int compress_type = -1;
|
|
char* dictionary = NULL;
|
|
int dictionary_size = 0;
|
|
if (!PyArg_ParseTuple(args, "ss|iii"PY_ARG_BYTES_LENGTH, &mode, &rawmode,
|
|
&optimize,
|
|
&compress_level, &compress_type,
|
|
&dictionary, &dictionary_size))
|
|
return NULL;
|
|
|
|
/* Copy to avoid referencing Python's memory, but there's no mechanism to
|
|
free this memory later, so this function (and several others here)
|
|
leaks. */
|
|
if (dictionary && dictionary_size > 0) {
|
|
char* p = malloc(dictionary_size);
|
|
if (!p)
|
|
return PyErr_NoMemory();
|
|
memcpy(p, dictionary, dictionary_size);
|
|
dictionary = p;
|
|
} else
|
|
dictionary = NULL;
|
|
|
|
encoder = PyImaging_EncoderNew(sizeof(ZIPSTATE));
|
|
if (encoder == NULL)
|
|
return NULL;
|
|
|
|
if (get_packer(encoder, mode, rawmode) < 0)
|
|
return NULL;
|
|
|
|
encoder->encode = ImagingZipEncode;
|
|
|
|
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
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* 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 "Jpeg.h"
|
|
|
|
static unsigned int** get_qtables_arrays(PyObject* qtables) {
|
|
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 numbers of quantization tables. Should be between 1 and 4.");
|
|
return NULL;
|
|
}
|
|
qarrays = (unsigned int**) PyMem_Malloc(num_tables * sizeof(unsigned int*));
|
|
if (!qarrays) {
|
|
Py_DECREF(tables);
|
|
PyErr_NoMemory();
|
|
return NULL;
|
|
}
|
|
for (i = 0; i < num_tables; i++) {
|
|
table = PySequence_Fast_GET_ITEM(tables, i);
|
|
if (!PySequence_Check(table)) {
|
|
Py_DECREF(tables);
|
|
PyErr_SetString(PyExc_ValueError, "Invalid quantization tables");
|
|
return NULL;
|
|
}
|
|
if (PySequence_Size(table) != DCTSIZE2) {
|
|
Py_DECREF(tables);
|
|
PyErr_SetString(PyExc_ValueError, "Invalid quantization tables");
|
|
return NULL;
|
|
}
|
|
table_data = PySequence_Fast(table, "expected a sequence");
|
|
qarrays[i] = (unsigned int*) PyMem_Malloc(DCTSIZE2 * sizeof(unsigned int));
|
|
if (!qarrays[i]) {
|
|
Py_DECREF(tables);
|
|
PyErr_NoMemory();
|
|
return NULL;
|
|
}
|
|
for (j = 0; j < DCTSIZE2; j++) {
|
|
qarrays[i][j] = PyInt_AS_LONG(PySequence_Fast_GET_ITEM(table_data, j));
|
|
}
|
|
}
|
|
|
|
Py_DECREF(tables);
|
|
|
|
if (PyErr_Occurred()) {
|
|
PyMem_Free(qarrays);
|
|
qarrays = NULL;
|
|
}
|
|
|
|
return qarrays;
|
|
}
|
|
|
|
|
|
PyObject*
|
|
PyImaging_JpegEncoderNew(PyObject* self, PyObject* args)
|
|
{
|
|
ImagingEncoderObject* encoder;
|
|
|
|
char *mode;
|
|
char *rawmode;
|
|
int quality = 0;
|
|
int progressive = 0;
|
|
int smooth = 0;
|
|
int optimize = 0;
|
|
int streamtype = 0; /* 0=interchange, 1=tables only, 2=image only */
|
|
int xdpi = 0, ydpi = 0;
|
|
int subsampling = -1; /* -1=default, 0=none, 1=medium, 2=high */
|
|
PyObject* qtables=NULL;
|
|
unsigned int **qarrays = NULL;
|
|
char* extra = NULL;
|
|
int extra_size;
|
|
char* rawExif = NULL;
|
|
int rawExifLen = 0;
|
|
|
|
if (!PyArg_ParseTuple(args, "ss|iiiiiiiiO"PY_ARG_BYTES_LENGTH""PY_ARG_BYTES_LENGTH,
|
|
&mode, &rawmode, &quality,
|
|
&progressive, &smooth, &optimize, &streamtype,
|
|
&xdpi, &ydpi, &subsampling, &qtables, &extra, &extra_size,
|
|
&rawExif, &rawExifLen))
|
|
return NULL;
|
|
|
|
encoder = PyImaging_EncoderNew(sizeof(JPEGENCODERSTATE));
|
|
if (encoder == NULL)
|
|
return NULL;
|
|
|
|
if (get_packer(encoder, mode, rawmode) < 0)
|
|
return NULL;
|
|
|
|
qarrays = get_qtables_arrays(qtables);
|
|
|
|
if (extra && extra_size > 0) {
|
|
char* p = malloc(extra_size);
|
|
if (!p)
|
|
return PyErr_NoMemory();
|
|
memcpy(p, extra, extra_size);
|
|
extra = p;
|
|
} else
|
|
extra = NULL;
|
|
|
|
if (rawExif && rawExifLen > 0) {
|
|
char* pp = malloc(rawExifLen);
|
|
if (!pp)
|
|
return PyErr_NoMemory();
|
|
memcpy(pp, rawExif, rawExifLen);
|
|
rawExif = pp;
|
|
} else
|
|
rawExif = NULL;
|
|
|
|
encoder->encode = ImagingJpegEncode;
|
|
|
|
((JPEGENCODERSTATE*)encoder->state.context)->quality = quality;
|
|
((JPEGENCODERSTATE*)encoder->state.context)->qtables = qarrays;
|
|
((JPEGENCODERSTATE*)encoder->state.context)->subsampling = subsampling;
|
|
((JPEGENCODERSTATE*)encoder->state.context)->progressive = progressive;
|
|
((JPEGENCODERSTATE*)encoder->state.context)->smooth = smooth;
|
|
((JPEGENCODERSTATE*)encoder->state.context)->optimize = optimize;
|
|
((JPEGENCODERSTATE*)encoder->state.context)->streamtype = streamtype;
|
|
((JPEGENCODERSTATE*)encoder->state.context)->xdpi = xdpi;
|
|
((JPEGENCODERSTATE*)encoder->state.context)->ydpi = ydpi;
|
|
((JPEGENCODERSTATE*)encoder->state.context)->extra = extra;
|
|
((JPEGENCODERSTATE*)encoder->state.context)->extra_size = extra_size;
|
|
((JPEGENCODERSTATE*)encoder->state.context)->rawExif = rawExif;
|
|
((JPEGENCODERSTATE*)encoder->state.context)->rawExifLen = rawExifLen;
|
|
|
|
return (PyObject*) encoder;
|
|
}
|
|
|
|
#endif
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* LibTiff */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
#ifdef HAVE_LIBTIFF
|
|
|
|
#include "TiffDecode.h"
|
|
|
|
#include <string.h>
|
|
|
|
PyObject*
|
|
PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)
|
|
{
|
|
ImagingEncoderObject* encoder;
|
|
|
|
char* mode;
|
|
char* rawmode;
|
|
char* compname;
|
|
char* filename;
|
|
int fp;
|
|
|
|
PyObject *dir;
|
|
PyObject *key, *value;
|
|
Py_ssize_t pos = 0;
|
|
int status;
|
|
|
|
Py_ssize_t d_size;
|
|
PyObject *keys, *values;
|
|
|
|
|
|
if (! PyArg_ParseTuple(args, "sssisO", &mode, &rawmode, &compname, &fp, &filename, &dir)) {
|
|
return NULL;
|
|
}
|
|
|
|
if (!PyDict_Check(dir)) {
|
|
PyErr_SetString(PyExc_ValueError, "Invalid Dictionary");
|
|
return NULL;
|
|
} else {
|
|
d_size = PyDict_Size(dir);
|
|
TRACE(("dict size: %d\n", (int)d_size));
|
|
keys = PyDict_Keys(dir);
|
|
values = PyDict_Values(dir);
|
|
for (pos=0;pos<d_size;pos++){
|
|
TRACE((" key: %d\n", (int)PyInt_AsLong(PyList_GetItem(keys,pos))));
|
|
}
|
|
pos = 0;
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
// While failes on 64 bit machines, complains that pos is an int instead of a Py_ssize_t
|
|
// while (PyDict_Next(dir, &pos, &key, &value)) {
|
|
for (pos=0;pos<d_size;pos++){
|
|
key = PyList_GetItem(keys,pos);
|
|
value = PyList_GetItem(values,pos);
|
|
status = 0;
|
|
TRACE(("Attempting to set key: %d\n", (int)PyInt_AsLong(key)));
|
|
if (PyInt_Check(value)) {
|
|
TRACE(("Setting from Int: %d %ld \n", (int)PyInt_AsLong(key),PyInt_AsLong(value)));
|
|
status = ImagingLibTiffSetField(&encoder->state,
|
|
(ttag_t) PyInt_AsLong(key),
|
|
PyInt_AsLong(value));
|
|
} else if(PyBytes_Check(value)) {
|
|
TRACE(("Setting from Bytes: %d, %s \n", (int)PyInt_AsLong(key),PyBytes_AsString(value)));
|
|
status = ImagingLibTiffSetField(&encoder->state,
|
|
(ttag_t) PyInt_AsLong(key),
|
|
PyBytes_AsString(value));
|
|
} else if(PyList_Check(value)) {
|
|
int len,i;
|
|
float *floatav;
|
|
int *intav;
|
|
TRACE(("Setting from List: %d \n", (int)PyInt_AsLong(key)));
|
|
len = (int)PyList_Size(value);
|
|
if (len) {
|
|
if (PyInt_Check(PyList_GetItem(value,0))) {
|
|
TRACE((" %d elements, setting as ints \n", len));
|
|
intav = malloc(sizeof(int)*len);
|
|
if (intav) {
|
|
for (i=0;i<len;i++) {
|
|
intav[i] = (int)PyInt_AsLong(PyList_GetItem(value,i));
|
|
}
|
|
status = ImagingLibTiffSetField(&encoder->state,
|
|
(ttag_t) PyInt_AsLong(key),
|
|
intav);
|
|
free(intav);
|
|
}
|
|
} else {
|
|
TRACE((" %d elements, setting as floats \n", len));
|
|
floatav = malloc(sizeof(float)*len);
|
|
if (floatav) {
|
|
for (i=0;i<len;i++) {
|
|
floatav[i] = (float)PyFloat_AsDouble(PyList_GetItem(value,i));
|
|
}
|
|
status = ImagingLibTiffSetField(&encoder->state,
|
|
(ttag_t) PyInt_AsLong(key),
|
|
floatav);
|
|
free(floatav);
|
|
}
|
|
}
|
|
}
|
|
} else if (PyFloat_Check(value)) {
|
|
TRACE(("Setting from Float: %d, %f \n", (int)PyInt_AsLong(key),PyFloat_AsDouble(value)));
|
|
status = ImagingLibTiffSetField(&encoder->state,
|
|
(ttag_t) PyInt_AsLong(key),
|
|
(float)PyFloat_AsDouble(value));
|
|
} else {
|
|
TRACE(("Unhandled type for key %d : %s \n",
|
|
(int)PyInt_AsLong(key),
|
|
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 2000 */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
#ifdef HAVE_OPENJPEG
|
|
|
|
#include "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)PyInt_AsLong(PyTuple_GET_ITEM(tuple, 0));
|
|
*y = (int)PyInt_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;
|
|
int num_resolutions = 0;
|
|
PyObject *cblk_size = NULL, *precinct_size = NULL;
|
|
PyObject *irreversible = NULL;
|
|
char *progression = "LRCP";
|
|
OPJ_PROG_ORDER prog_order;
|
|
char *cinema_mode = "no";
|
|
OPJ_CINEMA_MODE cine_mode;
|
|
int fd = -1;
|
|
|
|
if (!PyArg_ParseTuple(args, "ss|OOOsOIOOOssi", &mode, &format,
|
|
&offset, &tile_offset, &tile_size,
|
|
&quality_mode, &quality_layers, &num_resolutions,
|
|
&cblk_size, &precinct_size,
|
|
&irreversible, &progression, &cinema_mode,
|
|
&fd))
|
|
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;
|
|
|
|
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");
|
|
}
|
|
|
|
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 (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 = PyObject_IsTrue(irreversible);
|
|
context->progression = prog_order;
|
|
context->cinema_mode = cine_mode;
|
|
|
|
return (PyObject *)encoder;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
* Local Variables:
|
|
* c-basic-offset: 4
|
|
* End:
|
|
*
|
|
*/
|