_webp.c 29 KB

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