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