1// Copyright 2012 Google Inc. All Rights Reserved.
2//
3// Use of this source code is governed by a BSD-style license
4// that can be found in the COPYING file in the root of the source
5// tree. An additional intellectual property rights grant can be found
6// in the file PATENTS. All contributing project authors may
7// be found in the AUTHORS file in the root of the source tree.
8// -----------------------------------------------------------------------------
9//
10// WebP container demux.
11//
12
13#ifdef HAVE_CONFIG_H
14#include "src/webp/config.h"
15#endif
16
17#include <assert.h>
18#include <stdlib.h>
19#include <string.h>
20
21#include "src/utils/utils.h"
22#include "src/webp/decode.h" // WebPGetFeatures
23#include "src/webp/demux.h"
24#include "src/webp/format_constants.h"
25
26#define DMUX_MAJ_VERSION 1
27#define DMUX_MIN_VERSION 1
28#define DMUX_REV_VERSION 0
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'. 'min_size' is an optional size
209// requirement, it may be zero.
210static ParseStatus StoreFrame(int frame_num, uint32_t min_size,
211 MemBuffer* const mem, Frame* const frame) {
212 int alpha_chunks = 0;
213 int image_chunks = 0;
214 int done = (MemDataSize(mem) < CHUNK_HEADER_SIZE ||
215 MemDataSize(mem) < min_size);
216 ParseStatus status = PARSE_OK;
217
218 if (done) return PARSE_NEED_MORE_DATA;
219
220 do {
221 const size_t chunk_start_offset = mem->start_;
222 const uint32_t fourcc = ReadLE32(mem);
223 const uint32_t payload_size = ReadLE32(mem);
224 const uint32_t payload_size_padded = payload_size + (payload_size & 1);
225 const size_t payload_available = (payload_size_padded > MemDataSize(mem))
226 ? MemDataSize(mem) : payload_size_padded;
227 const size_t chunk_size = CHUNK_HEADER_SIZE + payload_available;
228
229 if (payload_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
230 if (SizeIsInvalid(mem, payload_size_padded)) return PARSE_ERROR;
231 if (payload_size_padded > MemDataSize(mem)) status = PARSE_NEED_MORE_DATA;
232
233 switch (fourcc) {
234 case MKFOURCC('A', 'L', 'P', 'H'):
235 if (alpha_chunks == 0) {
236 ++alpha_chunks;
237 frame->img_components_[1].offset_ = chunk_start_offset;
238 frame->img_components_[1].size_ = chunk_size;
239 frame->has_alpha_ = 1;
240 frame->frame_num_ = frame_num;
241 Skip(mem, payload_available);
242 } else {
243 goto Done;
244 }
245 break;
246 case MKFOURCC('V', 'P', '8', 'L'):
247 if (alpha_chunks > 0) return PARSE_ERROR; // VP8L has its own alpha
248 // fall through
249 case MKFOURCC('V', 'P', '8', ' '):
250 if (image_chunks == 0) {
251 // Extract the bitstream features, tolerating failures when the data
252 // is incomplete.
253 WebPBitstreamFeatures features;
254 const VP8StatusCode vp8_status =
255 WebPGetFeatures(mem->buf_ + chunk_start_offset, chunk_size,
256 &features);
257 if (status == PARSE_NEED_MORE_DATA &&
258 vp8_status == VP8_STATUS_NOT_ENOUGH_DATA) {
259 return PARSE_NEED_MORE_DATA;
260 } else if (vp8_status != VP8_STATUS_OK) {
261 // We have enough data, and yet WebPGetFeatures() failed.
262 return PARSE_ERROR;
263 }
264 ++image_chunks;
265 SetFrameInfo(chunk_start_offset, chunk_size, frame_num,
266 status == PARSE_OK, &features, frame);
267 Skip(mem, payload_available);
268 } else {
269 goto Done;
270 }
271 break;
272 Done:
273 default:
274 // Restore fourcc/size when moving up one level in parsing.
275 Rewind(mem, CHUNK_HEADER_SIZE);
276 done = 1;
277 break;
278 }
279
280 if (mem->start_ == mem->riff_end_) {
281 done = 1;
282 } else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) {
283 status = PARSE_NEED_MORE_DATA;
284 }
285 } while (!done && status == PARSE_OK);
286
287 return status;
288}
289
290// Creates a new Frame if 'actual_size' is within bounds and 'mem' contains
291// enough data ('min_size') to parse the payload.
292// Returns PARSE_OK on success with *frame pointing to the new Frame.
293// Returns PARSE_NEED_MORE_DATA with insufficient data, PARSE_ERROR otherwise.
294static ParseStatus NewFrame(const MemBuffer* const mem,
295 uint32_t min_size, uint32_t actual_size,
296 Frame** frame) {
297 if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR;
298 if (actual_size < min_size) return PARSE_ERROR;
299 if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA;
300
301 *frame = (Frame*)WebPSafeCalloc(1ULL, sizeof(**frame));
302 return (*frame == NULL) ? PARSE_ERROR : PARSE_OK;
303}
304
305// Parse a 'ANMF' chunk and any image bearing chunks that immediately follow.
306// 'frame_chunk_size' is the previously validated, padded chunk size.
307static ParseStatus ParseAnimationFrame(
308 WebPDemuxer* const dmux, uint32_t frame_chunk_size) {
309 const int is_animation = !!(dmux->feature_flags_ & ANIMATION_FLAG);
310 const uint32_t anmf_payload_size = frame_chunk_size - ANMF_CHUNK_SIZE;
311 int added_frame = 0;
312 int bits;
313 MemBuffer* const mem = &dmux->mem_;
314 Frame* frame;
315 ParseStatus status =
316 NewFrame(mem, ANMF_CHUNK_SIZE, frame_chunk_size, &frame);
317 if (status != PARSE_OK) return status;
318
319 frame->x_offset_ = 2 * ReadLE24s(mem);
320 frame->y_offset_ = 2 * ReadLE24s(mem);
321 frame->width_ = 1 + ReadLE24s(mem);
322 frame->height_ = 1 + ReadLE24s(mem);
323 frame->duration_ = ReadLE24s(mem);
324 bits = ReadByte(mem);
325 frame->dispose_method_ =
326 (bits & 1) ? WEBP_MUX_DISPOSE_BACKGROUND : WEBP_MUX_DISPOSE_NONE;
327 frame->blend_method_ = (bits & 2) ? WEBP_MUX_NO_BLEND : WEBP_MUX_BLEND;
328 if (frame->width_ * (uint64_t)frame->height_ >= MAX_IMAGE_AREA) {
329 WebPSafeFree(frame);
330 return PARSE_ERROR;
331 }
332
333 // Store a frame only if the animation flag is set there is some data for
334 // this frame is available.
335 status = StoreFrame(dmux->num_frames_ + 1, anmf_payload_size, mem, frame);
336 if (status != PARSE_ERROR && is_animation && frame->frame_num_ > 0) {
337 added_frame = AddFrame(dmux, frame);
338 if (added_frame) {
339 ++dmux->num_frames_;
340 } else {
341 status = PARSE_ERROR;
342 }
343 }
344
345 if (!added_frame) WebPSafeFree(frame);
346 return status;
347}
348
349// General chunk storage, starting with the header at 'start_offset', allowing
350// the user to request the payload via a fourcc string. 'size' includes the
351// header and the unpadded payload size.
352// Returns true on success, false otherwise.
353static int StoreChunk(WebPDemuxer* const dmux,
354 size_t start_offset, uint32_t size) {
355 Chunk* const chunk = (Chunk*)WebPSafeCalloc(1ULL, sizeof(*chunk));
356 if (chunk == NULL) return 0;
357
358 chunk->data_.offset_ = start_offset;
359 chunk->data_.size_ = size;
360 AddChunk(dmux, chunk);
361 return 1;
362}
363
364// -----------------------------------------------------------------------------
365// Primary chunk parsing
366
367static ParseStatus ReadHeader(MemBuffer* const mem) {
368 const size_t min_size = RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE;
369 uint32_t riff_size;
370
371 // Basic file level validation.
372 if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA;
373 if (memcmp(GetBuffer(mem), "RIFF", CHUNK_SIZE_BYTES) ||
374 memcmp(GetBuffer(mem) + CHUNK_HEADER_SIZE, "WEBP", CHUNK_SIZE_BYTES)) {
375 return PARSE_ERROR;
376 }
377
378 riff_size = GetLE32(GetBuffer(mem) + TAG_SIZE);
379 if (riff_size < CHUNK_HEADER_SIZE) return PARSE_ERROR;
380 if (riff_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
381
382 // There's no point in reading past the end of the RIFF chunk
383 mem->riff_end_ = riff_size + CHUNK_HEADER_SIZE;
384 if (mem->buf_size_ > mem->riff_end_) {
385 mem->buf_size_ = mem->end_ = mem->riff_end_;
386 }
387
388 Skip(mem, RIFF_HEADER_SIZE);
389 return PARSE_OK;
390}
391
392static ParseStatus ParseSingleImage(WebPDemuxer* const dmux) {
393 const size_t min_size = CHUNK_HEADER_SIZE;
394 MemBuffer* const mem = &dmux->mem_;
395 Frame* frame;
396 ParseStatus status;
397 int image_added = 0;
398
399 if (dmux->frames_ != NULL) return PARSE_ERROR;
400 if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR;
401 if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA;
402
403 frame = (Frame*)WebPSafeCalloc(1ULL, sizeof(*frame));
404 if (frame == NULL) return PARSE_ERROR;
405
406 // For the single image case we allow parsing of a partial frame, so no
407 // minimum size is imposed here.
408 status = StoreFrame(1, 0, &dmux->mem_, frame);
409 if (status != PARSE_ERROR) {
410 const int has_alpha = !!(dmux->feature_flags_ & ALPHA_FLAG);
411 // Clear any alpha when the alpha flag is missing.
412 if (!has_alpha && frame->img_components_[1].size_ > 0) {
413 frame->img_components_[1].offset_ = 0;
414 frame->img_components_[1].size_ = 0;
415 frame->has_alpha_ = 0;
416 }
417
418 // Use the frame width/height as the canvas values for non-vp8x files.
419 // Also, set ALPHA_FLAG if this is a lossless image with alpha.
420 if (!dmux->is_ext_format_ && frame->width_ > 0 && frame->height_ > 0) {
421 dmux->state_ = WEBP_DEMUX_PARSED_HEADER;
422 dmux->canvas_width_ = frame->width_;
423 dmux->canvas_height_ = frame->height_;
424 dmux->feature_flags_ |= frame->has_alpha_ ? ALPHA_FLAG : 0;
425 }
426 if (!AddFrame(dmux, frame)) {
427 status = PARSE_ERROR; // last frame was left incomplete
428 } else {
429 image_added = 1;
430 dmux->num_frames_ = 1;
431 }
432 }
433
434 if (!image_added) WebPSafeFree(frame);
435 return status;
436}
437
438static ParseStatus ParseVP8XChunks(WebPDemuxer* const dmux) {
439 const int is_animation = !!(dmux->feature_flags_ & ANIMATION_FLAG);
440 MemBuffer* const mem = &dmux->mem_;
441 int anim_chunks = 0;
442 ParseStatus status = PARSE_OK;
443
444 do {
445 int store_chunk = 1;
446 const size_t chunk_start_offset = mem->start_;
447 const uint32_t fourcc = ReadLE32(mem);
448 const uint32_t chunk_size = ReadLE32(mem);
449 const uint32_t chunk_size_padded = chunk_size + (chunk_size & 1);
450
451 if (chunk_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
452 if (SizeIsInvalid(mem, chunk_size_padded)) return PARSE_ERROR;
453
454 switch (fourcc) {
455 case MKFOURCC('V', 'P', '8', 'X'): {
456 return PARSE_ERROR;
457 }
458 case MKFOURCC('A', 'L', 'P', 'H'):
459 case MKFOURCC('V', 'P', '8', ' '):
460 case MKFOURCC('V', 'P', '8', 'L'): {
461 // check that this isn't an animation (all frames should be in an ANMF).
462 if (anim_chunks > 0 || is_animation) return PARSE_ERROR;
463
464 Rewind(mem, CHUNK_HEADER_SIZE);
465 status = ParseSingleImage(dmux);
466 break;
467 }
468 case MKFOURCC('A', 'N', 'I', 'M'): {
469 if (chunk_size_padded < ANIM_CHUNK_SIZE) return PARSE_ERROR;
470
471 if (MemDataSize(mem) < chunk_size_padded) {
472 status = PARSE_NEED_MORE_DATA;
473 } else if (anim_chunks == 0) {
474 ++anim_chunks;
475 dmux->bgcolor_ = ReadLE32(mem);
476 dmux->loop_count_ = ReadLE16s(mem);
477 Skip(mem, chunk_size_padded - ANIM_CHUNK_SIZE);
478 } else {
479 store_chunk = 0;
480 goto Skip;
481 }
482 break;
483 }
484 case MKFOURCC('A', 'N', 'M', 'F'): {
485 if (anim_chunks == 0) return PARSE_ERROR; // 'ANIM' precedes frames.
486 status = ParseAnimationFrame(dmux, chunk_size_padded);
487 break;
488 }
489 case MKFOURCC('I', 'C', 'C', 'P'): {
490 store_chunk = !!(dmux->feature_flags_ & ICCP_FLAG);
491 goto Skip;
492 }
493 case MKFOURCC('E', 'X', 'I', 'F'): {
494 store_chunk = !!(dmux->feature_flags_ & EXIF_FLAG);
495 goto Skip;
496 }
497 case MKFOURCC('X', 'M', 'P', ' '): {
498 store_chunk = !!(dmux->feature_flags_ & XMP_FLAG);
499 goto Skip;
500 }
501 Skip:
502 default: {
503 if (chunk_size_padded <= MemDataSize(mem)) {
504 if (store_chunk) {
505 // Store only the chunk header and unpadded size as only the payload
506 // will be returned to the user.
507 if (!StoreChunk(dmux, chunk_start_offset,
508 CHUNK_HEADER_SIZE + chunk_size)) {
509 return PARSE_ERROR;
510 }
511 }
512 Skip(mem, chunk_size_padded);
513 } else {
514 status = PARSE_NEED_MORE_DATA;
515 }
516 }
517 }
518
519 if (mem->start_ == mem->riff_end_) {
520 break;
521 } else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) {
522 status = PARSE_NEED_MORE_DATA;
523 }
524 } while (status == PARSE_OK);
525
526 return status;
527}
528
529static ParseStatus ParseVP8X(WebPDemuxer* const dmux) {
530 MemBuffer* const mem = &dmux->mem_;
531 uint32_t vp8x_size;
532
533 if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA;
534
535 dmux->is_ext_format_ = 1;
536 Skip(mem, TAG_SIZE); // VP8X
537 vp8x_size = ReadLE32(mem);
538 if (vp8x_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
539 if (vp8x_size < VP8X_CHUNK_SIZE) return PARSE_ERROR;
540 vp8x_size += vp8x_size & 1;
541 if (SizeIsInvalid(mem, vp8x_size)) return PARSE_ERROR;
542 if (MemDataSize(mem) < vp8x_size) return PARSE_NEED_MORE_DATA;
543
544 dmux->feature_flags_ = ReadByte(mem);
545 Skip(mem, 3); // Reserved.
546 dmux->canvas_width_ = 1 + ReadLE24s(mem);
547 dmux->canvas_height_ = 1 + ReadLE24s(mem);
548 if (dmux->canvas_width_ * (uint64_t)dmux->canvas_height_ >= MAX_IMAGE_AREA) {
549 return PARSE_ERROR; // image final dimension is too large
550 }
551 Skip(mem, vp8x_size - VP8X_CHUNK_SIZE); // skip any trailing data.
552 dmux->state_ = WEBP_DEMUX_PARSED_HEADER;
553
554 if (SizeIsInvalid(mem, CHUNK_HEADER_SIZE)) return PARSE_ERROR;
555 if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA;
556
557 return ParseVP8XChunks(dmux);
558}
559
560// -----------------------------------------------------------------------------
561// Format validation
562
563static int IsValidSimpleFormat(const WebPDemuxer* const dmux) {
564 const Frame* const frame = dmux->frames_;
565 if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1;
566
567 if (dmux->canvas_width_ <= 0 || dmux->canvas_height_ <= 0) return 0;
568 if (dmux->state_ == WEBP_DEMUX_DONE && frame == NULL) return 0;
569
570 if (frame->width_ <= 0 || frame->height_ <= 0) return 0;
571 return 1;
572}
573
574// If 'exact' is true, check that the image resolution matches the canvas.
575// If 'exact' is false, check that the x/y offsets do not exceed the canvas.
576static int CheckFrameBounds(const Frame* const frame, int exact,
577 int canvas_width, int canvas_height) {
578 if (exact) {
579 if (frame->x_offset_ != 0 || frame->y_offset_ != 0) {
580 return 0;
581 }
582 if (frame->width_ != canvas_width || frame->height_ != canvas_height) {
583 return 0;
584 }
585 } else {
586 if (frame->x_offset_ < 0 || frame->y_offset_ < 0) return 0;
587 if (frame->width_ + frame->x_offset_ > canvas_width) return 0;
588 if (frame->height_ + frame->y_offset_ > canvas_height) return 0;
589 }
590 return 1;
591}
592
593static int IsValidExtendedFormat(const WebPDemuxer* const dmux) {
594 const int is_animation = !!(dmux->feature_flags_ & ANIMATION_FLAG);
595 const Frame* f = dmux->frames_;
596
597 if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1;
598
599 if (dmux->canvas_width_ <= 0 || dmux->canvas_height_ <= 0) return 0;
600 if (dmux->loop_count_ < 0) return 0;
601 if (dmux->state_ == WEBP_DEMUX_DONE && dmux->frames_ == NULL) return 0;
602 if (dmux->feature_flags_ & ~ALL_VALID_FLAGS) return 0; // invalid bitstream
603
604 while (f != NULL) {
605 const int cur_frame_set = f->frame_num_;
606 int frame_count = 0;
607
608 // Check frame properties.
609 for (; f != NULL && f->frame_num_ == cur_frame_set; f = f->next_) {
610 const ChunkData* const image = f->img_components_;
611 const ChunkData* const alpha = f->img_components_ + 1;
612
613 if (!is_animation && f->frame_num_ > 1) return 0;
614
615 if (f->complete_) {
616 if (alpha->size_ == 0 && image->size_ == 0) return 0;
617 // Ensure alpha precedes image bitstream.
618 if (alpha->size_ > 0 && alpha->offset_ > image->offset_) {
619 return 0;
620 }
621
622 if (f->width_ <= 0 || f->height_ <= 0) return 0;
623 } else {
624 // There shouldn't be a partial frame in a complete file.
625 if (dmux->state_ == WEBP_DEMUX_DONE) return 0;
626
627 // Ensure alpha precedes image bitstream.
628 if (alpha->size_ > 0 && image->size_ > 0 &&
629 alpha->offset_ > image->offset_) {
630 return 0;
631 }
632 // There shouldn't be any frames after an incomplete one.
633 if (f->next_ != NULL) return 0;
634 }
635
636 if (f->width_ > 0 && f->height_ > 0 &&
637 !CheckFrameBounds(f, !is_animation,
638 dmux->canvas_width_, dmux->canvas_height_)) {
639 return 0;
640 }
641
642 ++frame_count;
643 }
644 }
645 return 1;
646}
647
648// -----------------------------------------------------------------------------
649// WebPDemuxer object
650
651static void InitDemux(WebPDemuxer* const dmux, const MemBuffer* const mem) {
652 dmux->state_ = WEBP_DEMUX_PARSING_HEADER;
653 dmux->loop_count_ = 1;
654 dmux->bgcolor_ = 0xFFFFFFFF; // White background by default.
655 dmux->canvas_width_ = -1;
656 dmux->canvas_height_ = -1;
657 dmux->frames_tail_ = &dmux->frames_;
658 dmux->chunks_tail_ = &dmux->chunks_;
659 dmux->mem_ = *mem;
660}
661
662static ParseStatus CreateRawImageDemuxer(MemBuffer* const mem,
663 WebPDemuxer** demuxer) {
664 WebPBitstreamFeatures features;
665 const VP8StatusCode status =
666 WebPGetFeatures(mem->buf_, mem->buf_size_, &features);
667 *demuxer = NULL;
668 if (status != VP8_STATUS_OK) {
669 return (status == VP8_STATUS_NOT_ENOUGH_DATA) ? PARSE_NEED_MORE_DATA
670 : PARSE_ERROR;
671 }
672
673 {
674 WebPDemuxer* const dmux = (WebPDemuxer*)WebPSafeCalloc(1ULL, sizeof(*dmux));
675 Frame* const frame = (Frame*)WebPSafeCalloc(1ULL, sizeof(*frame));
676 if (dmux == NULL || frame == NULL) goto Error;
677 InitDemux(dmux, mem);
678 SetFrameInfo(0, mem->buf_size_, 1 /*frame_num*/, 1 /*complete*/, &features,
679 frame);
680 if (!AddFrame(dmux, frame)) goto Error;
681 dmux->state_ = WEBP_DEMUX_DONE;
682 dmux->canvas_width_ = frame->width_;
683 dmux->canvas_height_ = frame->height_;
684 dmux->feature_flags_ |= frame->has_alpha_ ? ALPHA_FLAG : 0;
685 dmux->num_frames_ = 1;
686 assert(IsValidSimpleFormat(dmux));
687 *demuxer = dmux;
688 return PARSE_OK;
689
690 Error:
691 WebPSafeFree(dmux);
692 WebPSafeFree(frame);
693 return PARSE_ERROR;
694 }
695}
696
697WebPDemuxer* WebPDemuxInternal(const WebPData* data, int allow_partial,
698 WebPDemuxState* state, int version) {
699 const ChunkParser* parser;
700 int partial;
701 ParseStatus status = PARSE_ERROR;
702 MemBuffer mem;
703 WebPDemuxer* dmux;
704
705 if (state != NULL) *state = WEBP_DEMUX_PARSE_ERROR;
706
707 if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DEMUX_ABI_VERSION)) return NULL;
708 if (data == NULL || data->bytes == NULL || data->size == 0) return NULL;
709
710 if (!InitMemBuffer(&mem, data->bytes, data->size)) return NULL;
711 status = ReadHeader(&mem);
712 if (status != PARSE_OK) {
713 // If parsing of the webp file header fails attempt to handle a raw
714 // VP8/VP8L frame. Note 'allow_partial' is ignored in this case.
715 if (status == PARSE_ERROR) {
716 status = CreateRawImageDemuxer(&mem, &dmux);
717 if (status == PARSE_OK) {
718 if (state != NULL) *state = WEBP_DEMUX_DONE;
719 return dmux;
720 }
721 }
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
793static const Frame* GetFrame(const WebPDemuxer* const dmux, int frame_num) {
794 const Frame* f;
795 for (f = dmux->frames_; f != NULL; f = f->next_) {
796 if (frame_num == f->frame_num_) break;
797 }
798 return f;
799}
800
801static const uint8_t* GetFramePayload(const uint8_t* const mem_buf,
802 const Frame* const frame,
803 size_t* const data_size) {
804 *data_size = 0;
805 if (frame != NULL) {
806 const ChunkData* const image = frame->img_components_;
807 const ChunkData* const alpha = frame->img_components_ + 1;
808 size_t start_offset = image->offset_;
809 *data_size = image->size_;
810
811 // if alpha exists it precedes image, update the size allowing for
812 // intervening chunks.
813 if (alpha->size_ > 0) {
814 const size_t inter_size = (image->offset_ > 0)
815 ? image->offset_ - (alpha->offset_ + alpha->size_)
816 : 0;
817 start_offset = alpha->offset_;
818 *data_size += alpha->size_ + inter_size;
819 }
820 return mem_buf + start_offset;
821 }
822 return NULL;
823}
824
825// Create a whole 'frame' from VP8 (+ alpha) or lossless.
826static int SynthesizeFrame(const WebPDemuxer* const dmux,
827 const Frame* const frame,
828 WebPIterator* const iter) {
829 const uint8_t* const mem_buf = dmux->mem_.buf_;
830 size_t payload_size = 0;
831 const uint8_t* const payload = GetFramePayload(mem_buf, frame, &payload_size);
832 if (payload == NULL) return 0;
833 assert(frame != NULL);
834
835 iter->frame_num = frame->frame_num_;
836 iter->num_frames = dmux->num_frames_;
837 iter->x_offset = frame->x_offset_;
838 iter->y_offset = frame->y_offset_;
839 iter->width = frame->width_;
840 iter->height = frame->height_;
841 iter->has_alpha = frame->has_alpha_;
842 iter->duration = frame->duration_;
843 iter->dispose_method = frame->dispose_method_;
844 iter->blend_method = frame->blend_method_;
845 iter->complete = frame->complete_;
846 iter->fragment.bytes = payload;
847 iter->fragment.size = payload_size;
848 return 1;
849}
850
851static int SetFrame(int frame_num, WebPIterator* const iter) {
852 const Frame* frame;
853 const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_;
854 if (dmux == NULL || frame_num < 0) return 0;
855 if (frame_num > dmux->num_frames_) return 0;
856 if (frame_num == 0) frame_num = dmux->num_frames_;
857
858 frame = GetFrame(dmux, frame_num);
859 if (frame == NULL) return 0;
860
861 return SynthesizeFrame(dmux, frame, iter);
862}
863
864int WebPDemuxGetFrame(const WebPDemuxer* dmux, int frame, WebPIterator* iter) {
865 if (iter == NULL) return 0;
866
867 memset(iter, 0, sizeof(*iter));
868 iter->private_ = (void*)dmux;
869 return SetFrame(frame, iter);
870}
871
872int WebPDemuxNextFrame(WebPIterator* iter) {
873 if (iter == NULL) return 0;
874 return SetFrame(iter->frame_num + 1, iter);
875}
876
877int WebPDemuxPrevFrame(WebPIterator* iter) {
878 if (iter == NULL) return 0;
879 if (iter->frame_num <= 1) return 0;
880 return SetFrame(iter->frame_num - 1, iter);
881}
882
883void WebPDemuxReleaseIterator(WebPIterator* iter) {
884 (void)iter;
885}
886
887// -----------------------------------------------------------------------------
888// Chunk iteration
889
890static int ChunkCount(const WebPDemuxer* const dmux, const char fourcc[4]) {
891 const uint8_t* const mem_buf = dmux->mem_.buf_;
892 const Chunk* c;
893 int count = 0;
894 for (c = dmux->chunks_; c != NULL; c = c->next_) {
895 const uint8_t* const header = mem_buf + c->data_.offset_;
896 if (!memcmp(header, fourcc, TAG_SIZE)) ++count;
897 }
898 return count;
899}
900
901static const Chunk* GetChunk(const WebPDemuxer* const dmux,
902 const char fourcc[4], int chunk_num) {
903 const uint8_t* const mem_buf = dmux->mem_.buf_;
904 const Chunk* c;
905 int count = 0;
906 for (c = dmux->chunks_; c != NULL; c = c->next_) {
907 const uint8_t* const header = mem_buf + c->data_.offset_;
908 if (!memcmp(header, fourcc, TAG_SIZE)) ++count;
909 if (count == chunk_num) break;
910 }
911 return c;
912}
913
914static int SetChunk(const char fourcc[4], int chunk_num,
915 WebPChunkIterator* const iter) {
916 const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_;
917 int count;
918
919 if (dmux == NULL || fourcc == NULL || chunk_num < 0) return 0;
920 count = ChunkCount(dmux, fourcc);
921 if (count == 0) return 0;
922 if (chunk_num == 0) chunk_num = count;
923
924 if (chunk_num <= count) {
925 const uint8_t* const mem_buf = dmux->mem_.buf_;
926 const Chunk* const chunk = GetChunk(dmux, fourcc, chunk_num);
927 iter->chunk.bytes = mem_buf + chunk->data_.offset_ + CHUNK_HEADER_SIZE;
928 iter->chunk.size = chunk->data_.size_ - CHUNK_HEADER_SIZE;
929 iter->num_chunks = count;
930 iter->chunk_num = chunk_num;
931 return 1;
932 }
933 return 0;
934}
935
936int WebPDemuxGetChunk(const WebPDemuxer* dmux,
937 const char fourcc[4], int chunk_num,
938 WebPChunkIterator* iter) {
939 if (iter == NULL) return 0;
940
941 memset(iter, 0, sizeof(*iter));
942 iter->private_ = (void*)dmux;
943 return SetChunk(fourcc, chunk_num, iter);
944}
945
946int WebPDemuxNextChunk(WebPChunkIterator* iter) {
947 if (iter != NULL) {
948 const char* const fourcc =
949 (const char*)iter->chunk.bytes - CHUNK_HEADER_SIZE;
950 return SetChunk(fourcc, iter->chunk_num + 1, iter);
951 }
952 return 0;
953}
954
955int WebPDemuxPrevChunk(WebPChunkIterator* iter) {
956 if (iter != NULL && iter->chunk_num > 1) {
957 const char* const fourcc =
958 (const char*)iter->chunk.bytes - CHUNK_HEADER_SIZE;
959 return SetChunk(fourcc, iter->chunk_num - 1, iter);
960 }
961 return 0;
962}
963
964void WebPDemuxReleaseChunkIterator(WebPChunkIterator* iter) {
965 (void)iter;
966}
967
968