1 // Copyright 2012 Google Inc. All Rights Reserved.
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 // -----------------------------------------------------------------------------
10 // WebP container demux.
21 #include "../utils/utils.h"
22 #include "../webp/decode.h" // WebPGetFeatures
23 #include "../webp/demux.h"
24 #include "../webp/format_constants.h"
26 #define DMUX_MAJ_VERSION 0
27 #define DMUX_MIN_VERSION 2
28 #define DMUX_REV_VERSION 0
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
43 typedef struct Frame
{
44 int x_offset_
, y_offset_
;
48 WebPMuxAnimDispose dispose_method_
;
49 WebPMuxAnimBlend blend_method_
;
50 int is_fragment_
; // this is a frame fragment (and not a full frame).
51 int frame_num_
; // the referent frame number for use in assembling fragments.
52 int complete_
; // img_components_ contains a full image.
53 ChunkData img_components_
[2]; // 0=VP8{,L} 1=ALPH
57 typedef struct Chunk
{
64 WebPDemuxState state_
;
66 uint32_t feature_flags_
;
67 int canvas_width_
, canvas_height_
;
73 Chunk
* chunks_
; // non-image chunks
83 typedef struct ChunkParser
{
85 ParseStatus (*parse
)(WebPDemuxer
* const dmux
);
86 int (*valid
)(const WebPDemuxer
* const dmux
);
89 static ParseStatus
ParseSingleImage(WebPDemuxer
* const dmux
);
90 static ParseStatus
ParseVP8X(WebPDemuxer
* const dmux
);
91 static int IsValidSimpleFormat(const WebPDemuxer
* const dmux
);
92 static int IsValidExtendedFormat(const WebPDemuxer
* const dmux
);
94 static const ChunkParser kMasterChunks
[] = {
95 { { 'V', 'P', '8', ' ' }, ParseSingleImage
, IsValidSimpleFormat
},
96 { { 'V', 'P', '8', 'L' }, ParseSingleImage
, IsValidSimpleFormat
},
97 { { 'V', 'P', '8', 'X' }, ParseVP8X
, IsValidExtendedFormat
},
98 { { '0', '0', '0', '0' }, NULL
, NULL
},
101 //------------------------------------------------------------------------------
103 int WebPGetDemuxVersion(void) {
104 return (DMUX_MAJ_VERSION
<< 16) | (DMUX_MIN_VERSION
<< 8) | DMUX_REV_VERSION
;
107 // -----------------------------------------------------------------------------
110 static int RemapMemBuffer(MemBuffer
* const mem
,
111 const uint8_t* data
, size_t size
) {
112 if (size
< mem
->buf_size_
) return 0; // can't remap to a shorter buffer!
115 mem
->end_
= mem
->buf_size_
= size
;
119 static int InitMemBuffer(MemBuffer
* const mem
,
120 const uint8_t* data
, size_t size
) {
121 memset(mem
, 0, sizeof(*mem
));
122 return RemapMemBuffer(mem
, data
, size
);
125 // Return the remaining data size available in 'mem'.
126 static WEBP_INLINE
size_t MemDataSize(const MemBuffer
* const mem
) {
127 return (mem
->end_
- mem
->start_
);
130 // Return true if 'size' exceeds the end of the RIFF chunk.
131 static WEBP_INLINE
int SizeIsInvalid(const MemBuffer
* const mem
, size_t size
) {
132 return (size
> mem
->riff_end_
- mem
->start_
);
135 static WEBP_INLINE
void Skip(MemBuffer
* const mem
, size_t size
) {
139 static WEBP_INLINE
void Rewind(MemBuffer
* const mem
, size_t size
) {
143 static WEBP_INLINE
const uint8_t* GetBuffer(MemBuffer
* const mem
) {
144 return mem
->buf_
+ mem
->start_
;
147 // Read from 'mem' and skip the read bytes.
148 static WEBP_INLINE
uint8_t ReadByte(MemBuffer
* const mem
) {
149 const uint8_t byte
= mem
->buf_
[mem
->start_
];
154 static WEBP_INLINE
int ReadLE16s(MemBuffer
* const mem
) {
155 const uint8_t* const data
= mem
->buf_
+ mem
->start_
;
156 const int val
= GetLE16(data
);
161 static WEBP_INLINE
int ReadLE24s(MemBuffer
* const mem
) {
162 const uint8_t* const data
= mem
->buf_
+ mem
->start_
;
163 const int val
= GetLE24(data
);
168 static WEBP_INLINE
uint32_t ReadLE32(MemBuffer
* const mem
) {
169 const uint8_t* const data
= mem
->buf_
+ mem
->start_
;
170 const uint32_t val
= GetLE32(data
);
175 // -----------------------------------------------------------------------------
176 // Secondary chunk parsing
178 static void AddChunk(WebPDemuxer
* const dmux
, Chunk
* const chunk
) {
179 *dmux
->chunks_tail_
= chunk
;
181 dmux
->chunks_tail_
= &chunk
->next_
;
184 // Add a frame to the end of the list, ensuring the last frame is complete.
185 // Returns true on success, false otherwise.
186 static int AddFrame(WebPDemuxer
* const dmux
, Frame
* const frame
) {
187 const Frame
* const last_frame
= *dmux
->frames_tail_
;
188 if (last_frame
!= NULL
&& !last_frame
->complete_
) return 0;
190 *dmux
->frames_tail_
= frame
;
192 dmux
->frames_tail_
= &frame
->next_
;
196 // Store image bearing chunks to 'frame'.
197 static ParseStatus
StoreFrame(int frame_num
, uint32_t min_size
,
198 MemBuffer
* const mem
, Frame
* const frame
) {
199 int alpha_chunks
= 0;
200 int image_chunks
= 0;
201 int done
= (MemDataSize(mem
) < min_size
);
202 ParseStatus status
= PARSE_OK
;
204 if (done
) return PARSE_NEED_MORE_DATA
;
207 const size_t chunk_start_offset
= mem
->start_
;
208 const uint32_t fourcc
= ReadLE32(mem
);
209 const uint32_t payload_size
= ReadLE32(mem
);
210 const uint32_t payload_size_padded
= payload_size
+ (payload_size
& 1);
211 const size_t payload_available
= (payload_size_padded
> MemDataSize(mem
))
212 ? MemDataSize(mem
) : payload_size_padded
;
213 const size_t chunk_size
= CHUNK_HEADER_SIZE
+ payload_available
;
215 if (payload_size
> MAX_CHUNK_PAYLOAD
) return PARSE_ERROR
;
216 if (SizeIsInvalid(mem
, payload_size_padded
)) return PARSE_ERROR
;
217 if (payload_size_padded
> MemDataSize(mem
)) status
= PARSE_NEED_MORE_DATA
;
220 case MKFOURCC('A', 'L', 'P', 'H'):
221 if (alpha_chunks
== 0) {
223 frame
->img_components_
[1].offset_
= chunk_start_offset
;
224 frame
->img_components_
[1].size_
= chunk_size
;
225 frame
->has_alpha_
= 1;
226 frame
->frame_num_
= frame_num
;
227 Skip(mem
, payload_available
);
232 case MKFOURCC('V', 'P', '8', 'L'):
233 if (alpha_chunks
> 0) return PARSE_ERROR
; // VP8L has its own alpha
235 case MKFOURCC('V', 'P', '8', ' '):
236 if (image_chunks
== 0) {
237 // Extract the bitstream features, tolerating failures when the data
239 WebPBitstreamFeatures features
;
240 const VP8StatusCode vp8_status
=
241 WebPGetFeatures(mem
->buf_
+ chunk_start_offset
, chunk_size
,
243 if (status
== PARSE_NEED_MORE_DATA
&&
244 vp8_status
== VP8_STATUS_NOT_ENOUGH_DATA
) {
245 return PARSE_NEED_MORE_DATA
;
246 } else if (vp8_status
!= VP8_STATUS_OK
) {
247 // We have enough data, and yet WebPGetFeatures() failed.
251 frame
->img_components_
[0].offset_
= chunk_start_offset
;
252 frame
->img_components_
[0].size_
= chunk_size
;
253 frame
->width_
= features
.width
;
254 frame
->height_
= features
.height
;
255 frame
->has_alpha_
|= features
.has_alpha
;
256 frame
->frame_num_
= frame_num
;
257 frame
->complete_
= (status
== PARSE_OK
);
258 Skip(mem
, payload_available
);
265 // Restore fourcc/size when moving up one level in parsing.
266 Rewind(mem
, CHUNK_HEADER_SIZE
);
271 if (mem
->start_
== mem
->riff_end_
) {
273 } else if (MemDataSize(mem
) < CHUNK_HEADER_SIZE
) {
274 status
= PARSE_NEED_MORE_DATA
;
276 } while (!done
&& status
== PARSE_OK
);
281 // Creates a new Frame if 'actual_size' is within bounds and 'mem' contains
282 // enough data ('min_size') to parse the payload.
283 // Returns PARSE_OK on success with *frame pointing to the new Frame.
284 // Returns PARSE_NEED_MORE_DATA with insufficient data, PARSE_ERROR otherwise.
285 static ParseStatus
NewFrame(const MemBuffer
* const mem
,
286 uint32_t min_size
, uint32_t actual_size
,
288 if (SizeIsInvalid(mem
, min_size
)) return PARSE_ERROR
;
289 if (actual_size
< min_size
) return PARSE_ERROR
;
290 if (MemDataSize(mem
) < min_size
) return PARSE_NEED_MORE_DATA
;
292 *frame
= (Frame
*)calloc(1, sizeof(**frame
));
293 return (*frame
== NULL
) ? PARSE_ERROR
: PARSE_OK
;
296 // Parse a 'ANMF' chunk and any image bearing chunks that immediately follow.
297 // 'frame_chunk_size' is the previously validated, padded chunk size.
298 static ParseStatus
ParseAnimationFrame(
299 WebPDemuxer
* const dmux
, uint32_t frame_chunk_size
) {
300 const int is_animation
= !!(dmux
->feature_flags_
& ANIMATION_FLAG
);
301 const uint32_t anmf_payload_size
= frame_chunk_size
- ANMF_CHUNK_SIZE
;
304 MemBuffer
* const mem
= &dmux
->mem_
;
307 NewFrame(mem
, ANMF_CHUNK_SIZE
, frame_chunk_size
, &frame
);
308 if (status
!= PARSE_OK
) return status
;
310 frame
->x_offset_
= 2 * ReadLE24s(mem
);
311 frame
->y_offset_
= 2 * ReadLE24s(mem
);
312 frame
->width_
= 1 + ReadLE24s(mem
);
313 frame
->height_
= 1 + ReadLE24s(mem
);
314 frame
->duration_
= ReadLE24s(mem
);
315 bits
= ReadByte(mem
);
316 frame
->dispose_method_
=
317 (bits
& 1) ? WEBP_MUX_DISPOSE_BACKGROUND
: WEBP_MUX_DISPOSE_NONE
;
318 frame
->blend_method_
= (bits
& 2) ? WEBP_MUX_NO_BLEND
: WEBP_MUX_BLEND
;
319 if (frame
->width_
* (uint64_t)frame
->height_
>= MAX_IMAGE_AREA
) {
324 // Store a frame only if the animation flag is set there is some data for
325 // this frame is available.
326 status
= StoreFrame(dmux
->num_frames_
+ 1, anmf_payload_size
, mem
, frame
);
327 if (status
!= PARSE_ERROR
&& is_animation
&& frame
->frame_num_
> 0) {
328 added_frame
= AddFrame(dmux
, frame
);
332 status
= PARSE_ERROR
;
336 if (!added_frame
) free(frame
);
340 #ifdef WEBP_EXPERIMENTAL_FEATURES
341 // Parse a 'FRGM' chunk and any image bearing chunks that immediately follow.
342 // 'fragment_chunk_size' is the previously validated, padded chunk size.
343 static ParseStatus
ParseFragment(WebPDemuxer
* const dmux
,
344 uint32_t fragment_chunk_size
) {
345 const int frame_num
= 1; // All fragments belong to the 1st (and only) frame.
346 const int is_fragmented
= !!(dmux
->feature_flags_
& FRAGMENTS_FLAG
);
347 const uint32_t frgm_payload_size
= fragment_chunk_size
- FRGM_CHUNK_SIZE
;
348 int added_fragment
= 0;
349 MemBuffer
* const mem
= &dmux
->mem_
;
352 NewFrame(mem
, FRGM_CHUNK_SIZE
, fragment_chunk_size
, &frame
);
353 if (status
!= PARSE_OK
) return status
;
355 frame
->is_fragment_
= 1;
356 frame
->x_offset_
= 2 * ReadLE24s(mem
);
357 frame
->y_offset_
= 2 * ReadLE24s(mem
);
359 // Store a fragment only if the 'fragments' flag is set and there is some
361 status
= StoreFrame(frame_num
, frgm_payload_size
, mem
, frame
);
362 if (status
!= PARSE_ERROR
&& is_fragmented
&& frame
->frame_num_
> 0) {
363 added_fragment
= AddFrame(dmux
, frame
);
364 if (!added_fragment
) {
365 status
= PARSE_ERROR
;
367 dmux
->num_frames_
= 1;
371 if (!added_fragment
) free(frame
);
374 #endif // WEBP_EXPERIMENTAL_FEATURES
376 // General chunk storage, starting with the header at 'start_offset', allowing
377 // the user to request the payload via a fourcc string. 'size' includes the
378 // header and the unpadded payload size.
379 // Returns true on success, false otherwise.
380 static int StoreChunk(WebPDemuxer
* const dmux
,
381 size_t start_offset
, uint32_t size
) {
382 Chunk
* const chunk
= (Chunk
*)calloc(1, sizeof(*chunk
));
383 if (chunk
== NULL
) return 0;
385 chunk
->data_
.offset_
= start_offset
;
386 chunk
->data_
.size_
= size
;
387 AddChunk(dmux
, chunk
);
391 // -----------------------------------------------------------------------------
392 // Primary chunk parsing
394 static ParseStatus
ReadHeader(MemBuffer
* const mem
) {
395 const size_t min_size
= RIFF_HEADER_SIZE
+ CHUNK_HEADER_SIZE
;
398 // Basic file level validation.
399 if (MemDataSize(mem
) < min_size
) return PARSE_NEED_MORE_DATA
;
400 if (memcmp(GetBuffer(mem
), "RIFF", CHUNK_SIZE_BYTES
) ||
401 memcmp(GetBuffer(mem
) + CHUNK_HEADER_SIZE
, "WEBP", CHUNK_SIZE_BYTES
)) {
405 riff_size
= GetLE32(GetBuffer(mem
) + TAG_SIZE
);
406 if (riff_size
< CHUNK_HEADER_SIZE
) return PARSE_ERROR
;
407 if (riff_size
> MAX_CHUNK_PAYLOAD
) return PARSE_ERROR
;
409 // There's no point in reading past the end of the RIFF chunk
410 mem
->riff_end_
= riff_size
+ CHUNK_HEADER_SIZE
;
411 if (mem
->buf_size_
> mem
->riff_end_
) {
412 mem
->buf_size_
= mem
->end_
= mem
->riff_end_
;
415 Skip(mem
, RIFF_HEADER_SIZE
);
419 static ParseStatus
ParseSingleImage(WebPDemuxer
* const dmux
) {
420 const size_t min_size
= CHUNK_HEADER_SIZE
;
421 MemBuffer
* const mem
= &dmux
->mem_
;
426 if (dmux
->frames_
!= NULL
) return PARSE_ERROR
;
427 if (SizeIsInvalid(mem
, min_size
)) return PARSE_ERROR
;
428 if (MemDataSize(mem
) < min_size
) return PARSE_NEED_MORE_DATA
;
430 frame
= (Frame
*)calloc(1, sizeof(*frame
));
431 if (frame
== NULL
) return PARSE_ERROR
;
433 // For the single image case we allow parsing of a partial frame, but we need
434 // at least CHUNK_HEADER_SIZE for parsing.
435 status
= StoreFrame(1, CHUNK_HEADER_SIZE
, &dmux
->mem_
, frame
);
436 if (status
!= PARSE_ERROR
) {
437 const int has_alpha
= !!(dmux
->feature_flags_
& ALPHA_FLAG
);
438 // Clear any alpha when the alpha flag is missing.
439 if (!has_alpha
&& frame
->img_components_
[1].size_
> 0) {
440 frame
->img_components_
[1].offset_
= 0;
441 frame
->img_components_
[1].size_
= 0;
442 frame
->has_alpha_
= 0;
445 // Use the frame width/height as the canvas values for non-vp8x files.
446 // Also, set ALPHA_FLAG if this is a lossless image with alpha.
447 if (!dmux
->is_ext_format_
&& frame
->width_
> 0 && frame
->height_
> 0) {
448 dmux
->state_
= WEBP_DEMUX_PARSED_HEADER
;
449 dmux
->canvas_width_
= frame
->width_
;
450 dmux
->canvas_height_
= frame
->height_
;
451 dmux
->feature_flags_
|= frame
->has_alpha_
? ALPHA_FLAG
: 0;
453 if (!AddFrame(dmux
, frame
)) {
454 status
= PARSE_ERROR
; // last frame was left incomplete
457 dmux
->num_frames_
= 1;
461 if (!image_added
) free(frame
);
465 static ParseStatus
ParseVP8XChunks(WebPDemuxer
* const dmux
) {
466 const int is_animation
= !!(dmux
->feature_flags_
& ANIMATION_FLAG
);
467 MemBuffer
* const mem
= &dmux
->mem_
;
469 ParseStatus status
= PARSE_OK
;
473 const size_t chunk_start_offset
= mem
->start_
;
474 const uint32_t fourcc
= ReadLE32(mem
);
475 const uint32_t chunk_size
= ReadLE32(mem
);
476 const uint32_t chunk_size_padded
= chunk_size
+ (chunk_size
& 1);
478 if (chunk_size
> MAX_CHUNK_PAYLOAD
) return PARSE_ERROR
;
479 if (SizeIsInvalid(mem
, chunk_size_padded
)) return PARSE_ERROR
;
482 case MKFOURCC('V', 'P', '8', 'X'): {
485 case MKFOURCC('A', 'L', 'P', 'H'):
486 case MKFOURCC('V', 'P', '8', ' '):
487 case MKFOURCC('V', 'P', '8', 'L'): {
488 // check that this isn't an animation (all frames should be in an ANMF).
489 if (anim_chunks
> 0 || is_animation
) return PARSE_ERROR
;
491 Rewind(mem
, CHUNK_HEADER_SIZE
);
492 status
= ParseSingleImage(dmux
);
495 case MKFOURCC('A', 'N', 'I', 'M'): {
496 if (chunk_size_padded
< ANIM_CHUNK_SIZE
) return PARSE_ERROR
;
498 if (MemDataSize(mem
) < chunk_size_padded
) {
499 status
= PARSE_NEED_MORE_DATA
;
500 } else if (anim_chunks
== 0) {
502 dmux
->bgcolor_
= ReadLE32(mem
);
503 dmux
->loop_count_
= ReadLE16s(mem
);
504 Skip(mem
, chunk_size_padded
- ANIM_CHUNK_SIZE
);
511 case MKFOURCC('A', 'N', 'M', 'F'): {
512 if (anim_chunks
== 0) return PARSE_ERROR
; // 'ANIM' precedes frames.
513 status
= ParseAnimationFrame(dmux
, chunk_size_padded
);
516 #ifdef WEBP_EXPERIMENTAL_FEATURES
517 case MKFOURCC('F', 'R', 'G', 'M'): {
518 status
= ParseFragment(dmux
, chunk_size_padded
);
522 case MKFOURCC('I', 'C', 'C', 'P'): {
523 store_chunk
= !!(dmux
->feature_flags_
& ICCP_FLAG
);
526 case MKFOURCC('E', 'X', 'I', 'F'): {
527 store_chunk
= !!(dmux
->feature_flags_
& EXIF_FLAG
);
530 case MKFOURCC('X', 'M', 'P', ' '): {
531 store_chunk
= !!(dmux
->feature_flags_
& XMP_FLAG
);
536 if (chunk_size_padded
<= MemDataSize(mem
)) {
538 // Store only the chunk header and unpadded size as only the payload
539 // will be returned to the user.
540 if (!StoreChunk(dmux
, chunk_start_offset
,
541 CHUNK_HEADER_SIZE
+ chunk_size
)) {
545 Skip(mem
, chunk_size_padded
);
547 status
= PARSE_NEED_MORE_DATA
;
552 if (mem
->start_
== mem
->riff_end_
) {
554 } else if (MemDataSize(mem
) < CHUNK_HEADER_SIZE
) {
555 status
= PARSE_NEED_MORE_DATA
;
557 } while (status
== PARSE_OK
);
562 static ParseStatus
ParseVP8X(WebPDemuxer
* const dmux
) {
563 MemBuffer
* const mem
= &dmux
->mem_
;
566 if (MemDataSize(mem
) < CHUNK_HEADER_SIZE
) return PARSE_NEED_MORE_DATA
;
568 dmux
->is_ext_format_
= 1;
569 Skip(mem
, TAG_SIZE
); // VP8X
570 vp8x_size
= ReadLE32(mem
);
571 if (vp8x_size
> MAX_CHUNK_PAYLOAD
) return PARSE_ERROR
;
572 if (vp8x_size
< VP8X_CHUNK_SIZE
) return PARSE_ERROR
;
573 vp8x_size
+= vp8x_size
& 1;
574 if (SizeIsInvalid(mem
, vp8x_size
)) return PARSE_ERROR
;
575 if (MemDataSize(mem
) < vp8x_size
) return PARSE_NEED_MORE_DATA
;
577 dmux
->feature_flags_
= ReadByte(mem
);
578 Skip(mem
, 3); // Reserved.
579 dmux
->canvas_width_
= 1 + ReadLE24s(mem
);
580 dmux
->canvas_height_
= 1 + ReadLE24s(mem
);
581 if (dmux
->canvas_width_
* (uint64_t)dmux
->canvas_height_
>= MAX_IMAGE_AREA
) {
582 return PARSE_ERROR
; // image final dimension is too large
584 Skip(mem
, vp8x_size
- VP8X_CHUNK_SIZE
); // skip any trailing data.
585 dmux
->state_
= WEBP_DEMUX_PARSED_HEADER
;
587 if (SizeIsInvalid(mem
, CHUNK_HEADER_SIZE
)) return PARSE_ERROR
;
588 if (MemDataSize(mem
) < CHUNK_HEADER_SIZE
) return PARSE_NEED_MORE_DATA
;
590 return ParseVP8XChunks(dmux
);
593 // -----------------------------------------------------------------------------
596 static int IsValidSimpleFormat(const WebPDemuxer
* const dmux
) {
597 const Frame
* const frame
= dmux
->frames_
;
598 if (dmux
->state_
== WEBP_DEMUX_PARSING_HEADER
) return 1;
600 if (dmux
->canvas_width_
<= 0 || dmux
->canvas_height_
<= 0) return 0;
601 if (dmux
->state_
== WEBP_DEMUX_DONE
&& frame
== NULL
) return 0;
603 if (frame
->width_
<= 0 || frame
->height_
<= 0) return 0;
607 // If 'exact' is true, check that the image resolution matches the canvas.
608 // If 'exact' is false, check that the x/y offsets do not exceed the canvas.
609 // TODO(jzern): this is insufficient in the fragmented image case if the
610 // expectation is that the fragments completely cover the canvas.
611 static int CheckFrameBounds(const Frame
* const frame
, int exact
,
612 int canvas_width
, int canvas_height
) {
614 if (frame
->x_offset_
!= 0 || frame
->y_offset_
!= 0) {
617 if (frame
->width_
!= canvas_width
|| frame
->height_
!= canvas_height
) {
621 if (frame
->x_offset_
< 0 || frame
->y_offset_
< 0) return 0;
622 if (frame
->width_
+ frame
->x_offset_
> canvas_width
) return 0;
623 if (frame
->height_
+ frame
->y_offset_
> canvas_height
) return 0;
628 static int IsValidExtendedFormat(const WebPDemuxer
* const dmux
) {
629 const int is_animation
= !!(dmux
->feature_flags_
& ANIMATION_FLAG
);
630 const int is_fragmented
= !!(dmux
->feature_flags_
& FRAGMENTS_FLAG
);
631 const Frame
* f
= dmux
->frames_
;
633 if (dmux
->state_
== WEBP_DEMUX_PARSING_HEADER
) return 1;
635 if (dmux
->canvas_width_
<= 0 || dmux
->canvas_height_
<= 0) return 0;
636 if (dmux
->loop_count_
< 0) return 0;
637 if (dmux
->state_
== WEBP_DEMUX_DONE
&& dmux
->frames_
== NULL
) return 0;
638 #ifndef WEBP_EXPERIMENTAL_FEATURES
639 if (is_fragmented
) return 0;
643 const int cur_frame_set
= f
->frame_num_
;
644 int frame_count
= 0, fragment_count
= 0;
646 // Check frame properties and if the image is composed of fragments that
647 // each fragment came from a fragment.
648 for (; f
!= NULL
&& f
->frame_num_
== cur_frame_set
; f
= f
->next_
) {
649 const ChunkData
* const image
= f
->img_components_
;
650 const ChunkData
* const alpha
= f
->img_components_
+ 1;
652 if (is_fragmented
&& !f
->is_fragment_
) return 0;
653 if (!is_fragmented
&& f
->is_fragment_
) return 0;
654 if (!is_animation
&& f
->frame_num_
> 1) return 0;
657 if (alpha
->size_
== 0 && image
->size_
== 0) return 0;
658 // Ensure alpha precedes image bitstream.
659 if (alpha
->size_
> 0 && alpha
->offset_
> image
->offset_
) {
663 if (f
->width_
<= 0 || f
->height_
<= 0) return 0;
665 // There shouldn't be a partial frame in a complete file.
666 if (dmux
->state_
== WEBP_DEMUX_DONE
) return 0;
668 // Ensure alpha precedes image bitstream.
669 if (alpha
->size_
> 0 && image
->size_
> 0 &&
670 alpha
->offset_
> image
->offset_
) {
673 // There shouldn't be any frames after an incomplete one.
674 if (f
->next_
!= NULL
) return 0;
677 if (f
->width_
> 0 && f
->height_
> 0 &&
678 !CheckFrameBounds(f
, !(is_animation
|| is_fragmented
),
679 dmux
->canvas_width_
, dmux
->canvas_height_
)) {
683 fragment_count
+= f
->is_fragment_
;
686 if (!is_fragmented
&& frame_count
> 1) return 0;
687 if (fragment_count
> 0 && frame_count
!= fragment_count
) return 0;
692 // -----------------------------------------------------------------------------
693 // WebPDemuxer object
695 static void InitDemux(WebPDemuxer
* const dmux
, const MemBuffer
* const mem
) {
696 dmux
->state_
= WEBP_DEMUX_PARSING_HEADER
;
697 dmux
->loop_count_
= 1;
698 dmux
->bgcolor_
= 0xFFFFFFFF; // White background by default.
699 dmux
->canvas_width_
= -1;
700 dmux
->canvas_height_
= -1;
701 dmux
->frames_tail_
= &dmux
->frames_
;
702 dmux
->chunks_tail_
= &dmux
->chunks_
;
706 WebPDemuxer
* WebPDemuxInternal(const WebPData
* data
, int allow_partial
,
707 WebPDemuxState
* state
, int version
) {
708 const ChunkParser
* parser
;
710 ParseStatus status
= PARSE_ERROR
;
714 if (state
!= NULL
) *state
= WEBP_DEMUX_PARSE_ERROR
;
716 if (WEBP_ABI_IS_INCOMPATIBLE(version
, WEBP_DEMUX_ABI_VERSION
)) return NULL
;
717 if (data
== NULL
|| data
->bytes
== NULL
|| data
->size
== 0) return NULL
;
719 if (!InitMemBuffer(&mem
, data
->bytes
, data
->size
)) return NULL
;
720 status
= ReadHeader(&mem
);
721 if (status
!= PARSE_OK
) {
723 *state
= (status
== PARSE_NEED_MORE_DATA
) ? WEBP_DEMUX_PARSING_HEADER
724 : WEBP_DEMUX_PARSE_ERROR
;
729 partial
= (mem
.buf_size_
< mem
.riff_end_
);
730 if (!allow_partial
&& partial
) return NULL
;
732 dmux
= (WebPDemuxer
*)calloc(1, sizeof(*dmux
));
733 if (dmux
== NULL
) return NULL
;
734 InitDemux(dmux
, &mem
);
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
;
747 if (state
!= NULL
) *state
= dmux
->state_
;
749 if (status
== PARSE_ERROR
) {
750 WebPDemuxDelete(dmux
);
756 void WebPDemuxDelete(WebPDemuxer
* dmux
) {
759 if (dmux
== NULL
) return;
761 for (f
= dmux
->frames_
; f
!= NULL
;) {
762 Frame
* const cur_frame
= f
;
766 for (c
= dmux
->chunks_
; c
!= NULL
;) {
767 Chunk
* const cur_chunk
= c
;
774 // -----------------------------------------------------------------------------
776 uint32_t WebPDemuxGetI(const WebPDemuxer
* dmux
, WebPFormatFeature feature
) {
777 if (dmux
== NULL
) return 0;
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_
;
790 // -----------------------------------------------------------------------------
793 // Find the first 'frame_num' frame. There may be multiple such frames in a
795 static const Frame
* GetFrame(const WebPDemuxer
* const dmux
, int frame_num
) {
797 for (f
= dmux
->frames_
; f
!= NULL
; f
= f
->next_
) {
798 if (frame_num
== f
->frame_num_
) break;
803 // Returns fragment 'fragment_num' and the total count.
804 static const Frame
* GetFragment(
805 const Frame
* const frame_set
, int fragment_num
, int* const count
) {
806 const int this_frame
= frame_set
->frame_num_
;
807 const Frame
* f
= frame_set
;
808 const Frame
* fragment
= NULL
;
811 for (total
= 0; f
!= NULL
&& f
->frame_num_
== this_frame
; f
= f
->next_
) {
812 if (++total
== fragment_num
) fragment
= f
;
818 static const uint8_t* GetFramePayload(const uint8_t* const mem_buf
,
819 const Frame
* const frame
,
820 size_t* const data_size
) {
823 const ChunkData
* const image
= frame
->img_components_
;
824 const ChunkData
* const alpha
= frame
->img_components_
+ 1;
825 size_t start_offset
= image
->offset_
;
826 *data_size
= image
->size_
;
828 // if alpha exists it precedes image, update the size allowing for
829 // intervening chunks.
830 if (alpha
->size_
> 0) {
831 const size_t inter_size
= (image
->offset_
> 0)
832 ? image
->offset_
- (alpha
->offset_
+ alpha
->size_
)
834 start_offset
= alpha
->offset_
;
835 *data_size
+= alpha
->size_
+ inter_size
;
837 return mem_buf
+ start_offset
;
842 // Create a whole 'frame' from VP8 (+ alpha) or lossless.
843 static int SynthesizeFrame(const WebPDemuxer
* const dmux
,
844 const Frame
* const first_frame
,
845 int fragment_num
, WebPIterator
* const iter
) {
846 const uint8_t* const mem_buf
= dmux
->mem_
.buf_
;
848 size_t payload_size
= 0;
849 const Frame
* const fragment
=
850 GetFragment(first_frame
, fragment_num
, &num_fragments
);
851 const uint8_t* const payload
=
852 GetFramePayload(mem_buf
, fragment
, &payload_size
);
853 if (payload
== NULL
) return 0;
854 assert(first_frame
!= NULL
);
856 iter
->frame_num
= first_frame
->frame_num_
;
857 iter
->num_frames
= dmux
->num_frames_
;
858 iter
->fragment_num
= fragment_num
;
859 iter
->num_fragments
= num_fragments
;
860 iter
->x_offset
= fragment
->x_offset_
;
861 iter
->y_offset
= fragment
->y_offset_
;
862 iter
->width
= fragment
->width_
;
863 iter
->height
= fragment
->height_
;
864 iter
->has_alpha
= fragment
->has_alpha_
;
865 iter
->duration
= fragment
->duration_
;
866 iter
->dispose_method
= fragment
->dispose_method_
;
867 iter
->blend_method
= fragment
->blend_method_
;
868 iter
->complete
= fragment
->complete_
;
869 iter
->fragment
.bytes
= payload
;
870 iter
->fragment
.size
= payload_size
;
871 // TODO(jzern): adjust offsets for 'FRGM's embedded in 'ANMF's
875 static int SetFrame(int frame_num
, WebPIterator
* const iter
) {
877 const WebPDemuxer
* const dmux
= (WebPDemuxer
*)iter
->private_
;
878 if (dmux
== NULL
|| frame_num
< 0) return 0;
879 if (frame_num
> dmux
->num_frames_
) return 0;
880 if (frame_num
== 0) frame_num
= dmux
->num_frames_
;
882 frame
= GetFrame(dmux
, frame_num
);
883 if (frame
== NULL
) return 0;
885 return SynthesizeFrame(dmux
, frame
, 1, iter
);
888 int WebPDemuxGetFrame(const WebPDemuxer
* dmux
, int frame
, WebPIterator
* iter
) {
889 if (iter
== NULL
) return 0;
891 memset(iter
, 0, sizeof(*iter
));
892 iter
->private_
= (void*)dmux
;
893 return SetFrame(frame
, iter
);
896 int WebPDemuxNextFrame(WebPIterator
* iter
) {
897 if (iter
== NULL
) return 0;
898 return SetFrame(iter
->frame_num
+ 1, iter
);
901 int WebPDemuxPrevFrame(WebPIterator
* iter
) {
902 if (iter
== NULL
) return 0;
903 if (iter
->frame_num
<= 1) return 0;
904 return SetFrame(iter
->frame_num
- 1, iter
);
907 int WebPDemuxSelectFragment(WebPIterator
* iter
, int fragment_num
) {
908 if (iter
!= NULL
&& iter
->private_
!= NULL
&& fragment_num
> 0) {
909 const WebPDemuxer
* const dmux
= (WebPDemuxer
*)iter
->private_
;
910 const Frame
* const frame
= GetFrame(dmux
, iter
->frame_num
);
911 if (frame
== NULL
) return 0;
913 return SynthesizeFrame(dmux
, frame
, fragment_num
, iter
);
918 void WebPDemuxReleaseIterator(WebPIterator
* iter
) {
922 // -----------------------------------------------------------------------------
925 static int ChunkCount(const WebPDemuxer
* const dmux
, const char fourcc
[4]) {
926 const uint8_t* const mem_buf
= dmux
->mem_
.buf_
;
929 for (c
= dmux
->chunks_
; c
!= NULL
; c
= c
->next_
) {
930 const uint8_t* const header
= mem_buf
+ c
->data_
.offset_
;
931 if (!memcmp(header
, fourcc
, TAG_SIZE
)) ++count
;
936 static const Chunk
* GetChunk(const WebPDemuxer
* const dmux
,
937 const char fourcc
[4], int chunk_num
) {
938 const uint8_t* const mem_buf
= dmux
->mem_
.buf_
;
941 for (c
= dmux
->chunks_
; c
!= NULL
; c
= c
->next_
) {
942 const uint8_t* const header
= mem_buf
+ c
->data_
.offset_
;
943 if (!memcmp(header
, fourcc
, TAG_SIZE
)) ++count
;
944 if (count
== chunk_num
) break;
949 static int SetChunk(const char fourcc
[4], int chunk_num
,
950 WebPChunkIterator
* const iter
) {
951 const WebPDemuxer
* const dmux
= (WebPDemuxer
*)iter
->private_
;
954 if (dmux
== NULL
|| fourcc
== NULL
|| chunk_num
< 0) return 0;
955 count
= ChunkCount(dmux
, fourcc
);
956 if (count
== 0) return 0;
957 if (chunk_num
== 0) chunk_num
= count
;
959 if (chunk_num
<= count
) {
960 const uint8_t* const mem_buf
= dmux
->mem_
.buf_
;
961 const Chunk
* const chunk
= GetChunk(dmux
, fourcc
, chunk_num
);
962 iter
->chunk
.bytes
= mem_buf
+ chunk
->data_
.offset_
+ CHUNK_HEADER_SIZE
;
963 iter
->chunk
.size
= chunk
->data_
.size_
- CHUNK_HEADER_SIZE
;
964 iter
->num_chunks
= count
;
965 iter
->chunk_num
= chunk_num
;
971 int WebPDemuxGetChunk(const WebPDemuxer
* dmux
,
972 const char fourcc
[4], int chunk_num
,
973 WebPChunkIterator
* iter
) {
974 if (iter
== NULL
) return 0;
976 memset(iter
, 0, sizeof(*iter
));
977 iter
->private_
= (void*)dmux
;
978 return SetChunk(fourcc
, chunk_num
, iter
);
981 int WebPDemuxNextChunk(WebPChunkIterator
* iter
) {
983 const char* const fourcc
=
984 (const char*)iter
->chunk
.bytes
- CHUNK_HEADER_SIZE
;
985 return SetChunk(fourcc
, iter
->chunk_num
+ 1, iter
);
990 int WebPDemuxPrevChunk(WebPChunkIterator
* iter
) {
991 if (iter
!= NULL
&& iter
->chunk_num
> 1) {
992 const char* const fourcc
=
993 (const char*)iter
->chunk
.bytes
- CHUNK_HEADER_SIZE
;
994 return SetChunk(fourcc
, iter
->chunk_num
- 1, iter
);
999 void WebPDemuxReleaseChunkIterator(WebPChunkIterator
* iter
) {