PyObject* _anim_encoder_add(PyObject* self, PyObject* args) { uint8_t* rgb; Py_ssize_t size; int timestamp; int width; int height; char* mode; int lossless; float quality_factor; int method; WebPConfig config; WebPAnimEncoderObject* encp = (WebPAnimEncoderObject*)self; WebPAnimEncoder* enc = encp->enc; WebPPicture* frame = &(encp->frame); if (!PyArg_ParseTuple(args, "z#iiisifi", (char**)&rgb, &size, ×tamp, &width, &height, &mode, &lossless, &quality_factor, &method)) { return NULL; } // Check for NULL frame, which sets duration of final frame if (!rgb) { WebPAnimEncoderAdd(enc, NULL, timestamp, NULL); Py_RETURN_NONE; } // Setup config for this frame if (!WebPConfigInit(&config)) { PyErr_SetString(PyExc_RuntimeError, "failed to initialize config!"); return NULL; } config.lossless = lossless; config.quality = quality_factor; config.method = method; // Validate the config if (!WebPValidateConfig(&config)) { PyErr_SetString(PyExc_ValueError, "invalid configuration"); return NULL; } // Populate the frame with raw bytes passed to us frame->width = width; frame->height = height; frame->use_argb = 1; // Don't convert RGB pixels to YUV if (strcmp(mode, "RGBA")==0) { WebPPictureImportRGBA(frame, rgb, 4 * width); } else if (strcmp(mode, "RGBX")==0) { WebPPictureImportRGBX(frame, rgb, 4 * width); } // Add the frame to the encoder if (!WebPAnimEncoderAdd(enc, frame, timestamp, &config)) { PyErr_SetString(PyExc_RuntimeError, WebPAnimEncoderGetError(enc)); return NULL; } Py_RETURN_NONE; }
PyObject* _anim_encoder_assemble(PyObject* self, PyObject* args) { uint8_t* icc_bytes; uint8_t* exif_bytes; uint8_t* xmp_bytes; Py_ssize_t icc_size; Py_ssize_t exif_size; Py_ssize_t xmp_size; WebPData webp_data; WebPAnimEncoderObject* encp = (WebPAnimEncoderObject*)self; WebPAnimEncoder* enc = encp->enc; WebPMux* mux = NULL; PyObject* ret = NULL; if (!PyArg_ParseTuple(args, "s#s#s#", &icc_bytes, &icc_size, &exif_bytes, &exif_size, &xmp_bytes, &xmp_size)) { return NULL; } // Init the output buffer WebPDataInit(&webp_data); // Assemble everything into the output buffer if (!WebPAnimEncoderAssemble(enc, &webp_data)) { PyErr_SetString(PyExc_RuntimeError, WebPAnimEncoderGetError(enc)); return NULL; } // Re-mux to add metadata as needed if (icc_size > 0 || exif_size > 0 || xmp_size > 0) { WebPMuxError err = WEBP_MUX_OK; int i_icc_size = (int)icc_size; int i_exif_size = (int)exif_size; int i_xmp_size = (int)xmp_size; WebPData icc_profile = { icc_bytes, i_icc_size }; WebPData exif = { exif_bytes, i_exif_size }; WebPData xmp = { xmp_bytes, i_xmp_size }; mux = WebPMuxCreate(&webp_data, 1); if (mux == NULL) { PyErr_SetString(PyExc_RuntimeError, "could not re-mux to add metadata"); return NULL; } WebPDataClear(&webp_data); // Add ICCP chunk if (i_icc_size > 0) { err = WebPMuxSetChunk(mux, "ICCP", &icc_profile, 1); if (err != WEBP_MUX_OK) { return HandleMuxError(err, "ICCP"); } } // Add EXIF chunk if (i_exif_size > 0) { err = WebPMuxSetChunk(mux, "EXIF", &exif, 1); if (err != WEBP_MUX_OK) { return HandleMuxError(err, "EXIF"); } } // Add XMP chunk if (i_xmp_size > 0) { err = WebPMuxSetChunk(mux, "XMP ", &xmp, 1); if (err != WEBP_MUX_OK) { return HandleMuxError(err, "XMP"); } } err = WebPMuxAssemble(mux, &webp_data); if (err != WEBP_MUX_OK) { return HandleMuxError(err, NULL); } } // Convert to Python bytes ret = PyBytes_FromStringAndSize((char*)webp_data.bytes, webp_data.size); WebPDataClear(&webp_data); // If we had to re-mux, we should free it now that we're done with it if (mux != NULL) { WebPMuxDelete(mux); } return ret; }
/* Save an animation to disk */ gboolean save_animation(gint32 nLayers, gint32 *allLayers, FILE *outfile, WebPSaveParams *params, GError **error) { gboolean status = FALSE; gboolean innerStatus = TRUE; WebPAnimEncoderOptions enc_options; WebPAnimEncoder *enc = NULL; int frame_timestamp = 0; WebPData webp_data = {0}; WebPMux *mux; WebPMuxAnimParams anim_params = {0}; /* Prepare for encoding an animation */ WebPAnimEncoderOptionsInit(&enc_options); do { int i; gint32 drawable_ID = allLayers[0]; /* Create the encoder */ enc = WebPAnimEncoderNew(gimp_drawable_width(drawable_ID), gimp_drawable_height(drawable_ID), &enc_options); /* Encode each layer */ for (i = 0; i < nLayers; i++) { if ((innerStatus = save_layer(allLayers[i], NULL, NULL, TRUE, enc, frame_timestamp, params, error)) == FALSE) { break; } } /* Check to make sure each layer was encoded correctly */ if (innerStatus == FALSE) { break; } /* Add NULL frame */ WebPAnimEncoderAdd(enc, NULL, frame_timestamp, NULL); /* Initialize the WebP image structure */ WebPDataInit(&webp_data); /* Write the animation to the image */ if (!WebPAnimEncoderAssemble(enc, &webp_data)) { g_set_error(error, G_FILE_ERROR, 0, "Encoding error: '%s'", WebPAnimEncoderGetError(enc)); break; } /* Create a Mux */ mux = WebPMuxCreate(&webp_data, 1); /* Set animation parameters */ anim_params.loop_count = params->loop == TRUE ? 0 : 1; WebPMuxSetAnimationParams(mux, &anim_params); /* Assemble the image */ WebPMuxAssemble(mux, &webp_data); /* Write to disk */ if (fwrite(webp_data.bytes, webp_data.size, 1, outfile) != 1) { break; } /* Everything succeeded */ status = TRUE; } while(0); /* Free image data */ WebPDataClear(&webp_data); /* Free the animation encoder */ if (enc) { WebPAnimEncoderDelete(enc); } return status; }