go-libwebp

Experimental translation from libwebp to Go source.
Log | Files | Refs | README | LICENSE

demux.c (31935B)


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