#define PY_SSIZE_T_CLEAN #include #include "py3.h" #include #include #include #ifdef HAVE_WEBPMUX #include #endif PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args) { int width; int height; float quality_factor; uint8_t *rgb; uint8_t *icc_bytes; uint8_t *exif_bytes; uint8_t *output; char *mode; Py_ssize_t size; int icc_size; /* see comment below */ int exif_size; size_t ret_size; if (!PyArg_ParseTuple(args, "s#iifss#s#", (char**)&rgb, &size, &width, &height, &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; } 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; } 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 { WebPData output_data = {0}; WebPData image = { output, ret_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) { fprintf(stderr, "icc size %d, %d \n", icc_size, icc_size > 0); } /* icc_size and exif size used to be Py_ssize_t, now they're int. PyPy2.1 was having trouble with these, as they were getting cast badly. Since WebP can't take a 64 bit value, They were ending up as null PyArg_ParseTuple can kick out ints, we're going to use those instead so that we don't have 32/64 bit problems here. */ if (icc_size > 0) { if (dbg) { fprintf (stderr, "Adding ICC Profile\n"); } WebPData icc_profile = { icc_bytes, icc_size }; 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", exif_size); } if (exif_size > 0) { if (dbg){ fprintf (stderr, "Adding Exif Data\n"); } WebPData exif = { exif_bytes, exif_size }; 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