~paparazzi-uav/paparazzi/v5.0-manual

« back to all changes in this revision

Viewing changes to sw/ext/opencv_bebop/opencv/3rdparty/libwebp/demux/demux.c

  • Committer: Paparazzi buildbot
  • Date: 2016-05-18 15:00:29 UTC
  • Revision ID: felix.ruess+docbot@gmail.com-20160518150029-e8lgzi5kvb4p7un9
Manual import commit 4b8bbb730080dac23cf816b98908dacfabe2a8ec from v5.0 branch.

Show diffs side-by-side

added added

removed removed

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