go-libwebp

Experimental translation from libwebp to Go source.
Log | Files | Refs | README | LICENSE

img2webp.c (12319B)


      1 // Copyright 2016 Google Inc. All Rights Reserved.
      2 //
      3 // Use of this source code is governed by a BSD-style license
      4 // that can be found in the COPYING file in the root of the source
      5 // tree. An additional intellectual property rights grant can be found
      6 // in the file PATENTS. All contributing project authors may
      7 // be found in the AUTHORS file in the root of the source tree.
      8 // -----------------------------------------------------------------------------
      9 //
     10 //  generate an animated WebP out of a sequence of images
     11 //  (PNG, JPEG, ...)
     12 //
     13 //  Example usage:
     14 //     img2webp -o out.webp -q 40 -mixed -duration 40 input??.png
     15 //
     16 // Author: skal@google.com (Pascal Massimino)
     17 
     18 #include <stdio.h>
     19 #include <stdlib.h>
     20 #include <string.h>
     21 
     22 #ifdef HAVE_CONFIG_H
     23 #include "webp/config.h"
     24 #endif
     25 
     26 #include "../examples/example_util.h"
     27 #include "../imageio/image_dec.h"
     28 #include "../imageio/imageio_util.h"
     29 #include "./stopwatch.h"
     30 #include "./unicode.h"
     31 #include "sharpyuv/sharpyuv.h"
     32 #include "webp/encode.h"
     33 #include "webp/mux.h"
     34 #include "webp/mux_types.h"
     35 #include "webp/types.h"
     36 
     37 //------------------------------------------------------------------------------
     38 
     39 static void Help(void) {
     40   printf("Usage:\n\n");
     41   printf("  img2webp [file_options] [[frame_options] frame_file]...");
     42   printf(" [-o webp_file]\n\n");
     43 
     44   printf("File-level options (only used at the start of compression):\n");
     45   printf(" -min_size ............ minimize size\n");
     46   printf(" -kmax <int> .......... maximum number of frame between key-frames\n"
     47          "                        (0=only keyframes)\n");
     48   printf(" -kmin <int> .......... minimum number of frame between key-frames\n"
     49          "                        (0=disable key-frames altogether)\n");
     50   printf(" -mixed ............... use mixed lossy/lossless automatic mode\n");
     51   printf(" -near_lossless <int> . use near-lossless image preprocessing\n"
     52          "                        (0..100=off), default=100\n");
     53   printf(" -sharp_yuv ........... use sharper (and slower) RGB->YUV "
     54                                   "conversion\n                        "
     55                                   "(lossy only)\n");
     56   printf(" -loop <int> .......... loop count (default: 0, = infinite loop)\n");
     57   printf(" -v ................... verbose mode\n");
     58   printf(" -h ................... this help\n");
     59   printf(" -version ............. print version number and exit\n");
     60   printf("\n");
     61 
     62   printf("Per-frame options (only used for subsequent images input):\n");
     63   printf(" -d <int> ............. frame duration in ms (default: 100)\n");
     64   printf(" -lossless ............ use lossless mode (default)\n");
     65   printf(" -lossy ............... use lossy mode\n");
     66   printf(" -q <float> ........... quality\n");
     67   printf(" -m <int> ............. compression method (0=fast, 6=slowest), "
     68          "default=4\n");
     69   printf(" -exact, -noexact ..... preserve or alter RGB values in transparent "
     70                                   "area\n"
     71          "                        (default: -noexact, may cause artifacts\n"
     72          "                                  with lossy animations)\n");
     73 
     74   printf("\n");
     75   printf("example: img2webp -loop 2 in0.png -lossy in1.jpg\n"
     76          "                  -d 80 in2.tiff -o out.webp\n");
     77   printf("\nNote: if a single file name is passed as the argument, the "
     78          "arguments will be\n");
     79   printf("tokenized from this file. The file name must not start with "
     80          "the character '-'.\n");
     81   printf("\nSupported input formats:\n  %s\n",
     82          WebPGetEnabledInputFileFormats());
     83 }
     84 
     85 //------------------------------------------------------------------------------
     86 
     87 static int ReadImage(const char filename[], WebPPicture* const pic) {
     88   const uint8_t* data = NULL;
     89   size_t data_size = 0;
     90   WebPImageReader reader;
     91   int ok;
     92 #ifdef HAVE_WINCODEC_H
     93   // Try to decode the file using WIC falling back to the other readers for
     94   // e.g., WebP.
     95   ok = ReadPictureWithWIC(filename, pic, 1, NULL);
     96   if (ok) return 1;
     97 #endif
     98   if (!ImgIoUtilReadFile(filename, &data, &data_size)) return 0;
     99   reader = WebPGuessImageReader(data, data_size);
    100   ok = reader(data, data_size, pic, 1, NULL);
    101   WebPFree((void*)data);
    102   return ok;
    103 }
    104 
    105 static int SetLoopCount(int loop_count, WebPData* const webp_data) {
    106   int ok = 1;
    107   WebPMuxError err;
    108   uint32_t features;
    109   WebPMuxAnimParams new_params;
    110   WebPMux* const mux = WebPMuxCreate(webp_data, 1);
    111   if (mux == NULL) return 0;
    112 
    113   err = WebPMuxGetFeatures(mux, &features);
    114   ok = (err == WEBP_MUX_OK);
    115   if (!ok || !(features & ANIMATION_FLAG)) goto End;
    116 
    117   err = WebPMuxGetAnimationParams(mux, &new_params);
    118   ok = (err == WEBP_MUX_OK);
    119   if (ok) {
    120     new_params.loop_count = loop_count;
    121     err = WebPMuxSetAnimationParams(mux, &new_params);
    122     ok = (err == WEBP_MUX_OK);
    123   }
    124   if (ok) {
    125     WebPDataClear(webp_data);
    126     err = WebPMuxAssemble(mux, webp_data);
    127     ok = (err == WEBP_MUX_OK);
    128   }
    129 
    130  End:
    131   WebPMuxDelete(mux);
    132   if (!ok) {
    133     fprintf(stderr, "Error during loop-count setting\n");
    134   }
    135   return ok;
    136 }
    137 
    138 //------------------------------------------------------------------------------
    139 
    140 // Returns EXIT_SUCCESS on success, EXIT_FAILURE on failure.
    141 int main(int argc, const char* argv[]) {
    142   const char* output = NULL;
    143   WebPAnimEncoder* enc = NULL;
    144   int verbose = 0;
    145   int pic_num = 0;
    146   int duration = 100;
    147   int timestamp_ms = 0;
    148   int loop_count = 0;
    149   int width = 0, height = 0;
    150   WebPAnimEncoderOptions anim_config;
    151   WebPConfig config;
    152   WebPPicture pic;
    153   WebPData webp_data;
    154   int c;
    155   int have_input = 0;
    156   int last_input_index = 0;
    157   CommandLineArguments cmd_args;
    158   int ok;
    159 
    160   INIT_WARGV(argc, argv);
    161 
    162   ok = ExUtilInitCommandLineArguments(argc - 1, argv + 1, &cmd_args);
    163   if (!ok) FREE_WARGV_AND_RETURN(EXIT_FAILURE);
    164 
    165   argc = cmd_args.argc;
    166   argv = cmd_args.argv;
    167 
    168   WebPDataInit(&webp_data);
    169   if (!WebPAnimEncoderOptionsInit(&anim_config) ||
    170       !WebPConfigInit(&config) ||
    171       !WebPPictureInit(&pic)) {
    172     fprintf(stderr, "Library version mismatch!\n");
    173     ok = 0;
    174     goto End;
    175   }
    176 
    177   // 1st pass of option parsing
    178   for (c = 0; ok && c < argc; ++c) {
    179     if (argv[c][0] == '-') {
    180       int parse_error = 0;
    181       if (!strcmp(argv[c], "-o") && c + 1 < argc) {
    182         argv[c] = NULL;
    183         output = (const char*)GET_WARGV_SHIFTED(argv, ++c);
    184       } else if (!strcmp(argv[c], "-kmin") && c + 1 < argc) {
    185         argv[c] = NULL;
    186         anim_config.kmin = ExUtilGetInt(argv[++c], 0, &parse_error);
    187       } else if (!strcmp(argv[c], "-kmax") && c + 1 < argc) {
    188         argv[c] = NULL;
    189         anim_config.kmax = ExUtilGetInt(argv[++c], 0, &parse_error);
    190       } else if (!strcmp(argv[c], "-loop") && c + 1 < argc) {
    191         argv[c] = NULL;
    192         loop_count = ExUtilGetInt(argv[++c], 0, &parse_error);
    193         if (loop_count < 0) {
    194           fprintf(stderr, "Invalid non-positive loop-count (%d)\n", loop_count);
    195           parse_error = 1;
    196         }
    197       } else if (!strcmp(argv[c], "-min_size")) {
    198         anim_config.minimize_size = 1;
    199       } else if (!strcmp(argv[c], "-mixed")) {
    200         anim_config.allow_mixed = 1;
    201         config.lossless = 0;
    202       } else if (!strcmp(argv[c], "-near_lossless") && c + 1 < argc) {
    203         argv[c] = NULL;
    204         config.near_lossless = ExUtilGetInt(argv[++c], 0, &parse_error);
    205       } else if (!strcmp(argv[c], "-sharp_yuv")) {
    206         config.use_sharp_yuv = 1;
    207       } else if (!strcmp(argv[c], "-v")) {
    208         verbose = 1;
    209       } else if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
    210         Help();
    211         FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
    212       } else if (!strcmp(argv[c], "-version")) {
    213         const int enc_version = WebPGetEncoderVersion();
    214         const int mux_version = WebPGetMuxVersion();
    215         const int sharpyuv_version = SharpYuvGetVersion();
    216         printf("WebP Encoder version: %d.%d.%d\nWebP Mux version: %d.%d.%d\n",
    217                (enc_version >> 16) & 0xff, (enc_version >> 8) & 0xff,
    218                enc_version & 0xff, (mux_version >> 16) & 0xff,
    219                (mux_version >> 8) & 0xff, mux_version & 0xff);
    220         printf("libsharpyuv: %d.%d.%d\n", (sharpyuv_version >> 24) & 0xff,
    221                (sharpyuv_version >> 16) & 0xffff, sharpyuv_version & 0xff);
    222         goto End;
    223       } else {
    224         continue;
    225       }
    226       ok = !parse_error;
    227       if (!ok) goto End;
    228       argv[c] = NULL;   // mark option as 'parsed' during 1st pass
    229     } else {
    230       have_input |= 1;
    231     }
    232   }
    233   if (!have_input) {
    234     fprintf(stderr, "No input file(s) for generating animation!\n");
    235     ok = 0;
    236     Help();
    237     goto End;
    238   }
    239 
    240   // image-reading pass
    241   pic_num = 0;
    242   config.lossless = 1;
    243   for (c = 0; ok && c < argc; ++c) {
    244     if (argv[c] == NULL) continue;
    245     if (argv[c][0] == '-') {    // parse local options
    246       int parse_error = 0;
    247       if (!strcmp(argv[c], "-lossy")) {
    248         if (!anim_config.allow_mixed) config.lossless = 0;
    249       } else if (!strcmp(argv[c], "-lossless")) {
    250         if (!anim_config.allow_mixed) config.lossless = 1;
    251       } else if (!strcmp(argv[c], "-q") && c + 1 < argc) {
    252         config.quality = ExUtilGetFloat(argv[++c], &parse_error);
    253       } else if (!strcmp(argv[c], "-m") && c + 1 < argc) {
    254         config.method = ExUtilGetInt(argv[++c], 0, &parse_error);
    255       } else if (!strcmp(argv[c], "-d") && c + 1 < argc) {
    256         duration = ExUtilGetInt(argv[++c], 0, &parse_error);
    257         if (duration <= 0) {
    258           fprintf(stderr, "Invalid negative duration (%d)\n", duration);
    259           parse_error = 1;
    260         }
    261       } else if (!strcmp(argv[c], "-exact")) {
    262         config.exact = 1;
    263       } else if (!strcmp(argv[c], "-noexact")) {
    264         config.exact = 0;
    265       } else {
    266         parse_error = 1;   // shouldn't be here.
    267         fprintf(stderr, "Unknown option [%s]\n", argv[c]);
    268       }
    269       ok = !parse_error;
    270       if (!ok) goto End;
    271       continue;
    272     }
    273 
    274     if (ok) {
    275       ok = WebPValidateConfig(&config);
    276       if (!ok) {
    277         fprintf(stderr, "Invalid configuration.\n");
    278         goto End;
    279       }
    280     }
    281 
    282     // read next input image
    283     pic.use_argb = 1;
    284     ok = ReadImage((const char*)GET_WARGV_SHIFTED(argv, c), &pic);
    285     last_input_index = c;
    286     if (!ok) goto End;
    287 
    288     if (enc == NULL) {
    289       width  = pic.width;
    290       height = pic.height;
    291       enc = WebPAnimEncoderNew(width, height, &anim_config);
    292       ok = (enc != NULL);
    293       if (!ok) {
    294         fprintf(stderr, "Could not create WebPAnimEncoder object.\n");
    295       }
    296     }
    297 
    298     if (ok) {
    299       ok = (width == pic.width && height == pic.height);
    300       if (!ok) {
    301         fprintf(stderr, "Frame #%d dimension mismatched! "
    302                         "Got %d x %d. Was expecting %d x %d.\n",
    303                 pic_num, pic.width, pic.height, width, height);
    304       }
    305     }
    306 
    307     if (ok) {
    308       ok = WebPAnimEncoderAdd(enc, &pic, timestamp_ms, &config);
    309       if (!ok) {
    310         fprintf(stderr, "Error while adding frame #%d\n", pic_num);
    311       }
    312     }
    313     WebPPictureFree(&pic);
    314     if (!ok) goto End;
    315 
    316     if (verbose) {
    317       WFPRINTF(stderr, "Added frame #%3d at time %4d (file: %s)\n",
    318                pic_num, timestamp_ms, GET_WARGV_SHIFTED(argv, c));
    319     }
    320     timestamp_ms += duration;
    321     ++pic_num;
    322   }
    323 
    324   for (c = last_input_index + 1; c < argc; ++c) {
    325     if (argv[c] != NULL) {
    326       fprintf(stderr, "Warning: unused option [%s]!"
    327                       " Frame options go before the input frame.\n", argv[c]);
    328     }
    329   }
    330 
    331   // add a last fake frame to signal the last duration
    332   ok = ok && WebPAnimEncoderAdd(enc, NULL, timestamp_ms, NULL);
    333   ok = ok && WebPAnimEncoderAssemble(enc, &webp_data);
    334   if (!ok) {
    335     fprintf(stderr, "Error during final animation assembly.\n");
    336   }
    337 
    338  End:
    339   // free resources
    340   WebPAnimEncoderDelete(enc);
    341 
    342   if (ok && loop_count > 0) {  // Re-mux to add loop count.
    343     ok = SetLoopCount(loop_count, &webp_data);
    344   }
    345 
    346   if (ok) {
    347     if (output != NULL) {
    348       ok = ImgIoUtilWriteFile(output, webp_data.bytes, webp_data.size);
    349       if (ok) WFPRINTF(stderr, "output file: %s     ", (const W_CHAR*)output);
    350     } else {
    351       fprintf(stderr, "[no output file specified]   ");
    352     }
    353   }
    354 
    355   if (ok) {
    356     fprintf(stderr, "[%d frames, %u bytes].\n",
    357             pic_num, (unsigned int)webp_data.size);
    358   }
    359   WebPDataClear(&webp_data);
    360   ExUtilDeleteCommandLineArguments(&cmd_args);
    361   FREE_WARGV_AND_RETURN(ok ? EXIT_SUCCESS : EXIT_FAILURE);
    362 }