mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-25 01:16:16 +03:00
274 lines
8.1 KiB
C
274 lines
8.1 KiB
C
#define PY_SSIZE_T_CLEAN
|
|
#include <Python.h>
|
|
#include "py3.h"
|
|
#include <webp/encode.h>
|
|
#include <webp/decode.h>
|
|
#include <webp/types.h>
|
|
|
|
#ifdef HAVE_WEBPMUX
|
|
#include <webp/mux.h>
|
|
#endif
|
|
|
|
PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args)
|
|
{
|
|
int width;
|
|
int height;
|
|
int lossless;
|
|
float quality_factor;
|
|
uint8_t *rgb;
|
|
uint8_t *icc_bytes;
|
|
uint8_t *exif_bytes;
|
|
uint8_t *output;
|
|
char *mode;
|
|
Py_ssize_t size;
|
|
Py_ssize_t icc_size;
|
|
Py_ssize_t exif_size;
|
|
size_t ret_size;
|
|
|
|
if (!PyArg_ParseTuple(args, "s#iiifss#s#",
|
|
(char**)&rgb, &size, &width, &height, &lossless, &quality_factor, &mode,
|
|
&icc_bytes, &icc_size, &exif_bytes, &exif_size)) {
|
|
Py_RETURN_NONE;
|
|
}
|
|
if (strcmp(mode, "RGBA")==0){
|
|
if (size < width * height * 4){
|
|
Py_RETURN_NONE;
|
|
}
|
|
if (lossless) {
|
|
ret_size = WebPEncodeLosslessRGBA(rgb, width, height, 4* width, &output);
|
|
} else {
|
|
ret_size = WebPEncodeRGBA(rgb, width, height, 4* width, quality_factor, &output);
|
|
}
|
|
} else if (strcmp(mode, "RGB")==0){
|
|
if (size < width * height * 3){
|
|
Py_RETURN_NONE;
|
|
}
|
|
if (lossless) {
|
|
ret_size = WebPEncodeLosslessRGB(rgb, width, height, 3* width, &output);
|
|
} else {
|
|
ret_size = WebPEncodeRGB(rgb, width, height, 3* width, quality_factor, &output);
|
|
}
|
|
} else {
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
#ifndef HAVE_WEBPMUX
|
|
if (ret_size > 0) {
|
|
PyObject *ret = PyBytes_FromStringAndSize((char*)output, ret_size);
|
|
free(output);
|
|
return ret;
|
|
}
|
|
#else
|
|
{
|
|
/* I want to truncate the *_size items that get passed into webp
|
|
data. Pypy2.1.0 had some issues where the Py_ssize_t items had
|
|
data in the upper byte. (Not sure why, it shouldn't have been there)
|
|
*/
|
|
int i_icc_size = (int)icc_size;
|
|
int i_exif_size = (int)exif_size;
|
|
WebPData output_data = {0};
|
|
WebPData image = { output, ret_size };
|
|
WebPData icc_profile = { icc_bytes, i_icc_size };
|
|
WebPData exif = { exif_bytes, i_exif_size };
|
|
WebPMuxError err;
|
|
int dbg = 0;
|
|
|
|
int copy_data = 0; // value 1 indicates given data WILL be copied to the mux
|
|
// and value 0 indicates data will NOT be copied.
|
|
|
|
WebPMux* mux = WebPMuxNew();
|
|
WebPMuxSetImage(mux, &image, copy_data);
|
|
|
|
if (dbg) {
|
|
/* was getting %ld icc_size == 0, icc_size>0 was true */
|
|
fprintf(stderr, "icc size %d, %d \n", i_icc_size, i_icc_size > 0);
|
|
}
|
|
|
|
if (i_icc_size > 0) {
|
|
if (dbg) {
|
|
fprintf (stderr, "Adding ICC Profile\n");
|
|
}
|
|
err = WebPMuxSetChunk(mux, "ICCP", &icc_profile, copy_data);
|
|
if (dbg && err == WEBP_MUX_INVALID_ARGUMENT) {
|
|
fprintf(stderr, "Invalid ICC Argument\n");
|
|
} else if (dbg && err == WEBP_MUX_MEMORY_ERROR) {
|
|
fprintf(stderr, "ICC Memory Error\n");
|
|
}
|
|
}
|
|
|
|
if (dbg) {
|
|
fprintf(stderr, "exif size %d \n", i_exif_size);
|
|
}
|
|
if (i_exif_size > 0) {
|
|
if (dbg){
|
|
fprintf (stderr, "Adding Exif Data\n");
|
|
}
|
|
err = WebPMuxSetChunk(mux, "EXIF", &exif, copy_data);
|
|
if (dbg && err == WEBP_MUX_INVALID_ARGUMENT) {
|
|
fprintf(stderr, "Invalid Exif Argument\n");
|
|
} else if (dbg && err == WEBP_MUX_MEMORY_ERROR) {
|
|
fprintf(stderr, "Exif Memory Error\n");
|
|
}
|
|
}
|
|
|
|
WebPMuxAssemble(mux, &output_data);
|
|
WebPMuxDelete(mux);
|
|
|
|
output = (uint8_t*)output_data.bytes;
|
|
ret_size = output_data.size;
|
|
if (ret_size > 0) {
|
|
PyObject *ret = PyBytes_FromStringAndSize((char*)output, ret_size);
|
|
WebPDataClear(&output_data);
|
|
return ret;
|
|
}
|
|
}
|
|
#endif
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
|
|
PyObject* WebPDecode_wrapper(PyObject* self, PyObject* args)
|
|
{
|
|
PyBytesObject *webp_string;
|
|
uint8_t *webp;
|
|
Py_ssize_t size;
|
|
PyObject *ret, *bytes, *pymode, *icc_profile = Py_None, *exif = Py_None;
|
|
WebPDecoderConfig config;
|
|
VP8StatusCode vp8_status_code = VP8_STATUS_OK;
|
|
char* mode = "RGB";
|
|
|
|
if (!PyArg_ParseTuple(args, "S", &webp_string)) {
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
if (!WebPInitDecoderConfig(&config)) {
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
PyBytes_AsStringAndSize((PyObject *) webp_string, (char**)&webp, &size);
|
|
|
|
vp8_status_code = WebPGetFeatures(webp, size, &config.input);
|
|
if (vp8_status_code == VP8_STATUS_OK) {
|
|
// If we don't set it, we don't get alpha.
|
|
// Initialized to MODE_RGB
|
|
if (config.input.has_alpha) {
|
|
config.output.colorspace = MODE_RGBA;
|
|
mode = "RGBA";
|
|
}
|
|
|
|
#ifndef HAVE_WEBPMUX
|
|
vp8_status_code = WebPDecode(webp, size, &config);
|
|
#else
|
|
{
|
|
int copy_data = 0;
|
|
WebPData data = { webp, size };
|
|
WebPMuxFrameInfo image;
|
|
WebPData icc_profile_data = {0};
|
|
WebPData exif_data = {0};
|
|
|
|
WebPMux* mux = WebPMuxCreate(&data, copy_data);
|
|
WebPMuxGetFrame(mux, 1, &image);
|
|
webp = (uint8_t*)image.bitstream.bytes;
|
|
size = image.bitstream.size;
|
|
|
|
vp8_status_code = WebPDecode(webp, size, &config);
|
|
|
|
WebPMuxGetChunk(mux, "ICCP", &icc_profile_data);
|
|
if (icc_profile_data.size > 0) {
|
|
icc_profile = PyBytes_FromStringAndSize((const char*)icc_profile_data.bytes, icc_profile_data.size);
|
|
}
|
|
|
|
WebPMuxGetChunk(mux, "EXIF", &exif_data);
|
|
if (exif_data.size > 0) {
|
|
exif = PyBytes_FromStringAndSize((const char*)exif_data.bytes, exif_data.size);
|
|
}
|
|
|
|
WebPMuxDelete(mux);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (vp8_status_code != VP8_STATUS_OK) {
|
|
WebPFreeDecBuffer(&config.output);
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
if (config.output.colorspace < MODE_YUV) {
|
|
bytes = PyBytes_FromStringAndSize((char *)config.output.u.RGBA.rgba,
|
|
config.output.u.RGBA.size);
|
|
} else {
|
|
// Skipping YUV for now. Need Test Images.
|
|
// UNDONE -- unclear if we'll ever get here if we set mode_rgb*
|
|
bytes = PyBytes_FromStringAndSize((char *)config.output.u.YUVA.y,
|
|
config.output.u.YUVA.y_size);
|
|
}
|
|
|
|
#if PY_VERSION_HEX >= 0x03000000
|
|
pymode = PyUnicode_FromString(mode);
|
|
#else
|
|
pymode = PyString_FromString(mode);
|
|
#endif
|
|
ret = Py_BuildValue("SiiSSS", bytes, config.output.width,
|
|
config.output.height, pymode, icc_profile, exif);
|
|
WebPFreeDecBuffer(&config.output);
|
|
return ret;
|
|
}
|
|
|
|
// Return the decoder's version number, packed in hexadecimal using 8bits for
|
|
// each of major/minor/revision. E.g: v2.5.7 is 0x020507.
|
|
PyObject* WebPDecoderVersion_wrapper(PyObject* self, PyObject* args){
|
|
return Py_BuildValue("i", WebPGetDecoderVersion());
|
|
}
|
|
|
|
/*
|
|
* The version of webp that ships with (0.1.3) Ubuntu 12.04 doesn't handle alpha well.
|
|
* Files that are valid with 0.3 are reported as being invalid.
|
|
*/
|
|
PyObject* WebPDecoderBuggyAlpha_wrapper(PyObject* self, PyObject* args){
|
|
return Py_BuildValue("i", WebPGetDecoderVersion()==0x0103);
|
|
}
|
|
|
|
static PyMethodDef webpMethods[] =
|
|
{
|
|
{"WebPEncode", WebPEncode_wrapper, METH_VARARGS, "WebPEncode"},
|
|
{"WebPDecode", WebPDecode_wrapper, METH_VARARGS, "WebPDecode"},
|
|
{"WebPDecoderVersion", WebPDecoderVersion_wrapper, METH_VARARGS, "WebPVersion"},
|
|
{"WebPDecoderBuggyAlpha", WebPDecoderBuggyAlpha_wrapper, METH_VARARGS, "WebPDecoderBuggyAlpha"},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
void addMuxFlagToModule(PyObject* m) {
|
|
#ifdef HAVE_WEBPMUX
|
|
PyModule_AddObject(m, "HAVE_WEBPMUX", Py_True);
|
|
#else
|
|
PyModule_AddObject(m, "HAVE_WEBPMUX", Py_False);
|
|
#endif
|
|
}
|
|
|
|
|
|
#if PY_VERSION_HEX >= 0x03000000
|
|
PyMODINIT_FUNC
|
|
PyInit__webp(void) {
|
|
PyObject* m;
|
|
|
|
static PyModuleDef module_def = {
|
|
PyModuleDef_HEAD_INIT,
|
|
"_webp", /* m_name */
|
|
NULL, /* m_doc */
|
|
-1, /* m_size */
|
|
webpMethods, /* m_methods */
|
|
};
|
|
|
|
m = PyModule_Create(&module_def);
|
|
addMuxFlagToModule(m);
|
|
return m;
|
|
}
|
|
#else
|
|
PyMODINIT_FUNC
|
|
init_webp(void)
|
|
{
|
|
PyObject* m = Py_InitModule("_webp", webpMethods);
|
|
addMuxFlagToModule(m);
|
|
}
|
|
#endif
|