Blame examples/img2webp.c

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