/* 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; }
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; }
/* Save a layer from an image */ gboolean save_layer(gint32 drawable_ID, WebPWriterFunction writer, void *custom_ptr, #ifdef WEBP_0_5 gboolean animation, WebPAnimEncoder *enc, int frame_timestamp, #endif WebPSaveParams *params, GError **error) { gboolean status = FALSE; gint bpp; gint width; gint height; GimpImageType drawable_type; WebPConfig config; WebPPicture picture; guchar *buffer = NULL; #ifdef GIMP_2_9 GeglBuffer *geglbuffer; GeglRectangle extent; #else GimpDrawable *drawable = NULL; GimpPixelRgn region; #endif /* Retrieve the image data */ bpp = gimp_drawable_bpp(drawable_ID); width = gimp_drawable_width(drawable_ID); height = gimp_drawable_height(drawable_ID); drawable_type = gimp_drawable_type(drawable_ID); /* Initialize the WebP configuration with a preset and fill in the * remaining values */ WebPConfigPreset(&config, webp_preset_by_name(params->preset), params->quality); config.lossless = params->lossless; config.method = 6; /* better quality */ config.alpha_quality = params->alpha_quality; /* Prepare the WebP structure */ WebPPictureInit(&picture); picture.use_argb = 1; picture.width = width; picture.height = height; picture.writer = writer; picture.custom_ptr = custom_ptr; picture.progress_hook = webp_file_progress; do { /* Attempt to allocate a buffer of the appropriate size */ buffer = (guchar *)g_malloc(bpp * width * height); if(!buffer) { g_set_error(error, G_FILE_ERROR, 0, "Unable to allocate buffer for layer"); break; } #ifdef GIMP_2_9 /* Obtain the buffer and get its extent */ geglbuffer = gimp_drawable_get_buffer(drawable_ID); extent = *gegl_buffer_get_extent(geglbuffer); /* Read the layer buffer into our buffer */ gegl_buffer_get(geglbuffer, &extent, 1.0, NULL, buffer, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); g_object_unref(geglbuffer); #else /* Get the drawable */ drawable = gimp_drawable_get(drawable_ID); /* Obtain the pixel region for the drawable */ gimp_pixel_rgn_init(®ion, drawable, 0, 0, width, height, FALSE, FALSE); /* Read the region into the buffer */ gimp_pixel_rgn_get_rect(®ion, buffer, 0, 0, width, height); gimp_drawable_detach(drawable); #endif /* Use the appropriate function to import the data from the buffer */ if(drawable_type == GIMP_RGB_IMAGE) { WebPPictureImportRGB(&picture, buffer, width * bpp); } else { WebPPictureImportRGBA(&picture, buffer, width * bpp); } #ifdef WEBP_0_5 if (animation == TRUE) { if (!WebPAnimEncoderAdd(enc, &picture, frame_timestamp, &config)) { g_set_error(error, G_FILE_ERROR, picture.error_code, "WebP error: '%s'", webp_error_string(picture.error_code)); break; } } else { #endif if(!WebPEncode(&config, &picture)) { g_set_error(error, G_FILE_ERROR, picture.error_code, "WebP error: '%s'", webp_error_string(picture.error_code)); break; } #ifdef WEBP_0_5 } #endif /* Everything succeeded */ status = TRUE; } while(0); /* Free the buffer */ if (buffer) { free(buffer); } return status; }