Blame examples/gif2webp.c

Packit 9c6abc
// Copyright 2012 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
//  simple tool to convert animated GIFs to WebP
Packit 9c6abc
//
Packit 9c6abc
// Authors: Skal (pascal.massimino@gmail.com)
Packit 9c6abc
//          Urvang (urvang@google.com)
Packit 9c6abc
Packit 9c6abc
#include <assert.h>
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
#ifdef WEBP_HAVE_GIF
Packit 9c6abc
Packit 9c6abc
#if defined(HAVE_UNISTD_H) && HAVE_UNISTD_H
Packit 9c6abc
#include <unistd.h>
Packit 9c6abc
#endif
Packit 9c6abc
Packit 9c6abc
#include <gif_lib.h>
Packit 9c6abc
#include "webp/encode.h"
Packit 9c6abc
#include "webp/mux.h"
Packit 9c6abc
#include "../examples/example_util.h"
Packit 9c6abc
#include "../imageio/imageio_util.h"
Packit 9c6abc
#include "./gifdec.h"
Packit 9c6abc
Packit 9c6abc
#if !defined(STDIN_FILENO)
Packit 9c6abc
#define STDIN_FILENO 0
Packit 9c6abc
#endif
Packit 9c6abc
Packit 9c6abc
//------------------------------------------------------------------------------
Packit 9c6abc
Packit 9c6abc
static int transparent_index = GIF_INDEX_INVALID;  // Opaque by default.
Packit 9c6abc
Packit 9c6abc
static const char* const kErrorMessages[-WEBP_MUX_NOT_ENOUGH_DATA + 1] = {
Packit 9c6abc
  "WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", "WEBP_MUX_BAD_DATA",
Packit 9c6abc
  "WEBP_MUX_MEMORY_ERROR", "WEBP_MUX_NOT_ENOUGH_DATA"
Packit 9c6abc
};
Packit 9c6abc
Packit 9c6abc
static const char* ErrorString(WebPMuxError err) {
Packit 9c6abc
  assert(err <= WEBP_MUX_NOT_FOUND && err >= WEBP_MUX_NOT_ENOUGH_DATA);
Packit 9c6abc
  return kErrorMessages[-err];
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
enum {
Packit 9c6abc
  METADATA_ICC  = (1 << 0),
Packit 9c6abc
  METADATA_XMP  = (1 << 1),
Packit 9c6abc
  METADATA_ALL  = METADATA_ICC | METADATA_XMP
Packit 9c6abc
};
Packit 9c6abc
Packit 9c6abc
//------------------------------------------------------------------------------
Packit 9c6abc
Packit 9c6abc
static void Help(void) {
Packit 9c6abc
  printf("Usage:\n");
Packit 9c6abc
  printf(" gif2webp [options] gif_file -o webp_file\n");
Packit 9c6abc
  printf("Options:\n");
Packit 9c6abc
  printf("  -h / -help ............. this help\n");
Packit 9c6abc
  printf("  -lossy ................. encode image using lossy compression\n");
Packit 9c6abc
  printf("  -mixed ................. for each frame in the image, pick lossy\n"
Packit 9c6abc
         "                           or lossless compression heuristically\n");
Packit 9c6abc
  printf("  -q <float> ............. quality factor (0:small..100:big)\n");
Packit 9c6abc
  printf("  -m <int> ............... compression method (0=fast, 6=slowest)\n");
Packit 9c6abc
  printf("  -min_size .............. minimize output size (default:off)\n"
Packit 9c6abc
         "                           lossless compression by default; can be\n"
Packit 9c6abc
         "                           combined with -q, -m, -lossy or -mixed\n"
Packit 9c6abc
         "                           options\n");
Packit 9c6abc
  printf("  -kmin <int> ............ min distance between key frames\n");
Packit 9c6abc
  printf("  -kmax <int> ............ max distance between key frames\n");
Packit 9c6abc
  printf("  -f <int> ............... filter strength (0=off..100)\n");
Packit 9c6abc
  printf("  -metadata <string> ..... comma separated list of metadata to\n");
Packit 9c6abc
  printf("                           ");
Packit 9c6abc
  printf("copy from the input to the output if present\n");
Packit 9c6abc
  printf("                           ");
Packit 9c6abc
  printf("Valid values: all, none, icc, xmp (default)\n");
Packit 9c6abc
  printf("  -loop_compatibility .... use compatibility mode for Chrome\n");
Packit 9c6abc
  printf("                           version prior to M62 (inclusive)\n");
Packit 9c6abc
  printf("  -mt .................... use multi-threading if available\n");
Packit 9c6abc
  printf("\n");
Packit 9c6abc
  printf("  -version ............... print version number and exit\n");
Packit 9c6abc
  printf("  -v ..................... verbose\n");
Packit 9c6abc
  printf("  -quiet ................. don't print anything\n");
Packit 9c6abc
  printf("\n");
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
//------------------------------------------------------------------------------
Packit 9c6abc
Packit 9c6abc
int main(int argc, const char *argv[]) {
Packit 9c6abc
  int verbose = 0;
Packit 9c6abc
  int gif_error = GIF_ERROR;
Packit 9c6abc
  WebPMuxError err = WEBP_MUX_OK;
Packit 9c6abc
  int ok = 0;
Packit 9c6abc
  const char *in_file = NULL, *out_file = NULL;
Packit 9c6abc
  FILE* out = NULL;
Packit 9c6abc
  GifFileType* gif = NULL;
Packit 9c6abc
  int frame_duration = 0;
Packit 9c6abc
  int frame_timestamp = 0;
Packit 9c6abc
  GIFDisposeMethod orig_dispose = GIF_DISPOSE_NONE;
Packit 9c6abc
Packit 9c6abc
  WebPPicture frame;                // Frame rectangle only (not disposed).
Packit 9c6abc
  WebPPicture curr_canvas;          // Not disposed.
Packit 9c6abc
  WebPPicture prev_canvas;          // Disposed.
Packit 9c6abc
Packit 9c6abc
  WebPAnimEncoder* enc = NULL;
Packit 9c6abc
  WebPAnimEncoderOptions enc_options;
Packit 9c6abc
  WebPConfig config;
Packit 9c6abc
Packit 9c6abc
  int frame_number = 0;     // Whether we are processing the first frame.
Packit 9c6abc
  int done;
Packit 9c6abc
  int c;
Packit 9c6abc
  int quiet = 0;
Packit 9c6abc
  WebPData webp_data;
Packit 9c6abc
Packit 9c6abc
  int keep_metadata = METADATA_XMP;  // ICC not output by default.
Packit 9c6abc
  WebPData icc_data;
Packit 9c6abc
  int stored_icc = 0;         // Whether we have already stored an ICC profile.
Packit 9c6abc
  WebPData xmp_data;
Packit 9c6abc
  int stored_xmp = 0;         // Whether we have already stored an XMP profile.
Packit 9c6abc
  int loop_count = 0;         // default: infinite
Packit 9c6abc
  int stored_loop_count = 0;  // Whether we have found an explicit loop count.
Packit 9c6abc
  int loop_compatibility = 0;
Packit 9c6abc
  WebPMux* mux = NULL;
Packit 9c6abc
Packit 9c6abc
  int default_kmin = 1;  // Whether to use default kmin value.
Packit 9c6abc
  int default_kmax = 1;
Packit 9c6abc
Packit 9c6abc
  if (!WebPConfigInit(&config) || !WebPAnimEncoderOptionsInit(&enc_options) ||
Packit 9c6abc
      !WebPPictureInit(&frame) || !WebPPictureInit(&curr_canvas) ||
Packit 9c6abc
      !WebPPictureInit(&prev_canvas)) {
Packit 9c6abc
    fprintf(stderr, "Error! Version mismatch!\n");
Packit 9c6abc
    return -1;
Packit 9c6abc
  }
Packit 9c6abc
  config.lossless = 1;  // Use lossless compression by default.
Packit 9c6abc
Packit 9c6abc
  WebPDataInit(&webp_data);
Packit 9c6abc
  WebPDataInit(&icc_data);
Packit 9c6abc
  WebPDataInit(&xmp_data);
Packit 9c6abc
Packit 9c6abc
  if (argc == 1) {
Packit 9c6abc
    Help();
Packit 9c6abc
    return 0;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  for (c = 1; c < argc; ++c) {
Packit 9c6abc
    int parse_error = 0;
Packit 9c6abc
    if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
Packit 9c6abc
      Help();
Packit 9c6abc
      return 0;
Packit 9c6abc
    } else if (!strcmp(argv[c], "-o") && c < argc - 1) {
Packit 9c6abc
      out_file = argv[++c];
Packit 9c6abc
    } else if (!strcmp(argv[c], "-lossy")) {
Packit 9c6abc
      config.lossless = 0;
Packit 9c6abc
    } else if (!strcmp(argv[c], "-mixed")) {
Packit 9c6abc
      enc_options.allow_mixed = 1;
Packit 9c6abc
      config.lossless = 0;
Packit 9c6abc
    } else if (!strcmp(argv[c], "-loop_compatibility")) {
Packit 9c6abc
      loop_compatibility = 1;
Packit 9c6abc
    } else if (!strcmp(argv[c], "-q") && c < argc - 1) {
Packit 9c6abc
      config.quality = ExUtilGetFloat(argv[++c], &parse_error);
Packit 9c6abc
    } else if (!strcmp(argv[c], "-m") && c < argc - 1) {
Packit 9c6abc
      config.method = ExUtilGetInt(argv[++c], 0, &parse_error);
Packit 9c6abc
    } else if (!strcmp(argv[c], "-min_size")) {
Packit 9c6abc
      enc_options.minimize_size = 1;
Packit 9c6abc
    } else if (!strcmp(argv[c], "-kmax") && c < argc - 1) {
Packit 9c6abc
      enc_options.kmax = ExUtilGetInt(argv[++c], 0, &parse_error);
Packit 9c6abc
      default_kmax = 0;
Packit 9c6abc
    } else if (!strcmp(argv[c], "-kmin") && c < argc - 1) {
Packit 9c6abc
      enc_options.kmin = ExUtilGetInt(argv[++c], 0, &parse_error);
Packit 9c6abc
      default_kmin = 0;
Packit 9c6abc
    } else if (!strcmp(argv[c], "-f") && c < argc - 1) {
Packit 9c6abc
      config.filter_strength = ExUtilGetInt(argv[++c], 0, &parse_error);
Packit 9c6abc
    } else if (!strcmp(argv[c], "-metadata") && c < argc - 1) {
Packit 9c6abc
      static const struct {
Packit 9c6abc
        const char* option;
Packit 9c6abc
        int flag;
Packit 9c6abc
      } kTokens[] = {
Packit 9c6abc
        { "all",  METADATA_ALL },
Packit 9c6abc
        { "none", 0 },
Packit 9c6abc
        { "icc",  METADATA_ICC },
Packit 9c6abc
        { "xmp",  METADATA_XMP },
Packit 9c6abc
      };
Packit 9c6abc
      const size_t kNumTokens = sizeof(kTokens) / sizeof(*kTokens);
Packit 9c6abc
      const char* start = argv[++c];
Packit 9c6abc
      const char* const end = start + strlen(start);
Packit 9c6abc
Packit 9c6abc
      keep_metadata = 0;
Packit 9c6abc
      while (start < end) {
Packit 9c6abc
        size_t i;
Packit 9c6abc
        const char* token = strchr(start, ',');
Packit 9c6abc
        if (token == NULL) token = end;
Packit 9c6abc
Packit 9c6abc
        for (i = 0; i < kNumTokens; ++i) {
Packit 9c6abc
          if ((size_t)(token - start) == strlen(kTokens[i].option) &&
Packit 9c6abc
              !strncmp(start, kTokens[i].option, strlen(kTokens[i].option))) {
Packit 9c6abc
            if (kTokens[i].flag != 0) {
Packit 9c6abc
              keep_metadata |= kTokens[i].flag;
Packit 9c6abc
            } else {
Packit 9c6abc
              keep_metadata = 0;
Packit 9c6abc
            }
Packit 9c6abc
            break;
Packit 9c6abc
          }
Packit 9c6abc
        }
Packit 9c6abc
        if (i == kNumTokens) {
Packit 9c6abc
          fprintf(stderr, "Error! Unknown metadata type '%.*s'\n",
Packit 9c6abc
                  (int)(token - start), start);
Packit 9c6abc
          Help();
Packit 9c6abc
          return -1;
Packit 9c6abc
        }
Packit 9c6abc
        start = token + 1;
Packit 9c6abc
      }
Packit 9c6abc
    } else if (!strcmp(argv[c], "-mt")) {
Packit 9c6abc
      ++config.thread_level;
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
      return 0;
Packit 9c6abc
    } else if (!strcmp(argv[c], "-quiet")) {
Packit 9c6abc
      quiet = 1;
Packit 9c6abc
      enc_options.verbose = 0;
Packit 9c6abc
    } else if (!strcmp(argv[c], "-v")) {
Packit 9c6abc
      verbose = 1;
Packit 9c6abc
      enc_options.verbose = 1;
Packit 9c6abc
    } else if (!strcmp(argv[c], "--")) {
Packit 9c6abc
      if (c < argc - 1) in_file = argv[++c];
Packit 9c6abc
      break;
Packit 9c6abc
    } else if (argv[c][0] == '-') {
Packit 9c6abc
      fprintf(stderr, "Error! Unknown option '%s'\n", argv[c]);
Packit 9c6abc
      Help();
Packit 9c6abc
      return -1;
Packit 9c6abc
    } else {
Packit 9c6abc
      in_file = argv[c];
Packit 9c6abc
    }
Packit 9c6abc
Packit 9c6abc
    if (parse_error) {
Packit 9c6abc
      Help();
Packit 9c6abc
      return -1;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  // Appropriate default kmin, kmax values for lossy and lossless.
Packit 9c6abc
  if (default_kmin) {
Packit 9c6abc
    enc_options.kmin = config.lossless ? 9 : 3;
Packit 9c6abc
  }
Packit 9c6abc
  if (default_kmax) {
Packit 9c6abc
    enc_options.kmax = config.lossless ? 17 : 5;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  if (!WebPValidateConfig(&config)) {
Packit 9c6abc
    fprintf(stderr, "Error! Invalid configuration.\n");
Packit 9c6abc
    goto End;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  if (in_file == NULL) {
Packit 9c6abc
    fprintf(stderr, "No input file specified!\n");
Packit 9c6abc
    Help();
Packit 9c6abc
    goto End;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  // Start the decoder object
Packit 9c6abc
#if LOCAL_GIF_PREREQ(5,0)
Packit 9c6abc
  gif = !strcmp(in_file, "-") ? DGifOpenFileHandle(STDIN_FILENO, &gif_error)
Packit 9c6abc
                              : DGifOpenFileName(in_file, &gif_error);
Packit 9c6abc
#else
Packit 9c6abc
  gif = !strcmp(in_file, "-") ? DGifOpenFileHandle(STDIN_FILENO)
Packit 9c6abc
                              : DGifOpenFileName(in_file);
Packit 9c6abc
#endif
Packit 9c6abc
  if (gif == NULL) goto End;
Packit 9c6abc
Packit 9c6abc
  // Loop over GIF images
Packit 9c6abc
  done = 0;
Packit 9c6abc
  do {
Packit 9c6abc
    GifRecordType type;
Packit 9c6abc
    if (DGifGetRecordType(gif, &type) == GIF_ERROR) goto End;
Packit 9c6abc
Packit 9c6abc
    switch (type) {
Packit 9c6abc
      case IMAGE_DESC_RECORD_TYPE: {
Packit 9c6abc
        GIFFrameRect gif_rect;
Packit 9c6abc
        GifImageDesc* const image_desc = &gif->Image;
Packit 9c6abc
Packit 9c6abc
        if (!DGifGetImageDesc(gif)) goto End;
Packit 9c6abc
Packit 9c6abc
        if (frame_number == 0) {
Packit 9c6abc
          if (verbose) {
Packit 9c6abc
            printf("Canvas screen: %d x %d\n", gif->SWidth, gif->SHeight);
Packit 9c6abc
          }
Packit 9c6abc
          // Fix some broken GIF global headers that report
Packit 9c6abc
          // 0 x 0 screen dimension.
Packit 9c6abc
          if (gif->SWidth == 0 || gif->SHeight == 0) {
Packit 9c6abc
            image_desc->Left = 0;
Packit 9c6abc
            image_desc->Top = 0;
Packit 9c6abc
            gif->SWidth = image_desc->Width;
Packit 9c6abc
            gif->SHeight = image_desc->Height;
Packit 9c6abc
            if (gif->SWidth <= 0 || gif->SHeight <= 0) {
Packit 9c6abc
              goto End;
Packit 9c6abc
            }
Packit 9c6abc
            if (verbose) {
Packit 9c6abc
              printf("Fixed canvas screen dimension to: %d x %d\n",
Packit 9c6abc
                     gif->SWidth, gif->SHeight);
Packit 9c6abc
            }
Packit 9c6abc
          }
Packit 9c6abc
          // Allocate current buffer.
Packit 9c6abc
          frame.width = gif->SWidth;
Packit 9c6abc
          frame.height = gif->SHeight;
Packit 9c6abc
          frame.use_argb = 1;
Packit 9c6abc
          if (!WebPPictureAlloc(&frame)) goto End;
Packit 9c6abc
          GIFClearPic(&frame, NULL);
Packit 9c6abc
          WebPPictureCopy(&frame, &curr_canvas);
Packit 9c6abc
          WebPPictureCopy(&frame, &prev_canvas);
Packit 9c6abc
Packit 9c6abc
          // Background color.
Packit 9c6abc
          GIFGetBackgroundColor(gif->SColorMap, gif->SBackGroundColor,
Packit 9c6abc
                                transparent_index,
Packit 9c6abc
                                &enc_options.anim_params.bgcolor);
Packit 9c6abc
Packit 9c6abc
          // Initialize encoder.
Packit 9c6abc
          enc = WebPAnimEncoderNew(curr_canvas.width, curr_canvas.height,
Packit 9c6abc
                                   &enc_options);
Packit 9c6abc
          if (enc == NULL) {
Packit 9c6abc
            fprintf(stderr,
Packit 9c6abc
                    "Error! Could not create encoder object. Possibly due to "
Packit 9c6abc
                    "a memory error.\n");
Packit 9c6abc
            goto End;
Packit 9c6abc
          }
Packit 9c6abc
        }
Packit 9c6abc
Packit 9c6abc
        // Some even more broken GIF can have sub-rect with zero width/height.
Packit 9c6abc
        if (image_desc->Width == 0 || image_desc->Height == 0) {
Packit 9c6abc
          image_desc->Width = gif->SWidth;
Packit 9c6abc
          image_desc->Height = gif->SHeight;
Packit 9c6abc
        }
Packit 9c6abc
Packit 9c6abc
        if (!GIFReadFrame(gif, transparent_index, &gif_rect, &frame)) {
Packit 9c6abc
          goto End;
Packit 9c6abc
        }
Packit 9c6abc
        // Blend frame rectangle with previous canvas to compose full canvas.
Packit 9c6abc
        // Note that 'curr_canvas' is same as 'prev_canvas' at this point.
Packit 9c6abc
        GIFBlendFrames(&frame, &gif_rect, &curr_canvas);
Packit 9c6abc
Packit 9c6abc
        if (!WebPAnimEncoderAdd(enc, &curr_canvas, frame_timestamp, &config)) {
Packit 9c6abc
          fprintf(stderr, "Error while adding frame #%d: %s\n", frame_number,
Packit 9c6abc
                  WebPAnimEncoderGetError(enc));
Packit 9c6abc
          goto End;
Packit 9c6abc
        } else {
Packit 9c6abc
          ++frame_number;
Packit 9c6abc
        }
Packit 9c6abc
Packit 9c6abc
        // Update canvases.
Packit 9c6abc
        GIFDisposeFrame(orig_dispose, &gif_rect, &prev_canvas, &curr_canvas);
Packit 9c6abc
        GIFCopyPixels(&curr_canvas, &prev_canvas);
Packit 9c6abc
Packit 9c6abc
        // Force frames with a small or no duration to 100ms to be consistent
Packit 9c6abc
        // with web browsers and other transcoding tools. This also avoids
Packit 9c6abc
        // incorrect durations between frames when padding frames are
Packit 9c6abc
        // discarded.
Packit 9c6abc
        if (frame_duration <= 10) {
Packit 9c6abc
          frame_duration = 100;
Packit 9c6abc
        }
Packit 9c6abc
Packit 9c6abc
        // Update timestamp (for next frame).
Packit 9c6abc
        frame_timestamp += frame_duration;
Packit 9c6abc
Packit 9c6abc
        // In GIF, graphic control extensions are optional for a frame, so we
Packit 9c6abc
        // may not get one before reading the next frame. To handle this case,
Packit 9c6abc
        // we reset frame properties to reasonable defaults for the next frame.
Packit 9c6abc
        orig_dispose = GIF_DISPOSE_NONE;
Packit 9c6abc
        frame_duration = 0;
Packit 9c6abc
        transparent_index = GIF_INDEX_INVALID;
Packit 9c6abc
        break;
Packit 9c6abc
      }
Packit 9c6abc
      case EXTENSION_RECORD_TYPE: {
Packit 9c6abc
        int extension;
Packit 9c6abc
        GifByteType *data = NULL;
Packit 9c6abc
        if (DGifGetExtension(gif, &extension, &data) == GIF_ERROR) {
Packit 9c6abc
          goto End;
Packit 9c6abc
        }
Packit 9c6abc
        if (data == NULL) continue;
Packit 9c6abc
Packit 9c6abc
        switch (extension) {
Packit 9c6abc
          case COMMENT_EXT_FUNC_CODE: {
Packit 9c6abc
            break;  // Do nothing for now.
Packit 9c6abc
          }
Packit 9c6abc
          case GRAPHICS_EXT_FUNC_CODE: {
Packit 9c6abc
            if (!GIFReadGraphicsExtension(data, &frame_duration, &orig_dispose,
Packit 9c6abc
                                          &transparent_index)) {
Packit 9c6abc
              goto End;
Packit 9c6abc
            }
Packit 9c6abc
            break;
Packit 9c6abc
          }
Packit 9c6abc
          case PLAINTEXT_EXT_FUNC_CODE: {
Packit 9c6abc
            break;
Packit 9c6abc
          }
Packit 9c6abc
          case APPLICATION_EXT_FUNC_CODE: {
Packit 9c6abc
            if (data[0] != 11) break;    // Chunk is too short
Packit 9c6abc
            if (!memcmp(data + 1, "NETSCAPE2.0", 11) ||
Packit 9c6abc
                !memcmp(data + 1, "ANIMEXTS1.0", 11)) {
Packit 9c6abc
              if (!GIFReadLoopCount(gif, &data, &loop_count)) {
Packit 9c6abc
                goto End;
Packit 9c6abc
              }
Packit 9c6abc
              if (verbose) {
Packit 9c6abc
                fprintf(stderr, "Loop count: %d\n", loop_count);
Packit 9c6abc
              }
Packit 9c6abc
              stored_loop_count = loop_compatibility ? (loop_count != 0) : 1;
Packit 9c6abc
            } else {  // An extension containing metadata.
Packit 9c6abc
              // We only store the first encountered chunk of each type, and
Packit 9c6abc
              // only if requested by the user.
Packit 9c6abc
              const int is_xmp = (keep_metadata & METADATA_XMP) &&
Packit 9c6abc
                                 !stored_xmp &&
Packit 9c6abc
                                 !memcmp(data + 1, "XMP DataXMP", 11);
Packit 9c6abc
              const int is_icc = (keep_metadata & METADATA_ICC) &&
Packit 9c6abc
                                 !stored_icc &&
Packit 9c6abc
                                 !memcmp(data + 1, "ICCRGBG1012", 11);
Packit 9c6abc
              if (is_xmp || is_icc) {
Packit 9c6abc
                if (!GIFReadMetadata(gif, &data,
Packit 9c6abc
                                     is_xmp ? &xmp_data : &icc_data)) {
Packit 9c6abc
                  goto End;
Packit 9c6abc
                }
Packit 9c6abc
                if (is_icc) {
Packit 9c6abc
                  stored_icc = 1;
Packit 9c6abc
                } else if (is_xmp) {
Packit 9c6abc
                  stored_xmp = 1;
Packit 9c6abc
                }
Packit 9c6abc
              }
Packit 9c6abc
            }
Packit 9c6abc
            break;
Packit 9c6abc
          }
Packit 9c6abc
          default: {
Packit 9c6abc
            break;  // skip
Packit 9c6abc
          }
Packit 9c6abc
        }
Packit 9c6abc
        while (data != NULL) {
Packit 9c6abc
          if (DGifGetExtensionNext(gif, &data) == GIF_ERROR) goto End;
Packit 9c6abc
        }
Packit 9c6abc
        break;
Packit 9c6abc
      }
Packit 9c6abc
      case TERMINATE_RECORD_TYPE: {
Packit 9c6abc
        done = 1;
Packit 9c6abc
        break;
Packit 9c6abc
      }
Packit 9c6abc
      default: {
Packit 9c6abc
        if (verbose) {
Packit 9c6abc
          fprintf(stderr, "Skipping over unknown record type %d\n", type);
Packit 9c6abc
        }
Packit 9c6abc
        break;
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
  } while (!done);
Packit 9c6abc
Packit 9c6abc
  // Last NULL frame.
Packit 9c6abc
  if (!WebPAnimEncoderAdd(enc, NULL, frame_timestamp, NULL)) {
Packit 9c6abc
    fprintf(stderr, "Error flushing WebP muxer.\n");
Packit 9c6abc
    fprintf(stderr, "%s\n", WebPAnimEncoderGetError(enc));
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  if (!WebPAnimEncoderAssemble(enc, &webp_data)) {
Packit 9c6abc
    fprintf(stderr, "%s\n", WebPAnimEncoderGetError(enc));
Packit 9c6abc
    goto End;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  if (!loop_compatibility) {
Packit 9c6abc
    if (!stored_loop_count) {
Packit 9c6abc
      // if no loop-count element is seen, the default is '1' (loop-once)
Packit 9c6abc
      // and we need to signal it explicitly in WebP. Note however that
Packit 9c6abc
      // in case there's a single frame, we still don't need to store it.
Packit 9c6abc
      if (frame_number > 1) {
Packit 9c6abc
        stored_loop_count = 1;
Packit 9c6abc
        loop_count = 1;
Packit 9c6abc
      }
Packit 9c6abc
    } else if (loop_count > 0) {
Packit 9c6abc
      // adapt GIF's semantic to WebP's (except in the infinite-loop case)
Packit 9c6abc
      loop_count += 1;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  // loop_count of 0 is the default (infinite), so no need to signal it
Packit 9c6abc
  if (loop_count == 0) stored_loop_count = 0;
Packit 9c6abc
Packit 9c6abc
  if (stored_loop_count || stored_icc || stored_xmp) {
Packit 9c6abc
    // Re-mux to add loop count and/or metadata as needed.
Packit 9c6abc
    mux = WebPMuxCreate(&webp_data, 1);
Packit 9c6abc
    if (mux == NULL) {
Packit 9c6abc
      fprintf(stderr, "ERROR: Could not re-mux to add loop count/metadata.\n");
Packit 9c6abc
      goto End;
Packit 9c6abc
    }
Packit 9c6abc
    WebPDataClear(&webp_data);
Packit 9c6abc
Packit 9c6abc
    if (stored_loop_count) {  // Update loop count.
Packit 9c6abc
      WebPMuxAnimParams new_params;
Packit 9c6abc
      err = WebPMuxGetAnimationParams(mux, &new_params);
Packit 9c6abc
      if (err != WEBP_MUX_OK) {
Packit 9c6abc
        fprintf(stderr, "ERROR (%s): Could not fetch loop count.\n",
Packit 9c6abc
                ErrorString(err));
Packit 9c6abc
        goto End;
Packit 9c6abc
      }
Packit 9c6abc
      new_params.loop_count = loop_count;
Packit 9c6abc
      err = WebPMuxSetAnimationParams(mux, &new_params);
Packit 9c6abc
      if (err != WEBP_MUX_OK) {
Packit 9c6abc
        fprintf(stderr, "ERROR (%s): Could not update loop count.\n",
Packit 9c6abc
                ErrorString(err));
Packit 9c6abc
        goto End;
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
Packit 9c6abc
    if (stored_icc) {   // Add ICCP chunk.
Packit 9c6abc
      err = WebPMuxSetChunk(mux, "ICCP", &icc_data, 1);
Packit 9c6abc
      if (verbose) {
Packit 9c6abc
        fprintf(stderr, "ICC size: %d\n", (int)icc_data.size);
Packit 9c6abc
      }
Packit 9c6abc
      if (err != WEBP_MUX_OK) {
Packit 9c6abc
        fprintf(stderr, "ERROR (%s): Could not set ICC chunk.\n",
Packit 9c6abc
                ErrorString(err));
Packit 9c6abc
        goto End;
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
Packit 9c6abc
    if (stored_xmp) {   // Add XMP chunk.
Packit 9c6abc
      err = WebPMuxSetChunk(mux, "XMP ", &xmp_data, 1);
Packit 9c6abc
      if (verbose) {
Packit 9c6abc
        fprintf(stderr, "XMP size: %d\n", (int)xmp_data.size);
Packit 9c6abc
      }
Packit 9c6abc
      if (err != WEBP_MUX_OK) {
Packit 9c6abc
        fprintf(stderr, "ERROR (%s): Could not set XMP chunk.\n",
Packit 9c6abc
                ErrorString(err));
Packit 9c6abc
        goto End;
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
Packit 9c6abc
    err = WebPMuxAssemble(mux, &webp_data);
Packit 9c6abc
    if (err != WEBP_MUX_OK) {
Packit 9c6abc
      fprintf(stderr, "ERROR (%s): Could not assemble when re-muxing to add "
Packit 9c6abc
              "loop count/metadata.\n", ErrorString(err));
Packit 9c6abc
      goto End;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  if (out_file != NULL) {
Packit 9c6abc
    if (!ImgIoUtilWriteFile(out_file, webp_data.bytes, webp_data.size)) {
Packit 9c6abc
      fprintf(stderr, "Error writing output file: %s\n", out_file);
Packit 9c6abc
      goto End;
Packit 9c6abc
    }
Packit 9c6abc
    if (!quiet) {
Packit 9c6abc
      if (!strcmp(out_file, "-")) {
Packit 9c6abc
        fprintf(stderr, "Saved %d bytes to STDIO\n",
Packit 9c6abc
                (int)webp_data.size);
Packit 9c6abc
      } else {
Packit 9c6abc
        fprintf(stderr, "Saved output file (%d bytes): %s\n",
Packit 9c6abc
                (int)webp_data.size, out_file);
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
  } else {
Packit 9c6abc
    if (!quiet) {
Packit 9c6abc
      fprintf(stderr, "Nothing written; use -o flag to save the result "
Packit 9c6abc
                      "(%d bytes).\n", (int)webp_data.size);
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  // All OK.
Packit 9c6abc
  ok = 1;
Packit 9c6abc
  gif_error = GIF_OK;
Packit 9c6abc
Packit 9c6abc
 End:
Packit 9c6abc
  WebPDataClear(&icc_data);
Packit 9c6abc
  WebPDataClear(&xmp_data);
Packit 9c6abc
  WebPMuxDelete(mux);
Packit 9c6abc
  WebPDataClear(&webp_data);
Packit 9c6abc
  WebPPictureFree(&frame);
Packit 9c6abc
  WebPPictureFree(&curr_canvas);
Packit 9c6abc
  WebPPictureFree(&prev_canvas);
Packit 9c6abc
  WebPAnimEncoderDelete(enc);
Packit 9c6abc
  if (out != NULL && out_file != NULL) fclose(out);
Packit 9c6abc
Packit 9c6abc
  if (gif_error != GIF_OK) {
Packit 9c6abc
    GIFDisplayError(gif, gif_error);
Packit 9c6abc
  }
Packit 9c6abc
  if (gif != NULL) {
Packit 9c6abc
#if LOCAL_GIF_PREREQ(5,1)
Packit 9c6abc
    DGifCloseFile(gif, &gif_error);
Packit 9c6abc
#else
Packit 9c6abc
    DGifCloseFile(gif);
Packit 9c6abc
#endif
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  return !ok;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
#else  // !WEBP_HAVE_GIF
Packit 9c6abc
Packit 9c6abc
int main(int argc, const char *argv[]) {
Packit 9c6abc
  fprintf(stderr, "GIF support not enabled in %s.\n", argv[0]);
Packit 9c6abc
  (void)argc;
Packit 9c6abc
  return 0;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
#endif
Packit 9c6abc
Packit 9c6abc
//------------------------------------------------------------------------------