// Encoder functions PyObject* _anim_encoder_new(PyObject* self, PyObject* args) { int width, height; uint32_t bgcolor; int loop_count; int minimize_size; int kmin, kmax; int allow_mixed; int verbose; WebPAnimEncoderOptions enc_options; WebPAnimEncoderObject* encp = NULL; WebPAnimEncoder* enc = NULL; if (!PyArg_ParseTuple(args, "iiIiiiiii", &width, &height, &bgcolor, &loop_count, &minimize_size, &kmin, &kmax, &allow_mixed, &verbose)) { return NULL; } // Setup and configure the encoder's options (these are animation-specific) if (!WebPAnimEncoderOptionsInit(&enc_options)) { PyErr_SetString(PyExc_RuntimeError, "failed to initialize encoder options"); return NULL; } enc_options.anim_params.bgcolor = bgcolor; enc_options.anim_params.loop_count = loop_count; enc_options.minimize_size = minimize_size; enc_options.kmin = kmin; enc_options.kmax = kmax; enc_options.allow_mixed = allow_mixed; enc_options.verbose = verbose; // Validate canvas dimensions if (width <= 0 || height <= 0) { PyErr_SetString(PyExc_ValueError, "invalid canvas dimensions"); return NULL; } // Create a new animation encoder and picture frame encp = PyObject_New(WebPAnimEncoderObject, &WebPAnimEncoder_Type); if (encp) { if (WebPPictureInit(&(encp->frame))) { enc = WebPAnimEncoderNew(width, height, &enc_options); if (enc) { encp->enc = enc; return (PyObject*) encp; } WebPPictureFree(&(encp->frame)); } PyObject_Del(encp); } PyErr_SetString(PyExc_RuntimeError, "could not create encoder object"); return NULL; }
int main(int argc, const char *argv[]) { int verbose = 0; int gif_error = GIF_ERROR; WebPMuxError err = WEBP_MUX_OK; int ok = 0; const char *in_file = NULL, *out_file = NULL; FILE* out = NULL; GifFileType* gif = NULL; int frame_duration = 0; int frame_timestamp = 0; GIFDisposeMethod orig_dispose = GIF_DISPOSE_NONE; WebPPicture frame; // Frame rectangle only (not disposed). WebPPicture curr_canvas; // Not disposed. WebPPicture prev_canvas; // Disposed. WebPPicture prev_to_prev_canvas; // Disposed. WebPAnimEncoder* enc = NULL; WebPAnimEncoderOptions enc_options; WebPConfig config; int is_first_frame = 1; // Whether we are processing the first frame. int done; int c; int quiet = 0; WebPData webp_data; int keep_metadata = METADATA_XMP; // ICC not output by default. WebPData icc_data; int stored_icc = 0; // Whether we have already stored an ICC profile. WebPData xmp_data; int stored_xmp = 0; // Whether we have already stored an XMP profile. int loop_count = 0; int stored_loop_count = 0; // Whether we have found an explicit loop count. WebPMux* mux = NULL; int default_kmin = 1; // Whether to use default kmin value. int default_kmax = 1; if (!WebPConfigInit(&config) || !WebPAnimEncoderOptionsInit(&enc_options) || !WebPPictureInit(&frame) || !WebPPictureInit(&curr_canvas) || !WebPPictureInit(&prev_canvas) || !WebPPictureInit(&prev_to_prev_canvas)) { fprintf(stderr, "Error! Version mismatch!\n"); return -1; } config.lossless = 1; // Use lossless compression by default. WebPDataInit(&webp_data); WebPDataInit(&icc_data); WebPDataInit(&xmp_data); if (argc == 1) { Help(); return 0; } for (c = 1; c < argc; ++c) { int parse_error = 0; if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) { Help(); return 0; } else if (!strcmp(argv[c], "-o") && c < argc - 1) { out_file = argv[++c]; } else if (!strcmp(argv[c], "-lossy")) { config.lossless = 0; } else if (!strcmp(argv[c], "-mixed")) { enc_options.allow_mixed = 1; config.lossless = 0; } else if (!strcmp(argv[c], "-q") && c < argc - 1) { config.quality = ExUtilGetFloat(argv[++c], &parse_error); } else if (!strcmp(argv[c], "-m") && c < argc - 1) { config.method = ExUtilGetInt(argv[++c], 0, &parse_error); } else if (!strcmp(argv[c], "-min_size")) { enc_options.minimize_size = 1; } else if (!strcmp(argv[c], "-kmax") && c < argc - 1) { enc_options.kmax = ExUtilGetInt(argv[++c], 0, &parse_error); default_kmax = 0; } else if (!strcmp(argv[c], "-kmin") && c < argc - 1) { enc_options.kmin = ExUtilGetInt(argv[++c], 0, &parse_error); default_kmin = 0; } else if (!strcmp(argv[c], "-f") && c < argc - 1) { config.filter_strength = ExUtilGetInt(argv[++c], 0, &parse_error); } else if (!strcmp(argv[c], "-metadata") && c < argc - 1) { static const struct { const char* option; int flag; } kTokens[] = { { "all", METADATA_ALL }, { "none", 0 }, { "icc", METADATA_ICC }, { "xmp", METADATA_XMP }, }; const size_t kNumTokens = sizeof(kTokens) / sizeof(*kTokens); const char* start = argv[++c]; const char* const end = start + strlen(start); keep_metadata = 0; while (start < end) { size_t i; const char* token = strchr(start, ','); if (token == NULL) token = end; for (i = 0; i < kNumTokens; ++i) { if ((size_t)(token - start) == strlen(kTokens[i].option) && !strncmp(start, kTokens[i].option, strlen(kTokens[i].option))) { if (kTokens[i].flag != 0) { keep_metadata |= kTokens[i].flag; } else { keep_metadata = 0; } break; } } if (i == kNumTokens) { fprintf(stderr, "Error! Unknown metadata type '%.*s'\n", (int)(token - start), start); Help(); return -1; } start = token + 1; } } else if (!strcmp(argv[c], "-mt")) { ++config.thread_level; } else if (!strcmp(argv[c], "-version")) { const int enc_version = WebPGetEncoderVersion(); const int mux_version = WebPGetMuxVersion(); printf("WebP Encoder version: %d.%d.%d\nWebP Mux version: %d.%d.%d\n", (enc_version >> 16) & 0xff, (enc_version >> 8) & 0xff, enc_version & 0xff, (mux_version >> 16) & 0xff, (mux_version >> 8) & 0xff, mux_version & 0xff); return 0; } else if (!strcmp(argv[c], "-quiet")) { quiet = 1; } else if (!strcmp(argv[c], "-v")) { verbose = 1; enc_options.verbose = 1; } else if (!strcmp(argv[c], "--")) { if (c < argc - 1) in_file = argv[++c]; break; } else if (argv[c][0] == '-') { fprintf(stderr, "Error! Unknown option '%s'\n", argv[c]); Help(); return -1; } else { in_file = argv[c]; } if (parse_error) { Help(); return -1; } }
/* 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; }