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 "webp/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 1
29
30typedef 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
38typedef struct {
39  size_t offset_;
40  size_t size_;
41} ChunkData;
42
43typedef 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
57typedef struct Chunk {
58  ChunkData data_;
59  struct Chunk* next_;
60} Chunk;
61
62struct 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
77typedef enum {
78  PARSE_OK,
79  PARSE_NEED_MORE_DATA,
80  PARSE_ERROR
81} ParseStatus;
82
83typedef struct ChunkParser {
84  uint8_t id[4];
85  ParseStatus (*parse)(WebPDemuxer* const dmux);
86  int (*valid)(const WebPDemuxer* const dmux);
87} ChunkParser;
88
89static ParseStatus ParseSingleImage(WebPDemuxer* const dmux);
90static ParseStatus ParseVP8X(WebPDemuxer* const dmux);
91static int IsValidSimpleFormat(const WebPDemuxer* const dmux);
92static int IsValidExtendedFormat(const WebPDemuxer* const dmux);
93
94static 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
103int WebPGetDemuxVersion(void) {
104  return (DMUX_MAJ_VERSION << 16) | (DMUX_MIN_VERSION << 8) | DMUX_REV_VERSION;
105}
106
107// -----------------------------------------------------------------------------
108// MemBuffer
109
110static 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
119static 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'.
126static 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.
131static WEBP_INLINE int SizeIsInvalid(const MemBuffer* const mem, size_t size) {
132  return (size > mem->riff_end_ - mem->start_);
133}
134
135static WEBP_INLINE void Skip(MemBuffer* const mem, size_t size) {
136  mem->start_ += size;
137}
138
139static WEBP_INLINE void Rewind(MemBuffer* const mem, size_t size) {
140  mem->start_ -= size;
141}
142
143static 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.
148static 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
154static 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
161static 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
168static 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
178static 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.
186static 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'.
197static 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.
285static 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*)WebPSafeCalloc(1ULL, 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.
298static 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    WebPSafeFree(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) WebPSafeFree(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.
343static 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) WebPSafeFree(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.
380static int StoreChunk(WebPDemuxer* const dmux,
381                      size_t start_offset, uint32_t size) {
382  Chunk* const chunk = (Chunk*)WebPSafeCalloc(1ULL, 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
394static 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
419static 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*)WebPSafeCalloc(1ULL, 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) WebPSafeFree(frame);
462  return status;
463}
464
465static 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
562static 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
596static 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.
611static 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
628static 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
695static 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
706WebPDemuxer* 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*)WebPSafeCalloc(1ULL, 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
756void 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    WebPSafeFree(cur_frame);
765  }
766  for (c = dmux->chunks_; c != NULL;) {
767    Chunk* const cur_chunk = c;
768    c = c->next_;
769    WebPSafeFree(cur_chunk);
770  }
771  WebPSafeFree(dmux);
772}
773
774// -----------------------------------------------------------------------------
775
776uint32_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.
795static 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.
804static 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
818static 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.
843static 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
875static 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
888int 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
896int WebPDemuxNextFrame(WebPIterator* iter) {
897  if (iter == NULL) return 0;
898  return SetFrame(iter->frame_num + 1, iter);
899}
900
901int 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
907int 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
918void WebPDemuxReleaseIterator(WebPIterator* iter) {
919  (void)iter;
920}
921
922// -----------------------------------------------------------------------------
923// Chunk iteration
924
925static 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
936static 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
949static 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
971int 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
981int 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
990int 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
999void WebPDemuxReleaseChunkIterator(WebPChunkIterator* iter) {
1000  (void)iter;
1001}
1002
1003