| // Copyright 2016 Google Inc. All Rights Reserved. | |
| // | |
| // Use of this source code is governed by a BSD-style license | |
| // that can be found in the COPYING file in the root of the source | |
| // tree. An additional intellectual property rights grant can be found | |
| // in the file PATENTS. All contributing project authors may | |
| // be found in the AUTHORS file in the root of the source tree. | |
| // ----------------------------------------------------------------------------- | |
| // | |
| // generate an animated WebP out of a sequence of images | |
| // (PNG, JPEG, ...) | |
| // | |
| // Example usage: | |
| // img2webp -o out.webp -q 40 -mixed -duration 40 input??.png | |
| // | |
| // Author: skal@google.com (Pascal Massimino) | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #ifdef HAVE_CONFIG_H | |
| #include "webp/config.h" | |
| #endif | |
| #include "../examples/example_util.h" | |
| #include "../imageio/image_dec.h" | |
| #include "../imageio/imageio_util.h" | |
| #include "./stopwatch.h" | |
| #include "webp/encode.h" | |
| #include "webp/mux.h" | |
| //------------------------------------------------------------------------------ | |
| static void Help(void) { | |
| printf("Usage:\n\n"); | |
| printf(" img2webp [file-level options] [image files...] " | |
| "[per-frame options...]\n"); | |
| printf("\n"); | |
| printf("File-level options (only used at the start of compression):\n"); | |
| printf(" -min_size ............ minimize size\n"); | |
| printf(" -loop <int> .......... loop count (default: 0, = infinite loop)\n"); | |
| printf(" -kmax <int> .......... maximum number of frame between key-frames\n" | |
| " (0=only keyframes)\n"); | |
| printf(" -kmin <int> .......... minimum number of frame between key-frames\n" | |
| " (0=disable key-frames altogether)\n"); | |
| printf(" -mixed ............... use mixed lossy/lossless automatic mode\n"); | |
| printf(" -v ................... verbose mode\n"); | |
| printf(" -h ................... this help\n"); | |
| printf(" -version ............. print version number and exit\n"); | |
| printf("\n"); | |
| printf("Per-frame options (only used for subsequent images input):\n"); | |
| printf(" -d <int> ............. frame duration in ms (default: 100)\n"); | |
| printf(" -lossless ........... use lossless mode (default)\n"); | |
| printf(" -lossy ... ........... use lossy mode\n"); | |
| printf(" -q <float> ........... quality\n"); | |
| printf(" -m <int> ............. method to use\n"); | |
| printf("\n"); | |
| printf("example: img2webp -loop 2 in0.png -lossy in1.jpg\n" | |
| " -d 80 in2.tiff -o out.webp\n"); | |
| } | |
| //------------------------------------------------------------------------------ | |
| static int ReadImage(const char filename[], WebPPicture* const pic) { | |
| const uint8_t* data = NULL; | |
| size_t data_size = 0; | |
| WebPImageReader reader; | |
| int ok; | |
| #ifdef HAVE_WINCODEC_H | |
| // Try to decode the file using WIC falling back to the other readers for | |
| // e.g., WebP. | |
| ok = ReadPictureWithWIC(filename, pic, 1, NULL); | |
| if (ok) return 1; | |
| #endif | |
| if (!ImgIoUtilReadFile(filename, &data, &data_size)) return 0; | |
| reader = WebPGuessImageReader(data, data_size); | |
| ok = reader(data, data_size, pic, 1, NULL); | |
| free((void*)data); | |
| return ok; | |
| } | |
| static int SetLoopCount(int loop_count, WebPData* const webp_data) { | |
| int ok = 1; | |
| WebPMuxError err; | |
| uint32_t features; | |
| WebPMuxAnimParams new_params; | |
| WebPMux* const mux = WebPMuxCreate(webp_data, 1); | |
| if (mux == NULL) return 0; | |
| err = WebPMuxGetFeatures(mux, &features); | |
| ok = (err == WEBP_MUX_OK); | |
| if (!ok || !(features & ANIMATION_FLAG)) goto End; | |
| err = WebPMuxGetAnimationParams(mux, &new_params); | |
| ok = (err == WEBP_MUX_OK); | |
| if (ok) { | |
| new_params.loop_count = loop_count; | |
| err = WebPMuxSetAnimationParams(mux, &new_params); | |
| ok = (err == WEBP_MUX_OK); | |
| } | |
| if (ok) { | |
| WebPDataClear(webp_data); | |
| err = WebPMuxAssemble(mux, webp_data); | |
| ok = (err == WEBP_MUX_OK); | |
| } | |
| End: | |
| WebPMuxDelete(mux); | |
| if (!ok) { | |
| fprintf(stderr, "Error during loop-count setting\n"); | |
| } | |
| return ok; | |
| } | |
| //------------------------------------------------------------------------------ | |
| int main(int argc, const char* argv[]) { | |
| const char* output = NULL; | |
| WebPAnimEncoder* enc = NULL; | |
| int verbose = 0; | |
| int pic_num = 0; | |
| int duration = 100; | |
| int timestamp_ms = 0; | |
| int loop_count = 0; | |
| int width = 0, height = 0; | |
| WebPAnimEncoderOptions anim_config; | |
| WebPConfig config; | |
| WebPPicture pic; | |
| WebPData webp_data; | |
| int c; | |
| int have_input = 0; | |
| CommandLineArguments cmd_args; | |
| int ok = ExUtilInitCommandLineArguments(argc - 1, argv + 1, &cmd_args); | |
| if (!ok) return 1; | |
| argc = cmd_args.argc_; | |
| argv = cmd_args.argv_; | |
| WebPDataInit(&webp_data); | |
| if (!WebPAnimEncoderOptionsInit(&anim_config) || | |
| !WebPConfigInit(&config) || | |
| !WebPPictureInit(&pic)) { | |
| fprintf(stderr, "Library version mismatch!\n"); | |
| ok = 0; | |
| goto End; | |
| } | |
| // 1st pass of option parsing | |
| for (c = 0; ok && c < argc; ++c) { | |
| if (argv[c][0] == '-') { | |
| int parse_error = 0; | |
| if (!strcmp(argv[c], "-o") && c + 1 < argc) { | |
| argv[c] = NULL; | |
| output = argv[++c]; | |
| } else if (!strcmp(argv[c], "-kmin") && c + 1 < argc) { | |
| argv[c] = NULL; | |
| anim_config.kmin = ExUtilGetInt(argv[++c], 0, &parse_error); | |
| } else if (!strcmp(argv[c], "-kmax") && c + 1 < argc) { | |
| argv[c] = NULL; | |
| anim_config.kmax = ExUtilGetInt(argv[++c], 0, &parse_error); | |
| } else if (!strcmp(argv[c], "-loop") && c + 1 < argc) { | |
| argv[c] = NULL; | |
| loop_count = ExUtilGetInt(argv[++c], 0, &parse_error); | |
| if (loop_count < 0) { | |
| fprintf(stderr, "Invalid non-positive loop-count (%d)\n", loop_count); | |
| parse_error = 1; | |
| } | |
| } else if (!strcmp(argv[c], "-min_size")) { | |
| anim_config.minimize_size = 1; | |
| } else if (!strcmp(argv[c], "-mixed")) { | |
| anim_config.allow_mixed = 1; | |
| config.lossless = 0; | |
| } else if (!strcmp(argv[c], "-v")) { | |
| verbose = 1; | |
| } else if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) { | |
| Help(); | |
| goto End; | |
| } 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); | |
| goto End; | |
| } else { | |
| continue; | |
| } | |
| ok = !parse_error; | |
| if (!ok) goto End; | |
| argv[c] = NULL; // mark option as 'parsed' during 1st pass | |
| } else { | |
| have_input |= 1; | |
| } | |
| } | |
| if (!have_input) { | |
| fprintf(stderr, "No input file(s) for generating animation!\n"); | |
| goto End; | |
| } | |
| // image-reading pass | |
| pic_num = 0; | |
| config.lossless = 1; | |
| for (c = 0; ok && c < argc; ++c) { | |
| if (argv[c] == NULL) continue; | |
| if (argv[c][0] == '-') { // parse local options | |
| int parse_error = 0; | |
| if (!strcmp(argv[c], "-lossy")) { | |
| if (!anim_config.allow_mixed) config.lossless = 0; | |
| } else if (!strcmp(argv[c], "-lossless")) { | |
| if (!anim_config.allow_mixed) config.lossless = 1; | |
| } else if (!strcmp(argv[c], "-q") && c + 1 < argc) { | |
| config.quality = ExUtilGetFloat(argv[++c], &parse_error); | |
| } else if (!strcmp(argv[c], "-m") && c + 1 < argc) { | |
| config.method = ExUtilGetInt(argv[++c], 0, &parse_error); | |
| } else if (!strcmp(argv[c], "-d") && c + 1 < argc) { | |
| duration = ExUtilGetInt(argv[++c], 0, &parse_error); | |
| if (duration <= 0) { | |
| fprintf(stderr, "Invalid negative duration (%d)\n", duration); | |
| parse_error = 1; | |
| } | |
| } else { | |
| parse_error = 1; // shouldn't be here. | |
| fprintf(stderr, "Unknown option [%s]\n", argv[c]); | |
| } | |
| ok = !parse_error; | |
| if (!ok) goto End; | |
| continue; | |
| } | |
| if (ok) { | |
| ok = WebPValidateConfig(&config); | |
| if (!ok) { | |
| fprintf(stderr, "Invalid configuration.\n"); | |
| goto End; | |
| } | |
| } | |
| // read next input image | |
| pic.use_argb = 1; | |
| ok = ReadImage(argv[c], &pic); | |
| if (!ok) goto End; | |
| if (enc == NULL) { | |
| width = pic.width; | |
| height = pic.height; | |
| enc = WebPAnimEncoderNew(width, height, &anim_config); | |
| ok = (enc != NULL); | |
| if (!ok) { | |
| fprintf(stderr, "Could not create WebPAnimEncoder object.\n"); | |
| } | |
| } | |
| if (ok) { | |
| ok = (width == pic.width && height == pic.height); | |
| if (!ok) { | |
| fprintf(stderr, "Frame #%d dimension mismatched! " | |
| "Got %d x %d. Was expecting %d x %d.\n", | |
| pic_num, pic.width, pic.height, width, height); | |
| } | |
| } | |
| if (ok) { | |
| ok = WebPAnimEncoderAdd(enc, &pic, timestamp_ms, &config); | |
| if (!ok) { | |
| fprintf(stderr, "Error while adding frame #%d\n", pic_num); | |
| } | |
| } | |
| WebPPictureFree(&pic); | |
| if (!ok) goto End; | |
| if (verbose) { | |
| fprintf(stderr, "Added frame #%3d at time %4d (file: %s)\n", | |
| pic_num, timestamp_ms, argv[c]); | |
| } | |
| timestamp_ms += duration; | |
| ++pic_num; | |
| } | |
| // add a last fake frame to signal the last duration | |
| ok = ok && WebPAnimEncoderAdd(enc, NULL, timestamp_ms, NULL); | |
| ok = ok && WebPAnimEncoderAssemble(enc, &webp_data); | |
| if (!ok) { | |
| fprintf(stderr, "Error during final animation assembly.\n"); | |
| } | |
| End: | |
| // free resources | |
| WebPAnimEncoderDelete(enc); | |
| if (ok && loop_count > 0) { // Re-mux to add loop count. | |
| ok = SetLoopCount(loop_count, &webp_data); | |
| } | |
| if (ok) { | |
| if (output != NULL) { | |
| ok = ImgIoUtilWriteFile(output, webp_data.bytes, webp_data.size); | |
| if (ok) fprintf(stderr, "output file: %s ", output); | |
| } else { | |
| fprintf(stderr, "[no output file specified] "); | |
| } | |
| } | |
| if (ok) { | |
| fprintf(stderr, "[%d frames, %u bytes].\n", | |
| pic_num, (unsigned int)webp_data.size); | |
| } | |
| WebPDataClear(&webp_data); | |
| ExUtilDeleteCommandLineArguments(&cmd_args); | |
| return ok ? 0 : 1; | |
| } |