Blame examples/webpmux.c

Packit 9c6abc
// Copyright 2011 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 command-line to create a WebP container file and to extract or strip
Packit 9c6abc
//  relevant data from the container file.
Packit 9c6abc
//
Packit 9c6abc
// Authors: Vikas (vikaas.arora@gmail.com),
Packit 9c6abc
//          Urvang (urvang@google.com)
Packit 9c6abc
Packit 9c6abc
/*  Usage examples:
Packit 9c6abc
Packit 9c6abc
  Create container WebP file:
Packit 9c6abc
    webpmux -frame anim_1.webp +100+10+10   \
Packit 9c6abc
            -frame anim_2.webp +100+25+25+1 \
Packit 9c6abc
            -frame anim_3.webp +100+50+50+1 \
Packit 9c6abc
            -frame anim_4.webp +100         \
Packit 9c6abc
            -loop 10 -bgcolor 128,255,255,255 \
Packit 9c6abc
            -o out_animation_container.webp
Packit 9c6abc
Packit 9c6abc
    webpmux -set icc image_profile.icc in.webp -o out_icc_container.webp
Packit 9c6abc
    webpmux -set exif image_metadata.exif in.webp -o out_exif_container.webp
Packit 9c6abc
    webpmux -set xmp image_metadata.xmp in.webp -o out_xmp_container.webp
Packit 9c6abc
Packit 9c6abc
  Extract relevant data from WebP container file:
Packit 9c6abc
    webpmux -get frame n in.webp -o out_frame.webp
Packit 9c6abc
    webpmux -get icc in.webp -o image_profile.icc
Packit 9c6abc
    webpmux -get exif in.webp -o image_metadata.exif
Packit 9c6abc
    webpmux -get xmp in.webp -o image_metadata.xmp
Packit 9c6abc
Packit 9c6abc
  Strip data from WebP Container file:
Packit 9c6abc
    webpmux -strip icc in.webp -o out.webp
Packit 9c6abc
    webpmux -strip exif in.webp -o out.webp
Packit 9c6abc
    webpmux -strip xmp in.webp -o out.webp
Packit 9c6abc
Packit 9c6abc
  Change duration of frame intervals:
Packit 9c6abc
    webpmux -duration 150 in.webp -o out.webp
Packit 9c6abc
    webpmux -duration 33,2 in.webp -o out.webp
Packit 9c6abc
    webpmux -duration 200,10,0 -duration 150,6,50 in.webp -o out.webp
Packit 9c6abc
Packit 9c6abc
  Misc:
Packit 9c6abc
    webpmux -info in.webp
Packit 9c6abc
    webpmux [ -h | -help ]
Packit 9c6abc
    webpmux -version
Packit 9c6abc
    webpmux argument_file_name
Packit 9c6abc
*/
Packit 9c6abc
Packit 9c6abc
#ifdef HAVE_CONFIG_H
Packit 9c6abc
#include "webp/config.h"
Packit 9c6abc
#endif
Packit 9c6abc
Packit 9c6abc
#include <assert.h>
Packit 9c6abc
#include <stdio.h>
Packit 9c6abc
#include <stdlib.h>
Packit 9c6abc
#include <string.h>
Packit 9c6abc
#include "webp/decode.h"
Packit 9c6abc
#include "webp/mux.h"
Packit 9c6abc
#include "../examples/example_util.h"
Packit 9c6abc
#include "../imageio/imageio_util.h"
Packit 9c6abc
Packit 9c6abc
//------------------------------------------------------------------------------
Packit 9c6abc
// Config object to parse command-line arguments.
Packit 9c6abc
Packit 9c6abc
typedef enum {
Packit 9c6abc
  NIL_ACTION = 0,
Packit 9c6abc
  ACTION_GET,
Packit 9c6abc
  ACTION_SET,
Packit 9c6abc
  ACTION_STRIP,
Packit 9c6abc
  ACTION_INFO,
Packit 9c6abc
  ACTION_HELP,
Packit 9c6abc
  ACTION_DURATION
Packit 9c6abc
} ActionType;
Packit 9c6abc
Packit 9c6abc
typedef enum {
Packit 9c6abc
  NIL_SUBTYPE = 0,
Packit 9c6abc
  SUBTYPE_ANMF,
Packit 9c6abc
  SUBTYPE_LOOP,
Packit 9c6abc
  SUBTYPE_BGCOLOR
Packit 9c6abc
} FeatureSubType;
Packit 9c6abc
Packit 9c6abc
typedef struct {
Packit 9c6abc
  FeatureSubType subtype_;
Packit 9c6abc
  const char* filename_;
Packit 9c6abc
  const char* params_;
Packit 9c6abc
} FeatureArg;
Packit 9c6abc
Packit 9c6abc
typedef enum {
Packit 9c6abc
  NIL_FEATURE = 0,
Packit 9c6abc
  FEATURE_EXIF,
Packit 9c6abc
  FEATURE_XMP,
Packit 9c6abc
  FEATURE_ICCP,
Packit 9c6abc
  FEATURE_ANMF,
Packit 9c6abc
  FEATURE_DURATION,
Packit 9c6abc
  LAST_FEATURE
Packit 9c6abc
} FeatureType;
Packit 9c6abc
Packit 9c6abc
static const char* const kFourccList[LAST_FEATURE] = {
Packit 9c6abc
  NULL, "EXIF", "XMP ", "ICCP", "ANMF"
Packit 9c6abc
};
Packit 9c6abc
Packit 9c6abc
static const char* const kDescriptions[LAST_FEATURE] = {
Packit 9c6abc
  NULL, "EXIF metadata", "XMP metadata", "ICC profile",
Packit 9c6abc
  "Animation frame"
Packit 9c6abc
};
Packit 9c6abc
Packit 9c6abc
typedef struct {
Packit 9c6abc
  CommandLineArguments cmd_args_;
Packit 9c6abc
Packit 9c6abc
  ActionType action_type_;
Packit 9c6abc
  const char* input_;
Packit 9c6abc
  const char* output_;
Packit 9c6abc
  FeatureType type_;
Packit 9c6abc
  FeatureArg* args_;
Packit 9c6abc
  int arg_count_;
Packit 9c6abc
} Config;
Packit 9c6abc
Packit 9c6abc
//------------------------------------------------------------------------------
Packit 9c6abc
// Helper functions.
Packit 9c6abc
Packit 9c6abc
static int CountOccurrences(const CommandLineArguments* const args,
Packit 9c6abc
                            const char* const arg) {
Packit 9c6abc
  int i;
Packit 9c6abc
  int num_occurences = 0;
Packit 9c6abc
Packit 9c6abc
  for (i = 0; i < args->argc_; ++i) {
Packit 9c6abc
    if (!strcmp(args->argv_[i], arg)) {
Packit 9c6abc
      ++num_occurences;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  return num_occurences;
Packit 9c6abc
}
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
#define RETURN_IF_ERROR(ERR_MSG)                                     \
Packit 9c6abc
  if (err != WEBP_MUX_OK) {                                          \
Packit 9c6abc
    fprintf(stderr, ERR_MSG);                                        \
Packit 9c6abc
    return err;                                                      \
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
#define RETURN_IF_ERROR3(ERR_MSG, FORMAT_STR1, FORMAT_STR2)          \
Packit 9c6abc
  if (err != WEBP_MUX_OK) {                                          \
Packit 9c6abc
    fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2);              \
Packit 9c6abc
    return err;                                                      \
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
#define ERROR_GOTO1(ERR_MSG, LABEL)                                  \
Packit 9c6abc
  do {                                                               \
Packit 9c6abc
    fprintf(stderr, ERR_MSG);                                        \
Packit 9c6abc
    ok = 0;                                                          \
Packit 9c6abc
    goto LABEL;                                                      \
Packit 9c6abc
  } while (0)
Packit 9c6abc
Packit 9c6abc
#define ERROR_GOTO2(ERR_MSG, FORMAT_STR, LABEL)                      \
Packit 9c6abc
  do {                                                               \
Packit 9c6abc
    fprintf(stderr, ERR_MSG, FORMAT_STR);                            \
Packit 9c6abc
    ok = 0;                                                          \
Packit 9c6abc
    goto LABEL;                                                      \
Packit 9c6abc
  } while (0)
Packit 9c6abc
Packit 9c6abc
#define ERROR_GOTO3(ERR_MSG, FORMAT_STR1, FORMAT_STR2, LABEL)        \
Packit 9c6abc
  do {                                                               \
Packit 9c6abc
    fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2);              \
Packit 9c6abc
    ok = 0;                                                          \
Packit 9c6abc
    goto LABEL;                                                      \
Packit 9c6abc
  } while (0)
Packit 9c6abc
Packit 9c6abc
static WebPMuxError DisplayInfo(const WebPMux* mux) {
Packit 9c6abc
  int width, height;
Packit 9c6abc
  uint32_t flag;
Packit 9c6abc
Packit 9c6abc
  WebPMuxError err = WebPMuxGetCanvasSize(mux, &width, &height);
Packit 9c6abc
  assert(err == WEBP_MUX_OK);  // As WebPMuxCreate() was successful earlier.
Packit 9c6abc
  printf("Canvas size: %d x %d\n", width, height);
Packit 9c6abc
Packit 9c6abc
  err = WebPMuxGetFeatures(mux, &flag;;
Packit 9c6abc
  RETURN_IF_ERROR("Failed to retrieve features\n");
Packit 9c6abc
Packit 9c6abc
  if (flag == 0) {
Packit 9c6abc
    printf("No features present.\n");
Packit 9c6abc
    return err;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  // Print the features present.
Packit 9c6abc
  printf("Features present:");
Packit 9c6abc
  if (flag & ANIMATION_FLAG) printf(" animation");
Packit 9c6abc
  if (flag & ICCP_FLAG)      printf(" ICC profile");
Packit 9c6abc
  if (flag & EXIF_FLAG)      printf(" EXIF metadata");
Packit 9c6abc
  if (flag & XMP_FLAG)       printf(" XMP metadata");
Packit 9c6abc
  if (flag & ALPHA_FLAG)     printf(" transparency");
Packit 9c6abc
  printf("\n");
Packit 9c6abc
Packit 9c6abc
  if (flag & ANIMATION_FLAG) {
Packit 9c6abc
    const WebPChunkId id = WEBP_CHUNK_ANMF;
Packit 9c6abc
    const char* const type_str = "frame";
Packit 9c6abc
    int nFrames;
Packit 9c6abc
Packit 9c6abc
    WebPMuxAnimParams params;
Packit 9c6abc
    err = WebPMuxGetAnimationParams(mux, &params);
Packit 9c6abc
    assert(err == WEBP_MUX_OK);
Packit 9c6abc
    printf("Background color : 0x%.8X  Loop Count : %d\n",
Packit 9c6abc
           params.bgcolor, params.loop_count);
Packit 9c6abc
Packit 9c6abc
    err = WebPMuxNumChunks(mux, id, &nFrames);
Packit 9c6abc
    assert(err == WEBP_MUX_OK);
Packit 9c6abc
Packit 9c6abc
    printf("Number of %ss: %d\n", type_str, nFrames);
Packit 9c6abc
    if (nFrames > 0) {
Packit 9c6abc
      int i;
Packit 9c6abc
      printf("No.: width height alpha x_offset y_offset ");
Packit 9c6abc
      printf("duration   dispose blend ");
Packit 9c6abc
      printf("image_size  compression\n");
Packit 9c6abc
      for (i = 1; i <= nFrames; i++) {
Packit 9c6abc
        WebPMuxFrameInfo frame;
Packit 9c6abc
        err = WebPMuxGetFrame(mux, i, &frame);
Packit 9c6abc
        if (err == WEBP_MUX_OK) {
Packit 9c6abc
          WebPBitstreamFeatures features;
Packit 9c6abc
          const VP8StatusCode status = WebPGetFeatures(
Packit 9c6abc
              frame.bitstream.bytes, frame.bitstream.size, &features);
Packit 9c6abc
          assert(status == VP8_STATUS_OK);  // Checked by WebPMuxCreate().
Packit 9c6abc
          (void)status;
Packit 9c6abc
          printf("%3d: %5d %5d %5s %8d %8d ", i, features.width,
Packit 9c6abc
                 features.height, features.has_alpha ? "yes" : "no",
Packit 9c6abc
                 frame.x_offset, frame.y_offset);
Packit 9c6abc
          {
Packit 9c6abc
            const char* const dispose =
Packit 9c6abc
                (frame.dispose_method == WEBP_MUX_DISPOSE_NONE) ? "none"
Packit 9c6abc
                                                                : "background";
Packit 9c6abc
            const char* const blend =
Packit 9c6abc
                (frame.blend_method == WEBP_MUX_BLEND) ? "yes" : "no";
Packit 9c6abc
            printf("%8d %10s %5s ", frame.duration, dispose, blend);
Packit 9c6abc
          }
Packit 9c6abc
          printf("%10d %11s\n", (int)frame.bitstream.size,
Packit 9c6abc
                 (features.format == 1) ? "lossy" :
Packit 9c6abc
                 (features.format == 2) ? "lossless" :
Packit 9c6abc
                                          "undefined");
Packit 9c6abc
        }
Packit 9c6abc
        WebPDataClear(&frame.bitstream);
Packit 9c6abc
        RETURN_IF_ERROR3("Failed to retrieve %s#%d\n", type_str, i);
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  if (flag & ICCP_FLAG) {
Packit 9c6abc
    WebPData icc_profile;
Packit 9c6abc
    err = WebPMuxGetChunk(mux, "ICCP", &icc_profile);
Packit 9c6abc
    assert(err == WEBP_MUX_OK);
Packit 9c6abc
    printf("Size of the ICC profile data: %d\n", (int)icc_profile.size);
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  if (flag & EXIF_FLAG) {
Packit 9c6abc
    WebPData exif;
Packit 9c6abc
    err = WebPMuxGetChunk(mux, "EXIF", &exif;;
Packit 9c6abc
    assert(err == WEBP_MUX_OK);
Packit 9c6abc
    printf("Size of the EXIF metadata: %d\n", (int)exif.size);
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  if (flag & XMP_FLAG) {
Packit 9c6abc
    WebPData xmp;
Packit 9c6abc
    err = WebPMuxGetChunk(mux, "XMP ", &xmp;;
Packit 9c6abc
    assert(err == WEBP_MUX_OK);
Packit 9c6abc
    printf("Size of the XMP metadata: %d\n", (int)xmp.size);
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  if ((flag & ALPHA_FLAG) && !(flag & ANIMATION_FLAG)) {
Packit 9c6abc
    WebPMuxFrameInfo image;
Packit 9c6abc
    err = WebPMuxGetFrame(mux, 1, &image);
Packit 9c6abc
    if (err == WEBP_MUX_OK) {
Packit 9c6abc
      printf("Size of the image (with alpha): %d\n", (int)image.bitstream.size);
Packit 9c6abc
    }
Packit 9c6abc
    WebPDataClear(&image.bitstream);
Packit 9c6abc
    RETURN_IF_ERROR("Failed to retrieve the image\n");
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  return WEBP_MUX_OK;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static void PrintHelp(void) {
Packit 9c6abc
  printf("Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT\n");
Packit 9c6abc
  printf("       webpmux -set SET_OPTIONS INPUT -o OUTPUT\n");
Packit 9c6abc
  printf("       webpmux -duration DURATION_OPTIONS [-duration ...]\n");
Packit 9c6abc
  printf("               INPUT -o OUTPUT\n");
Packit 9c6abc
  printf("       webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT\n");
Packit 9c6abc
  printf("       webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]"
Packit 9c6abc
         "\n");
Packit 9c6abc
  printf("               [-bgcolor BACKGROUND_COLOR] -o OUTPUT\n");
Packit 9c6abc
  printf("       webpmux -info INPUT\n");
Packit 9c6abc
  printf("       webpmux [-h|-help]\n");
Packit 9c6abc
  printf("       webpmux -version\n");
Packit 9c6abc
  printf("       webpmux argument_file_name\n");
Packit 9c6abc
Packit 9c6abc
  printf("\n");
Packit 9c6abc
  printf("GET_OPTIONS:\n");
Packit 9c6abc
  printf(" Extract relevant data:\n");
Packit 9c6abc
  printf("   icc       get ICC profile\n");
Packit 9c6abc
  printf("   exif      get EXIF metadata\n");
Packit 9c6abc
  printf("   xmp       get XMP metadata\n");
Packit 9c6abc
  printf("   frame n   get nth frame\n");
Packit 9c6abc
Packit 9c6abc
  printf("\n");
Packit 9c6abc
  printf("SET_OPTIONS:\n");
Packit 9c6abc
  printf(" Set color profile/metadata:\n");
Packit 9c6abc
  printf("   icc  file.icc     set ICC profile\n");
Packit 9c6abc
  printf("   exif file.exif    set EXIF metadata\n");
Packit 9c6abc
  printf("   xmp  file.xmp     set XMP metadata\n");
Packit 9c6abc
  printf("   where:    'file.icc' contains the ICC profile to be set,\n");
Packit 9c6abc
  printf("             'file.exif' contains the EXIF metadata to be set\n");
Packit 9c6abc
  printf("             'file.xmp' contains the XMP metadata to be set\n");
Packit 9c6abc
Packit 9c6abc
  printf("\n");
Packit 9c6abc
  printf("DURATION_OPTIONS:\n");
Packit 9c6abc
  printf(" Set duration of selected frames:\n");
Packit 9c6abc
  printf("   duration            set duration for each frames\n");
Packit 9c6abc
  printf("   duration,frame      set duration of a particular frame\n");
Packit 9c6abc
  printf("   duration,start,end  set duration of frames in the\n");
Packit 9c6abc
  printf("                        interval [start,end])\n");
Packit 9c6abc
  printf("   where: 'duration' is the duration in milliseconds\n");
Packit 9c6abc
  printf("          'start' is the start frame index\n");
Packit 9c6abc
  printf("          'end' is the inclusive end frame index\n");
Packit 9c6abc
  printf("           The special 'end' value '0' means: last frame.\n");
Packit 9c6abc
Packit 9c6abc
  printf("\n");
Packit 9c6abc
  printf("STRIP_OPTIONS:\n");
Packit 9c6abc
  printf(" Strip color profile/metadata:\n");
Packit 9c6abc
  printf("   icc       strip ICC profile\n");
Packit 9c6abc
  printf("   exif      strip EXIF metadata\n");
Packit 9c6abc
  printf("   xmp       strip XMP metadata\n");
Packit 9c6abc
Packit 9c6abc
  printf("\n");
Packit 9c6abc
  printf("FRAME_OPTIONS(i):\n");
Packit 9c6abc
  printf(" Create animation:\n");
Packit 9c6abc
  printf("   file_i +di+[xi+yi[+mi[bi]]]\n");
Packit 9c6abc
  printf("   where:    'file_i' is the i'th animation frame (WebP format),\n");
Packit 9c6abc
  printf("             'di' is the pause duration before next frame,\n");
Packit 9c6abc
  printf("             'xi','yi' specify the image offset for this frame,\n");
Packit 9c6abc
  printf("             'mi' is the dispose method for this frame (0 or 1),\n");
Packit 9c6abc
  printf("             'bi' is the blending method for this frame (+b or -b)"
Packit 9c6abc
         "\n");
Packit 9c6abc
Packit 9c6abc
  printf("\n");
Packit 9c6abc
  printf("LOOP_COUNT:\n");
Packit 9c6abc
  printf(" Number of times to repeat the animation.\n");
Packit 9c6abc
  printf(" Valid range is 0 to 65535 [Default: 0 (infinite)].\n");
Packit 9c6abc
Packit 9c6abc
  printf("\n");
Packit 9c6abc
  printf("BACKGROUND_COLOR:\n");
Packit 9c6abc
  printf(" Background color of the canvas.\n");
Packit 9c6abc
  printf("  A,R,G,B\n");
Packit 9c6abc
  printf("  where:    'A', 'R', 'G' and 'B' are integers in the range 0 to 255 "
Packit 9c6abc
         "specifying\n");
Packit 9c6abc
  printf("            the Alpha, Red, Green and Blue component values "
Packit 9c6abc
         "respectively\n");
Packit 9c6abc
  printf("            [Default: 255,255,255,255]\n");
Packit 9c6abc
Packit 9c6abc
  printf("\nINPUT & OUTPUT are in WebP format.\n");
Packit 9c6abc
Packit 9c6abc
  printf("\nNote: The nature of EXIF, XMP and ICC data is not checked");
Packit 9c6abc
  printf(" and is assumed to be\nvalid.\n");
Packit 9c6abc
  printf("\nNote: if a single file name is passed as the argument, the "
Packit 9c6abc
         "arguments will be\n");
Packit 9c6abc
  printf("tokenized from this file. The file name must not start with "
Packit 9c6abc
         "the character '-'.\n");
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static void WarnAboutOddOffset(const WebPMuxFrameInfo* const info) {
Packit 9c6abc
  if ((info->x_offset | info->y_offset) & 1) {
Packit 9c6abc
    fprintf(stderr, "Warning: odd offsets will be snapped to even values"
Packit 9c6abc
            " (%d, %d) -> (%d, %d)\n", info->x_offset, info->y_offset,
Packit 9c6abc
            info->x_offset & ~1, info->y_offset & ~1);
Packit 9c6abc
  }
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static int CreateMux(const char* const filename, WebPMux** mux) {
Packit 9c6abc
  WebPData bitstream;
Packit 9c6abc
  assert(mux != NULL);
Packit 9c6abc
  if (!ExUtilReadFileToWebPData(filename, &bitstream)) return 0;
Packit 9c6abc
  *mux = WebPMuxCreate(&bitstream, 1);
Packit 9c6abc
  WebPDataClear(&bitstream);
Packit 9c6abc
  if (*mux != NULL) return 1;
Packit 9c6abc
  fprintf(stderr, "Failed to create mux object from file %s.\n", filename);
Packit 9c6abc
  return 0;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static int WriteData(const char* filename, const WebPData* const webpdata) {
Packit 9c6abc
  int ok = 0;
Packit 9c6abc
  FILE* fout = strcmp(filename, "-") ? fopen(filename, "wb")
Packit 9c6abc
                                     : ImgIoUtilSetBinaryMode(stdout);
Packit 9c6abc
  if (fout == NULL) {
Packit 9c6abc
    fprintf(stderr, "Error opening output WebP file %s!\n", filename);
Packit 9c6abc
    return 0;
Packit 9c6abc
  }
Packit 9c6abc
  if (fwrite(webpdata->bytes, webpdata->size, 1, fout) != 1) {
Packit 9c6abc
    fprintf(stderr, "Error writing file %s!\n", filename);
Packit 9c6abc
  } else {
Packit 9c6abc
    fprintf(stderr, "Saved file %s (%d bytes)\n",
Packit 9c6abc
            filename, (int)webpdata->size);
Packit 9c6abc
    ok = 1;
Packit 9c6abc
  }
Packit 9c6abc
  if (fout != stdout) fclose(fout);
Packit 9c6abc
  return ok;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static int WriteWebP(WebPMux* const mux, const char* filename) {
Packit 9c6abc
  int ok;
Packit 9c6abc
  WebPData webp_data;
Packit 9c6abc
  const WebPMuxError err = WebPMuxAssemble(mux, &webp_data);
Packit 9c6abc
  if (err != WEBP_MUX_OK) {
Packit 9c6abc
    fprintf(stderr, "Error (%s) assembling the WebP file.\n", ErrorString(err));
Packit 9c6abc
    return 0;
Packit 9c6abc
  }
Packit 9c6abc
  ok = WriteData(filename, &webp_data);
Packit 9c6abc
  WebPDataClear(&webp_data);
Packit 9c6abc
  return ok;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static WebPMux* DuplicateMuxHeader(const WebPMux* const mux) {
Packit 9c6abc
  WebPMux* new_mux = WebPMuxNew();
Packit 9c6abc
  WebPMuxAnimParams p;
Packit 9c6abc
  WebPMuxError err;
Packit 9c6abc
  int i;
Packit 9c6abc
  int ok = 1;
Packit 9c6abc
Packit 9c6abc
  if (new_mux == NULL) return NULL;
Packit 9c6abc
Packit 9c6abc
  err = WebPMuxGetAnimationParams(mux, &p);
Packit 9c6abc
  if (err == WEBP_MUX_OK) {
Packit 9c6abc
    err = WebPMuxSetAnimationParams(new_mux, &p);
Packit 9c6abc
    if (err != WEBP_MUX_OK) {
Packit 9c6abc
      ERROR_GOTO2("Error (%s) handling animation params.\n",
Packit 9c6abc
                  ErrorString(err), End);
Packit 9c6abc
    }
Packit 9c6abc
  } else {
Packit 9c6abc
    /* it might not be an animation. Just keep moving. */
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  for (i = 1; i <= 3; ++i) {
Packit 9c6abc
    WebPData metadata;
Packit 9c6abc
    err = WebPMuxGetChunk(mux, kFourccList[i], &metadata);
Packit 9c6abc
    if (err == WEBP_MUX_OK && metadata.size > 0) {
Packit 9c6abc
      err = WebPMuxSetChunk(new_mux, kFourccList[i], &metadata, 1);
Packit 9c6abc
      if (err != WEBP_MUX_OK) {
Packit 9c6abc
        ERROR_GOTO1("Error transferring metadata in DuplicateMux().", End);
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
 End:
Packit 9c6abc
  if (!ok) {
Packit 9c6abc
    WebPMuxDelete(new_mux);
Packit 9c6abc
    new_mux = NULL;
Packit 9c6abc
  }
Packit 9c6abc
  return new_mux;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static int ParseFrameArgs(const char* args, WebPMuxFrameInfo* const info) {
Packit 9c6abc
  int dispose_method, dummy;
Packit 9c6abc
  char plus_minus, blend_method;
Packit 9c6abc
  const int num_args = sscanf(args, "+%d+%d+%d+%d%c%c+%d", &info->duration,
Packit 9c6abc
                              &info->x_offset, &info->y_offset, &dispose_method,
Packit 9c6abc
                              &plus_minus, &blend_method, &dummy);
Packit 9c6abc
  switch (num_args) {
Packit 9c6abc
    case 1:
Packit 9c6abc
      info->x_offset = info->y_offset = 0;  // fall through
Packit 9c6abc
    case 3:
Packit 9c6abc
      dispose_method = 0;  // fall through
Packit 9c6abc
    case 4:
Packit 9c6abc
      plus_minus = '+';
Packit 9c6abc
      blend_method = 'b';  // fall through
Packit 9c6abc
    case 6:
Packit 9c6abc
      break;
Packit 9c6abc
    case 2:
Packit 9c6abc
    case 5:
Packit 9c6abc
    default:
Packit 9c6abc
      return 0;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  WarnAboutOddOffset(info);
Packit 9c6abc
Packit 9c6abc
  // Note: The sanity of the following conversion is checked by
Packit 9c6abc
  // WebPMuxPushFrame().
Packit 9c6abc
  info->dispose_method = (WebPMuxAnimDispose)dispose_method;
Packit 9c6abc
Packit 9c6abc
  if (blend_method != 'b') return 0;
Packit 9c6abc
  if (plus_minus != '-' && plus_minus != '+') return 0;
Packit 9c6abc
  info->blend_method =
Packit 9c6abc
      (plus_minus == '+') ? WEBP_MUX_BLEND : WEBP_MUX_NO_BLEND;
Packit 9c6abc
  return 1;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static int ParseBgcolorArgs(const char* args, uint32_t* const bgcolor) {
Packit 9c6abc
  uint32_t a, r, g, b;
Packit 9c6abc
  if (sscanf(args, "%u,%u,%u,%u", &a, &r, &g, &b) != 4) return 0;
Packit 9c6abc
  if (a >= 256 || r >= 256 || g >= 256 || b >= 256) return 0;
Packit 9c6abc
  *bgcolor = (a << 24) | (r << 16) | (g << 8) | (b << 0);
Packit 9c6abc
  return 1;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
//------------------------------------------------------------------------------
Packit 9c6abc
// Clean-up.
Packit 9c6abc
Packit 9c6abc
static void DeleteConfig(Config* const config) {
Packit 9c6abc
  if (config != NULL) {
Packit 9c6abc
    free(config->args_);
Packit 9c6abc
    ExUtilDeleteCommandLineArguments(&config->cmd_args_);
Packit 9c6abc
    memset(config, 0, sizeof(*config));
Packit 9c6abc
  }
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
//------------------------------------------------------------------------------
Packit 9c6abc
// Parsing.
Packit 9c6abc
Packit 9c6abc
// Basic syntactic checks on the command-line arguments.
Packit 9c6abc
// Returns 1 on valid, 0 otherwise.
Packit 9c6abc
// Also fills up num_feature_args to be number of feature arguments given.
Packit 9c6abc
// (e.g. if there are 4 '-frame's and 1 '-loop', then num_feature_args = 5).
Packit 9c6abc
static int ValidateCommandLine(const CommandLineArguments* const cmd_args,
Packit 9c6abc
                               int* num_feature_args) {
Packit 9c6abc
  int num_frame_args;
Packit 9c6abc
  int num_loop_args;
Packit 9c6abc
  int num_bgcolor_args;
Packit 9c6abc
  int num_durations_args;
Packit 9c6abc
  int ok = 1;
Packit 9c6abc
Packit 9c6abc
  assert(num_feature_args != NULL);
Packit 9c6abc
  *num_feature_args = 0;
Packit 9c6abc
Packit 9c6abc
  // Simple checks.
Packit 9c6abc
  if (CountOccurrences(cmd_args, "-get") > 1) {
Packit 9c6abc
    ERROR_GOTO1("ERROR: Multiple '-get' arguments specified.\n", ErrValidate);
Packit 9c6abc
  }
Packit 9c6abc
  if (CountOccurrences(cmd_args, "-set") > 1) {
Packit 9c6abc
    ERROR_GOTO1("ERROR: Multiple '-set' arguments specified.\n", ErrValidate);
Packit 9c6abc
  }
Packit 9c6abc
  if (CountOccurrences(cmd_args, "-strip") > 1) {
Packit 9c6abc
    ERROR_GOTO1("ERROR: Multiple '-strip' arguments specified.\n", ErrValidate);
Packit 9c6abc
  }
Packit 9c6abc
  if (CountOccurrences(cmd_args, "-info") > 1) {
Packit 9c6abc
    ERROR_GOTO1("ERROR: Multiple '-info' arguments specified.\n", ErrValidate);
Packit 9c6abc
  }
Packit 9c6abc
  if (CountOccurrences(cmd_args, "-o") > 1) {
Packit 9c6abc
    ERROR_GOTO1("ERROR: Multiple output files specified.\n", ErrValidate);
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  // Compound checks.
Packit 9c6abc
  num_frame_args = CountOccurrences(cmd_args, "-frame");
Packit 9c6abc
  num_loop_args = CountOccurrences(cmd_args, "-loop");
Packit 9c6abc
  num_bgcolor_args = CountOccurrences(cmd_args, "-bgcolor");
Packit 9c6abc
  num_durations_args = CountOccurrences(cmd_args, "-duration");
Packit 9c6abc
Packit 9c6abc
  if (num_loop_args > 1) {
Packit 9c6abc
    ERROR_GOTO1("ERROR: Multiple loop counts specified.\n", ErrValidate);
Packit 9c6abc
  }
Packit 9c6abc
  if (num_bgcolor_args > 1) {
Packit 9c6abc
    ERROR_GOTO1("ERROR: Multiple background colors specified.\n", ErrValidate);
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  if ((num_frame_args == 0) && (num_loop_args + num_bgcolor_args > 0)) {
Packit 9c6abc
    ERROR_GOTO1("ERROR: Loop count and background color are relevant only in "
Packit 9c6abc
                "case of animation.\n", ErrValidate);
Packit 9c6abc
  }
Packit 9c6abc
  if (num_durations_args > 0 && num_frame_args != 0) {
Packit 9c6abc
    ERROR_GOTO1("ERROR: Can not combine -duration and -frame commands.\n",
Packit 9c6abc
                ErrValidate);
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  assert(ok == 1);
Packit 9c6abc
  if (num_durations_args > 0) {
Packit 9c6abc
    *num_feature_args = num_durations_args;
Packit 9c6abc
  } else if (num_frame_args == 0) {
Packit 9c6abc
    // Single argument ('set' action for ICCP/EXIF/XMP, OR a 'get' action).
Packit 9c6abc
    *num_feature_args = 1;
Packit 9c6abc
  } else {
Packit 9c6abc
    // Multiple arguments ('set' action for animation)
Packit 9c6abc
    *num_feature_args = num_frame_args + num_loop_args + num_bgcolor_args;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
 ErrValidate:
Packit 9c6abc
  return ok;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
#define ACTION_IS_NIL (config->action_type_ == NIL_ACTION)
Packit 9c6abc
Packit 9c6abc
#define FEATURETYPE_IS_NIL (config->type_ == NIL_FEATURE)
Packit 9c6abc
Packit 9c6abc
#define CHECK_NUM_ARGS_LESS(NUM, LABEL)                                  \
Packit 9c6abc
  if (argc < i + (NUM)) {                                                \
Packit 9c6abc
    fprintf(stderr, "ERROR: Too few arguments for '%s'.\n", argv[i]);    \
Packit 9c6abc
    goto LABEL;                                                          \
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
#define CHECK_NUM_ARGS_NOT_EQUAL(NUM, LABEL)                             \
Packit 9c6abc
  if (argc != i + (NUM)) {                                               \
Packit 9c6abc
    fprintf(stderr, "ERROR: Too many arguments for '%s'.\n", argv[i]);   \
Packit 9c6abc
    goto LABEL;                                                          \
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
// Parses command-line arguments to fill up config object. Also performs some
Packit 9c6abc
// semantic checks.
Packit 9c6abc
static int ParseCommandLine(Config* config) {
Packit 9c6abc
  int i = 0;
Packit 9c6abc
  int feature_arg_index = 0;
Packit 9c6abc
  int ok = 1;
Packit 9c6abc
  int argc = config->cmd_args_.argc_;
Packit 9c6abc
  const char* const* argv = config->cmd_args_.argv_;
Packit 9c6abc
Packit 9c6abc
  while (i < argc) {
Packit 9c6abc
    FeatureArg* const arg = &config->args_[feature_arg_index];
Packit 9c6abc
    if (argv[i][0] == '-') {  // One of the action types or output.
Packit 9c6abc
      if (!strcmp(argv[i], "-set")) {
Packit 9c6abc
        if (ACTION_IS_NIL) {
Packit 9c6abc
          config->action_type_ = ACTION_SET;
Packit 9c6abc
        } else {
Packit 9c6abc
          ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
Packit 9c6abc
        }
Packit 9c6abc
        ++i;
Packit 9c6abc
      } else if (!strcmp(argv[i], "-duration")) {
Packit 9c6abc
        CHECK_NUM_ARGS_LESS(2, ErrParse);
Packit 9c6abc
        if (ACTION_IS_NIL || config->action_type_ == ACTION_DURATION) {
Packit 9c6abc
          config->action_type_ = ACTION_DURATION;
Packit 9c6abc
        } else {
Packit 9c6abc
          ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
Packit 9c6abc
        }
Packit 9c6abc
        if (FEATURETYPE_IS_NIL || config->type_ == FEATURE_DURATION) {
Packit 9c6abc
          config->type_ = FEATURE_DURATION;
Packit 9c6abc
        } else {
Packit 9c6abc
          ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
Packit 9c6abc
        }
Packit 9c6abc
        arg->params_ = argv[i + 1];
Packit 9c6abc
        ++feature_arg_index;
Packit 9c6abc
        i += 2;
Packit 9c6abc
      } else if (!strcmp(argv[i], "-get")) {
Packit 9c6abc
        if (ACTION_IS_NIL) {
Packit 9c6abc
          config->action_type_ = ACTION_GET;
Packit 9c6abc
        } else {
Packit 9c6abc
          ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
Packit 9c6abc
        }
Packit 9c6abc
        ++i;
Packit 9c6abc
      } else if (!strcmp(argv[i], "-strip")) {
Packit 9c6abc
        if (ACTION_IS_NIL) {
Packit 9c6abc
          config->action_type_ = ACTION_STRIP;
Packit 9c6abc
          config->arg_count_ = 0;
Packit 9c6abc
        } else {
Packit 9c6abc
          ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
Packit 9c6abc
        }
Packit 9c6abc
        ++i;
Packit 9c6abc
      } else if (!strcmp(argv[i], "-frame")) {
Packit 9c6abc
        CHECK_NUM_ARGS_LESS(3, ErrParse);
Packit 9c6abc
        if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
Packit 9c6abc
          config->action_type_ = ACTION_SET;
Packit 9c6abc
        } else {
Packit 9c6abc
          ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
Packit 9c6abc
        }
Packit 9c6abc
        if (FEATURETYPE_IS_NIL || config->type_ == FEATURE_ANMF) {
Packit 9c6abc
          config->type_ = FEATURE_ANMF;
Packit 9c6abc
        } else {
Packit 9c6abc
          ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
Packit 9c6abc
        }
Packit 9c6abc
        arg->subtype_ = SUBTYPE_ANMF;
Packit 9c6abc
        arg->filename_ = argv[i + 1];
Packit 9c6abc
        arg->params_ = argv[i + 2];
Packit 9c6abc
        ++feature_arg_index;
Packit 9c6abc
        i += 3;
Packit 9c6abc
      } else if (!strcmp(argv[i], "-loop") || !strcmp(argv[i], "-bgcolor")) {
Packit 9c6abc
        CHECK_NUM_ARGS_LESS(2, ErrParse);
Packit 9c6abc
        if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
Packit 9c6abc
          config->action_type_ = ACTION_SET;
Packit 9c6abc
        } else {
Packit 9c6abc
          ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
Packit 9c6abc
        }
Packit 9c6abc
        if (FEATURETYPE_IS_NIL || config->type_ == FEATURE_ANMF) {
Packit 9c6abc
          config->type_ = FEATURE_ANMF;
Packit 9c6abc
        } else {
Packit 9c6abc
          ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
Packit 9c6abc
        }
Packit 9c6abc
        arg->subtype_ =
Packit 9c6abc
            !strcmp(argv[i], "-loop") ? SUBTYPE_LOOP : SUBTYPE_BGCOLOR;
Packit 9c6abc
        arg->params_ = argv[i + 1];
Packit 9c6abc
        ++feature_arg_index;
Packit 9c6abc
        i += 2;
Packit 9c6abc
      } else if (!strcmp(argv[i], "-o")) {
Packit 9c6abc
        CHECK_NUM_ARGS_LESS(2, ErrParse);
Packit 9c6abc
        config->output_ = argv[i + 1];
Packit 9c6abc
        i += 2;
Packit 9c6abc
      } else if (!strcmp(argv[i], "-info")) {
Packit 9c6abc
        CHECK_NUM_ARGS_NOT_EQUAL(2, ErrParse);
Packit 9c6abc
        if (config->action_type_ != NIL_ACTION) {
Packit 9c6abc
          ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
Packit 9c6abc
        } else {
Packit 9c6abc
          config->action_type_ = ACTION_INFO;
Packit 9c6abc
          config->arg_count_ = 0;
Packit 9c6abc
          config->input_ = argv[i + 1];
Packit 9c6abc
        }
Packit 9c6abc
        i += 2;
Packit 9c6abc
      } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help")) {
Packit 9c6abc
        PrintHelp();
Packit 9c6abc
        DeleteConfig(config);
Packit 9c6abc
        exit(0);
Packit 9c6abc
      } else if (!strcmp(argv[i], "-version")) {
Packit 9c6abc
        const int version = WebPGetMuxVersion();
Packit 9c6abc
        printf("%d.%d.%d\n",
Packit 9c6abc
               (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
Packit 9c6abc
        DeleteConfig(config);
Packit 9c6abc
        exit(0);
Packit 9c6abc
      } else if (!strcmp(argv[i], "--")) {
Packit 9c6abc
        if (i < argc - 1) {
Packit 9c6abc
          ++i;
Packit 9c6abc
          if (config->input_ == NULL) {
Packit 9c6abc
            config->input_ = argv[i];
Packit 9c6abc
          } else {
Packit 9c6abc
            ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
Packit 9c6abc
                        argv[i], ErrParse);
Packit 9c6abc
          }
Packit 9c6abc
        }
Packit 9c6abc
        break;
Packit 9c6abc
      } else {
Packit 9c6abc
        ERROR_GOTO2("ERROR: Unknown option: '%s'.\n", argv[i], ErrParse);
Packit 9c6abc
      }
Packit 9c6abc
    } else {  // One of the feature types or input.
Packit 9c6abc
      if (ACTION_IS_NIL) {
Packit 9c6abc
        ERROR_GOTO1("ERROR: Action must be specified before other arguments.\n",
Packit 9c6abc
                    ErrParse);
Packit 9c6abc
      }
Packit 9c6abc
      if (!strcmp(argv[i], "icc") || !strcmp(argv[i], "exif") ||
Packit 9c6abc
          !strcmp(argv[i], "xmp")) {
Packit 9c6abc
        if (FEATURETYPE_IS_NIL) {
Packit 9c6abc
          config->type_ = (!strcmp(argv[i], "icc")) ? FEATURE_ICCP :
Packit 9c6abc
              (!strcmp(argv[i], "exif")) ? FEATURE_EXIF : FEATURE_XMP;
Packit 9c6abc
        } else {
Packit 9c6abc
          ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
Packit 9c6abc
        }
Packit 9c6abc
        if (config->action_type_ == ACTION_SET) {
Packit 9c6abc
          CHECK_NUM_ARGS_LESS(2, ErrParse);
Packit 9c6abc
          arg->filename_ = argv[i + 1];
Packit 9c6abc
          ++feature_arg_index;
Packit 9c6abc
          i += 2;
Packit 9c6abc
        } else {
Packit 9c6abc
          ++i;
Packit 9c6abc
        }
Packit 9c6abc
      } else if (!strcmp(argv[i], "frame") &&
Packit 9c6abc
                 (config->action_type_ == ACTION_GET)) {
Packit 9c6abc
        CHECK_NUM_ARGS_LESS(2, ErrParse);
Packit 9c6abc
        config->type_ = FEATURE_ANMF;
Packit 9c6abc
        arg->params_ = argv[i + 1];
Packit 9c6abc
        ++feature_arg_index;
Packit 9c6abc
        i += 2;
Packit 9c6abc
      } else {  // Assume input file.
Packit 9c6abc
        if (config->input_ == NULL) {
Packit 9c6abc
          config->input_ = argv[i];
Packit 9c6abc
        } else {
Packit 9c6abc
          ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
Packit 9c6abc
                      argv[i], ErrParse);
Packit 9c6abc
        }
Packit 9c6abc
        ++i;
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
 ErrParse:
Packit 9c6abc
  return ok;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// Additional checks after config is filled.
Packit 9c6abc
static int ValidateConfig(Config* const config) {
Packit 9c6abc
  int ok = 1;
Packit 9c6abc
Packit 9c6abc
  // Action.
Packit 9c6abc
  if (ACTION_IS_NIL) {
Packit 9c6abc
    ERROR_GOTO1("ERROR: No action specified.\n", ErrValidate2);
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  // Feature type.
Packit 9c6abc
  if (FEATURETYPE_IS_NIL && config->action_type_ != ACTION_INFO) {
Packit 9c6abc
    ERROR_GOTO1("ERROR: No feature specified.\n", ErrValidate2);
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  // Input file.
Packit 9c6abc
  if (config->input_ == NULL) {
Packit 9c6abc
    if (config->action_type_ != ACTION_SET) {
Packit 9c6abc
      ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
Packit 9c6abc
    } else if (config->type_ != FEATURE_ANMF) {
Packit 9c6abc
      ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  // Output file.
Packit 9c6abc
  if (config->output_ == NULL && config->action_type_ != ACTION_INFO) {
Packit 9c6abc
    ERROR_GOTO1("ERROR: No output file specified.\n", ErrValidate2);
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
 ErrValidate2:
Packit 9c6abc
  return ok;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// Create config object from command-line arguments.
Packit 9c6abc
static int InitializeConfig(int argc, const char* argv[],
Packit 9c6abc
                            Config* const config) {
Packit 9c6abc
  int num_feature_args = 0;
Packit 9c6abc
  int ok;
Packit 9c6abc
Packit 9c6abc
  memset(config, 0, sizeof(*config));
Packit 9c6abc
Packit 9c6abc
  ok = ExUtilInitCommandLineArguments(argc, argv, &config->cmd_args_);
Packit 9c6abc
  if (!ok) return 0;
Packit 9c6abc
Packit 9c6abc
  // Validate command-line arguments.
Packit 9c6abc
  if (!ValidateCommandLine(&config->cmd_args_, &num_feature_args)) {
Packit 9c6abc
    ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  config->arg_count_ = num_feature_args;
Packit 9c6abc
  config->args_ = (FeatureArg*)calloc(num_feature_args, sizeof(*config->args_));
Packit 9c6abc
  if (config->args_ == NULL) {
Packit 9c6abc
    ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  // Parse command-line.
Packit 9c6abc
  if (!ParseCommandLine(config) || !ValidateConfig(config)) {
Packit 9c6abc
    ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
 Err1:
Packit 9c6abc
  return ok;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
#undef ACTION_IS_NIL
Packit 9c6abc
#undef FEATURETYPE_IS_NIL
Packit 9c6abc
#undef CHECK_NUM_ARGS_LESS
Packit 9c6abc
#undef CHECK_NUM_ARGS_MORE
Packit 9c6abc
Packit 9c6abc
//------------------------------------------------------------------------------
Packit 9c6abc
// Processing.
Packit 9c6abc
Packit 9c6abc
static int GetFrame(const WebPMux* mux, const Config* config) {
Packit 9c6abc
  WebPMuxError err = WEBP_MUX_OK;
Packit 9c6abc
  WebPMux* mux_single = NULL;
Packit 9c6abc
  int num = 0;
Packit 9c6abc
  int ok = 1;
Packit 9c6abc
  int parse_error = 0;
Packit 9c6abc
  const WebPChunkId id = WEBP_CHUNK_ANMF;
Packit 9c6abc
  WebPMuxFrameInfo info;
Packit 9c6abc
  WebPDataInit(&info.bitstream);
Packit 9c6abc
Packit 9c6abc
  num = ExUtilGetInt(config->args_[0].params_, 10, &parse_error);
Packit 9c6abc
  if (num < 0) {
Packit 9c6abc
    ERROR_GOTO1("ERROR: Frame/Fragment index must be non-negative.\n", ErrGet);
Packit 9c6abc
  }
Packit 9c6abc
  if (parse_error) goto ErrGet;
Packit 9c6abc
Packit 9c6abc
  err = WebPMuxGetFrame(mux, num, &info;;
Packit 9c6abc
  if (err == WEBP_MUX_OK && info.id != id) err = WEBP_MUX_NOT_FOUND;
Packit 9c6abc
  if (err != WEBP_MUX_OK) {
Packit 9c6abc
    ERROR_GOTO3("ERROR (%s): Could not get frame %d.\n",
Packit 9c6abc
                ErrorString(err), num, ErrGet);
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  mux_single = WebPMuxNew();
Packit 9c6abc
  if (mux_single == NULL) {
Packit 9c6abc
    err = WEBP_MUX_MEMORY_ERROR;
Packit 9c6abc
    ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
Packit 9c6abc
                ErrorString(err), ErrGet);
Packit 9c6abc
  }
Packit 9c6abc
  err = WebPMuxSetImage(mux_single, &info.bitstream, 1);
Packit 9c6abc
  if (err != WEBP_MUX_OK) {
Packit 9c6abc
    ERROR_GOTO2("ERROR (%s): Could not create single image mux object.\n",
Packit 9c6abc
                ErrorString(err), ErrGet);
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  ok = WriteWebP(mux_single, config->output_);
Packit 9c6abc
Packit 9c6abc
 ErrGet:
Packit 9c6abc
  WebPDataClear(&info.bitstream);
Packit 9c6abc
  WebPMuxDelete(mux_single);
Packit 9c6abc
  return ok && !parse_error;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// Read and process config.
Packit 9c6abc
static int Process(const Config* config) {
Packit 9c6abc
  WebPMux* mux = NULL;
Packit 9c6abc
  WebPData chunk;
Packit 9c6abc
  WebPMuxError err = WEBP_MUX_OK;
Packit 9c6abc
  int ok = 1;
Packit 9c6abc
Packit 9c6abc
  switch (config->action_type_) {
Packit 9c6abc
    case ACTION_GET: {
Packit 9c6abc
      ok = CreateMux(config->input_, &mux;;
Packit 9c6abc
      if (!ok) goto Err2;
Packit 9c6abc
      switch (config->type_) {
Packit 9c6abc
        case FEATURE_ANMF:
Packit 9c6abc
          ok = GetFrame(mux, config);
Packit 9c6abc
          break;
Packit 9c6abc
Packit 9c6abc
        case FEATURE_ICCP:
Packit 9c6abc
        case FEATURE_EXIF:
Packit 9c6abc
        case FEATURE_XMP:
Packit 9c6abc
          err = WebPMuxGetChunk(mux, kFourccList[config->type_], &chunk);
Packit 9c6abc
          if (err != WEBP_MUX_OK) {
Packit 9c6abc
            ERROR_GOTO3("ERROR (%s): Could not get the %s.\n",
Packit 9c6abc
                        ErrorString(err), kDescriptions[config->type_], Err2);
Packit 9c6abc
          }
Packit 9c6abc
          ok = WriteData(config->output_, &chunk);
Packit 9c6abc
          break;
Packit 9c6abc
Packit 9c6abc
        default:
Packit 9c6abc
          ERROR_GOTO1("ERROR: Invalid feature for action 'get'.\n", Err2);
Packit 9c6abc
          break;
Packit 9c6abc
      }
Packit 9c6abc
      break;
Packit 9c6abc
    }
Packit 9c6abc
    case ACTION_SET: {
Packit 9c6abc
      switch (config->type_) {
Packit 9c6abc
        case FEATURE_ANMF: {
Packit 9c6abc
          int i;
Packit 9c6abc
          WebPMuxAnimParams params = { 0xFFFFFFFF, 0 };
Packit 9c6abc
          mux = WebPMuxNew();
Packit 9c6abc
          if (mux == NULL) {
Packit 9c6abc
            ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
Packit 9c6abc
                        ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
Packit 9c6abc
          }
Packit 9c6abc
          for (i = 0; i < config->arg_count_; ++i) {
Packit 9c6abc
            switch (config->args_[i].subtype_) {
Packit 9c6abc
              case SUBTYPE_BGCOLOR: {
Packit 9c6abc
                uint32_t bgcolor;
Packit 9c6abc
                ok = ParseBgcolorArgs(config->args_[i].params_, &bgcolor);
Packit 9c6abc
                if (!ok) {
Packit 9c6abc
                  ERROR_GOTO1("ERROR: Could not parse the background color \n",
Packit 9c6abc
                              Err2);
Packit 9c6abc
                }
Packit 9c6abc
                params.bgcolor = bgcolor;
Packit 9c6abc
                break;
Packit 9c6abc
              }
Packit 9c6abc
              case SUBTYPE_LOOP: {
Packit 9c6abc
                int parse_error = 0;
Packit 9c6abc
                const int loop_count =
Packit 9c6abc
                    ExUtilGetInt(config->args_[i].params_, 10, &parse_error);
Packit 9c6abc
                if (loop_count < 0 || loop_count > 65535) {
Packit 9c6abc
                  // Note: This is only a 'necessary' condition for loop_count
Packit 9c6abc
                  // to be valid. The 'sufficient' conditioned in checked in
Packit 9c6abc
                  // WebPMuxSetAnimationParams() method called later.
Packit 9c6abc
                  ERROR_GOTO1("ERROR: Loop count must be in the range 0 to "
Packit 9c6abc
                              "65535.\n", Err2);
Packit 9c6abc
                }
Packit 9c6abc
                ok = !parse_error;
Packit 9c6abc
                if (!ok) goto Err2;
Packit 9c6abc
                params.loop_count = loop_count;
Packit 9c6abc
                break;
Packit 9c6abc
              }
Packit 9c6abc
              case SUBTYPE_ANMF: {
Packit 9c6abc
                WebPMuxFrameInfo frame;
Packit 9c6abc
                frame.id = WEBP_CHUNK_ANMF;
Packit 9c6abc
                ok = ExUtilReadFileToWebPData(config->args_[i].filename_,
Packit 9c6abc
                                              &frame.bitstream);
Packit 9c6abc
                if (!ok) goto Err2;
Packit 9c6abc
                ok = ParseFrameArgs(config->args_[i].params_, &frame);
Packit 9c6abc
                if (!ok) {
Packit 9c6abc
                  WebPDataClear(&frame.bitstream);
Packit 9c6abc
                  ERROR_GOTO1("ERROR: Could not parse frame properties.\n",
Packit 9c6abc
                              Err2);
Packit 9c6abc
                }
Packit 9c6abc
                err = WebPMuxPushFrame(mux, &frame, 1);
Packit 9c6abc
                WebPDataClear(&frame.bitstream);
Packit 9c6abc
                if (err != WEBP_MUX_OK) {
Packit 9c6abc
                  ERROR_GOTO3("ERROR (%s): Could not add a frame at index %d."
Packit 9c6abc
                              "\n", ErrorString(err), i, Err2);
Packit 9c6abc
                }
Packit 9c6abc
                break;
Packit 9c6abc
              }
Packit 9c6abc
              default: {
Packit 9c6abc
                ERROR_GOTO1("ERROR: Invalid subtype for 'frame'", Err2);
Packit 9c6abc
                break;
Packit 9c6abc
              }
Packit 9c6abc
            }
Packit 9c6abc
          }
Packit 9c6abc
          err = WebPMuxSetAnimationParams(mux, &params);
Packit 9c6abc
          if (err != WEBP_MUX_OK) {
Packit 9c6abc
            ERROR_GOTO2("ERROR (%s): Could not set animation parameters.\n",
Packit 9c6abc
                        ErrorString(err), Err2);
Packit 9c6abc
          }
Packit 9c6abc
          break;
Packit 9c6abc
        }
Packit 9c6abc
Packit 9c6abc
        case FEATURE_ICCP:
Packit 9c6abc
        case FEATURE_EXIF:
Packit 9c6abc
        case FEATURE_XMP: {
Packit 9c6abc
          ok = CreateMux(config->input_, &mux;;
Packit 9c6abc
          if (!ok) goto Err2;
Packit 9c6abc
          ok = ExUtilReadFileToWebPData(config->args_[0].filename_, &chunk);
Packit 9c6abc
          if (!ok) goto Err2;
Packit 9c6abc
          err = WebPMuxSetChunk(mux, kFourccList[config->type_], &chunk, 1);
Packit 9c6abc
          free((void*)chunk.bytes);
Packit 9c6abc
          if (err != WEBP_MUX_OK) {
Packit 9c6abc
            ERROR_GOTO3("ERROR (%s): Could not set the %s.\n",
Packit 9c6abc
                        ErrorString(err), kDescriptions[config->type_], Err2);
Packit 9c6abc
          }
Packit 9c6abc
          break;
Packit 9c6abc
        }
Packit 9c6abc
        default: {
Packit 9c6abc
          ERROR_GOTO1("ERROR: Invalid feature for action 'set'.\n", Err2);
Packit 9c6abc
          break;
Packit 9c6abc
        }
Packit 9c6abc
      }
Packit 9c6abc
      ok = WriteWebP(mux, config->output_);
Packit 9c6abc
      break;
Packit 9c6abc
    }
Packit 9c6abc
    case ACTION_DURATION: {
Packit 9c6abc
      int num_frames;
Packit 9c6abc
      ok = CreateMux(config->input_, &mux;;
Packit 9c6abc
      if (!ok) goto Err2;
Packit 9c6abc
      err = WebPMuxNumChunks(mux, WEBP_CHUNK_ANMF, &num_frames);
Packit 9c6abc
      ok = (err == WEBP_MUX_OK);
Packit 9c6abc
      if (!ok) {
Packit 9c6abc
        ERROR_GOTO1("ERROR: can not parse the number of frames.\n", Err2);
Packit 9c6abc
      }
Packit 9c6abc
      if (num_frames == 0) {
Packit 9c6abc
        fprintf(stderr, "Doesn't look like the source is animated. "
Packit 9c6abc
                        "Skipping duration setting.\n");
Packit 9c6abc
        ok = WriteWebP(mux, config->output_);
Packit 9c6abc
        if (!ok) goto Err2;
Packit 9c6abc
      } else {
Packit 9c6abc
        int i;
Packit 9c6abc
        int* durations = NULL;
Packit 9c6abc
        WebPMux* new_mux = DuplicateMuxHeader(mux);
Packit 9c6abc
        if (new_mux == NULL) goto Err2;
Packit 9c6abc
        durations = (int*)malloc((size_t)num_frames * sizeof(*durations));
Packit 9c6abc
        if (durations == NULL) goto Err2;
Packit 9c6abc
        for (i = 0; i < num_frames; ++i) durations[i] = -1;
Packit 9c6abc
Packit 9c6abc
        // Parse intervals to process.
Packit 9c6abc
        for (i = 0; i < config->arg_count_; ++i) {
Packit 9c6abc
          int k;
Packit 9c6abc
          int args[3];
Packit 9c6abc
          int duration, start, end;
Packit 9c6abc
          const int nb_args = ExUtilGetInts(config->args_[i].params_,
Packit 9c6abc
                                            10, 3, args);
Packit 9c6abc
          ok = (nb_args >= 1);
Packit 9c6abc
          if (!ok) goto Err3;
Packit 9c6abc
          duration = args[0];
Packit 9c6abc
          if (duration < 0) {
Packit 9c6abc
            ERROR_GOTO1("ERROR: duration must be strictly positive.\n", Err3);
Packit 9c6abc
          }
Packit 9c6abc
Packit 9c6abc
          if (nb_args == 1) {   // only duration is present -> use full interval
Packit 9c6abc
            start = 1;
Packit 9c6abc
            end = num_frames;
Packit 9c6abc
          } else {
Packit 9c6abc
            start = args[1];
Packit 9c6abc
            if (start <= 0) {
Packit 9c6abc
              start = 1;
Packit 9c6abc
            } else if (start > num_frames) {
Packit 9c6abc
              start = num_frames;
Packit 9c6abc
            }
Packit 9c6abc
            end = (nb_args >= 3) ? args[2] : start;
Packit 9c6abc
            if (end == 0 || end > num_frames) end = num_frames;
Packit 9c6abc
          }
Packit 9c6abc
Packit 9c6abc
          for (k = start; k <= end; ++k) {
Packit 9c6abc
            assert(k >= 1 && k <= num_frames);
Packit 9c6abc
            durations[k - 1] = duration;
Packit 9c6abc
          }
Packit 9c6abc
        }
Packit 9c6abc
Packit 9c6abc
        // Apply non-negative durations to their destination frames.
Packit 9c6abc
        for (i = 1; i <= num_frames; ++i) {
Packit 9c6abc
          WebPMuxFrameInfo frame;
Packit 9c6abc
          err = WebPMuxGetFrame(mux, i, &frame);
Packit 9c6abc
          if (err != WEBP_MUX_OK || frame.id != WEBP_CHUNK_ANMF) {
Packit 9c6abc
            ERROR_GOTO2("ERROR: can not retrieve frame #%d.\n", i, Err3);
Packit 9c6abc
          }
Packit 9c6abc
          if (durations[i - 1] >= 0) frame.duration = durations[i - 1];
Packit 9c6abc
          err = WebPMuxPushFrame(new_mux, &frame, 1);
Packit 9c6abc
          if (err != WEBP_MUX_OK) {
Packit 9c6abc
            ERROR_GOTO2("ERROR: error push frame data #%d\n", i, Err3);
Packit 9c6abc
          }
Packit 9c6abc
          WebPDataClear(&frame.bitstream);
Packit 9c6abc
        }
Packit 9c6abc
        WebPMuxDelete(mux);
Packit 9c6abc
        ok = WriteWebP(new_mux, config->output_);
Packit 9c6abc
        mux = new_mux;  // transfer for the WebPMuxDelete() call
Packit 9c6abc
        new_mux = NULL;
Packit 9c6abc
Packit 9c6abc
 Err3:
Packit 9c6abc
        free(durations);
Packit 9c6abc
        WebPMuxDelete(new_mux);
Packit 9c6abc
        if (!ok) goto Err2;
Packit 9c6abc
      }
Packit 9c6abc
      break;
Packit 9c6abc
    }
Packit 9c6abc
    case ACTION_STRIP: {
Packit 9c6abc
      ok = CreateMux(config->input_, &mux;;
Packit 9c6abc
      if (!ok) goto Err2;
Packit 9c6abc
      if (config->type_ == FEATURE_ICCP || config->type_ == FEATURE_EXIF ||
Packit 9c6abc
          config->type_ == FEATURE_XMP) {
Packit 9c6abc
        err = WebPMuxDeleteChunk(mux, kFourccList[config->type_]);
Packit 9c6abc
        if (err != WEBP_MUX_OK) {
Packit 9c6abc
          ERROR_GOTO3("ERROR (%s): Could not strip the %s.\n",
Packit 9c6abc
                      ErrorString(err), kDescriptions[config->type_], Err2);
Packit 9c6abc
        }
Packit 9c6abc
      } else {
Packit 9c6abc
        ERROR_GOTO1("ERROR: Invalid feature for action 'strip'.\n", Err2);
Packit 9c6abc
        break;
Packit 9c6abc
      }
Packit 9c6abc
      ok = WriteWebP(mux, config->output_);
Packit 9c6abc
      break;
Packit 9c6abc
    }
Packit 9c6abc
    case ACTION_INFO: {
Packit 9c6abc
      ok = CreateMux(config->input_, &mux;;
Packit 9c6abc
      if (!ok) goto Err2;
Packit 9c6abc
      ok = (DisplayInfo(mux) == WEBP_MUX_OK);
Packit 9c6abc
      break;
Packit 9c6abc
    }
Packit 9c6abc
    default: {
Packit 9c6abc
      assert(0);  // Invalid action.
Packit 9c6abc
      break;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
 Err2:
Packit 9c6abc
  WebPMuxDelete(mux);
Packit 9c6abc
  return ok;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
//------------------------------------------------------------------------------
Packit 9c6abc
// Main.
Packit 9c6abc
Packit 9c6abc
int main(int argc, const char* argv[]) {
Packit 9c6abc
  Config config;
Packit 9c6abc
  int ok = InitializeConfig(argc - 1, argv + 1, &config);
Packit 9c6abc
  if (ok) {
Packit 9c6abc
    ok = Process(&config);
Packit 9c6abc
  } else {
Packit 9c6abc
    PrintHelp();
Packit 9c6abc
  }
Packit 9c6abc
  DeleteConfig(&config);
Packit 9c6abc
  return !ok;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
//------------------------------------------------------------------------------