github.com/cellofellow/gopkg@v0.0.0-20140722061823-eec0544a62ad/image/webp/libwebp/src/demux/demux.c (about)

     1  // Copyright 2012 Google Inc. All Rights Reserved.
     2  //
     3  // Use of this source code is governed by a BSD-style license
     4  // that can be found in the COPYING file in the root of the source
     5  // tree. An additional intellectual property rights grant can be found
     6  // in the file PATENTS. All contributing project authors may
     7  // be found in the AUTHORS file in the root of the source tree.
     8  // -----------------------------------------------------------------------------
     9  //
    10  //  WebP container demux.
    11  //
    12  
    13  #ifdef HAVE_CONFIG_H
    14  #include "config.h"
    15  #endif
    16  
    17  #include <assert.h>
    18  #include <stdlib.h>
    19  #include <string.h>
    20  
    21  #include "../utils/utils.h"
    22  #include "../webp/decode.h"     // WebPGetFeatures
    23  #include "../webp/demux.h"
    24  #include "../webp/format_constants.h"
    25  
    26  #define DMUX_MAJ_VERSION 0
    27  #define DMUX_MIN_VERSION 2
    28  #define DMUX_REV_VERSION 0
    29  
    30  typedef struct {
    31    size_t start_;        // start location of the data
    32    size_t end_;          // end location
    33    size_t riff_end_;     // riff chunk end location, can be > end_.
    34    size_t buf_size_;     // size of the buffer
    35    const uint8_t* buf_;
    36  } MemBuffer;
    37  
    38  typedef struct {
    39    size_t offset_;
    40    size_t size_;
    41  } ChunkData;
    42  
    43  typedef struct Frame {
    44    int x_offset_, y_offset_;
    45    int width_, height_;
    46    int has_alpha_;
    47    int duration_;
    48    WebPMuxAnimDispose dispose_method_;
    49    WebPMuxAnimBlend blend_method_;
    50    int is_fragment_;  // this is a frame fragment (and not a full frame).
    51    int frame_num_;  // the referent frame number for use in assembling fragments.
    52    int complete_;   // img_components_ contains a full image.
    53    ChunkData img_components_[2];  // 0=VP8{,L} 1=ALPH
    54    struct Frame* next_;
    55  } Frame;
    56  
    57  typedef struct Chunk {
    58    ChunkData data_;
    59    struct Chunk* next_;
    60  } Chunk;
    61  
    62  struct WebPDemuxer {
    63    MemBuffer mem_;
    64    WebPDemuxState state_;
    65    int is_ext_format_;
    66    uint32_t feature_flags_;
    67    int canvas_width_, canvas_height_;
    68    int loop_count_;
    69    uint32_t bgcolor_;
    70    int num_frames_;
    71    Frame* frames_;
    72    Frame** frames_tail_;
    73    Chunk* chunks_;  // non-image chunks
    74    Chunk** chunks_tail_;
    75  };
    76  
    77  typedef enum {
    78    PARSE_OK,
    79    PARSE_NEED_MORE_DATA,
    80    PARSE_ERROR
    81  } ParseStatus;
    82  
    83  typedef struct ChunkParser {
    84    uint8_t id[4];
    85    ParseStatus (*parse)(WebPDemuxer* const dmux);
    86    int (*valid)(const WebPDemuxer* const dmux);
    87  } ChunkParser;
    88  
    89  static ParseStatus ParseSingleImage(WebPDemuxer* const dmux);
    90  static ParseStatus ParseVP8X(WebPDemuxer* const dmux);
    91  static int IsValidSimpleFormat(const WebPDemuxer* const dmux);
    92  static int IsValidExtendedFormat(const WebPDemuxer* const dmux);
    93  
    94  static const ChunkParser kMasterChunks[] = {
    95    { { 'V', 'P', '8', ' ' }, ParseSingleImage, IsValidSimpleFormat },
    96    { { 'V', 'P', '8', 'L' }, ParseSingleImage, IsValidSimpleFormat },
    97    { { 'V', 'P', '8', 'X' }, ParseVP8X,        IsValidExtendedFormat },
    98    { { '0', '0', '0', '0' }, NULL,             NULL },
    99  };
   100  
   101  //------------------------------------------------------------------------------
   102  
   103  int WebPGetDemuxVersion(void) {
   104    return (DMUX_MAJ_VERSION << 16) | (DMUX_MIN_VERSION << 8) | DMUX_REV_VERSION;
   105  }
   106  
   107  // -----------------------------------------------------------------------------
   108  // MemBuffer
   109  
   110  static int RemapMemBuffer(MemBuffer* const mem,
   111                            const uint8_t* data, size_t size) {
   112    if (size < mem->buf_size_) return 0;  // can't remap to a shorter buffer!
   113  
   114    mem->buf_ = data;
   115    mem->end_ = mem->buf_size_ = size;
   116    return 1;
   117  }
   118  
   119  static int InitMemBuffer(MemBuffer* const mem,
   120                           const uint8_t* data, size_t size) {
   121    memset(mem, 0, sizeof(*mem));
   122    return RemapMemBuffer(mem, data, size);
   123  }
   124  
   125  // Return the remaining data size available in 'mem'.
   126  static WEBP_INLINE size_t MemDataSize(const MemBuffer* const mem) {
   127    return (mem->end_ - mem->start_);
   128  }
   129  
   130  // Return true if 'size' exceeds the end of the RIFF chunk.
   131  static WEBP_INLINE int SizeIsInvalid(const MemBuffer* const mem, size_t size) {
   132    return (size > mem->riff_end_ - mem->start_);
   133  }
   134  
   135  static WEBP_INLINE void Skip(MemBuffer* const mem, size_t size) {
   136    mem->start_ += size;
   137  }
   138  
   139  static WEBP_INLINE void Rewind(MemBuffer* const mem, size_t size) {
   140    mem->start_ -= size;
   141  }
   142  
   143  static WEBP_INLINE const uint8_t* GetBuffer(MemBuffer* const mem) {
   144    return mem->buf_ + mem->start_;
   145  }
   146  
   147  // Read from 'mem' and skip the read bytes.
   148  static WEBP_INLINE uint8_t ReadByte(MemBuffer* const mem) {
   149    const uint8_t byte = mem->buf_[mem->start_];
   150    Skip(mem, 1);
   151    return byte;
   152  }
   153  
   154  static WEBP_INLINE int ReadLE16s(MemBuffer* const mem) {
   155    const uint8_t* const data = mem->buf_ + mem->start_;
   156    const int val = GetLE16(data);
   157    Skip(mem, 2);
   158    return val;
   159  }
   160  
   161  static WEBP_INLINE int ReadLE24s(MemBuffer* const mem) {
   162    const uint8_t* const data = mem->buf_ + mem->start_;
   163    const int val = GetLE24(data);
   164    Skip(mem, 3);
   165    return val;
   166  }
   167  
   168  static WEBP_INLINE uint32_t ReadLE32(MemBuffer* const mem) {
   169    const uint8_t* const data = mem->buf_ + mem->start_;
   170    const uint32_t val = GetLE32(data);
   171    Skip(mem, 4);
   172    return val;
   173  }
   174  
   175  // -----------------------------------------------------------------------------
   176  // Secondary chunk parsing
   177  
   178  static void AddChunk(WebPDemuxer* const dmux, Chunk* const chunk) {
   179    *dmux->chunks_tail_ = chunk;
   180    chunk->next_ = NULL;
   181    dmux->chunks_tail_ = &chunk->next_;
   182  }
   183  
   184  // Add a frame to the end of the list, ensuring the last frame is complete.
   185  // Returns true on success, false otherwise.
   186  static int AddFrame(WebPDemuxer* const dmux, Frame* const frame) {
   187    const Frame* const last_frame = *dmux->frames_tail_;
   188    if (last_frame != NULL && !last_frame->complete_) return 0;
   189  
   190    *dmux->frames_tail_ = frame;
   191    frame->next_ = NULL;
   192    dmux->frames_tail_ = &frame->next_;
   193    return 1;
   194  }
   195  
   196  // Store image bearing chunks to 'frame'.
   197  static ParseStatus StoreFrame(int frame_num, uint32_t min_size,
   198                                MemBuffer* const mem, Frame* const frame) {
   199    int alpha_chunks = 0;
   200    int image_chunks = 0;
   201    int done = (MemDataSize(mem) < min_size);
   202    ParseStatus status = PARSE_OK;
   203  
   204    if (done) return PARSE_NEED_MORE_DATA;
   205  
   206    do {
   207      const size_t chunk_start_offset = mem->start_;
   208      const uint32_t fourcc = ReadLE32(mem);
   209      const uint32_t payload_size = ReadLE32(mem);
   210      const uint32_t payload_size_padded = payload_size + (payload_size & 1);
   211      const size_t payload_available = (payload_size_padded > MemDataSize(mem))
   212                                     ? MemDataSize(mem) : payload_size_padded;
   213      const size_t chunk_size = CHUNK_HEADER_SIZE + payload_available;
   214  
   215      if (payload_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
   216      if (SizeIsInvalid(mem, payload_size_padded)) return PARSE_ERROR;
   217      if (payload_size_padded > MemDataSize(mem)) status = PARSE_NEED_MORE_DATA;
   218  
   219      switch (fourcc) {
   220        case MKFOURCC('A', 'L', 'P', 'H'):
   221          if (alpha_chunks == 0) {
   222            ++alpha_chunks;
   223            frame->img_components_[1].offset_ = chunk_start_offset;
   224            frame->img_components_[1].size_ = chunk_size;
   225            frame->has_alpha_ = 1;
   226            frame->frame_num_ = frame_num;
   227            Skip(mem, payload_available);
   228          } else {
   229            goto Done;
   230          }
   231          break;
   232        case MKFOURCC('V', 'P', '8', 'L'):
   233          if (alpha_chunks > 0) return PARSE_ERROR;  // VP8L has its own alpha
   234          // fall through
   235        case MKFOURCC('V', 'P', '8', ' '):
   236          if (image_chunks == 0) {
   237            // Extract the bitstream features, tolerating failures when the data
   238            // is incomplete.
   239            WebPBitstreamFeatures features;
   240            const VP8StatusCode vp8_status =
   241                WebPGetFeatures(mem->buf_ + chunk_start_offset, chunk_size,
   242                                &features);
   243            if (status == PARSE_NEED_MORE_DATA &&
   244                vp8_status == VP8_STATUS_NOT_ENOUGH_DATA) {
   245              return PARSE_NEED_MORE_DATA;
   246            } else if (vp8_status != VP8_STATUS_OK) {
   247              // We have enough data, and yet WebPGetFeatures() failed.
   248              return PARSE_ERROR;
   249            }
   250            ++image_chunks;
   251            frame->img_components_[0].offset_ = chunk_start_offset;
   252            frame->img_components_[0].size_ = chunk_size;
   253            frame->width_ = features.width;
   254            frame->height_ = features.height;
   255            frame->has_alpha_ |= features.has_alpha;
   256            frame->frame_num_ = frame_num;
   257            frame->complete_ = (status == PARSE_OK);
   258            Skip(mem, payload_available);
   259          } else {
   260            goto Done;
   261          }
   262          break;
   263   Done:
   264        default:
   265          // Restore fourcc/size when moving up one level in parsing.
   266          Rewind(mem, CHUNK_HEADER_SIZE);
   267          done = 1;
   268          break;
   269      }
   270  
   271      if (mem->start_ == mem->riff_end_) {
   272        done = 1;
   273      } else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) {
   274        status = PARSE_NEED_MORE_DATA;
   275      }
   276    } while (!done && status == PARSE_OK);
   277  
   278    return status;
   279  }
   280  
   281  // Creates a new Frame if 'actual_size' is within bounds and 'mem' contains
   282  // enough data ('min_size') to parse the payload.
   283  // Returns PARSE_OK on success with *frame pointing to the new Frame.
   284  // Returns PARSE_NEED_MORE_DATA with insufficient data, PARSE_ERROR otherwise.
   285  static ParseStatus NewFrame(const MemBuffer* const mem,
   286                              uint32_t min_size, uint32_t actual_size,
   287                              Frame** frame) {
   288    if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR;
   289    if (actual_size < min_size) return PARSE_ERROR;
   290    if (MemDataSize(mem) < min_size)  return PARSE_NEED_MORE_DATA;
   291  
   292    *frame = (Frame*)calloc(1, sizeof(**frame));
   293    return (*frame == NULL) ? PARSE_ERROR : PARSE_OK;
   294  }
   295  
   296  // Parse a 'ANMF' chunk and any image bearing chunks that immediately follow.
   297  // 'frame_chunk_size' is the previously validated, padded chunk size.
   298  static ParseStatus ParseAnimationFrame(
   299      WebPDemuxer* const dmux, uint32_t frame_chunk_size) {
   300    const int is_animation = !!(dmux->feature_flags_ & ANIMATION_FLAG);
   301    const uint32_t anmf_payload_size = frame_chunk_size - ANMF_CHUNK_SIZE;
   302    int added_frame = 0;
   303    int bits;
   304    MemBuffer* const mem = &dmux->mem_;
   305    Frame* frame;
   306    ParseStatus status =
   307        NewFrame(mem, ANMF_CHUNK_SIZE, frame_chunk_size, &frame);
   308    if (status != PARSE_OK) return status;
   309  
   310    frame->x_offset_       = 2 * ReadLE24s(mem);
   311    frame->y_offset_       = 2 * ReadLE24s(mem);
   312    frame->width_          = 1 + ReadLE24s(mem);
   313    frame->height_         = 1 + ReadLE24s(mem);
   314    frame->duration_       = ReadLE24s(mem);
   315    bits = ReadByte(mem);
   316    frame->dispose_method_ =
   317        (bits & 1) ? WEBP_MUX_DISPOSE_BACKGROUND : WEBP_MUX_DISPOSE_NONE;
   318    frame->blend_method_ = (bits & 2) ? WEBP_MUX_NO_BLEND : WEBP_MUX_BLEND;
   319    if (frame->width_ * (uint64_t)frame->height_ >= MAX_IMAGE_AREA) {
   320      free(frame);
   321      return PARSE_ERROR;
   322    }
   323  
   324    // Store a frame only if the animation flag is set there is some data for
   325    // this frame is available.
   326    status = StoreFrame(dmux->num_frames_ + 1, anmf_payload_size, mem, frame);
   327    if (status != PARSE_ERROR && is_animation && frame->frame_num_ > 0) {
   328      added_frame = AddFrame(dmux, frame);
   329      if (added_frame) {
   330        ++dmux->num_frames_;
   331      } else {
   332        status = PARSE_ERROR;
   333      }
   334    }
   335  
   336    if (!added_frame) free(frame);
   337    return status;
   338  }
   339  
   340  #ifdef WEBP_EXPERIMENTAL_FEATURES
   341  // Parse a 'FRGM' chunk and any image bearing chunks that immediately follow.
   342  // 'fragment_chunk_size' is the previously validated, padded chunk size.
   343  static ParseStatus ParseFragment(WebPDemuxer* const dmux,
   344                                   uint32_t fragment_chunk_size) {
   345    const int frame_num = 1;  // All fragments belong to the 1st (and only) frame.
   346    const int is_fragmented = !!(dmux->feature_flags_ & FRAGMENTS_FLAG);
   347    const uint32_t frgm_payload_size = fragment_chunk_size - FRGM_CHUNK_SIZE;
   348    int added_fragment = 0;
   349    MemBuffer* const mem = &dmux->mem_;
   350    Frame* frame;
   351    ParseStatus status =
   352        NewFrame(mem, FRGM_CHUNK_SIZE, fragment_chunk_size, &frame);
   353    if (status != PARSE_OK) return status;
   354  
   355    frame->is_fragment_ = 1;
   356    frame->x_offset_ = 2 * ReadLE24s(mem);
   357    frame->y_offset_ = 2 * ReadLE24s(mem);
   358  
   359    // Store a fragment only if the 'fragments' flag is set and there is some
   360    // data available.
   361    status = StoreFrame(frame_num, frgm_payload_size, mem, frame);
   362    if (status != PARSE_ERROR && is_fragmented && frame->frame_num_ > 0) {
   363      added_fragment = AddFrame(dmux, frame);
   364      if (!added_fragment) {
   365        status = PARSE_ERROR;
   366      } else {
   367        dmux->num_frames_ = 1;
   368      }
   369    }
   370  
   371    if (!added_fragment) free(frame);
   372    return status;
   373  }
   374  #endif  // WEBP_EXPERIMENTAL_FEATURES
   375  
   376  // General chunk storage, starting with the header at 'start_offset', allowing
   377  // the user to request the payload via a fourcc string. 'size' includes the
   378  // header and the unpadded payload size.
   379  // Returns true on success, false otherwise.
   380  static int StoreChunk(WebPDemuxer* const dmux,
   381                        size_t start_offset, uint32_t size) {
   382    Chunk* const chunk = (Chunk*)calloc(1, sizeof(*chunk));
   383    if (chunk == NULL) return 0;
   384  
   385    chunk->data_.offset_ = start_offset;
   386    chunk->data_.size_ = size;
   387    AddChunk(dmux, chunk);
   388    return 1;
   389  }
   390  
   391  // -----------------------------------------------------------------------------
   392  // Primary chunk parsing
   393  
   394  static ParseStatus ReadHeader(MemBuffer* const mem) {
   395    const size_t min_size = RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE;
   396    uint32_t riff_size;
   397  
   398    // Basic file level validation.
   399    if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA;
   400    if (memcmp(GetBuffer(mem), "RIFF", CHUNK_SIZE_BYTES) ||
   401        memcmp(GetBuffer(mem) + CHUNK_HEADER_SIZE, "WEBP", CHUNK_SIZE_BYTES)) {
   402      return PARSE_ERROR;
   403    }
   404  
   405    riff_size = GetLE32(GetBuffer(mem) + TAG_SIZE);
   406    if (riff_size < CHUNK_HEADER_SIZE) return PARSE_ERROR;
   407    if (riff_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
   408  
   409    // There's no point in reading past the end of the RIFF chunk
   410    mem->riff_end_ = riff_size + CHUNK_HEADER_SIZE;
   411    if (mem->buf_size_ > mem->riff_end_) {
   412      mem->buf_size_ = mem->end_ = mem->riff_end_;
   413    }
   414  
   415    Skip(mem, RIFF_HEADER_SIZE);
   416    return PARSE_OK;
   417  }
   418  
   419  static ParseStatus ParseSingleImage(WebPDemuxer* const dmux) {
   420    const size_t min_size = CHUNK_HEADER_SIZE;
   421    MemBuffer* const mem = &dmux->mem_;
   422    Frame* frame;
   423    ParseStatus status;
   424    int image_added = 0;
   425  
   426    if (dmux->frames_ != NULL) return PARSE_ERROR;
   427    if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR;
   428    if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA;
   429  
   430    frame = (Frame*)calloc(1, sizeof(*frame));
   431    if (frame == NULL) return PARSE_ERROR;
   432  
   433    // For the single image case we allow parsing of a partial frame, but we need
   434    // at least CHUNK_HEADER_SIZE for parsing.
   435    status = StoreFrame(1, CHUNK_HEADER_SIZE, &dmux->mem_, frame);
   436    if (status != PARSE_ERROR) {
   437      const int has_alpha = !!(dmux->feature_flags_ & ALPHA_FLAG);
   438      // Clear any alpha when the alpha flag is missing.
   439      if (!has_alpha && frame->img_components_[1].size_ > 0) {
   440        frame->img_components_[1].offset_ = 0;
   441        frame->img_components_[1].size_ = 0;
   442        frame->has_alpha_ = 0;
   443      }
   444  
   445      // Use the frame width/height as the canvas values for non-vp8x files.
   446      // Also, set ALPHA_FLAG if this is a lossless image with alpha.
   447      if (!dmux->is_ext_format_ && frame->width_ > 0 && frame->height_ > 0) {
   448        dmux->state_ = WEBP_DEMUX_PARSED_HEADER;
   449        dmux->canvas_width_ = frame->width_;
   450        dmux->canvas_height_ = frame->height_;
   451        dmux->feature_flags_ |= frame->has_alpha_ ? ALPHA_FLAG : 0;
   452      }
   453      if (!AddFrame(dmux, frame)) {
   454        status = PARSE_ERROR;  // last frame was left incomplete
   455      } else {
   456        image_added = 1;
   457        dmux->num_frames_ = 1;
   458      }
   459    }
   460  
   461    if (!image_added) free(frame);
   462    return status;
   463  }
   464  
   465  static ParseStatus ParseVP8XChunks(WebPDemuxer* const dmux) {
   466    const int is_animation = !!(dmux->feature_flags_ & ANIMATION_FLAG);
   467    MemBuffer* const mem = &dmux->mem_;
   468    int anim_chunks = 0;
   469    ParseStatus status = PARSE_OK;
   470  
   471    do {
   472      int store_chunk = 1;
   473      const size_t chunk_start_offset = mem->start_;
   474      const uint32_t fourcc = ReadLE32(mem);
   475      const uint32_t chunk_size = ReadLE32(mem);
   476      const uint32_t chunk_size_padded = chunk_size + (chunk_size & 1);
   477  
   478      if (chunk_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
   479      if (SizeIsInvalid(mem, chunk_size_padded)) return PARSE_ERROR;
   480  
   481      switch (fourcc) {
   482        case MKFOURCC('V', 'P', '8', 'X'): {
   483          return PARSE_ERROR;
   484        }
   485        case MKFOURCC('A', 'L', 'P', 'H'):
   486        case MKFOURCC('V', 'P', '8', ' '):
   487        case MKFOURCC('V', 'P', '8', 'L'): {
   488          // check that this isn't an animation (all frames should be in an ANMF).
   489          if (anim_chunks > 0 || is_animation) return PARSE_ERROR;
   490  
   491          Rewind(mem, CHUNK_HEADER_SIZE);
   492          status = ParseSingleImage(dmux);
   493          break;
   494        }
   495        case MKFOURCC('A', 'N', 'I', 'M'): {
   496          if (chunk_size_padded < ANIM_CHUNK_SIZE) return PARSE_ERROR;
   497  
   498          if (MemDataSize(mem) < chunk_size_padded) {
   499            status = PARSE_NEED_MORE_DATA;
   500          } else if (anim_chunks == 0) {
   501            ++anim_chunks;
   502            dmux->bgcolor_ = ReadLE32(mem);
   503            dmux->loop_count_ = ReadLE16s(mem);
   504            Skip(mem, chunk_size_padded - ANIM_CHUNK_SIZE);
   505          } else {
   506            store_chunk = 0;
   507            goto Skip;
   508          }
   509          break;
   510        }
   511        case MKFOURCC('A', 'N', 'M', 'F'): {
   512          if (anim_chunks == 0) return PARSE_ERROR;  // 'ANIM' precedes frames.
   513          status = ParseAnimationFrame(dmux, chunk_size_padded);
   514          break;
   515        }
   516  #ifdef WEBP_EXPERIMENTAL_FEATURES
   517        case MKFOURCC('F', 'R', 'G', 'M'): {
   518          status = ParseFragment(dmux, chunk_size_padded);
   519          break;
   520        }
   521  #endif
   522        case MKFOURCC('I', 'C', 'C', 'P'): {
   523          store_chunk = !!(dmux->feature_flags_ & ICCP_FLAG);
   524          goto Skip;
   525        }
   526        case MKFOURCC('E', 'X', 'I', 'F'): {
   527          store_chunk = !!(dmux->feature_flags_ & EXIF_FLAG);
   528          goto Skip;
   529        }
   530        case MKFOURCC('X', 'M', 'P', ' '): {
   531          store_chunk = !!(dmux->feature_flags_ & XMP_FLAG);
   532          goto Skip;
   533        }
   534   Skip:
   535        default: {
   536          if (chunk_size_padded <= MemDataSize(mem)) {
   537            if (store_chunk) {
   538              // Store only the chunk header and unpadded size as only the payload
   539              // will be returned to the user.
   540              if (!StoreChunk(dmux, chunk_start_offset,
   541                              CHUNK_HEADER_SIZE + chunk_size)) {
   542                return PARSE_ERROR;
   543              }
   544            }
   545            Skip(mem, chunk_size_padded);
   546          } else {
   547            status = PARSE_NEED_MORE_DATA;
   548          }
   549        }
   550      }
   551  
   552      if (mem->start_ == mem->riff_end_) {
   553        break;
   554      } else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) {
   555        status = PARSE_NEED_MORE_DATA;
   556      }
   557    } while (status == PARSE_OK);
   558  
   559    return status;
   560  }
   561  
   562  static ParseStatus ParseVP8X(WebPDemuxer* const dmux) {
   563    MemBuffer* const mem = &dmux->mem_;
   564    uint32_t vp8x_size;
   565  
   566    if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA;
   567  
   568    dmux->is_ext_format_ = 1;
   569    Skip(mem, TAG_SIZE);  // VP8X
   570    vp8x_size = ReadLE32(mem);
   571    if (vp8x_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
   572    if (vp8x_size < VP8X_CHUNK_SIZE) return PARSE_ERROR;
   573    vp8x_size += vp8x_size & 1;
   574    if (SizeIsInvalid(mem, vp8x_size)) return PARSE_ERROR;
   575    if (MemDataSize(mem) < vp8x_size) return PARSE_NEED_MORE_DATA;
   576  
   577    dmux->feature_flags_ = ReadByte(mem);
   578    Skip(mem, 3);  // Reserved.
   579    dmux->canvas_width_  = 1 + ReadLE24s(mem);
   580    dmux->canvas_height_ = 1 + ReadLE24s(mem);
   581    if (dmux->canvas_width_ * (uint64_t)dmux->canvas_height_ >= MAX_IMAGE_AREA) {
   582      return PARSE_ERROR;  // image final dimension is too large
   583    }
   584    Skip(mem, vp8x_size - VP8X_CHUNK_SIZE);  // skip any trailing data.
   585    dmux->state_ = WEBP_DEMUX_PARSED_HEADER;
   586  
   587    if (SizeIsInvalid(mem, CHUNK_HEADER_SIZE)) return PARSE_ERROR;
   588    if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA;
   589  
   590    return ParseVP8XChunks(dmux);
   591  }
   592  
   593  // -----------------------------------------------------------------------------
   594  // Format validation
   595  
   596  static int IsValidSimpleFormat(const WebPDemuxer* const dmux) {
   597    const Frame* const frame = dmux->frames_;
   598    if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1;
   599  
   600    if (dmux->canvas_width_ <= 0 || dmux->canvas_height_ <= 0) return 0;
   601    if (dmux->state_ == WEBP_DEMUX_DONE && frame == NULL) return 0;
   602  
   603    if (frame->width_ <= 0 || frame->height_ <= 0) return 0;
   604    return 1;
   605  }
   606  
   607  // If 'exact' is true, check that the image resolution matches the canvas.
   608  // If 'exact' is false, check that the x/y offsets do not exceed the canvas.
   609  // TODO(jzern): this is insufficient in the fragmented image case if the
   610  // expectation is that the fragments completely cover the canvas.
   611  static int CheckFrameBounds(const Frame* const frame, int exact,
   612                              int canvas_width, int canvas_height) {
   613    if (exact) {
   614      if (frame->x_offset_ != 0 || frame->y_offset_ != 0) {
   615        return 0;
   616      }
   617      if (frame->width_ != canvas_width || frame->height_ != canvas_height) {
   618        return 0;
   619      }
   620    } else {
   621      if (frame->x_offset_ < 0 || frame->y_offset_ < 0) return 0;
   622      if (frame->width_ + frame->x_offset_ > canvas_width) return 0;
   623      if (frame->height_ + frame->y_offset_ > canvas_height) return 0;
   624    }
   625    return 1;
   626  }
   627  
   628  static int IsValidExtendedFormat(const WebPDemuxer* const dmux) {
   629    const int is_animation = !!(dmux->feature_flags_ & ANIMATION_FLAG);
   630    const int is_fragmented = !!(dmux->feature_flags_ & FRAGMENTS_FLAG);
   631    const Frame* f = dmux->frames_;
   632  
   633    if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1;
   634  
   635    if (dmux->canvas_width_ <= 0 || dmux->canvas_height_ <= 0) return 0;
   636    if (dmux->loop_count_ < 0) return 0;
   637    if (dmux->state_ == WEBP_DEMUX_DONE && dmux->frames_ == NULL) return 0;
   638  #ifndef WEBP_EXPERIMENTAL_FEATURES
   639    if (is_fragmented) return 0;
   640  #endif
   641  
   642    while (f != NULL) {
   643      const int cur_frame_set = f->frame_num_;
   644      int frame_count = 0, fragment_count = 0;
   645  
   646      // Check frame properties and if the image is composed of fragments that
   647      // each fragment came from a fragment.
   648      for (; f != NULL && f->frame_num_ == cur_frame_set; f = f->next_) {
   649        const ChunkData* const image = f->img_components_;
   650        const ChunkData* const alpha = f->img_components_ + 1;
   651  
   652        if (is_fragmented && !f->is_fragment_) return 0;
   653        if (!is_fragmented && f->is_fragment_) return 0;
   654        if (!is_animation && f->frame_num_ > 1) return 0;
   655  
   656        if (f->complete_) {
   657          if (alpha->size_ == 0 && image->size_ == 0) return 0;
   658          // Ensure alpha precedes image bitstream.
   659          if (alpha->size_ > 0 && alpha->offset_ > image->offset_) {
   660            return 0;
   661          }
   662  
   663          if (f->width_ <= 0 || f->height_ <= 0) return 0;
   664        } else {
   665          // There shouldn't be a partial frame in a complete file.
   666          if (dmux->state_ == WEBP_DEMUX_DONE) return 0;
   667  
   668          // Ensure alpha precedes image bitstream.
   669          if (alpha->size_ > 0 && image->size_ > 0 &&
   670              alpha->offset_ > image->offset_) {
   671            return 0;
   672          }
   673          // There shouldn't be any frames after an incomplete one.
   674          if (f->next_ != NULL) return 0;
   675        }
   676  
   677        if (f->width_ > 0 && f->height_ > 0 &&
   678            !CheckFrameBounds(f, !(is_animation || is_fragmented),
   679                              dmux->canvas_width_, dmux->canvas_height_)) {
   680          return 0;
   681        }
   682  
   683        fragment_count += f->is_fragment_;
   684        ++frame_count;
   685      }
   686      if (!is_fragmented && frame_count > 1) return 0;
   687      if (fragment_count > 0 && frame_count != fragment_count) return 0;
   688    }
   689    return 1;
   690  }
   691  
   692  // -----------------------------------------------------------------------------
   693  // WebPDemuxer object
   694  
   695  static void InitDemux(WebPDemuxer* const dmux, const MemBuffer* const mem) {
   696    dmux->state_ = WEBP_DEMUX_PARSING_HEADER;
   697    dmux->loop_count_ = 1;
   698    dmux->bgcolor_ = 0xFFFFFFFF;  // White background by default.
   699    dmux->canvas_width_ = -1;
   700    dmux->canvas_height_ = -1;
   701    dmux->frames_tail_ = &dmux->frames_;
   702    dmux->chunks_tail_ = &dmux->chunks_;
   703    dmux->mem_ = *mem;
   704  }
   705  
   706  WebPDemuxer* WebPDemuxInternal(const WebPData* data, int allow_partial,
   707                                 WebPDemuxState* state, int version) {
   708    const ChunkParser* parser;
   709    int partial;
   710    ParseStatus status = PARSE_ERROR;
   711    MemBuffer mem;
   712    WebPDemuxer* dmux;
   713  
   714    if (state != NULL) *state = WEBP_DEMUX_PARSE_ERROR;
   715  
   716    if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DEMUX_ABI_VERSION)) return NULL;
   717    if (data == NULL || data->bytes == NULL || data->size == 0) return NULL;
   718  
   719    if (!InitMemBuffer(&mem, data->bytes, data->size)) return NULL;
   720    status = ReadHeader(&mem);
   721    if (status != PARSE_OK) {
   722      if (state != NULL) {
   723        *state = (status == PARSE_NEED_MORE_DATA) ? WEBP_DEMUX_PARSING_HEADER
   724                                                  : WEBP_DEMUX_PARSE_ERROR;
   725      }
   726      return NULL;
   727    }
   728  
   729    partial = (mem.buf_size_ < mem.riff_end_);
   730    if (!allow_partial && partial) return NULL;
   731  
   732    dmux = (WebPDemuxer*)calloc(1, sizeof(*dmux));
   733    if (dmux == NULL) return NULL;
   734    InitDemux(dmux, &mem);
   735  
   736    status = PARSE_ERROR;
   737    for (parser = kMasterChunks; parser->parse != NULL; ++parser) {
   738      if (!memcmp(parser->id, GetBuffer(&dmux->mem_), TAG_SIZE)) {
   739        status = parser->parse(dmux);
   740        if (status == PARSE_OK) dmux->state_ = WEBP_DEMUX_DONE;
   741        if (status == PARSE_NEED_MORE_DATA && !partial) status = PARSE_ERROR;
   742        if (status != PARSE_ERROR && !parser->valid(dmux)) status = PARSE_ERROR;
   743        if (status == PARSE_ERROR) dmux->state_ = WEBP_DEMUX_PARSE_ERROR;
   744        break;
   745      }
   746    }
   747    if (state != NULL) *state = dmux->state_;
   748  
   749    if (status == PARSE_ERROR) {
   750      WebPDemuxDelete(dmux);
   751      return NULL;
   752    }
   753    return dmux;
   754  }
   755  
   756  void WebPDemuxDelete(WebPDemuxer* dmux) {
   757    Chunk* c;
   758    Frame* f;
   759    if (dmux == NULL) return;
   760  
   761    for (f = dmux->frames_; f != NULL;) {
   762      Frame* const cur_frame = f;
   763      f = f->next_;
   764      free(cur_frame);
   765    }
   766    for (c = dmux->chunks_; c != NULL;) {
   767      Chunk* const cur_chunk = c;
   768      c = c->next_;
   769      free(cur_chunk);
   770    }
   771    free(dmux);
   772  }
   773  
   774  // -----------------------------------------------------------------------------
   775  
   776  uint32_t WebPDemuxGetI(const WebPDemuxer* dmux, WebPFormatFeature feature) {
   777    if (dmux == NULL) return 0;
   778  
   779    switch (feature) {
   780      case WEBP_FF_FORMAT_FLAGS:     return dmux->feature_flags_;
   781      case WEBP_FF_CANVAS_WIDTH:     return (uint32_t)dmux->canvas_width_;
   782      case WEBP_FF_CANVAS_HEIGHT:    return (uint32_t)dmux->canvas_height_;
   783      case WEBP_FF_LOOP_COUNT:       return (uint32_t)dmux->loop_count_;
   784      case WEBP_FF_BACKGROUND_COLOR: return dmux->bgcolor_;
   785      case WEBP_FF_FRAME_COUNT:      return (uint32_t)dmux->num_frames_;
   786    }
   787    return 0;
   788  }
   789  
   790  // -----------------------------------------------------------------------------
   791  // Frame iteration
   792  
   793  // Find the first 'frame_num' frame. There may be multiple such frames in a
   794  // fragmented frame.
   795  static const Frame* GetFrame(const WebPDemuxer* const dmux, int frame_num) {
   796    const Frame* f;
   797    for (f = dmux->frames_; f != NULL; f = f->next_) {
   798      if (frame_num == f->frame_num_) break;
   799    }
   800    return f;
   801  }
   802  
   803  // Returns fragment 'fragment_num' and the total count.
   804  static const Frame* GetFragment(
   805      const Frame* const frame_set, int fragment_num, int* const count) {
   806    const int this_frame = frame_set->frame_num_;
   807    const Frame* f = frame_set;
   808    const Frame* fragment = NULL;
   809    int total;
   810  
   811    for (total = 0; f != NULL && f->frame_num_ == this_frame; f = f->next_) {
   812      if (++total == fragment_num) fragment = f;
   813    }
   814    *count = total;
   815    return fragment;
   816  }
   817  
   818  static const uint8_t* GetFramePayload(const uint8_t* const mem_buf,
   819                                        const Frame* const frame,
   820                                        size_t* const data_size) {
   821    *data_size = 0;
   822    if (frame != NULL) {
   823      const ChunkData* const image = frame->img_components_;
   824      const ChunkData* const alpha = frame->img_components_ + 1;
   825      size_t start_offset = image->offset_;
   826      *data_size = image->size_;
   827  
   828      // if alpha exists it precedes image, update the size allowing for
   829      // intervening chunks.
   830      if (alpha->size_ > 0) {
   831        const size_t inter_size = (image->offset_ > 0)
   832                                ? image->offset_ - (alpha->offset_ + alpha->size_)
   833                                : 0;
   834        start_offset = alpha->offset_;
   835        *data_size  += alpha->size_ + inter_size;
   836      }
   837      return mem_buf + start_offset;
   838    }
   839    return NULL;
   840  }
   841  
   842  // Create a whole 'frame' from VP8 (+ alpha) or lossless.
   843  static int SynthesizeFrame(const WebPDemuxer* const dmux,
   844                             const Frame* const first_frame,
   845                             int fragment_num, WebPIterator* const iter) {
   846    const uint8_t* const mem_buf = dmux->mem_.buf_;
   847    int num_fragments;
   848    size_t payload_size = 0;
   849    const Frame* const fragment =
   850        GetFragment(first_frame, fragment_num, &num_fragments);
   851    const uint8_t* const payload =
   852        GetFramePayload(mem_buf, fragment, &payload_size);
   853    if (payload == NULL) return 0;
   854    assert(first_frame != NULL);
   855  
   856    iter->frame_num      = first_frame->frame_num_;
   857    iter->num_frames     = dmux->num_frames_;
   858    iter->fragment_num   = fragment_num;
   859    iter->num_fragments  = num_fragments;
   860    iter->x_offset       = fragment->x_offset_;
   861    iter->y_offset       = fragment->y_offset_;
   862    iter->width          = fragment->width_;
   863    iter->height         = fragment->height_;
   864    iter->has_alpha      = fragment->has_alpha_;
   865    iter->duration       = fragment->duration_;
   866    iter->dispose_method = fragment->dispose_method_;
   867    iter->blend_method   = fragment->blend_method_;
   868    iter->complete       = fragment->complete_;
   869    iter->fragment.bytes = payload;
   870    iter->fragment.size  = payload_size;
   871    // TODO(jzern): adjust offsets for 'FRGM's embedded in 'ANMF's
   872    return 1;
   873  }
   874  
   875  static int SetFrame(int frame_num, WebPIterator* const iter) {
   876    const Frame* frame;
   877    const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_;
   878    if (dmux == NULL || frame_num < 0) return 0;
   879    if (frame_num > dmux->num_frames_) return 0;
   880    if (frame_num == 0) frame_num = dmux->num_frames_;
   881  
   882    frame = GetFrame(dmux, frame_num);
   883    if (frame == NULL) return 0;
   884  
   885    return SynthesizeFrame(dmux, frame, 1, iter);
   886  }
   887  
   888  int WebPDemuxGetFrame(const WebPDemuxer* dmux, int frame, WebPIterator* iter) {
   889    if (iter == NULL) return 0;
   890  
   891    memset(iter, 0, sizeof(*iter));
   892    iter->private_ = (void*)dmux;
   893    return SetFrame(frame, iter);
   894  }
   895  
   896  int WebPDemuxNextFrame(WebPIterator* iter) {
   897    if (iter == NULL) return 0;
   898    return SetFrame(iter->frame_num + 1, iter);
   899  }
   900  
   901  int WebPDemuxPrevFrame(WebPIterator* iter) {
   902    if (iter == NULL) return 0;
   903    if (iter->frame_num <= 1) return 0;
   904    return SetFrame(iter->frame_num - 1, iter);
   905  }
   906  
   907  int WebPDemuxSelectFragment(WebPIterator* iter, int fragment_num) {
   908    if (iter != NULL && iter->private_ != NULL && fragment_num > 0) {
   909      const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_;
   910      const Frame* const frame = GetFrame(dmux, iter->frame_num);
   911      if (frame == NULL) return 0;
   912  
   913      return SynthesizeFrame(dmux, frame, fragment_num, iter);
   914    }
   915    return 0;
   916  }
   917  
   918  void WebPDemuxReleaseIterator(WebPIterator* iter) {
   919    (void)iter;
   920  }
   921  
   922  // -----------------------------------------------------------------------------
   923  // Chunk iteration
   924  
   925  static int ChunkCount(const WebPDemuxer* const dmux, const char fourcc[4]) {
   926    const uint8_t* const mem_buf = dmux->mem_.buf_;
   927    const Chunk* c;
   928    int count = 0;
   929    for (c = dmux->chunks_; c != NULL; c = c->next_) {
   930      const uint8_t* const header = mem_buf + c->data_.offset_;
   931      if (!memcmp(header, fourcc, TAG_SIZE)) ++count;
   932    }
   933    return count;
   934  }
   935  
   936  static const Chunk* GetChunk(const WebPDemuxer* const dmux,
   937                               const char fourcc[4], int chunk_num) {
   938    const uint8_t* const mem_buf = dmux->mem_.buf_;
   939    const Chunk* c;
   940    int count = 0;
   941    for (c = dmux->chunks_; c != NULL; c = c->next_) {
   942      const uint8_t* const header = mem_buf + c->data_.offset_;
   943      if (!memcmp(header, fourcc, TAG_SIZE)) ++count;
   944      if (count == chunk_num) break;
   945    }
   946    return c;
   947  }
   948  
   949  static int SetChunk(const char fourcc[4], int chunk_num,
   950                      WebPChunkIterator* const iter) {
   951    const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_;
   952    int count;
   953  
   954    if (dmux == NULL || fourcc == NULL || chunk_num < 0) return 0;
   955    count = ChunkCount(dmux, fourcc);
   956    if (count == 0) return 0;
   957    if (chunk_num == 0) chunk_num = count;
   958  
   959    if (chunk_num <= count) {
   960      const uint8_t* const mem_buf = dmux->mem_.buf_;
   961      const Chunk* const chunk = GetChunk(dmux, fourcc, chunk_num);
   962      iter->chunk.bytes = mem_buf + chunk->data_.offset_ + CHUNK_HEADER_SIZE;
   963      iter->chunk.size  = chunk->data_.size_ - CHUNK_HEADER_SIZE;
   964      iter->num_chunks  = count;
   965      iter->chunk_num   = chunk_num;
   966      return 1;
   967    }
   968    return 0;
   969  }
   970  
   971  int WebPDemuxGetChunk(const WebPDemuxer* dmux,
   972                        const char fourcc[4], int chunk_num,
   973                        WebPChunkIterator* iter) {
   974    if (iter == NULL) return 0;
   975  
   976    memset(iter, 0, sizeof(*iter));
   977    iter->private_ = (void*)dmux;
   978    return SetChunk(fourcc, chunk_num, iter);
   979  }
   980  
   981  int WebPDemuxNextChunk(WebPChunkIterator* iter) {
   982    if (iter != NULL) {
   983      const char* const fourcc =
   984          (const char*)iter->chunk.bytes - CHUNK_HEADER_SIZE;
   985      return SetChunk(fourcc, iter->chunk_num + 1, iter);
   986    }
   987    return 0;
   988  }
   989  
   990  int WebPDemuxPrevChunk(WebPChunkIterator* iter) {
   991    if (iter != NULL && iter->chunk_num > 1) {
   992      const char* const fourcc =
   993          (const char*)iter->chunk.bytes - CHUNK_HEADER_SIZE;
   994      return SetChunk(fourcc, iter->chunk_num - 1, iter);
   995    }
   996    return 0;
   997  }
   998  
   999  void WebPDemuxReleaseChunkIterator(WebPChunkIterator* iter) {
  1000    (void)iter;
  1001  }
  1002