_webp.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877
  1. #define PY_SSIZE_T_CLEAN
  2. #include <Python.h>
  3. #include "Imaging.h"
  4. #include "py3.h"
  5. #include <webp/encode.h>
  6. #include <webp/decode.h>
  7. #include <webp/types.h>
  8. #ifdef HAVE_WEBPMUX
  9. #include <webp/mux.h>
  10. #include <webp/demux.h>
  11. /*
  12. * Check the versions from mux.h and demux.h, to ensure the WebPAnimEncoder and
  13. * WebPAnimDecoder APIs are present (initial support was added in 0.5.0). The
  14. * very early versions had some significant differences, so we require later
  15. * versions, before enabling animation support.
  16. */
  17. #if WEBP_MUX_ABI_VERSION >= 0x0104 && WEBP_DEMUX_ABI_VERSION >= 0x0105
  18. #define HAVE_WEBPANIM
  19. #endif
  20. #endif
  21. /* -------------------------------------------------------------------- */
  22. /* WebP Muxer Error Handling */
  23. /* -------------------------------------------------------------------- */
  24. #ifdef HAVE_WEBPMUX
  25. static const char* const kErrorMessages[-WEBP_MUX_NOT_ENOUGH_DATA + 1] = {
  26. "WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", "WEBP_MUX_BAD_DATA",
  27. "WEBP_MUX_MEMORY_ERROR", "WEBP_MUX_NOT_ENOUGH_DATA"
  28. };
  29. PyObject* HandleMuxError(WebPMuxError err, char* chunk) {
  30. char message[100];
  31. int message_len;
  32. assert(err <= WEBP_MUX_NOT_FOUND && err >= WEBP_MUX_NOT_ENOUGH_DATA);
  33. // Check for a memory error first
  34. if (err == WEBP_MUX_MEMORY_ERROR) {
  35. return PyErr_NoMemory();
  36. }
  37. // Create the error message
  38. if (chunk == NULL) {
  39. message_len = sprintf(message, "could not assemble chunks: %s", kErrorMessages[-err]);
  40. } else {
  41. message_len = sprintf(message, "could not set %.4s chunk: %s", chunk, kErrorMessages[-err]);
  42. }
  43. if (message_len < 0) {
  44. PyErr_SetString(PyExc_RuntimeError, "failed to construct error message");
  45. return NULL;
  46. }
  47. // Set the proper error type
  48. switch (err) {
  49. case WEBP_MUX_NOT_FOUND:
  50. case WEBP_MUX_INVALID_ARGUMENT:
  51. PyErr_SetString(PyExc_ValueError, message);
  52. break;
  53. case WEBP_MUX_BAD_DATA:
  54. case WEBP_MUX_NOT_ENOUGH_DATA:
  55. PyErr_SetString(PyExc_IOError, message);
  56. break;
  57. default:
  58. PyErr_SetString(PyExc_RuntimeError, message);
  59. break;
  60. }
  61. return NULL;
  62. }
  63. #endif
  64. /* -------------------------------------------------------------------- */
  65. /* WebP Animation Support */
  66. /* -------------------------------------------------------------------- */
  67. #ifdef HAVE_WEBPANIM
  68. // Encoder type
  69. typedef struct {
  70. PyObject_HEAD
  71. WebPAnimEncoder* enc;
  72. WebPPicture frame;
  73. } WebPAnimEncoderObject;
  74. static PyTypeObject WebPAnimEncoder_Type;
  75. // Decoder type
  76. typedef struct {
  77. PyObject_HEAD
  78. WebPAnimDecoder* dec;
  79. WebPAnimInfo info;
  80. WebPData data;
  81. char* mode;
  82. } WebPAnimDecoderObject;
  83. static PyTypeObject WebPAnimDecoder_Type;
  84. // Encoder functions
  85. PyObject* _anim_encoder_new(PyObject* self, PyObject* args)
  86. {
  87. int width, height;
  88. uint32_t bgcolor;
  89. int loop_count;
  90. int minimize_size;
  91. int kmin, kmax;
  92. int allow_mixed;
  93. int verbose;
  94. WebPAnimEncoderOptions enc_options;
  95. WebPAnimEncoderObject* encp = NULL;
  96. WebPAnimEncoder* enc = NULL;
  97. if (!PyArg_ParseTuple(args, "iiIiiiiii",
  98. &width, &height, &bgcolor, &loop_count, &minimize_size,
  99. &kmin, &kmax, &allow_mixed, &verbose)) {
  100. return NULL;
  101. }
  102. // Setup and configure the encoder's options (these are animation-specific)
  103. if (!WebPAnimEncoderOptionsInit(&enc_options)) {
  104. PyErr_SetString(PyExc_RuntimeError, "failed to initialize encoder options");
  105. return NULL;
  106. }
  107. enc_options.anim_params.bgcolor = bgcolor;
  108. enc_options.anim_params.loop_count = loop_count;
  109. enc_options.minimize_size = minimize_size;
  110. enc_options.kmin = kmin;
  111. enc_options.kmax = kmax;
  112. enc_options.allow_mixed = allow_mixed;
  113. enc_options.verbose = verbose;
  114. // Validate canvas dimensions
  115. if (width <= 0 || height <= 0) {
  116. PyErr_SetString(PyExc_ValueError, "invalid canvas dimensions");
  117. return NULL;
  118. }
  119. // Create a new animation encoder and picture frame
  120. encp = PyObject_New(WebPAnimEncoderObject, &WebPAnimEncoder_Type);
  121. if (encp) {
  122. if (WebPPictureInit(&(encp->frame))) {
  123. enc = WebPAnimEncoderNew(width, height, &enc_options);
  124. if (enc) {
  125. encp->enc = enc;
  126. return (PyObject*) encp;
  127. }
  128. WebPPictureFree(&(encp->frame));
  129. }
  130. PyObject_Del(encp);
  131. }
  132. PyErr_SetString(PyExc_RuntimeError, "could not create encoder object");
  133. return NULL;
  134. }
  135. PyObject* _anim_encoder_dealloc(PyObject* self)
  136. {
  137. WebPAnimEncoderObject* encp = (WebPAnimEncoderObject*)self;
  138. WebPPictureFree(&(encp->frame));
  139. WebPAnimEncoderDelete(encp->enc);
  140. Py_RETURN_NONE;
  141. }
  142. PyObject* _anim_encoder_add(PyObject* self, PyObject* args)
  143. {
  144. uint8_t* rgb;
  145. Py_ssize_t size;
  146. int timestamp;
  147. int width;
  148. int height;
  149. char* mode;
  150. int lossless;
  151. float quality_factor;
  152. int method;
  153. WebPConfig config;
  154. WebPAnimEncoderObject* encp = (WebPAnimEncoderObject*)self;
  155. WebPAnimEncoder* enc = encp->enc;
  156. WebPPicture* frame = &(encp->frame);
  157. if (!PyArg_ParseTuple(args, "z#iiisifi",
  158. (char**)&rgb, &size, &timestamp, &width, &height, &mode,
  159. &lossless, &quality_factor, &method)) {
  160. return NULL;
  161. }
  162. // Check for NULL frame, which sets duration of final frame
  163. if (!rgb) {
  164. WebPAnimEncoderAdd(enc, NULL, timestamp, NULL);
  165. Py_RETURN_NONE;
  166. }
  167. // Setup config for this frame
  168. if (!WebPConfigInit(&config)) {
  169. PyErr_SetString(PyExc_RuntimeError, "failed to initialize config!");
  170. return NULL;
  171. }
  172. config.lossless = lossless;
  173. config.quality = quality_factor;
  174. config.method = method;
  175. // Validate the config
  176. if (!WebPValidateConfig(&config)) {
  177. PyErr_SetString(PyExc_ValueError, "invalid configuration");
  178. return NULL;
  179. }
  180. // Populate the frame with raw bytes passed to us
  181. frame->width = width;
  182. frame->height = height;
  183. frame->use_argb = 1; // Don't convert RGB pixels to YUV
  184. if (strcmp(mode, "RGBA")==0) {
  185. WebPPictureImportRGBA(frame, rgb, 4 * width);
  186. } else if (strcmp(mode, "RGBX")==0) {
  187. WebPPictureImportRGBX(frame, rgb, 4 * width);
  188. } else {
  189. WebPPictureImportRGB(frame, rgb, 3 * width);
  190. }
  191. // Add the frame to the encoder
  192. if (!WebPAnimEncoderAdd(enc, frame, timestamp, &config)) {
  193. PyErr_SetString(PyExc_RuntimeError, WebPAnimEncoderGetError(enc));
  194. return NULL;
  195. }
  196. Py_RETURN_NONE;
  197. }
  198. PyObject* _anim_encoder_assemble(PyObject* self, PyObject* args)
  199. {
  200. uint8_t* icc_bytes;
  201. uint8_t* exif_bytes;
  202. uint8_t* xmp_bytes;
  203. Py_ssize_t icc_size;
  204. Py_ssize_t exif_size;
  205. Py_ssize_t xmp_size;
  206. WebPData webp_data;
  207. WebPAnimEncoderObject* encp = (WebPAnimEncoderObject*)self;
  208. WebPAnimEncoder* enc = encp->enc;
  209. WebPMux* mux = NULL;
  210. PyObject* ret = NULL;
  211. if (!PyArg_ParseTuple(args, "s#s#s#",
  212. &icc_bytes, &icc_size, &exif_bytes, &exif_size, &xmp_bytes, &xmp_size)) {
  213. return NULL;
  214. }
  215. // Init the output buffer
  216. WebPDataInit(&webp_data);
  217. // Assemble everything into the output buffer
  218. if (!WebPAnimEncoderAssemble(enc, &webp_data)) {
  219. PyErr_SetString(PyExc_RuntimeError, WebPAnimEncoderGetError(enc));
  220. return NULL;
  221. }
  222. // Re-mux to add metadata as needed
  223. if (icc_size > 0 || exif_size > 0 || xmp_size > 0) {
  224. WebPMuxError err = WEBP_MUX_OK;
  225. int i_icc_size = (int)icc_size;
  226. int i_exif_size = (int)exif_size;
  227. int i_xmp_size = (int)xmp_size;
  228. WebPData icc_profile = { icc_bytes, i_icc_size };
  229. WebPData exif = { exif_bytes, i_exif_size };
  230. WebPData xmp = { xmp_bytes, i_xmp_size };
  231. mux = WebPMuxCreate(&webp_data, 1);
  232. if (mux == NULL) {
  233. PyErr_SetString(PyExc_RuntimeError, "could not re-mux to add metadata");
  234. return NULL;
  235. }
  236. WebPDataClear(&webp_data);
  237. // Add ICCP chunk
  238. if (i_icc_size > 0) {
  239. err = WebPMuxSetChunk(mux, "ICCP", &icc_profile, 1);
  240. if (err != WEBP_MUX_OK) {
  241. return HandleMuxError(err, "ICCP");
  242. }
  243. }
  244. // Add EXIF chunk
  245. if (i_exif_size > 0) {
  246. err = WebPMuxSetChunk(mux, "EXIF", &exif, 1);
  247. if (err != WEBP_MUX_OK) {
  248. return HandleMuxError(err, "EXIF");
  249. }
  250. }
  251. // Add XMP chunk
  252. if (i_xmp_size > 0) {
  253. err = WebPMuxSetChunk(mux, "XMP ", &xmp, 1);
  254. if (err != WEBP_MUX_OK) {
  255. return HandleMuxError(err, "XMP");
  256. }
  257. }
  258. err = WebPMuxAssemble(mux, &webp_data);
  259. if (err != WEBP_MUX_OK) {
  260. return HandleMuxError(err, NULL);
  261. }
  262. }
  263. // Convert to Python bytes
  264. ret = PyBytes_FromStringAndSize((char*)webp_data.bytes, webp_data.size);
  265. WebPDataClear(&webp_data);
  266. // If we had to re-mux, we should free it now that we're done with it
  267. if (mux != NULL) {
  268. WebPMuxDelete(mux);
  269. }
  270. return ret;
  271. }
  272. // Decoder functions
  273. PyObject* _anim_decoder_new(PyObject* self, PyObject* args)
  274. {
  275. PyBytesObject *webp_string;
  276. const uint8_t *webp;
  277. Py_ssize_t size;
  278. WebPData webp_src;
  279. char* mode;
  280. WebPDecoderConfig config;
  281. WebPAnimDecoderObject* decp = NULL;
  282. WebPAnimDecoder* dec = NULL;
  283. if (!PyArg_ParseTuple(args, "S", &webp_string)) {
  284. return NULL;
  285. }
  286. PyBytes_AsStringAndSize((PyObject *)webp_string, (char**)&webp, &size);
  287. webp_src.bytes = webp;
  288. webp_src.size = size;
  289. // Sniff the mode, since the decoder API doesn't tell us
  290. mode = "RGBA";
  291. if (WebPGetFeatures(webp, size, &config.input) == VP8_STATUS_OK) {
  292. if (!config.input.has_alpha) {
  293. mode = "RGBX";
  294. }
  295. }
  296. // Create the decoder (default mode is RGBA, if no options passed)
  297. decp = PyObject_New(WebPAnimDecoderObject, &WebPAnimDecoder_Type);
  298. if (decp) {
  299. decp->mode = mode;
  300. if (WebPDataCopy(&webp_src, &(decp->data))) {
  301. dec = WebPAnimDecoderNew(&(decp->data), NULL);
  302. if (dec) {
  303. if (WebPAnimDecoderGetInfo(dec, &(decp->info))) {
  304. decp->dec = dec;
  305. return (PyObject*)decp;
  306. }
  307. }
  308. }
  309. PyObject_Del(decp);
  310. }
  311. PyErr_SetString(PyExc_RuntimeError, "could not create decoder object");
  312. return NULL;
  313. }
  314. PyObject* _anim_decoder_dealloc(PyObject* self)
  315. {
  316. WebPAnimDecoderObject* decp = (WebPAnimDecoderObject *)self;
  317. WebPDataClear(&(decp->data));
  318. WebPAnimDecoderDelete(decp->dec);
  319. Py_RETURN_NONE;
  320. }
  321. PyObject* _anim_decoder_get_info(PyObject* self, PyObject* args)
  322. {
  323. WebPAnimDecoderObject* decp = (WebPAnimDecoderObject *)self;
  324. WebPAnimInfo* info = &(decp->info);
  325. return Py_BuildValue("IIIIIs",
  326. info->canvas_width, info->canvas_height,
  327. info->loop_count,
  328. info->bgcolor,
  329. info->frame_count,
  330. decp->mode
  331. );
  332. }
  333. PyObject* _anim_decoder_get_chunk(PyObject* self, PyObject* args)
  334. {
  335. char* mode;
  336. WebPAnimDecoderObject* decp = (WebPAnimDecoderObject *)self;
  337. const WebPDemuxer* demux;
  338. WebPChunkIterator iter;
  339. PyObject *ret;
  340. if (!PyArg_ParseTuple(args, "s", &mode)) {
  341. return NULL;
  342. }
  343. demux = WebPAnimDecoderGetDemuxer(decp->dec);
  344. if (!WebPDemuxGetChunk(demux, mode, 1, &iter)) {
  345. Py_RETURN_NONE;
  346. }
  347. ret = PyBytes_FromStringAndSize((const char*)iter.chunk.bytes, iter.chunk.size);
  348. WebPDemuxReleaseChunkIterator(&iter);
  349. return ret;
  350. }
  351. PyObject* _anim_decoder_get_next(PyObject* self, PyObject* args)
  352. {
  353. uint8_t* buf;
  354. int timestamp;
  355. PyObject* bytes;
  356. PyObject* ret;
  357. WebPAnimDecoderObject* decp = (WebPAnimDecoderObject*)self;
  358. if (!WebPAnimDecoderGetNext(decp->dec, &buf, &timestamp)) {
  359. PyErr_SetString(PyExc_IOError, "failed to read next frame");
  360. return NULL;
  361. }
  362. bytes = PyBytes_FromStringAndSize((char *)buf,
  363. decp->info.canvas_width * 4 * decp->info.canvas_height);
  364. ret = Py_BuildValue("Si", bytes, timestamp);
  365. Py_DECREF(bytes);
  366. return ret;
  367. }
  368. PyObject* _anim_decoder_has_more_frames(PyObject* self, PyObject* args)
  369. {
  370. WebPAnimDecoderObject* decp = (WebPAnimDecoderObject*)self;
  371. return Py_BuildValue("i", WebPAnimDecoderHasMoreFrames(decp->dec));
  372. }
  373. PyObject* _anim_decoder_reset(PyObject* self, PyObject* args)
  374. {
  375. WebPAnimDecoderObject* decp = (WebPAnimDecoderObject *)self;
  376. WebPAnimDecoderReset(decp->dec);
  377. Py_RETURN_NONE;
  378. }
  379. /* -------------------------------------------------------------------- */
  380. /* Type Definitions */
  381. /* -------------------------------------------------------------------- */
  382. // WebPAnimEncoder methods
  383. static struct PyMethodDef _anim_encoder_methods[] = {
  384. {"add", (PyCFunction)_anim_encoder_add, METH_VARARGS, "add"},
  385. {"assemble", (PyCFunction)_anim_encoder_assemble, METH_VARARGS, "assemble"},
  386. {NULL, NULL} /* sentinel */
  387. };
  388. // WebPAnimDecoder type definition
  389. static PyTypeObject WebPAnimEncoder_Type = {
  390. PyVarObject_HEAD_INIT(NULL, 0)
  391. "WebPAnimEncoder", /*tp_name */
  392. sizeof(WebPAnimEncoderObject), /*tp_size */
  393. 0, /*tp_itemsize */
  394. /* methods */
  395. (destructor)_anim_encoder_dealloc, /*tp_dealloc*/
  396. 0, /*tp_print*/
  397. 0, /*tp_getattr*/
  398. 0, /*tp_setattr*/
  399. 0, /*tp_compare*/
  400. 0, /*tp_repr*/
  401. 0, /*tp_as_number */
  402. 0, /*tp_as_sequence */
  403. 0, /*tp_as_mapping */
  404. 0, /*tp_hash*/
  405. 0, /*tp_call*/
  406. 0, /*tp_str*/
  407. 0, /*tp_getattro*/
  408. 0, /*tp_setattro*/
  409. 0, /*tp_as_buffer*/
  410. Py_TPFLAGS_DEFAULT, /*tp_flags*/
  411. 0, /*tp_doc*/
  412. 0, /*tp_traverse*/
  413. 0, /*tp_clear*/
  414. 0, /*tp_richcompare*/
  415. 0, /*tp_weaklistoffset*/
  416. 0, /*tp_iter*/
  417. 0, /*tp_iternext*/
  418. _anim_encoder_methods, /*tp_methods*/
  419. 0, /*tp_members*/
  420. 0, /*tp_getset*/
  421. };
  422. // WebPAnimDecoder methods
  423. static struct PyMethodDef _anim_decoder_methods[] = {
  424. {"get_info", (PyCFunction)_anim_decoder_get_info, METH_VARARGS, "get_info"},
  425. {"get_chunk", (PyCFunction)_anim_decoder_get_chunk, METH_VARARGS, "get_chunk"},
  426. {"get_next", (PyCFunction)_anim_decoder_get_next, METH_VARARGS, "get_next"},
  427. {"has_more_frames", (PyCFunction)_anim_decoder_has_more_frames, METH_VARARGS, "has_more_frames"},
  428. {"reset", (PyCFunction)_anim_decoder_reset, METH_VARARGS, "reset"},
  429. {NULL, NULL} /* sentinel */
  430. };
  431. // WebPAnimDecoder type definition
  432. static PyTypeObject WebPAnimDecoder_Type = {
  433. PyVarObject_HEAD_INIT(NULL, 0)
  434. "WebPAnimDecoder", /*tp_name */
  435. sizeof(WebPAnimDecoderObject), /*tp_size */
  436. 0, /*tp_itemsize */
  437. /* methods */
  438. (destructor)_anim_decoder_dealloc, /*tp_dealloc*/
  439. 0, /*tp_print*/
  440. 0, /*tp_getattr*/
  441. 0, /*tp_setattr*/
  442. 0, /*tp_compare*/
  443. 0, /*tp_repr*/
  444. 0, /*tp_as_number */
  445. 0, /*tp_as_sequence */
  446. 0, /*tp_as_mapping */
  447. 0, /*tp_hash*/
  448. 0, /*tp_call*/
  449. 0, /*tp_str*/
  450. 0, /*tp_getattro*/
  451. 0, /*tp_setattro*/
  452. 0, /*tp_as_buffer*/
  453. Py_TPFLAGS_DEFAULT, /*tp_flags*/
  454. 0, /*tp_doc*/
  455. 0, /*tp_traverse*/
  456. 0, /*tp_clear*/
  457. 0, /*tp_richcompare*/
  458. 0, /*tp_weaklistoffset*/
  459. 0, /*tp_iter*/
  460. 0, /*tp_iternext*/
  461. _anim_decoder_methods, /*tp_methods*/
  462. 0, /*tp_members*/
  463. 0, /*tp_getset*/
  464. };
  465. #endif
  466. /* -------------------------------------------------------------------- */
  467. /* Legacy WebP Support */
  468. /* -------------------------------------------------------------------- */
  469. PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args)
  470. {
  471. int width;
  472. int height;
  473. int lossless;
  474. float quality_factor;
  475. uint8_t* rgb;
  476. uint8_t* icc_bytes;
  477. uint8_t* exif_bytes;
  478. uint8_t* xmp_bytes;
  479. uint8_t* output;
  480. char* mode;
  481. Py_ssize_t size;
  482. Py_ssize_t icc_size;
  483. Py_ssize_t exif_size;
  484. Py_ssize_t xmp_size;
  485. size_t ret_size;
  486. if (!PyArg_ParseTuple(args, PY_ARG_BYTES_LENGTH"iiifss#s#s#",
  487. (char**)&rgb, &size, &width, &height, &lossless, &quality_factor, &mode,
  488. &icc_bytes, &icc_size, &exif_bytes, &exif_size, &xmp_bytes, &xmp_size)) {
  489. return NULL;
  490. }
  491. if (strcmp(mode, "RGBA")==0){
  492. if (size < width * height * 4){
  493. Py_RETURN_NONE;
  494. }
  495. #if WEBP_ENCODER_ABI_VERSION >= 0x0100
  496. if (lossless) {
  497. ret_size = WebPEncodeLosslessRGBA(rgb, width, height, 4 * width, &output);
  498. } else
  499. #endif
  500. {
  501. ret_size = WebPEncodeRGBA(rgb, width, height, 4 * width, quality_factor, &output);
  502. }
  503. } else if (strcmp(mode, "RGB")==0){
  504. if (size < width * height * 3){
  505. Py_RETURN_NONE;
  506. }
  507. #if WEBP_ENCODER_ABI_VERSION >= 0x0100
  508. if (lossless) {
  509. ret_size = WebPEncodeLosslessRGB(rgb, width, height, 3 * width, &output);
  510. } else
  511. #endif
  512. {
  513. ret_size = WebPEncodeRGB(rgb, width, height, 3 * width, quality_factor, &output);
  514. }
  515. } else {
  516. Py_RETURN_NONE;
  517. }
  518. #ifndef HAVE_WEBPMUX
  519. if (ret_size > 0) {
  520. PyObject *ret = PyBytes_FromStringAndSize((char*)output, ret_size);
  521. free(output);
  522. return ret;
  523. }
  524. #else
  525. {
  526. /* I want to truncate the *_size items that get passed into WebP
  527. data. Pypy2.1.0 had some issues where the Py_ssize_t items had
  528. data in the upper byte. (Not sure why, it shouldn't have been there)
  529. */
  530. int i_icc_size = (int)icc_size;
  531. int i_exif_size = (int)exif_size;
  532. int i_xmp_size = (int)xmp_size;
  533. WebPData output_data = {0};
  534. WebPData image = { output, ret_size };
  535. WebPData icc_profile = { icc_bytes, i_icc_size };
  536. WebPData exif = { exif_bytes, i_exif_size };
  537. WebPData xmp = { xmp_bytes, i_xmp_size };
  538. WebPMuxError err;
  539. int dbg = 0;
  540. int copy_data = 0; // value 1 indicates given data WILL be copied to the mux
  541. // and value 0 indicates data will NOT be copied.
  542. WebPMux* mux = WebPMuxNew();
  543. WebPMuxSetImage(mux, &image, copy_data);
  544. if (dbg) {
  545. /* was getting %ld icc_size == 0, icc_size>0 was true */
  546. fprintf(stderr, "icc size %d, %d \n", i_icc_size, i_icc_size > 0);
  547. }
  548. if (i_icc_size > 0) {
  549. if (dbg) {
  550. fprintf(stderr, "Adding ICC Profile\n");
  551. }
  552. err = WebPMuxSetChunk(mux, "ICCP", &icc_profile, copy_data);
  553. if (err != WEBP_MUX_OK) {
  554. return HandleMuxError(err, "ICCP");
  555. }
  556. }
  557. if (dbg) {
  558. fprintf(stderr, "exif size %d \n", i_exif_size);
  559. }
  560. if (i_exif_size > 0) {
  561. if (dbg) {
  562. fprintf(stderr, "Adding Exif Data\n");
  563. }
  564. err = WebPMuxSetChunk(mux, "EXIF", &exif, copy_data);
  565. if (err != WEBP_MUX_OK) {
  566. return HandleMuxError(err, "EXIF");
  567. }
  568. }
  569. if (dbg) {
  570. fprintf(stderr, "xmp size %d \n", i_xmp_size);
  571. }
  572. if (i_xmp_size > 0) {
  573. if (dbg){
  574. fprintf(stderr, "Adding XMP Data\n");
  575. }
  576. err = WebPMuxSetChunk(mux, "XMP ", &xmp, copy_data);
  577. if (err != WEBP_MUX_OK) {
  578. return HandleMuxError(err, "XMP ");
  579. }
  580. }
  581. WebPMuxAssemble(mux, &output_data);
  582. WebPMuxDelete(mux);
  583. free(output);
  584. ret_size = output_data.size;
  585. if (ret_size > 0) {
  586. PyObject *ret = PyBytes_FromStringAndSize((char*)output_data.bytes, ret_size);
  587. WebPDataClear(&output_data);
  588. return ret;
  589. }
  590. }
  591. #endif
  592. Py_RETURN_NONE;
  593. }
  594. PyObject* WebPDecode_wrapper(PyObject* self, PyObject* args)
  595. {
  596. PyBytesObject* webp_string;
  597. const uint8_t* webp;
  598. Py_ssize_t size;
  599. PyObject *ret = Py_None, *bytes = NULL, *pymode = NULL, *icc_profile = NULL, *exif = NULL;
  600. WebPDecoderConfig config;
  601. VP8StatusCode vp8_status_code = VP8_STATUS_OK;
  602. char* mode = "RGB";
  603. if (!PyArg_ParseTuple(args, "S", &webp_string)) {
  604. return NULL;
  605. }
  606. if (!WebPInitDecoderConfig(&config)) {
  607. Py_RETURN_NONE;
  608. }
  609. PyBytes_AsStringAndSize((PyObject*) webp_string, (char**)&webp, &size);
  610. vp8_status_code = WebPGetFeatures(webp, size, &config.input);
  611. if (vp8_status_code == VP8_STATUS_OK) {
  612. // If we don't set it, we don't get alpha.
  613. // Initialized to MODE_RGB
  614. if (config.input.has_alpha) {
  615. config.output.colorspace = MODE_RGBA;
  616. mode = "RGBA";
  617. }
  618. #ifndef HAVE_WEBPMUX
  619. vp8_status_code = WebPDecode(webp, size, &config);
  620. #else
  621. {
  622. int copy_data = 0;
  623. WebPData data = { webp, size };
  624. WebPMuxFrameInfo image;
  625. WebPData icc_profile_data = {0};
  626. WebPData exif_data = {0};
  627. WebPMux* mux = WebPMuxCreate(&data, copy_data);
  628. if (NULL == mux)
  629. goto end;
  630. if (WEBP_MUX_OK != WebPMuxGetFrame(mux, 1, &image))
  631. {
  632. WebPMuxDelete(mux);
  633. goto end;
  634. }
  635. webp = image.bitstream.bytes;
  636. size = image.bitstream.size;
  637. vp8_status_code = WebPDecode(webp, size, &config);
  638. if (WEBP_MUX_OK == WebPMuxGetChunk(mux, "ICCP", &icc_profile_data))
  639. icc_profile = PyBytes_FromStringAndSize((const char*)icc_profile_data.bytes, icc_profile_data.size);
  640. if (WEBP_MUX_OK == WebPMuxGetChunk(mux, "EXIF", &exif_data))
  641. exif = PyBytes_FromStringAndSize((const char*)exif_data.bytes, exif_data.size);
  642. WebPDataClear(&image.bitstream);
  643. WebPMuxDelete(mux);
  644. }
  645. #endif
  646. }
  647. if (vp8_status_code != VP8_STATUS_OK)
  648. goto end;
  649. if (config.output.colorspace < MODE_YUV) {
  650. bytes = PyBytes_FromStringAndSize((char*)config.output.u.RGBA.rgba,
  651. config.output.u.RGBA.size);
  652. } else {
  653. // Skipping YUV for now. Need Test Images.
  654. // UNDONE -- unclear if we'll ever get here if we set mode_rgb*
  655. bytes = PyBytes_FromStringAndSize((char*)config.output.u.YUVA.y,
  656. config.output.u.YUVA.y_size);
  657. }
  658. #if PY_VERSION_HEX >= 0x03000000
  659. pymode = PyUnicode_FromString(mode);
  660. #else
  661. pymode = PyString_FromString(mode);
  662. #endif
  663. ret = Py_BuildValue("SiiSSS", bytes, config.output.width,
  664. config.output.height, pymode,
  665. NULL == icc_profile ? Py_None : icc_profile,
  666. NULL == exif ? Py_None : exif);
  667. end:
  668. WebPFreeDecBuffer(&config.output);
  669. Py_XDECREF(bytes);
  670. Py_XDECREF(pymode);
  671. Py_XDECREF(icc_profile);
  672. Py_XDECREF(exif);
  673. if (Py_None == ret)
  674. Py_RETURN_NONE;
  675. return ret;
  676. }
  677. // Return the decoder's version number, packed in hexadecimal using 8bits for
  678. // each of major/minor/revision. E.g: v2.5.7 is 0x020507.
  679. PyObject* WebPDecoderVersion_wrapper(PyObject* self, PyObject* args){
  680. return Py_BuildValue("i", WebPGetDecoderVersion());
  681. }
  682. /*
  683. * The version of webp that ships with (0.1.3) Ubuntu 12.04 doesn't handle alpha well.
  684. * Files that are valid with 0.3 are reported as being invalid.
  685. */
  686. int WebPDecoderBuggyAlpha(void) {
  687. return WebPGetDecoderVersion()==0x0103;
  688. }
  689. PyObject* WebPDecoderBuggyAlpha_wrapper(PyObject* self, PyObject* args){
  690. return Py_BuildValue("i", WebPDecoderBuggyAlpha());
  691. }
  692. /* -------------------------------------------------------------------- */
  693. /* Module Setup */
  694. /* -------------------------------------------------------------------- */
  695. static PyMethodDef webpMethods[] =
  696. {
  697. #ifdef HAVE_WEBPANIM
  698. {"WebPAnimDecoder", _anim_decoder_new, METH_VARARGS, "WebPAnimDecoder"},
  699. {"WebPAnimEncoder", _anim_encoder_new, METH_VARARGS, "WebPAnimEncoder"},
  700. #endif
  701. {"WebPEncode", WebPEncode_wrapper, METH_VARARGS, "WebPEncode"},
  702. {"WebPDecode", WebPDecode_wrapper, METH_VARARGS, "WebPDecode"},
  703. {"WebPDecoderVersion", WebPDecoderVersion_wrapper, METH_VARARGS, "WebPVersion"},
  704. {"WebPDecoderBuggyAlpha", WebPDecoderBuggyAlpha_wrapper, METH_VARARGS, "WebPDecoderBuggyAlpha"},
  705. {NULL, NULL}
  706. };
  707. void addMuxFlagToModule(PyObject* m) {
  708. #ifdef HAVE_WEBPMUX
  709. PyModule_AddObject(m, "HAVE_WEBPMUX", Py_True);
  710. #else
  711. PyModule_AddObject(m, "HAVE_WEBPMUX", Py_False);
  712. #endif
  713. }
  714. void addAnimFlagToModule(PyObject* m) {
  715. #ifdef HAVE_WEBPANIM
  716. PyModule_AddObject(m, "HAVE_WEBPANIM", Py_True);
  717. #else
  718. PyModule_AddObject(m, "HAVE_WEBPANIM", Py_False);
  719. #endif
  720. }
  721. void addTransparencyFlagToModule(PyObject* m) {
  722. PyModule_AddObject(m, "HAVE_TRANSPARENCY",
  723. PyBool_FromLong(!WebPDecoderBuggyAlpha()));
  724. }
  725. static int setup_module(PyObject* m) {
  726. addMuxFlagToModule(m);
  727. addAnimFlagToModule(m);
  728. addTransparencyFlagToModule(m);
  729. #ifdef HAVE_WEBPANIM
  730. /* Ready object types */
  731. if (PyType_Ready(&WebPAnimDecoder_Type) < 0 ||
  732. PyType_Ready(&WebPAnimEncoder_Type) < 0)
  733. return -1;
  734. #endif
  735. return 0;
  736. }
  737. #if PY_VERSION_HEX >= 0x03000000
  738. PyMODINIT_FUNC
  739. PyInit__webp(void) {
  740. PyObject* m;
  741. static PyModuleDef module_def = {
  742. PyModuleDef_HEAD_INIT,
  743. "_webp", /* m_name */
  744. NULL, /* m_doc */
  745. -1, /* m_size */
  746. webpMethods, /* m_methods */
  747. };
  748. m = PyModule_Create(&module_def);
  749. if (setup_module(m) < 0)
  750. return NULL;
  751. return m;
  752. }
  753. #else
  754. PyMODINIT_FUNC
  755. init_webp(void)
  756. {
  757. PyObject* m = Py_InitModule("_webp", webpMethods);
  758. setup_module(m);
  759. }
  760. #endif