Blame examples/webpinfo.c

Packit 9c6abc
// Copyright 2017 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
//  Command-line tool to print out the chunk level structure of WebP files
Packit 9c6abc
//  along with basic integrity checks.
Packit 9c6abc
//
Packit 9c6abc
//  Author: Hui Su (huisu@google.com)
Packit 9c6abc
Packit 9c6abc
#include <assert.h>
Packit 9c6abc
#include <stdio.h>
Packit 9c6abc
Packit 9c6abc
#ifdef HAVE_CONFIG_H
Packit 9c6abc
#include "webp/config.h"
Packit 9c6abc
#endif
Packit 9c6abc
Packit 9c6abc
#include "../imageio/imageio_util.h"
Packit 9c6abc
#include "webp/decode.h"
Packit 9c6abc
#include "webp/format_constants.h"
Packit 9c6abc
#include "webp/mux_types.h"
Packit 9c6abc
Packit 9c6abc
#if defined(_MSC_VER) && _MSC_VER < 1900
Packit 9c6abc
#define snprintf _snprintf
Packit 9c6abc
#endif
Packit 9c6abc
Packit 9c6abc
#define LOG_ERROR(MESSAGE)                     \
Packit 9c6abc
  do {                                         \
Packit 9c6abc
    if (webp_info->show_diagnosis_) {          \
Packit 9c6abc
      fprintf(stderr, "Error: %s\n", MESSAGE); \
Packit 9c6abc
    }                                          \
Packit 9c6abc
  } while (0)
Packit 9c6abc
Packit 9c6abc
#define LOG_WARN(MESSAGE)                        \
Packit 9c6abc
  do {                                           \
Packit 9c6abc
    if (webp_info->show_diagnosis_) {            \
Packit 9c6abc
      fprintf(stderr, "Warning: %s\n", MESSAGE); \
Packit 9c6abc
    }                                            \
Packit 9c6abc
  } while (0)
Packit 9c6abc
Packit 9c6abc
static const char* const kFormats[3] = {
Packit 9c6abc
  "Unknown",
Packit 9c6abc
  "Lossy",
Packit 9c6abc
  "Lossless"
Packit 9c6abc
};
Packit 9c6abc
Packit 9c6abc
static const char* const kLosslessTransforms[4] = {
Packit 9c6abc
  "Predictor",
Packit 9c6abc
  "Cross Color",
Packit 9c6abc
  "Subtract Green",
Packit 9c6abc
  "Color Indexing"
Packit 9c6abc
};
Packit 9c6abc
Packit 9c6abc
static const char* const kAlphaFilterMethods[4] = {
Packit 9c6abc
  "None",
Packit 9c6abc
  "Horizontal",
Packit 9c6abc
  "Vertical",
Packit 9c6abc
  "Gradient"
Packit 9c6abc
};
Packit 9c6abc
Packit 9c6abc
typedef enum {
Packit 9c6abc
  WEBP_INFO_OK = 0,
Packit 9c6abc
  WEBP_INFO_TRUNCATED_DATA,
Packit 9c6abc
  WEBP_INFO_PARSE_ERROR,
Packit 9c6abc
  WEBP_INFO_INVALID_PARAM,
Packit 9c6abc
  WEBP_INFO_BITSTREAM_ERROR,
Packit 9c6abc
  WEBP_INFO_MISSING_DATA,
Packit 9c6abc
  WEBP_INFO_INVALID_COMMAND
Packit 9c6abc
} WebPInfoStatus;
Packit 9c6abc
Packit 9c6abc
typedef enum ChunkID {
Packit 9c6abc
  CHUNK_VP8,
Packit 9c6abc
  CHUNK_VP8L,
Packit 9c6abc
  CHUNK_VP8X,
Packit 9c6abc
  CHUNK_ALPHA,
Packit 9c6abc
  CHUNK_ANIM,
Packit 9c6abc
  CHUNK_ANMF,
Packit 9c6abc
  CHUNK_ICCP,
Packit 9c6abc
  CHUNK_EXIF,
Packit 9c6abc
  CHUNK_XMP,
Packit 9c6abc
  CHUNK_UNKNOWN,
Packit 9c6abc
  CHUNK_TYPES = CHUNK_UNKNOWN
Packit 9c6abc
} ChunkID;
Packit 9c6abc
Packit 9c6abc
typedef struct {
Packit 9c6abc
  size_t start_;
Packit 9c6abc
  size_t end_;
Packit 9c6abc
  const uint8_t* buf_;
Packit 9c6abc
} MemBuffer;
Packit 9c6abc
Packit 9c6abc
typedef struct {
Packit 9c6abc
  size_t offset_;
Packit 9c6abc
  size_t size_;
Packit 9c6abc
  const uint8_t* payload_;
Packit 9c6abc
  ChunkID id_;
Packit 9c6abc
} ChunkData;
Packit 9c6abc
Packit 9c6abc
typedef struct WebPInfo {
Packit 9c6abc
  int canvas_width_;
Packit 9c6abc
  int canvas_height_;
Packit 9c6abc
  int loop_count_;
Packit 9c6abc
  int num_frames_;
Packit 9c6abc
  int chunk_counts_[CHUNK_TYPES];
Packit 9c6abc
  int anmf_subchunk_counts_[3];  // 0 VP8; 1 VP8L; 2 ALPH.
Packit 9c6abc
  uint32_t bgcolor_;
Packit 9c6abc
  int feature_flags_;
Packit 9c6abc
  int has_alpha_;
Packit 9c6abc
  // Used for parsing ANMF chunks.
Packit 9c6abc
  int frame_width_, frame_height_;
Packit 9c6abc
  size_t anim_frame_data_size_;
Packit 9c6abc
  int is_processing_anim_frame_, seen_alpha_subchunk_, seen_image_subchunk_;
Packit 9c6abc
  // Print output control.
Packit 9c6abc
  int quiet_, show_diagnosis_, show_summary_;
Packit 9c6abc
  int parse_bitstream_;
Packit 9c6abc
} WebPInfo;
Packit 9c6abc
Packit 9c6abc
static void WebPInfoInit(WebPInfo* const webp_info) {
Packit 9c6abc
  memset(webp_info, 0, sizeof(*webp_info));
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static const char kWebPChunkTags[CHUNK_TYPES][4] = {
Packit 9c6abc
  { 'V', 'P', '8', ' ' },
Packit 9c6abc
  { 'V', 'P', '8', 'L' },
Packit 9c6abc
  { 'V', 'P', '8', 'X' },
Packit 9c6abc
  { 'A', 'L', 'P', 'H' },
Packit 9c6abc
  { 'A', 'N', 'I', 'M' },
Packit 9c6abc
  { 'A', 'N', 'M', 'F' },
Packit 9c6abc
  { 'I', 'C', 'C', 'P' },
Packit 9c6abc
  { 'E', 'X', 'I', 'F' },
Packit 9c6abc
  { 'X', 'M', 'P', ' ' },
Packit 9c6abc
};
Packit 9c6abc
Packit 9c6abc
// -----------------------------------------------------------------------------
Packit 9c6abc
// Data reading.
Packit 9c6abc
Packit 9c6abc
static int GetLE16(const uint8_t* const data) {
Packit 9c6abc
  return (data[0] << 0) | (data[1] << 8);
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static int GetLE24(const uint8_t* const data) {
Packit 9c6abc
  return GetLE16(data) | (data[2] << 16);
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static uint32_t GetLE32(const uint8_t* const data) {
Packit 9c6abc
  return GetLE16(data) | ((uint32_t)GetLE16(data + 2) << 16);
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static int ReadLE16(const uint8_t** data) {
Packit 9c6abc
  const int val = GetLE16(*data);
Packit 9c6abc
  *data += 2;
Packit 9c6abc
  return val;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static int ReadLE24(const uint8_t** data) {
Packit 9c6abc
  const int val = GetLE24(*data);
Packit 9c6abc
  *data += 3;
Packit 9c6abc
  return val;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static uint32_t ReadLE32(const uint8_t** data) {
Packit 9c6abc
  const uint32_t val = GetLE32(*data);
Packit 9c6abc
  *data += 4;
Packit 9c6abc
  return val;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static int ReadFileToWebPData(const char* const filename,
Packit 9c6abc
                              WebPData* const webp_data) {
Packit 9c6abc
  const uint8_t* data;
Packit 9c6abc
  size_t size;
Packit 9c6abc
  if (!ImgIoUtilReadFile(filename, &data, &size)) return 0;
Packit 9c6abc
  webp_data->bytes = data;
Packit 9c6abc
  webp_data->size = size;
Packit 9c6abc
  return 1;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// -----------------------------------------------------------------------------
Packit 9c6abc
// MemBuffer object.
Packit 9c6abc
Packit 9c6abc
static void InitMemBuffer(MemBuffer* const mem, const WebPData* webp_data) {
Packit 9c6abc
  mem->buf_ = webp_data->bytes;
Packit 9c6abc
  mem->start_ = 0;
Packit 9c6abc
  mem->end_ = webp_data->size;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static size_t MemDataSize(const MemBuffer* const mem) {
Packit 9c6abc
  return (mem->end_ - mem->start_);
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static const uint8_t* GetBuffer(MemBuffer* const mem) {
Packit 9c6abc
  return mem->buf_ + mem->start_;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static void Skip(MemBuffer* const mem, size_t size) {
Packit 9c6abc
  mem->start_ += size;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static uint32_t ReadMemBufLE32(MemBuffer* const mem) {
Packit 9c6abc
  const uint8_t* const data = mem->buf_ + mem->start_;
Packit 9c6abc
  const uint32_t val = GetLE32(data);
Packit 9c6abc
  assert(MemDataSize(mem) >= 4);
Packit 9c6abc
  Skip(mem, 4);
Packit 9c6abc
  return val;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// -----------------------------------------------------------------------------
Packit 9c6abc
// Lossy bitstream analysis.
Packit 9c6abc
Packit 9c6abc
static int GetBits(const uint8_t* const data, size_t data_size, size_t nb,
Packit 9c6abc
                   int* val, uint64_t* const bit_pos) {
Packit 9c6abc
  *val = 0;
Packit 9c6abc
  while (nb-- > 0) {
Packit 9c6abc
    const uint64_t p = (*bit_pos)++;
Packit 9c6abc
    if ((p >> 3) >= data_size) {
Packit 9c6abc
      return 0;
Packit 9c6abc
    } else {
Packit 9c6abc
      const int bit = !!(data[p >> 3] & (128 >> ((p & 7))));
Packit 9c6abc
      *val = (*val << 1) | bit;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  return 1;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static int GetSignedBits(const uint8_t* const data, size_t data_size, size_t nb,
Packit 9c6abc
                         int* val, uint64_t* const bit_pos) {
Packit 9c6abc
  int sign;
Packit 9c6abc
  if (!GetBits(data, data_size, nb, val, bit_pos)) return 0;
Packit 9c6abc
  if (!GetBits(data, data_size, 1, &sign, bit_pos)) return 0;
Packit 9c6abc
  if (sign) *val = -(*val);
Packit 9c6abc
  return 1;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
#define GET_BITS(v, n)                                 \
Packit 9c6abc
  do {                                                 \
Packit 9c6abc
    if (!GetBits(data, data_size, n, &(v), bit_pos)) { \
Packit 9c6abc
      LOG_ERROR("Truncated lossy bitstream.");         \
Packit 9c6abc
      return WEBP_INFO_TRUNCATED_DATA;                 \
Packit 9c6abc
    }                                                  \
Packit 9c6abc
  } while (0)
Packit 9c6abc
Packit 9c6abc
#define GET_SIGNED_BITS(v, n)                                \
Packit 9c6abc
  do {                                                       \
Packit 9c6abc
    if (!GetSignedBits(data, data_size, n, &(v), bit_pos)) { \
Packit 9c6abc
      LOG_ERROR("Truncated lossy bitstream.");               \
Packit 9c6abc
      return WEBP_INFO_TRUNCATED_DATA;                       \
Packit 9c6abc
    }                                                        \
Packit 9c6abc
  } while (0)
Packit 9c6abc
Packit 9c6abc
static WebPInfoStatus ParseLossySegmentHeader(const WebPInfo* const webp_info,
Packit 9c6abc
                                              const uint8_t* const data,
Packit 9c6abc
                                              size_t data_size,
Packit 9c6abc
                                              uint64_t* const bit_pos) {
Packit 9c6abc
  int use_segment;
Packit 9c6abc
  GET_BITS(use_segment, 1);
Packit 9c6abc
  printf("  Use segment:      %d\n", use_segment);
Packit 9c6abc
  if (use_segment) {
Packit 9c6abc
    int update_map, update_data;
Packit 9c6abc
    GET_BITS(update_map, 1);
Packit 9c6abc
    GET_BITS(update_data, 1);
Packit 9c6abc
    printf("  Update map:       %d\n"
Packit 9c6abc
           "  Update data:      %d\n",
Packit 9c6abc
           update_map, update_data);
Packit 9c6abc
    if (update_data) {
Packit 9c6abc
      int i, a_delta;
Packit 9c6abc
      int quantizer[4] = {0, 0, 0, 0};
Packit 9c6abc
      int filter_strength[4] = {0, 0, 0, 0};
Packit 9c6abc
      GET_BITS(a_delta, 1);
Packit 9c6abc
      printf("  Absolute delta:   %d\n", a_delta);
Packit 9c6abc
      for (i = 0; i < 4; ++i) {
Packit 9c6abc
        int bit;
Packit 9c6abc
        GET_BITS(bit, 1);
Packit 9c6abc
        if (bit) GET_SIGNED_BITS(quantizer[i], 7);
Packit 9c6abc
      }
Packit 9c6abc
      for (i = 0; i < 4; ++i) {
Packit 9c6abc
        int bit;
Packit 9c6abc
        GET_BITS(bit, 1);
Packit 9c6abc
        if (bit) GET_SIGNED_BITS(filter_strength[i], 6);
Packit 9c6abc
      }
Packit 9c6abc
      printf("  Quantizer:        %d %d %d %d\n", quantizer[0], quantizer[1],
Packit 9c6abc
             quantizer[2], quantizer[3]);
Packit 9c6abc
      printf("  Filter strength:  %d %d %d %d\n", filter_strength[0],
Packit 9c6abc
             filter_strength[1], filter_strength[2], filter_strength[3]);
Packit 9c6abc
    }
Packit 9c6abc
    if (update_map) {
Packit 9c6abc
      int i;
Packit 9c6abc
      int prob_segment[3] = {255, 255, 255};
Packit 9c6abc
      for (i = 0; i < 3; ++i) {
Packit 9c6abc
        int bit;
Packit 9c6abc
        GET_BITS(bit, 1);
Packit 9c6abc
        if (bit) GET_BITS(prob_segment[i], 8);
Packit 9c6abc
      }
Packit 9c6abc
      printf("  Prob segment:     %d %d %d\n",
Packit 9c6abc
             prob_segment[0], prob_segment[1], prob_segment[2]);
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  return WEBP_INFO_OK;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static WebPInfoStatus ParseLossyFilterHeader(const WebPInfo* const webp_info,
Packit 9c6abc
                                             const uint8_t* const data,
Packit 9c6abc
                                             size_t data_size,
Packit 9c6abc
                                             uint64_t* const bit_pos) {
Packit 9c6abc
  int simple_filter, level, sharpness, use_lf_delta;
Packit 9c6abc
  GET_BITS(simple_filter, 1);
Packit 9c6abc
  GET_BITS(level, 6);
Packit 9c6abc
  GET_BITS(sharpness, 3);
Packit 9c6abc
  GET_BITS(use_lf_delta, 1);
Packit 9c6abc
  printf("  Simple filter:    %d\n", simple_filter);
Packit 9c6abc
  printf("  Level:            %d\n", level);
Packit 9c6abc
  printf("  Sharpness:        %d\n", sharpness);
Packit 9c6abc
  printf("  Use lf delta:     %d\n", use_lf_delta);
Packit 9c6abc
  if (use_lf_delta) {
Packit 9c6abc
    int update;
Packit 9c6abc
    GET_BITS(update, 1);
Packit 9c6abc
    printf("  Update lf delta:  %d\n", update);
Packit 9c6abc
    if (update) {
Packit 9c6abc
      int i;
Packit 9c6abc
      for (i = 0; i < 4 + 4; ++i) {
Packit 9c6abc
        int temp;
Packit 9c6abc
        GET_BITS(temp, 1);
Packit 9c6abc
        if (temp) GET_BITS(temp, 7);
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  return WEBP_INFO_OK;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static WebPInfoStatus ParseLossyHeader(const ChunkData* const chunk_data,
Packit 9c6abc
                                       const WebPInfo* const webp_info) {
Packit 9c6abc
  const uint8_t* data = chunk_data->payload_;
Packit 9c6abc
  size_t data_size = chunk_data->size_ - CHUNK_HEADER_SIZE;
Packit 9c6abc
  const uint32_t bits = (uint32_t)data[0] | (data[1] << 8) | (data[2] << 16);
Packit 9c6abc
  const int key_frame = !(bits & 1);
Packit 9c6abc
  const int profile = (bits >> 1) & 7;
Packit 9c6abc
  const int display = (bits >> 4) & 1;
Packit 9c6abc
  const uint32_t partition0_length = (bits >> 5);
Packit 9c6abc
  WebPInfoStatus status = WEBP_INFO_OK;
Packit 9c6abc
  uint64_t bit_position = 0;
Packit 9c6abc
  uint64_t* const bit_pos = &bit_position;
Packit 9c6abc
  int colorspace, clamp_type;
Packit 9c6abc
  printf("  Parsing lossy bitstream...\n");
Packit 9c6abc
  // Calling WebPGetFeatures() in ProcessImageChunk() should ensure this.
Packit 9c6abc
  assert(chunk_data->size_ >= CHUNK_HEADER_SIZE + 10);
Packit 9c6abc
  if (profile > 3) {
Packit 9c6abc
    LOG_ERROR("Unknown profile.");
Packit 9c6abc
    return WEBP_INFO_BITSTREAM_ERROR;
Packit 9c6abc
  }
Packit 9c6abc
  if (!display) {
Packit 9c6abc
    LOG_ERROR("Frame is not displayable.");
Packit 9c6abc
    return WEBP_INFO_BITSTREAM_ERROR;
Packit 9c6abc
  }
Packit 9c6abc
  data += 3;
Packit 9c6abc
  data_size -= 3;
Packit 9c6abc
  printf("  Key frame:        %s\n"
Packit 9c6abc
         "  Profile:          %d\n"
Packit 9c6abc
         "  Display:          %s\n"
Packit 9c6abc
         "  Part. 0 length:   %d\n",
Packit 9c6abc
         key_frame ? "Yes" : "No", profile,
Packit 9c6abc
         display ? "Yes" : "No", partition0_length);
Packit 9c6abc
  if (key_frame) {
Packit 9c6abc
    if (!(data[0] == 0x9d && data[1] == 0x01 && data[2] == 0x2a)) {
Packit 9c6abc
      LOG_ERROR("Invalid lossy bitstream signature.");
Packit 9c6abc
      return WEBP_INFO_BITSTREAM_ERROR;
Packit 9c6abc
    }
Packit 9c6abc
    printf("  Width:            %d\n"
Packit 9c6abc
           "  X scale:          %d\n"
Packit 9c6abc
           "  Height:           %d\n"
Packit 9c6abc
           "  Y scale:          %d\n",
Packit 9c6abc
           ((data[4] << 8) | data[3]) & 0x3fff, data[4] >> 6,
Packit 9c6abc
           ((data[6] << 8) | data[5]) & 0x3fff, data[6] >> 6);
Packit 9c6abc
    data += 7;
Packit 9c6abc
    data_size -= 7;
Packit 9c6abc
  } else {
Packit 9c6abc
    LOG_ERROR("Non-keyframe detected in lossy bitstream.");
Packit 9c6abc
    return WEBP_INFO_BITSTREAM_ERROR;
Packit 9c6abc
  }
Packit 9c6abc
  if (partition0_length >= data_size) {
Packit 9c6abc
    LOG_ERROR("Bad partition length.");
Packit 9c6abc
    return WEBP_INFO_BITSTREAM_ERROR;
Packit 9c6abc
  }
Packit 9c6abc
  GET_BITS(colorspace, 1);
Packit 9c6abc
  GET_BITS(clamp_type, 1);
Packit 9c6abc
  printf("  Color space:      %d\n", colorspace);
Packit 9c6abc
  printf("  Clamp type:       %d\n", clamp_type);
Packit 9c6abc
  status = ParseLossySegmentHeader(webp_info, data, data_size, bit_pos);
Packit 9c6abc
  if (status != WEBP_INFO_OK) return status;
Packit 9c6abc
  status = ParseLossyFilterHeader(webp_info, data, data_size, bit_pos);
Packit 9c6abc
  if (status != WEBP_INFO_OK) return status;
Packit 9c6abc
  {  // Partition number and size.
Packit 9c6abc
    const uint8_t* part_size = data + partition0_length;
Packit 9c6abc
    int num_parts, i;
Packit 9c6abc
    size_t part_data_size;
Packit 9c6abc
    GET_BITS(num_parts, 2);
Packit 9c6abc
    num_parts = 1 << num_parts;
Packit 9c6abc
    if ((int)(data_size - partition0_length) < (num_parts - 1) * 3) {
Packit 9c6abc
      LOG_ERROR("Truncated lossy bitstream.");
Packit 9c6abc
      return WEBP_INFO_TRUNCATED_DATA;
Packit 9c6abc
    }
Packit 9c6abc
    part_data_size = data_size - partition0_length - (num_parts - 1) * 3;
Packit 9c6abc
    printf("  Total partitions: %d\n", num_parts);
Packit 9c6abc
    for (i = 1; i < num_parts; ++i) {
Packit 9c6abc
      const size_t psize =
Packit 9c6abc
          part_size[0] | (part_size[1] << 8) | (part_size[2] << 16);
Packit 9c6abc
      if (psize > part_data_size) {
Packit 9c6abc
        LOG_ERROR("Truncated partition.");
Packit 9c6abc
        return WEBP_INFO_TRUNCATED_DATA;
Packit 9c6abc
      }
Packit 9c6abc
      printf("  Part. %d length:   %d\n", i, (int)psize);
Packit 9c6abc
      part_data_size -= psize;
Packit 9c6abc
      part_size += 3;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  // Quantizer.
Packit 9c6abc
  {
Packit 9c6abc
    int base_q, bit;
Packit 9c6abc
    int dq_y1_dc = 0, dq_y2_dc = 0, dq_y2_ac = 0, dq_uv_dc = 0, dq_uv_ac = 0;
Packit 9c6abc
    GET_BITS(base_q, 7);
Packit 9c6abc
    GET_BITS(bit, 1);
Packit 9c6abc
    if (bit) GET_SIGNED_BITS(dq_y1_dc, 4);
Packit 9c6abc
    GET_BITS(bit, 1);
Packit 9c6abc
    if (bit) GET_SIGNED_BITS(dq_y2_dc, 4);
Packit 9c6abc
    GET_BITS(bit, 1);
Packit 9c6abc
    if (bit) GET_SIGNED_BITS(dq_y2_ac, 4);
Packit 9c6abc
    GET_BITS(bit, 1);
Packit 9c6abc
    if (bit) GET_SIGNED_BITS(dq_uv_dc, 4);
Packit 9c6abc
    GET_BITS(bit, 1);
Packit 9c6abc
    if (bit) GET_SIGNED_BITS(dq_uv_ac, 4);
Packit 9c6abc
    printf("  Base Q:           %d\n", base_q);
Packit 9c6abc
    printf("  DQ Y1 DC:         %d\n", dq_y1_dc);
Packit 9c6abc
    printf("  DQ Y2 DC:         %d\n", dq_y2_dc);
Packit 9c6abc
    printf("  DQ Y2 AC:         %d\n", dq_y2_ac);
Packit 9c6abc
    printf("  DQ UV DC:         %d\n", dq_uv_dc);
Packit 9c6abc
    printf("  DQ UV AC:         %d\n", dq_uv_ac);
Packit 9c6abc
  }
Packit 9c6abc
  if ((*bit_pos >> 3) >= partition0_length) {
Packit 9c6abc
    LOG_ERROR("Truncated lossy bitstream.");
Packit 9c6abc
    return WEBP_INFO_TRUNCATED_DATA;
Packit 9c6abc
  }
Packit 9c6abc
  return WEBP_INFO_OK;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// -----------------------------------------------------------------------------
Packit 9c6abc
// Lossless bitstream analysis.
Packit 9c6abc
Packit 9c6abc
static int LLGetBits(const uint8_t* const data, size_t data_size, size_t nb,
Packit 9c6abc
                     int* val, uint64_t* const bit_pos) {
Packit 9c6abc
  uint32_t i = 0;
Packit 9c6abc
  *val = 0;
Packit 9c6abc
  while (i < nb) {
Packit 9c6abc
    const uint64_t p = (*bit_pos)++;
Packit 9c6abc
    if ((p >> 3) >= data_size) {
Packit 9c6abc
      return 0;
Packit 9c6abc
    } else {
Packit 9c6abc
      const int bit = !!(data[p >> 3] & (1 << ((p & 7))));
Packit 9c6abc
      *val = *val | (bit << i);
Packit 9c6abc
      ++i;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  return 1;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
#define LL_GET_BITS(v, n)                                \
Packit 9c6abc
  do {                                                   \
Packit 9c6abc
    if (!LLGetBits(data, data_size, n, &(v), bit_pos)) { \
Packit 9c6abc
      LOG_ERROR("Truncated lossless bitstream.");        \
Packit 9c6abc
      return WEBP_INFO_TRUNCATED_DATA;                   \
Packit 9c6abc
    }                                                    \
Packit 9c6abc
  } while (0)
Packit 9c6abc
Packit 9c6abc
static WebPInfoStatus ParseLosslessTransform(WebPInfo* const webp_info,
Packit 9c6abc
                                             const uint8_t* const data,
Packit 9c6abc
                                             size_t data_size,
Packit 9c6abc
                                             uint64_t* const  bit_pos) {
Packit 9c6abc
  int use_transform, block_size, n_colors;
Packit 9c6abc
  LL_GET_BITS(use_transform, 1);
Packit 9c6abc
  printf("  Use transform:    %s\n", use_transform ? "Yes" : "No");
Packit 9c6abc
  if (use_transform) {
Packit 9c6abc
    int type;
Packit 9c6abc
    LL_GET_BITS(type, 2);
Packit 9c6abc
    printf("  1st transform:    %s (%d)\n", kLosslessTransforms[type], type);
Packit 9c6abc
    switch (type) {
Packit 9c6abc
      case PREDICTOR_TRANSFORM:
Packit 9c6abc
      case CROSS_COLOR_TRANSFORM:
Packit 9c6abc
        LL_GET_BITS(block_size, 3);
Packit 9c6abc
        block_size = 1 << (block_size + 2);
Packit 9c6abc
        printf("  Tran. block size: %d\n", block_size);
Packit 9c6abc
        break;
Packit 9c6abc
      case COLOR_INDEXING_TRANSFORM:
Packit 9c6abc
        LL_GET_BITS(n_colors, 8);
Packit 9c6abc
        n_colors += 1;
Packit 9c6abc
        printf("  No. of colors:    %d\n", n_colors);
Packit 9c6abc
        break;
Packit 9c6abc
      default: break;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  return WEBP_INFO_OK;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static WebPInfoStatus ParseLosslessHeader(const ChunkData* const chunk_data,
Packit 9c6abc
                                          WebPInfo* const webp_info) {
Packit 9c6abc
  const uint8_t* data = chunk_data->payload_;
Packit 9c6abc
  size_t data_size = chunk_data->size_ - CHUNK_HEADER_SIZE;
Packit 9c6abc
  uint64_t bit_position = 0;
Packit 9c6abc
  uint64_t* const bit_pos = &bit_position;
Packit 9c6abc
  WebPInfoStatus status;
Packit 9c6abc
  printf("  Parsing lossless bitstream...\n");
Packit 9c6abc
  if (data_size < VP8L_FRAME_HEADER_SIZE) {
Packit 9c6abc
    LOG_ERROR("Truncated lossless bitstream.");
Packit 9c6abc
    return WEBP_INFO_TRUNCATED_DATA;
Packit 9c6abc
  }
Packit 9c6abc
  if (data[0] != VP8L_MAGIC_BYTE) {
Packit 9c6abc
    LOG_ERROR("Invalid lossless bitstream signature.");
Packit 9c6abc
    return WEBP_INFO_BITSTREAM_ERROR;
Packit 9c6abc
  }
Packit 9c6abc
  data += 1;
Packit 9c6abc
  data_size -= 1;
Packit 9c6abc
  {
Packit 9c6abc
    int width, height, has_alpha, version;
Packit 9c6abc
    LL_GET_BITS(width, 14);
Packit 9c6abc
    LL_GET_BITS(height, 14);
Packit 9c6abc
    LL_GET_BITS(has_alpha, 1);
Packit 9c6abc
    LL_GET_BITS(version, 3);
Packit 9c6abc
    width += 1;
Packit 9c6abc
    height += 1;
Packit 9c6abc
    printf("  Width:            %d\n", width);
Packit 9c6abc
    printf("  Height:           %d\n", height);
Packit 9c6abc
    printf("  Alpha:            %d\n", has_alpha);
Packit 9c6abc
    printf("  Version:          %d\n", version);
Packit 9c6abc
  }
Packit 9c6abc
  status = ParseLosslessTransform(webp_info, data, data_size, bit_pos);
Packit 9c6abc
  if (status != WEBP_INFO_OK) return status;
Packit 9c6abc
  return WEBP_INFO_OK;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static WebPInfoStatus ParseAlphaHeader(const ChunkData* const chunk_data,
Packit 9c6abc
                                       WebPInfo* const webp_info) {
Packit 9c6abc
  const uint8_t* data = chunk_data->payload_;
Packit 9c6abc
  size_t data_size = chunk_data->size_ - CHUNK_HEADER_SIZE;
Packit 9c6abc
  if (data_size <= ALPHA_HEADER_LEN) {
Packit 9c6abc
    LOG_ERROR("Truncated ALPH chunk.");
Packit 9c6abc
    return WEBP_INFO_TRUNCATED_DATA;
Packit 9c6abc
  }
Packit 9c6abc
  printf("  Parsing ALPH chunk...\n");
Packit 9c6abc
  {
Packit 9c6abc
    const int compression_method = (data[0] >> 0) & 0x03;
Packit 9c6abc
    const int filter = (data[0] >> 2) & 0x03;
Packit 9c6abc
    const int pre_processing = (data[0] >> 4) & 0x03;
Packit 9c6abc
    const int reserved_bits = (data[0] >> 6) & 0x03;
Packit 9c6abc
    printf("  Compression:      %d\n", compression_method);
Packit 9c6abc
    printf("  Filter:           %s (%d)\n",
Packit 9c6abc
           kAlphaFilterMethods[filter], filter);
Packit 9c6abc
    printf("  Pre-processing:   %d\n", pre_processing);
Packit 9c6abc
    if (compression_method > ALPHA_LOSSLESS_COMPRESSION) {
Packit 9c6abc
      LOG_ERROR("Invalid Alpha compression method.");
Packit 9c6abc
      return WEBP_INFO_BITSTREAM_ERROR;
Packit 9c6abc
    }
Packit 9c6abc
    if (pre_processing > ALPHA_PREPROCESSED_LEVELS) {
Packit 9c6abc
      LOG_ERROR("Invalid Alpha pre-processing method.");
Packit 9c6abc
      return WEBP_INFO_BITSTREAM_ERROR;
Packit 9c6abc
    }
Packit 9c6abc
    if (reserved_bits != 0) {
Packit 9c6abc
      LOG_WARN("Reserved bits in ALPH chunk header are not all 0.");
Packit 9c6abc
    }
Packit 9c6abc
    data += ALPHA_HEADER_LEN;
Packit 9c6abc
    data_size -= ALPHA_HEADER_LEN;
Packit 9c6abc
    if (compression_method == ALPHA_LOSSLESS_COMPRESSION) {
Packit 9c6abc
      uint64_t bit_pos = 0;
Packit 9c6abc
      WebPInfoStatus status =
Packit 9c6abc
          ParseLosslessTransform(webp_info, data, data_size, &bit_pos);
Packit 9c6abc
      if (status != WEBP_INFO_OK) return status;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  return WEBP_INFO_OK;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// -----------------------------------------------------------------------------
Packit 9c6abc
// Chunk parsing.
Packit 9c6abc
Packit 9c6abc
static WebPInfoStatus ParseRIFFHeader(const WebPInfo* const webp_info,
Packit 9c6abc
                                      MemBuffer* const mem) {
Packit 9c6abc
  const size_t min_size = RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE;
Packit 9c6abc
  size_t riff_size;
Packit 9c6abc
Packit 9c6abc
  if (MemDataSize(mem) < min_size) {
Packit 9c6abc
    LOG_ERROR("Truncated data detected when parsing RIFF header.");
Packit 9c6abc
    return WEBP_INFO_TRUNCATED_DATA;
Packit 9c6abc
  }
Packit 9c6abc
  if (memcmp(GetBuffer(mem), "RIFF", CHUNK_SIZE_BYTES) ||
Packit 9c6abc
      memcmp(GetBuffer(mem) + CHUNK_HEADER_SIZE, "WEBP", CHUNK_SIZE_BYTES)) {
Packit 9c6abc
    LOG_ERROR("Corrupted RIFF header.");
Packit 9c6abc
    return WEBP_INFO_PARSE_ERROR;
Packit 9c6abc
  }
Packit 9c6abc
  riff_size = GetLE32(GetBuffer(mem) + TAG_SIZE);
Packit 9c6abc
  if (riff_size < CHUNK_HEADER_SIZE) {
Packit 9c6abc
    LOG_ERROR("RIFF size is too small.");
Packit 9c6abc
    return WEBP_INFO_PARSE_ERROR;
Packit 9c6abc
  }
Packit 9c6abc
  if (riff_size > MAX_CHUNK_PAYLOAD) {
Packit 9c6abc
    LOG_ERROR("RIFF size is over limit.");
Packit 9c6abc
    return WEBP_INFO_PARSE_ERROR;
Packit 9c6abc
  }
Packit 9c6abc
  riff_size += CHUNK_HEADER_SIZE;
Packit 9c6abc
  if (!webp_info->quiet_) {
Packit 9c6abc
    printf("RIFF HEADER:\n");
Packit 9c6abc
    printf("  File size: %6d\n", (int)riff_size);
Packit 9c6abc
  }
Packit 9c6abc
  if (riff_size < mem->end_) {
Packit 9c6abc
    LOG_WARN("RIFF size is smaller than the file size.");
Packit 9c6abc
    mem->end_ = riff_size;
Packit 9c6abc
  } else if (riff_size > mem->end_) {
Packit 9c6abc
    LOG_ERROR("Truncated data detected when parsing RIFF payload.");
Packit 9c6abc
    return WEBP_INFO_TRUNCATED_DATA;
Packit 9c6abc
  }
Packit 9c6abc
  Skip(mem, RIFF_HEADER_SIZE);
Packit 9c6abc
  return WEBP_INFO_OK;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static WebPInfoStatus ParseChunk(const WebPInfo* const webp_info,
Packit 9c6abc
                                 MemBuffer* const mem,
Packit 9c6abc
                                 ChunkData* const chunk_data) {
Packit 9c6abc
  memset(chunk_data, 0, sizeof(*chunk_data));
Packit 9c6abc
  if (MemDataSize(mem) < CHUNK_HEADER_SIZE) {
Packit 9c6abc
    LOG_ERROR("Truncated data detected when parsing chunk header.");
Packit 9c6abc
    return WEBP_INFO_TRUNCATED_DATA;
Packit 9c6abc
  } else {
Packit 9c6abc
    const size_t chunk_start_offset = mem->start_;
Packit 9c6abc
    const uint32_t fourcc = ReadMemBufLE32(mem);
Packit 9c6abc
    const uint32_t payload_size = ReadMemBufLE32(mem);
Packit 9c6abc
    const uint32_t payload_size_padded = payload_size + (payload_size & 1);
Packit 9c6abc
    const size_t chunk_size = CHUNK_HEADER_SIZE + payload_size_padded;
Packit 9c6abc
    int i;
Packit 9c6abc
    if (payload_size > MAX_CHUNK_PAYLOAD) {
Packit 9c6abc
      LOG_ERROR("Size of chunk payload is over limit.");
Packit 9c6abc
      return WEBP_INFO_INVALID_PARAM;
Packit 9c6abc
    }
Packit 9c6abc
    if (payload_size_padded > MemDataSize(mem)){
Packit 9c6abc
      LOG_ERROR("Truncated data detected when parsing chunk payload.");
Packit 9c6abc
      return WEBP_INFO_TRUNCATED_DATA;
Packit 9c6abc
    }
Packit 9c6abc
    for (i = 0; i < CHUNK_TYPES; ++i) {
Packit 9c6abc
      if (!memcmp(kWebPChunkTags[i], &fourcc, TAG_SIZE)) break;
Packit 9c6abc
    }
Packit 9c6abc
    chunk_data->offset_ = chunk_start_offset;
Packit 9c6abc
    chunk_data->size_ = chunk_size;
Packit 9c6abc
    chunk_data->id_ = (ChunkID)i;
Packit 9c6abc
    chunk_data->payload_ = GetBuffer(mem);
Packit 9c6abc
    if (chunk_data->id_ == CHUNK_ANMF) {
Packit 9c6abc
      if (payload_size != payload_size_padded) {
Packit 9c6abc
        LOG_ERROR("ANMF chunk size should always be even.");
Packit 9c6abc
        return WEBP_INFO_PARSE_ERROR;
Packit 9c6abc
      }
Packit 9c6abc
      // There are sub-chunks to be parsed in an ANMF chunk.
Packit 9c6abc
      Skip(mem, ANMF_CHUNK_SIZE);
Packit 9c6abc
    } else {
Packit 9c6abc
      Skip(mem, payload_size_padded);
Packit 9c6abc
    }
Packit 9c6abc
    return WEBP_INFO_OK;
Packit 9c6abc
  }
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// -----------------------------------------------------------------------------
Packit 9c6abc
// Chunk analysis.
Packit 9c6abc
Packit 9c6abc
static WebPInfoStatus ProcessVP8XChunk(const ChunkData* const chunk_data,
Packit 9c6abc
                                       WebPInfo* const webp_info) {
Packit 9c6abc
  const uint8_t* data = chunk_data->payload_;
Packit 9c6abc
  if (webp_info->chunk_counts_[CHUNK_VP8] ||
Packit 9c6abc
      webp_info->chunk_counts_[CHUNK_VP8L] ||
Packit 9c6abc
      webp_info->chunk_counts_[CHUNK_VP8X]) {
Packit 9c6abc
    LOG_ERROR("Already seen a VP8/VP8L/VP8X chunk when parsing VP8X chunk.");
Packit 9c6abc
    return WEBP_INFO_PARSE_ERROR;
Packit 9c6abc
  }
Packit 9c6abc
  if (chunk_data->size_ != VP8X_CHUNK_SIZE + CHUNK_HEADER_SIZE) {
Packit 9c6abc
    LOG_ERROR("Corrupted VP8X chunk.");
Packit 9c6abc
    return WEBP_INFO_PARSE_ERROR;
Packit 9c6abc
  }
Packit 9c6abc
  ++webp_info->chunk_counts_[CHUNK_VP8X];
Packit 9c6abc
  webp_info->feature_flags_ = *data;
Packit 9c6abc
  data += 4;
Packit 9c6abc
  webp_info->canvas_width_ = 1 + ReadLE24(&data);
Packit 9c6abc
  webp_info->canvas_height_ = 1 + ReadLE24(&data);
Packit 9c6abc
  if (!webp_info->quiet_) {
Packit 9c6abc
    printf("  ICCP: %d\n  Alpha: %d\n  EXIF: %d\n  XMP: %d\n  Animation: %d\n",
Packit 9c6abc
           (webp_info->feature_flags_ & ICCP_FLAG) != 0,
Packit 9c6abc
           (webp_info->feature_flags_ & ALPHA_FLAG) != 0,
Packit 9c6abc
           (webp_info->feature_flags_ & EXIF_FLAG) != 0,
Packit 9c6abc
           (webp_info->feature_flags_ & XMP_FLAG) != 0,
Packit 9c6abc
           (webp_info->feature_flags_ & ANIMATION_FLAG) != 0);
Packit 9c6abc
    printf("  Canvas size %d x %d\n",
Packit 9c6abc
           webp_info->canvas_width_, webp_info->canvas_height_);
Packit 9c6abc
  }
Packit 9c6abc
  if (webp_info->canvas_width_ > MAX_CANVAS_SIZE) {
Packit 9c6abc
    LOG_WARN("Canvas width is out of range in VP8X chunk.");
Packit 9c6abc
  }
Packit 9c6abc
  if (webp_info->canvas_height_ > MAX_CANVAS_SIZE) {
Packit 9c6abc
    LOG_WARN("Canvas height is out of range in VP8X chunk.");
Packit 9c6abc
  }
Packit 9c6abc
  if ((uint64_t)webp_info->canvas_width_ * webp_info->canvas_height_ >
Packit 9c6abc
      MAX_IMAGE_AREA) {
Packit 9c6abc
    LOG_WARN("Canvas area is out of range in VP8X chunk.");
Packit 9c6abc
  }
Packit 9c6abc
  return WEBP_INFO_OK;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static WebPInfoStatus ProcessANIMChunk(const ChunkData* const chunk_data,
Packit 9c6abc
                                       WebPInfo* const webp_info) {
Packit 9c6abc
  const uint8_t* data = chunk_data->payload_;
Packit 9c6abc
  if (!webp_info->chunk_counts_[CHUNK_VP8X]) {
Packit 9c6abc
    LOG_ERROR("ANIM chunk detected before VP8X chunk.");
Packit 9c6abc
    return WEBP_INFO_PARSE_ERROR;
Packit 9c6abc
  }
Packit 9c6abc
  if (chunk_data->size_ != ANIM_CHUNK_SIZE + CHUNK_HEADER_SIZE) {
Packit 9c6abc
    LOG_ERROR("Corrupted ANIM chunk.");
Packit 9c6abc
    return WEBP_INFO_PARSE_ERROR;
Packit 9c6abc
  }
Packit 9c6abc
  webp_info->bgcolor_ = ReadLE32(&data);
Packit 9c6abc
  webp_info->loop_count_ = ReadLE16(&data);
Packit 9c6abc
  ++webp_info->chunk_counts_[CHUNK_ANIM];
Packit 9c6abc
  if (!webp_info->quiet_) {
Packit 9c6abc
    printf("  Background color:(ARGB) %02x %02x %02x %02x\n",
Packit 9c6abc
           (webp_info->bgcolor_ >> 24) & 0xff,
Packit 9c6abc
           (webp_info->bgcolor_ >> 16) & 0xff,
Packit 9c6abc
           (webp_info->bgcolor_ >> 8) & 0xff,
Packit 9c6abc
           webp_info->bgcolor_ & 0xff);
Packit 9c6abc
    printf("  Loop count      : %d\n", webp_info->loop_count_);
Packit 9c6abc
  }
Packit 9c6abc
  if (webp_info->loop_count_ > MAX_LOOP_COUNT) {
Packit 9c6abc
    LOG_WARN("Loop count is out of range in ANIM chunk.");
Packit 9c6abc
  }
Packit 9c6abc
  return WEBP_INFO_OK;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static WebPInfoStatus ProcessANMFChunk(const ChunkData* const chunk_data,
Packit 9c6abc
                                       WebPInfo* const webp_info) {
Packit 9c6abc
  const uint8_t* data = chunk_data->payload_;
Packit 9c6abc
  int offset_x, offset_y, width, height, duration, blend, dispose, temp;
Packit 9c6abc
  if (webp_info->is_processing_anim_frame_) {
Packit 9c6abc
    LOG_ERROR("ANMF chunk detected within another ANMF chunk.");
Packit 9c6abc
    return WEBP_INFO_PARSE_ERROR;
Packit 9c6abc
  }
Packit 9c6abc
  if (!webp_info->chunk_counts_[CHUNK_ANIM]) {
Packit 9c6abc
    LOG_ERROR("ANMF chunk detected before ANIM chunk.");
Packit 9c6abc
    return WEBP_INFO_PARSE_ERROR;
Packit 9c6abc
  }
Packit 9c6abc
  if (chunk_data->size_ <= CHUNK_HEADER_SIZE + ANMF_CHUNK_SIZE) {
Packit 9c6abc
    LOG_ERROR("Truncated data detected when parsing ANMF chunk.");
Packit 9c6abc
    return WEBP_INFO_TRUNCATED_DATA;
Packit 9c6abc
  }
Packit 9c6abc
  offset_x = 2 * ReadLE24(&data);
Packit 9c6abc
  offset_y = 2 * ReadLE24(&data);
Packit 9c6abc
  width = 1 + ReadLE24(&data);
Packit 9c6abc
  height = 1 + ReadLE24(&data);
Packit 9c6abc
  duration = ReadLE24(&data);
Packit 9c6abc
  temp = *data;
Packit 9c6abc
  dispose = temp & 1;
Packit 9c6abc
  blend = (temp >> 1) & 1;
Packit 9c6abc
  ++webp_info->chunk_counts_[CHUNK_ANMF];
Packit 9c6abc
  if (!webp_info->quiet_) {
Packit 9c6abc
    printf("  Offset_X: %d\n  Offset_Y: %d\n  Width: %d\n  Height: %d\n"
Packit 9c6abc
           "  Duration: %d\n  Dispose: %d\n  Blend: %d\n",
Packit 9c6abc
           offset_x, offset_y, width, height, duration, dispose, blend);
Packit 9c6abc
  }
Packit 9c6abc
  if (duration > MAX_DURATION) {
Packit 9c6abc
    LOG_ERROR("Invalid duration parameter in ANMF chunk.");
Packit 9c6abc
    return WEBP_INFO_INVALID_PARAM;
Packit 9c6abc
  }
Packit 9c6abc
  if (offset_x > MAX_POSITION_OFFSET || offset_y > MAX_POSITION_OFFSET) {
Packit 9c6abc
    LOG_ERROR("Invalid offset parameters in ANMF chunk.");
Packit 9c6abc
    return WEBP_INFO_INVALID_PARAM;
Packit 9c6abc
  }
Packit 9c6abc
  if ((uint64_t)offset_x + width > (uint64_t)webp_info->canvas_width_ ||
Packit 9c6abc
      (uint64_t)offset_y + height > (uint64_t)webp_info->canvas_height_) {
Packit 9c6abc
    LOG_ERROR("Frame exceeds canvas in ANMF chunk.");
Packit 9c6abc
    return WEBP_INFO_INVALID_PARAM;
Packit 9c6abc
  }
Packit 9c6abc
  webp_info->is_processing_anim_frame_ = 1;
Packit 9c6abc
  webp_info->seen_alpha_subchunk_ = 0;
Packit 9c6abc
  webp_info->seen_image_subchunk_ = 0;
Packit 9c6abc
  webp_info->frame_width_ = width;
Packit 9c6abc
  webp_info->frame_height_ = height;
Packit 9c6abc
  webp_info->anim_frame_data_size_ =
Packit 9c6abc
      chunk_data->size_ - CHUNK_HEADER_SIZE - ANMF_CHUNK_SIZE;
Packit 9c6abc
  return WEBP_INFO_OK;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static WebPInfoStatus ProcessImageChunk(const ChunkData* const chunk_data,
Packit 9c6abc
                                        WebPInfo* const webp_info) {
Packit 9c6abc
  const uint8_t* data = chunk_data->payload_ - CHUNK_HEADER_SIZE;
Packit 9c6abc
  WebPBitstreamFeatures features;
Packit 9c6abc
  const VP8StatusCode vp8_status =
Packit 9c6abc
      WebPGetFeatures(data, chunk_data->size_, &features);
Packit 9c6abc
  if (vp8_status != VP8_STATUS_OK) {
Packit 9c6abc
    LOG_ERROR("VP8/VP8L bitstream error.");
Packit 9c6abc
    return WEBP_INFO_BITSTREAM_ERROR;
Packit 9c6abc
  }
Packit 9c6abc
  if (!webp_info->quiet_) {
Packit 9c6abc
    assert(features.format >= 0 && features.format <= 2);
Packit 9c6abc
    printf("  Width: %d\n  Height: %d\n  Alpha: %d\n  Animation: %d\n"
Packit 9c6abc
           "  Format: %s (%d)\n",
Packit 9c6abc
           features.width, features.height, features.has_alpha,
Packit 9c6abc
           features.has_animation, kFormats[features.format], features.format);
Packit 9c6abc
  }
Packit 9c6abc
  if (webp_info->is_processing_anim_frame_) {
Packit 9c6abc
    ++webp_info->anmf_subchunk_counts_[chunk_data->id_ == CHUNK_VP8 ? 0 : 1];
Packit 9c6abc
    if (chunk_data->id_ == CHUNK_VP8L && webp_info->seen_alpha_subchunk_) {
Packit 9c6abc
      LOG_ERROR("Both VP8L and ALPH sub-chunks are present in an ANMF chunk.");
Packit 9c6abc
      return WEBP_INFO_PARSE_ERROR;
Packit 9c6abc
    }
Packit 9c6abc
    if (webp_info->frame_width_ != features.width ||
Packit 9c6abc
        webp_info->frame_height_ != features.height) {
Packit 9c6abc
      LOG_ERROR("Frame size in VP8/VP8L sub-chunk differs from ANMF header.");
Packit 9c6abc
      return WEBP_INFO_PARSE_ERROR;
Packit 9c6abc
    }
Packit 9c6abc
    if (webp_info->seen_image_subchunk_) {
Packit 9c6abc
      LOG_ERROR("Consecutive VP8/VP8L sub-chunks in an ANMF chunk.");
Packit 9c6abc
      return WEBP_INFO_PARSE_ERROR;
Packit 9c6abc
    }
Packit 9c6abc
    webp_info->seen_image_subchunk_ = 1;
Packit 9c6abc
  } else {
Packit 9c6abc
    if (webp_info->chunk_counts_[CHUNK_VP8] ||
Packit 9c6abc
        webp_info->chunk_counts_[CHUNK_VP8L]) {
Packit 9c6abc
      LOG_ERROR("Multiple VP8/VP8L chunks detected.");
Packit 9c6abc
      return WEBP_INFO_PARSE_ERROR;
Packit 9c6abc
    }
Packit 9c6abc
    if (chunk_data->id_ == CHUNK_VP8L &&
Packit 9c6abc
        webp_info->chunk_counts_[CHUNK_ALPHA]) {
Packit 9c6abc
      LOG_WARN("Both VP8L and ALPH chunks are detected.");
Packit 9c6abc
    }
Packit 9c6abc
    if (webp_info->chunk_counts_[CHUNK_ANIM] ||
Packit 9c6abc
        webp_info->chunk_counts_[CHUNK_ANMF]) {
Packit 9c6abc
      LOG_ERROR("VP8/VP8L chunk and ANIM/ANMF chunk are both detected.");
Packit 9c6abc
      return WEBP_INFO_PARSE_ERROR;
Packit 9c6abc
    }
Packit 9c6abc
    if (webp_info->chunk_counts_[CHUNK_VP8X]) {
Packit 9c6abc
      if (webp_info->canvas_width_ != features.width ||
Packit 9c6abc
          webp_info->canvas_height_ != features.height) {
Packit 9c6abc
        LOG_ERROR("Image size in VP8/VP8L chunk differs from VP8X chunk.");
Packit 9c6abc
        return WEBP_INFO_PARSE_ERROR;
Packit 9c6abc
      }
Packit 9c6abc
    } else {
Packit 9c6abc
      webp_info->canvas_width_ = features.width;
Packit 9c6abc
      webp_info->canvas_height_ = features.height;
Packit 9c6abc
      if (webp_info->canvas_width_ < 1 || webp_info->canvas_height_ < 1 ||
Packit 9c6abc
          webp_info->canvas_width_ > MAX_CANVAS_SIZE ||
Packit 9c6abc
          webp_info->canvas_height_ > MAX_CANVAS_SIZE ||
Packit 9c6abc
          (uint64_t)webp_info->canvas_width_ * webp_info->canvas_height_ >
Packit 9c6abc
              MAX_IMAGE_AREA) {
Packit 9c6abc
        LOG_WARN("Invalid parameters in VP8/VP8L chunk.");
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
    ++webp_info->chunk_counts_[chunk_data->id_];
Packit 9c6abc
  }
Packit 9c6abc
  ++webp_info->num_frames_;
Packit 9c6abc
  webp_info->has_alpha_ |= features.has_alpha;
Packit 9c6abc
  if (webp_info->parse_bitstream_) {
Packit 9c6abc
    const int is_lossy = (chunk_data->id_ == CHUNK_VP8);
Packit 9c6abc
    const WebPInfoStatus status =
Packit 9c6abc
        is_lossy ? ParseLossyHeader(chunk_data, webp_info)
Packit 9c6abc
                 : ParseLosslessHeader(chunk_data, webp_info);
Packit 9c6abc
    if (status != WEBP_INFO_OK) return status;
Packit 9c6abc
  }
Packit 9c6abc
  return WEBP_INFO_OK;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static WebPInfoStatus ProcessALPHChunk(const ChunkData* const chunk_data,
Packit 9c6abc
                                       WebPInfo* const webp_info) {
Packit 9c6abc
  if (webp_info->is_processing_anim_frame_) {
Packit 9c6abc
    ++webp_info->anmf_subchunk_counts_[2];
Packit 9c6abc
    if (webp_info->seen_alpha_subchunk_) {
Packit 9c6abc
      LOG_ERROR("Consecutive ALPH sub-chunks in an ANMF chunk.");
Packit 9c6abc
      return WEBP_INFO_PARSE_ERROR;
Packit 9c6abc
    }
Packit 9c6abc
    webp_info->seen_alpha_subchunk_ = 1;
Packit 9c6abc
Packit 9c6abc
    if (webp_info->seen_image_subchunk_) {
Packit 9c6abc
      LOG_ERROR("ALPHA sub-chunk detected after VP8 sub-chunk "
Packit 9c6abc
                "in an ANMF chunk.");
Packit 9c6abc
      return WEBP_INFO_PARSE_ERROR;
Packit 9c6abc
    }
Packit 9c6abc
  } else {
Packit 9c6abc
    if (webp_info->chunk_counts_[CHUNK_ANIM] ||
Packit 9c6abc
        webp_info->chunk_counts_[CHUNK_ANMF]) {
Packit 9c6abc
      LOG_ERROR("ALPHA chunk and ANIM/ANMF chunk are both detected.");
Packit 9c6abc
      return WEBP_INFO_PARSE_ERROR;
Packit 9c6abc
    }
Packit 9c6abc
    if (!webp_info->chunk_counts_[CHUNK_VP8X]) {
Packit 9c6abc
      LOG_ERROR("ALPHA chunk detected before VP8X chunk.");
Packit 9c6abc
      return WEBP_INFO_PARSE_ERROR;
Packit 9c6abc
    }
Packit 9c6abc
    if (webp_info->chunk_counts_[CHUNK_VP8]) {
Packit 9c6abc
      LOG_ERROR("ALPHA chunk detected after VP8 chunk.");
Packit 9c6abc
      return WEBP_INFO_PARSE_ERROR;
Packit 9c6abc
    }
Packit 9c6abc
    if (webp_info->chunk_counts_[CHUNK_ALPHA]) {
Packit 9c6abc
      LOG_ERROR("Multiple ALPHA chunks detected.");
Packit 9c6abc
      return WEBP_INFO_PARSE_ERROR;
Packit 9c6abc
    }
Packit 9c6abc
    ++webp_info->chunk_counts_[CHUNK_ALPHA];
Packit 9c6abc
  }
Packit 9c6abc
  webp_info->has_alpha_ = 1;
Packit 9c6abc
  if (webp_info->parse_bitstream_) {
Packit 9c6abc
    const WebPInfoStatus status = ParseAlphaHeader(chunk_data, webp_info);
Packit 9c6abc
    if (status != WEBP_INFO_OK) return status;
Packit 9c6abc
  }
Packit 9c6abc
  return WEBP_INFO_OK;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static WebPInfoStatus ProcessICCPChunk(const ChunkData* const chunk_data,
Packit 9c6abc
                                       WebPInfo* const webp_info) {
Packit 9c6abc
  (void)chunk_data;
Packit 9c6abc
  if (!webp_info->chunk_counts_[CHUNK_VP8X]) {
Packit 9c6abc
    LOG_ERROR("ICCP chunk detected before VP8X chunk.");
Packit 9c6abc
    return WEBP_INFO_PARSE_ERROR;
Packit 9c6abc
  }
Packit 9c6abc
  if (webp_info->chunk_counts_[CHUNK_VP8] ||
Packit 9c6abc
      webp_info->chunk_counts_[CHUNK_VP8L] ||
Packit 9c6abc
      webp_info->chunk_counts_[CHUNK_ANIM]) {
Packit 9c6abc
    LOG_ERROR("ICCP chunk detected after image data.");
Packit 9c6abc
    return WEBP_INFO_PARSE_ERROR;
Packit 9c6abc
  }
Packit 9c6abc
  ++webp_info->chunk_counts_[CHUNK_ICCP];
Packit 9c6abc
  return WEBP_INFO_OK;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static WebPInfoStatus ProcessChunk(const ChunkData* const chunk_data,
Packit 9c6abc
                                   WebPInfo* const webp_info) {
Packit 9c6abc
  WebPInfoStatus status = WEBP_INFO_OK;
Packit 9c6abc
  ChunkID id = chunk_data->id_;
Packit 9c6abc
  if (chunk_data->id_ == CHUNK_UNKNOWN) {
Packit 9c6abc
    char error_message[50];
Packit 9c6abc
    snprintf(error_message, 50, "Unknown chunk at offset %6d, length %6d",
Packit 9c6abc
            (int)chunk_data->offset_, (int)chunk_data->size_);
Packit 9c6abc
    LOG_WARN(error_message);
Packit 9c6abc
  } else {
Packit 9c6abc
    if (!webp_info->quiet_) {
Packit 9c6abc
      const char* tag = kWebPChunkTags[chunk_data->id_];
Packit 9c6abc
      printf("Chunk %c%c%c%c at offset %6d, length %6d\n",
Packit 9c6abc
             tag[0], tag[1], tag[2], tag[3], (int)chunk_data->offset_,
Packit 9c6abc
             (int)chunk_data->size_);
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  switch (id) {
Packit 9c6abc
    case CHUNK_VP8:
Packit 9c6abc
    case CHUNK_VP8L:
Packit 9c6abc
      status = ProcessImageChunk(chunk_data, webp_info);
Packit 9c6abc
      break;
Packit 9c6abc
    case CHUNK_VP8X:
Packit 9c6abc
      status = ProcessVP8XChunk(chunk_data, webp_info);
Packit 9c6abc
      break;
Packit 9c6abc
    case CHUNK_ALPHA:
Packit 9c6abc
      status = ProcessALPHChunk(chunk_data, webp_info);
Packit 9c6abc
      break;
Packit 9c6abc
    case CHUNK_ANIM:
Packit 9c6abc
      status = ProcessANIMChunk(chunk_data, webp_info);
Packit 9c6abc
      break;
Packit 9c6abc
    case CHUNK_ANMF:
Packit 9c6abc
      status = ProcessANMFChunk(chunk_data, webp_info);
Packit 9c6abc
      break;
Packit 9c6abc
    case CHUNK_ICCP:
Packit 9c6abc
      status = ProcessICCPChunk(chunk_data, webp_info);
Packit 9c6abc
      break;
Packit 9c6abc
    case CHUNK_EXIF:
Packit 9c6abc
    case CHUNK_XMP:
Packit 9c6abc
      ++webp_info->chunk_counts_[id];
Packit 9c6abc
      break;
Packit 9c6abc
    case CHUNK_UNKNOWN:
Packit 9c6abc
    default:
Packit 9c6abc
      break;
Packit 9c6abc
  }
Packit 9c6abc
  if (webp_info->is_processing_anim_frame_ && id != CHUNK_ANMF) {
Packit 9c6abc
    if (webp_info->anim_frame_data_size_ == chunk_data->size_) {
Packit 9c6abc
      if (!webp_info->seen_image_subchunk_) {
Packit 9c6abc
        LOG_ERROR("No VP8/VP8L chunk detected in an ANMF chunk.");
Packit 9c6abc
        return WEBP_INFO_PARSE_ERROR;
Packit 9c6abc
      }
Packit 9c6abc
      webp_info->is_processing_anim_frame_ = 0;
Packit 9c6abc
    } else if (webp_info->anim_frame_data_size_ > chunk_data->size_) {
Packit 9c6abc
      webp_info->anim_frame_data_size_ -= chunk_data->size_;
Packit 9c6abc
    } else {
Packit 9c6abc
      LOG_ERROR("Truncated data detected when parsing ANMF chunk.");
Packit 9c6abc
      return WEBP_INFO_TRUNCATED_DATA;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  return status;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static WebPInfoStatus Validate(const WebPInfo* const webp_info) {
Packit 9c6abc
  if (webp_info->num_frames_ < 1) {
Packit 9c6abc
    LOG_ERROR("No image/frame detected.");
Packit 9c6abc
    return WEBP_INFO_MISSING_DATA;
Packit 9c6abc
  }
Packit 9c6abc
  if (webp_info->chunk_counts_[CHUNK_VP8X]) {
Packit 9c6abc
    const int iccp = !!(webp_info->feature_flags_ & ICCP_FLAG);
Packit 9c6abc
    const int exif = !!(webp_info->feature_flags_ & EXIF_FLAG);
Packit 9c6abc
    const int xmp = !!(webp_info->feature_flags_ & XMP_FLAG);
Packit 9c6abc
    const int animation = !!(webp_info->feature_flags_ & ANIMATION_FLAG);
Packit 9c6abc
    const int alpha = !!(webp_info->feature_flags_ & ALPHA_FLAG);
Packit 9c6abc
    if (!alpha && webp_info->has_alpha_) {
Packit 9c6abc
      LOG_ERROR("Unexpected alpha data detected.");
Packit 9c6abc
      return WEBP_INFO_PARSE_ERROR;
Packit 9c6abc
    }
Packit 9c6abc
    if (alpha && !webp_info->has_alpha_) {
Packit 9c6abc
      LOG_WARN("Alpha flag is set with no alpha data present.");
Packit 9c6abc
    }
Packit 9c6abc
    if (iccp && !webp_info->chunk_counts_[CHUNK_ICCP]) {
Packit 9c6abc
      LOG_ERROR("Missing ICCP chunk.");
Packit 9c6abc
      return WEBP_INFO_MISSING_DATA;
Packit 9c6abc
    }
Packit 9c6abc
    if (exif && !webp_info->chunk_counts_[CHUNK_EXIF]) {
Packit 9c6abc
      LOG_ERROR("Missing EXIF chunk.");
Packit 9c6abc
      return WEBP_INFO_MISSING_DATA;
Packit 9c6abc
    }
Packit 9c6abc
    if (xmp && !webp_info->chunk_counts_[CHUNK_XMP]) {
Packit 9c6abc
      LOG_ERROR("Missing XMP chunk.");
Packit 9c6abc
      return WEBP_INFO_MISSING_DATA;
Packit 9c6abc
    }
Packit 9c6abc
    if (!iccp && webp_info->chunk_counts_[CHUNK_ICCP]) {
Packit 9c6abc
      LOG_ERROR("Unexpected ICCP chunk detected.");
Packit 9c6abc
      return WEBP_INFO_PARSE_ERROR;
Packit 9c6abc
    }
Packit 9c6abc
    if (!exif && webp_info->chunk_counts_[CHUNK_EXIF]) {
Packit 9c6abc
      LOG_ERROR("Unexpected EXIF chunk detected.");
Packit 9c6abc
      return WEBP_INFO_PARSE_ERROR;
Packit 9c6abc
    }
Packit 9c6abc
    if (!xmp && webp_info->chunk_counts_[CHUNK_XMP]) {
Packit 9c6abc
      LOG_ERROR("Unexpected XMP chunk detected.");
Packit 9c6abc
      return WEBP_INFO_PARSE_ERROR;
Packit 9c6abc
    }
Packit 9c6abc
    // Incomplete animation frame.
Packit 9c6abc
    if (webp_info->is_processing_anim_frame_) return WEBP_INFO_MISSING_DATA;
Packit 9c6abc
    if (!animation && webp_info->num_frames_ > 1) {
Packit 9c6abc
      LOG_ERROR("More than 1 frame detected in non-animation file.");
Packit 9c6abc
      return WEBP_INFO_PARSE_ERROR;
Packit 9c6abc
    }
Packit 9c6abc
    if (animation && (!webp_info->chunk_counts_[CHUNK_ANIM] ||
Packit 9c6abc
        !webp_info->chunk_counts_[CHUNK_ANMF])) {
Packit 9c6abc
      LOG_ERROR("No ANIM/ANMF chunk detected in animation file.");
Packit 9c6abc
      return WEBP_INFO_PARSE_ERROR;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  return WEBP_INFO_OK;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static void ShowSummary(const WebPInfo* const webp_info) {
Packit 9c6abc
  int i;
Packit 9c6abc
  printf("Summary:\n");
Packit 9c6abc
  printf("Number of frames: %d\n", webp_info->num_frames_);
Packit 9c6abc
  printf("Chunk type  :  VP8 VP8L VP8X ALPH ANIM ANMF(VP8 /VP8L/ALPH) ICCP "
Packit 9c6abc
      "EXIF  XMP\n");
Packit 9c6abc
  printf("Chunk counts: ");
Packit 9c6abc
  for (i = 0; i < CHUNK_TYPES; ++i) {
Packit 9c6abc
    printf("%4d ", webp_info->chunk_counts_[i]);
Packit 9c6abc
    if (i == CHUNK_ANMF) {
Packit 9c6abc
      printf("%4d %4d %4d  ",
Packit 9c6abc
             webp_info->anmf_subchunk_counts_[0],
Packit 9c6abc
             webp_info->anmf_subchunk_counts_[1],
Packit 9c6abc
             webp_info->anmf_subchunk_counts_[2]);
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  printf("\n");
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static WebPInfoStatus AnalyzeWebP(WebPInfo* const webp_info,
Packit 9c6abc
                                  const WebPData* webp_data) {
Packit 9c6abc
  ChunkData chunk_data;
Packit 9c6abc
  MemBuffer mem_buffer;
Packit 9c6abc
  WebPInfoStatus webp_info_status = WEBP_INFO_OK;
Packit 9c6abc
Packit 9c6abc
  InitMemBuffer(&mem_buffer, webp_data);
Packit 9c6abc
  webp_info_status = ParseRIFFHeader(webp_info, &mem_buffer);
Packit 9c6abc
  if (webp_info_status != WEBP_INFO_OK) goto Error;
Packit 9c6abc
Packit 9c6abc
  //  Loop through all the chunks. Terminate immediately in case of error.
Packit 9c6abc
  while (webp_info_status == WEBP_INFO_OK && MemDataSize(&mem_buffer) > 0) {
Packit 9c6abc
    webp_info_status = ParseChunk(webp_info, &mem_buffer, &chunk_data);
Packit 9c6abc
    if (webp_info_status != WEBP_INFO_OK) goto Error;
Packit 9c6abc
    webp_info_status = ProcessChunk(&chunk_data, webp_info);
Packit 9c6abc
  }
Packit 9c6abc
  if (webp_info_status != WEBP_INFO_OK) goto Error;
Packit 9c6abc
  if (webp_info->show_summary_) ShowSummary(webp_info);
Packit 9c6abc
Packit 9c6abc
  //  Final check.
Packit 9c6abc
  webp_info_status = Validate(webp_info);
Packit 9c6abc
Packit 9c6abc
 Error:
Packit 9c6abc
  if (!webp_info->quiet_) {
Packit 9c6abc
    if (webp_info_status == WEBP_INFO_OK) {
Packit 9c6abc
      printf("No error detected.\n");
Packit 9c6abc
    } else {
Packit 9c6abc
      printf("Errors detected.\n");
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  return webp_info_status;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static void HelpShort(void) {
Packit 9c6abc
  printf("Usage: webpinfo [options] in_files\n"
Packit 9c6abc
         "Try -longhelp for an exhaustive list of options.\n");
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static void HelpLong(void) {
Packit 9c6abc
  printf("Usage: webpinfo [options] in_files\n"
Packit 9c6abc
         "Note: there could be multiple input files;\n"
Packit 9c6abc
         "      options must come before input files.\n"
Packit 9c6abc
         "Options:\n"
Packit 9c6abc
         "  -version ........... Print version number and exit.\n"
Packit 9c6abc
         "  -quiet ............. Do not show chunk parsing information.\n"
Packit 9c6abc
         "  -diag .............. Show parsing error diagnosis.\n"
Packit 9c6abc
         "  -summary ........... Show chunk stats summary.\n"
Packit 9c6abc
         "  -bitstream_info .... Parse bitstream header.\n");
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
int main(int argc, const char* argv[]) {
Packit 9c6abc
  int c, quiet = 0, show_diag = 0, show_summary = 0;
Packit 9c6abc
  int parse_bitstream = 0;
Packit 9c6abc
  WebPInfoStatus webp_info_status = WEBP_INFO_OK;
Packit 9c6abc
  WebPInfo webp_info;
Packit 9c6abc
Packit 9c6abc
  if (argc == 1) {
Packit 9c6abc
    HelpShort();
Packit 9c6abc
    return WEBP_INFO_OK;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  // Parse command-line input.
Packit 9c6abc
  for (c = 1; c < argc; ++c) {
Packit 9c6abc
    if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
Packit 9c6abc
      HelpShort();
Packit 9c6abc
      return WEBP_INFO_OK;
Packit 9c6abc
    } else if (!strcmp(argv[c], "-H") || !strcmp(argv[c], "-longhelp")) {
Packit 9c6abc
      HelpLong();
Packit 9c6abc
      return WEBP_INFO_OK;
Packit 9c6abc
    } else if (!strcmp(argv[c], "-quiet")) {
Packit 9c6abc
      quiet = 1;
Packit 9c6abc
    } else if (!strcmp(argv[c], "-diag")) {
Packit 9c6abc
      show_diag = 1;
Packit 9c6abc
    } else if (!strcmp(argv[c], "-summary")) {
Packit 9c6abc
      show_summary = 1;
Packit 9c6abc
    } else if (!strcmp(argv[c], "-bitstream_info")) {
Packit 9c6abc
      parse_bitstream = 1;
Packit 9c6abc
    } else if (!strcmp(argv[c], "-version")) {
Packit 9c6abc
      const int version = WebPGetDecoderVersion();
Packit 9c6abc
      printf("WebP Decoder version: %d.%d.%d\n",
Packit 9c6abc
             (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
Packit 9c6abc
      return 0;
Packit 9c6abc
    } else {  // Assume the remaining are all input files.
Packit 9c6abc
      break;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  if (c == argc) {
Packit 9c6abc
    HelpShort();
Packit 9c6abc
    return WEBP_INFO_INVALID_COMMAND;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  // Process input files one by one.
Packit 9c6abc
  for (; c < argc; ++c) {
Packit 9c6abc
    WebPData webp_data;
Packit 9c6abc
    const char* in_file = NULL;
Packit 9c6abc
    WebPInfoInit(&webp_info);
Packit 9c6abc
    webp_info.quiet_ = quiet;
Packit 9c6abc
    webp_info.show_diagnosis_ = show_diag;
Packit 9c6abc
    webp_info.show_summary_ = show_summary;
Packit 9c6abc
    webp_info.parse_bitstream_ = parse_bitstream;
Packit 9c6abc
    in_file = argv[c];
Packit 9c6abc
    if (in_file == NULL || !ReadFileToWebPData(in_file, &webp_data)) {
Packit 9c6abc
      webp_info_status = WEBP_INFO_INVALID_COMMAND;
Packit 9c6abc
      fprintf(stderr, "Failed to open input file %s.\n", in_file);
Packit 9c6abc
      continue;
Packit 9c6abc
    }
Packit 9c6abc
    if (!webp_info.quiet_) printf("File: %s\n", in_file);
Packit 9c6abc
    webp_info_status = AnalyzeWebP(&webp_info, &webp_data);
Packit 9c6abc
    WebPDataClear(&webp_data);
Packit 9c6abc
  }
Packit 9c6abc
  return webp_info_status;
Packit 9c6abc
}