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 #if defined(__cplusplus) || defined(c_plusplus)
30 #define DMUX_MAJ_VERSION 0
31 #define DMUX_MIN_VERSION 1
32 #define DMUX_REV_VERSION 1
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
47 typedef struct Frame
{
48 int x_offset_
, y_offset_
;
52 WebPMuxAnimDispose dispose_method_
;
53 WebPMuxAnimBlend blend_method_
;
54 int is_fragment_
; // this is a frame fragment (and not a full frame).
55 int frame_num_
; // the referent frame number for use in assembling fragments.
56 int complete_
; // img_components_ contains a full image.
57 ChunkData img_components_
[2]; // 0=VP8{,L} 1=ALPH
61 typedef struct Chunk
{
68 WebPDemuxState state_
;
70 uint32_t feature_flags_
;
71 int canvas_width_
, canvas_height_
;
77 Chunk
* chunks_
; // non-image chunks
86 typedef struct ChunkParser
{
88 ParseStatus (*parse
)(WebPDemuxer
* const dmux
);
89 int (*valid
)(const WebPDemuxer
* const dmux
);
92 static ParseStatus
ParseSingleImage(WebPDemuxer
* const dmux
);
93 static ParseStatus
ParseVP8X(WebPDemuxer
* const dmux
);
94 static int IsValidSimpleFormat(const WebPDemuxer
* const dmux
);
95 static int IsValidExtendedFormat(const WebPDemuxer
* const dmux
);
97 static const ChunkParser kMasterChunks
[] = {
98 { { 'V', 'P', '8', ' ' }, ParseSingleImage
, IsValidSimpleFormat
},
99 { { 'V', 'P', '8', 'L' }, ParseSingleImage
, IsValidSimpleFormat
},
100 { { 'V', 'P', '8', 'X' }, ParseVP8X
, IsValidExtendedFormat
},
101 { { '0', '0', '0', '0' }, NULL
, NULL
},
104 //------------------------------------------------------------------------------
106 int WebPGetDemuxVersion(void) {
107 return (DMUX_MAJ_VERSION
<< 16) | (DMUX_MIN_VERSION
<< 8) | DMUX_REV_VERSION
;
110 // -----------------------------------------------------------------------------
113 static int RemapMemBuffer(MemBuffer
* const mem
,
114 const uint8_t* data
, size_t size
) {
115 if (size
< mem
->buf_size_
) return 0; // can't remap to a shorter buffer!
118 mem
->end_
= mem
->buf_size_
= size
;
122 static int InitMemBuffer(MemBuffer
* const mem
,
123 const uint8_t* data
, size_t size
) {
124 memset(mem
, 0, sizeof(*mem
));
125 return RemapMemBuffer(mem
, data
, size
);
128 // Return the remaining data size available in 'mem'.
129 static WEBP_INLINE
size_t MemDataSize(const MemBuffer
* const mem
) {
130 return (mem
->end_
- mem
->start_
);
133 // Return true if 'size' exceeds the end of the RIFF chunk.
134 static WEBP_INLINE
int SizeIsInvalid(const MemBuffer
* const mem
, size_t size
) {
135 return (size
> mem
->riff_end_
- mem
->start_
);
138 static WEBP_INLINE
void Skip(MemBuffer
* const mem
, size_t size
) {
142 static WEBP_INLINE
void Rewind(MemBuffer
* const mem
, size_t size
) {
146 static WEBP_INLINE
const uint8_t* GetBuffer(MemBuffer
* const mem
) {
147 return mem
->buf_
+ mem
->start_
;
150 // Read from 'mem' and skip the read bytes.
151 static WEBP_INLINE
uint8_t ReadByte(MemBuffer
* const mem
) {
152 const uint8_t byte
= mem
->buf_
[mem
->start_
];
157 static WEBP_INLINE
int ReadLE16s(MemBuffer
* const mem
) {
158 const uint8_t* const data
= mem
->buf_
+ mem
->start_
;
159 const int val
= GetLE16(data
);
164 static WEBP_INLINE
int ReadLE24s(MemBuffer
* const mem
) {
165 const uint8_t* const data
= mem
->buf_
+ mem
->start_
;
166 const int val
= GetLE24(data
);
171 static WEBP_INLINE
uint32_t ReadLE32(MemBuffer
* const mem
) {
172 const uint8_t* const data
= mem
->buf_
+ mem
->start_
;
173 const uint32_t val
= GetLE32(data
);
178 // -----------------------------------------------------------------------------
179 // Secondary chunk parsing
181 static void AddChunk(WebPDemuxer
* const dmux
, Chunk
* const chunk
) {
182 Chunk
** c
= &dmux
->chunks_
;
183 while (*c
!= NULL
) c
= &(*c
)->next_
;
188 // Add a frame to the end of the list, ensuring the last frame is complete.
189 // Returns true on success, false otherwise.
190 static int AddFrame(WebPDemuxer
* const dmux
, Frame
* const frame
) {
191 const Frame
* const last_frame
= *dmux
->frames_tail_
;
192 if (last_frame
!= NULL
&& !last_frame
->complete_
) return 0;
194 *dmux
->frames_tail_
= frame
;
196 dmux
->frames_tail_
= &frame
->next_
;
200 // Store image bearing chunks to 'frame'.
201 static ParseStatus
StoreFrame(int frame_num
, uint32_t min_size
,
202 MemBuffer
* const mem
, Frame
* const frame
) {
203 int alpha_chunks
= 0;
204 int image_chunks
= 0;
205 int done
= (MemDataSize(mem
) < min_size
);
206 ParseStatus status
= PARSE_OK
;
208 if (done
) return PARSE_NEED_MORE_DATA
;
211 const size_t chunk_start_offset
= mem
->start_
;
212 const uint32_t fourcc
= ReadLE32(mem
);
213 const uint32_t payload_size
= ReadLE32(mem
);
214 const uint32_t payload_size_padded
= payload_size
+ (payload_size
& 1);
215 const size_t payload_available
= (payload_size_padded
> MemDataSize(mem
))
216 ? MemDataSize(mem
) : payload_size_padded
;
217 const size_t chunk_size
= CHUNK_HEADER_SIZE
+ payload_available
;
219 if (payload_size
> MAX_CHUNK_PAYLOAD
) return PARSE_ERROR
;
220 if (SizeIsInvalid(mem
, payload_size_padded
)) return PARSE_ERROR
;
221 if (payload_size_padded
> MemDataSize(mem
)) status
= PARSE_NEED_MORE_DATA
;
224 case MKFOURCC('A', 'L', 'P', 'H'):
225 if (alpha_chunks
== 0) {
227 frame
->img_components_
[1].offset_
= chunk_start_offset
;
228 frame
->img_components_
[1].size_
= chunk_size
;
229 frame
->has_alpha_
= 1;
230 frame
->frame_num_
= frame_num
;
231 Skip(mem
, payload_available
);
236 case MKFOURCC('V', 'P', '8', 'L'):
237 if (alpha_chunks
> 0) return PARSE_ERROR
; // VP8L has its own alpha
239 case MKFOURCC('V', 'P', '8', ' '):
240 if (image_chunks
== 0) {
241 // Extract the bitstream features, tolerating failures when the data
243 WebPBitstreamFeatures features
;
244 const VP8StatusCode vp8_status
=
245 WebPGetFeatures(mem
->buf_
+ chunk_start_offset
, chunk_size
,
247 if (status
== PARSE_NEED_MORE_DATA
&&
248 vp8_status
== VP8_STATUS_NOT_ENOUGH_DATA
) {
249 return PARSE_NEED_MORE_DATA
;
250 } else if (vp8_status
!= VP8_STATUS_OK
) {
251 // We have enough data, and yet WebPGetFeatures() failed.
255 frame
->img_components_
[0].offset_
= chunk_start_offset
;
256 frame
->img_components_
[0].size_
= chunk_size
;
257 frame
->width_
= features
.width
;
258 frame
->height_
= features
.height
;
259 frame
->has_alpha_
|= features
.has_alpha
;
260 frame
->frame_num_
= frame_num
;
261 frame
->complete_
= (status
== PARSE_OK
);
262 Skip(mem
, payload_available
);
269 // Restore fourcc/size when moving up one level in parsing.
270 Rewind(mem
, CHUNK_HEADER_SIZE
);
275 if (mem
->start_
== mem
->riff_end_
) {
277 } else if (MemDataSize(mem
) < CHUNK_HEADER_SIZE
) {
278 status
= PARSE_NEED_MORE_DATA
;
280 } while (!done
&& status
== PARSE_OK
);
285 // Creates a new Frame if 'actual_size' is within bounds and 'mem' contains
286 // enough data ('min_size') to parse the payload.
287 // Returns PARSE_OK on success with *frame pointing to the new Frame.
288 // Returns PARSE_NEED_MORE_DATA with insufficient data, PARSE_ERROR otherwise.
289 static ParseStatus
NewFrame(const MemBuffer
* const mem
,
290 uint32_t min_size
, uint32_t actual_size
,
292 if (SizeIsInvalid(mem
, min_size
)) return PARSE_ERROR
;
293 if (actual_size
< min_size
) return PARSE_ERROR
;
294 if (MemDataSize(mem
) < min_size
) return PARSE_NEED_MORE_DATA
;
296 *frame
= (Frame
*)calloc(1, sizeof(**frame
));
297 return (*frame
== NULL
) ? PARSE_ERROR
: PARSE_OK
;
300 // Parse a 'ANMF' chunk and any image bearing chunks that immediately follow.
301 // 'frame_chunk_size' is the previously validated, padded chunk size.
302 static ParseStatus
ParseAnimationFrame(
303 WebPDemuxer
* const dmux
, uint32_t frame_chunk_size
) {
304 const int has_frames
= !!(dmux
->feature_flags_
& ANIMATION_FLAG
);
305 const uint32_t anmf_payload_size
= frame_chunk_size
- ANMF_CHUNK_SIZE
;
308 MemBuffer
* const mem
= &dmux
->mem_
;
311 NewFrame(mem
, ANMF_CHUNK_SIZE
, frame_chunk_size
, &frame
);
312 if (status
!= PARSE_OK
) return status
;
314 frame
->x_offset_
= 2 * ReadLE24s(mem
);
315 frame
->y_offset_
= 2 * ReadLE24s(mem
);
316 frame
->width_
= 1 + ReadLE24s(mem
);
317 frame
->height_
= 1 + ReadLE24s(mem
);
318 frame
->duration_
= ReadLE24s(mem
);
319 bits
= ReadByte(mem
);
320 frame
->dispose_method_
=
321 (bits
& 1) ? WEBP_MUX_DISPOSE_BACKGROUND
: WEBP_MUX_DISPOSE_NONE
;
322 frame
->blend_method_
= (bits
& 2) ? WEBP_MUX_NO_BLEND
: WEBP_MUX_BLEND
;
323 if (frame
->width_
* (uint64_t)frame
->height_
>= MAX_IMAGE_AREA
) {
328 // Store a frame only if the animation flag is set there is some data for
329 // this frame is available.
330 status
= StoreFrame(dmux
->num_frames_
+ 1, anmf_payload_size
, mem
, frame
);
331 if (status
!= PARSE_ERROR
&& has_frames
&& frame
->frame_num_
> 0) {
332 added_frame
= AddFrame(dmux
, frame
);
336 status
= PARSE_ERROR
;
340 if (!added_frame
) free(frame
);
344 #ifdef WEBP_EXPERIMENTAL_FEATURES
345 // Parse a 'FRGM' chunk and any image bearing chunks that immediately follow.
346 // 'fragment_chunk_size' is the previously validated, padded chunk size.
347 static ParseStatus
ParseFragment(WebPDemuxer
* const dmux
,
348 uint32_t fragment_chunk_size
) {
349 const int frame_num
= 1; // All fragments belong to the 1st (and only) frame.
350 const int has_fragments
= !!(dmux
->feature_flags_
& FRAGMENTS_FLAG
);
351 const uint32_t frgm_payload_size
= fragment_chunk_size
- FRGM_CHUNK_SIZE
;
352 int added_fragment
= 0;
353 MemBuffer
* const mem
= &dmux
->mem_
;
356 NewFrame(mem
, FRGM_CHUNK_SIZE
, fragment_chunk_size
, &frame
);
357 if (status
!= PARSE_OK
) return status
;
359 frame
->is_fragment_
= 1;
360 frame
->x_offset_
= 2 * ReadLE24s(mem
);
361 frame
->y_offset_
= 2 * ReadLE24s(mem
);
363 // Store a fragment only if the fragments flag is set there is some data for
364 // this fragment is available.
365 status
= StoreFrame(frame_num
, frgm_payload_size
, mem
, frame
);
366 if (status
!= PARSE_ERROR
&& has_fragments
&& frame
->frame_num_
> 0) {
367 added_fragment
= AddFrame(dmux
, frame
);
368 if (!added_fragment
) {
369 status
= PARSE_ERROR
;
371 dmux
->num_frames_
= 1;
375 if (!added_fragment
) free(frame
);
378 #endif // WEBP_EXPERIMENTAL_FEATURES
380 // General chunk storage, starting with the header at 'start_offset', allowing
381 // the user to request the payload via a fourcc string. 'size' includes the
382 // header and the unpadded payload size.
383 // Returns true on success, false otherwise.
384 static int StoreChunk(WebPDemuxer
* const dmux
,
385 size_t start_offset
, uint32_t size
) {
386 Chunk
* const chunk
= (Chunk
*)calloc(1, sizeof(*chunk
));
387 if (chunk
== NULL
) return 0;
389 chunk
->data_
.offset_
= start_offset
;
390 chunk
->data_
.size_
= size
;
391 AddChunk(dmux
, chunk
);
395 // -----------------------------------------------------------------------------
396 // Primary chunk parsing
398 static int ReadHeader(MemBuffer
* const mem
) {
399 const size_t min_size
= RIFF_HEADER_SIZE
+ CHUNK_HEADER_SIZE
;
402 // Basic file level validation.
403 if (MemDataSize(mem
) < min_size
) return 0;
404 if (memcmp(GetBuffer(mem
), "RIFF", CHUNK_SIZE_BYTES
) ||
405 memcmp(GetBuffer(mem
) + CHUNK_HEADER_SIZE
, "WEBP", CHUNK_SIZE_BYTES
)) {
409 riff_size
= GetLE32(GetBuffer(mem
) + TAG_SIZE
);
410 if (riff_size
< CHUNK_HEADER_SIZE
) return 0;
411 if (riff_size
> MAX_CHUNK_PAYLOAD
) return 0;
413 // There's no point in reading past the end of the RIFF chunk
414 mem
->riff_end_
= riff_size
+ CHUNK_HEADER_SIZE
;
415 if (mem
->buf_size_
> mem
->riff_end_
) {
416 mem
->buf_size_
= mem
->end_
= mem
->riff_end_
;
419 Skip(mem
, RIFF_HEADER_SIZE
);
423 static ParseStatus
ParseSingleImage(WebPDemuxer
* const dmux
) {
424 const size_t min_size
= CHUNK_HEADER_SIZE
;
425 MemBuffer
* const mem
= &dmux
->mem_
;
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
;
433 frame
= (Frame
*)calloc(1, sizeof(*frame
));
434 if (frame
== NULL
) return PARSE_ERROR
;
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 if (status
!= PARSE_ERROR
) {
440 const int has_alpha
= !!(dmux
->feature_flags_
& ALPHA_FLAG
);
441 // Clear any alpha when the alpha flag is missing.
442 if (!has_alpha
&& frame
->img_components_
[1].size_
> 0) {
443 frame
->img_components_
[1].offset_
= 0;
444 frame
->img_components_
[1].size_
= 0;
445 frame
->has_alpha_
= 0;
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_
|= frame
->has_alpha_
? ALPHA_FLAG
: 0;
456 AddFrame(dmux
, frame
);
457 dmux
->num_frames_
= 1;
465 static ParseStatus
ParseVP8X(WebPDemuxer
* const dmux
) {
466 MemBuffer
* const mem
= &dmux
->mem_
;
469 ParseStatus status
= PARSE_OK
;
471 if (MemDataSize(mem
) < CHUNK_HEADER_SIZE
) return PARSE_NEED_MORE_DATA
;
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
;
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
489 Skip(mem
, vp8x_size
- VP8X_CHUNK_SIZE
); // skip any trailing data.
490 dmux
->state_
= WEBP_DEMUX_PARSED_HEADER
;
492 if (SizeIsInvalid(mem
, CHUNK_HEADER_SIZE
)) return PARSE_ERROR
;
493 if (MemDataSize(mem
) < CHUNK_HEADER_SIZE
) return PARSE_NEED_MORE_DATA
;
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);
502 if (chunk_size
> MAX_CHUNK_PAYLOAD
) return PARSE_ERROR
;
503 if (SizeIsInvalid(mem
, chunk_size_padded
)) return PARSE_ERROR
;
506 case MKFOURCC('V', 'P', '8', 'X'): {
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
;
515 Rewind(mem
, CHUNK_HEADER_SIZE
);
516 status
= ParseSingleImage(dmux
);
519 case MKFOURCC('A', 'N', 'I', 'M'): {
520 if (chunk_size_padded
< ANIM_CHUNK_SIZE
) return PARSE_ERROR
;
522 if (MemDataSize(mem
) < chunk_size_padded
) {
523 status
= PARSE_NEED_MORE_DATA
;
524 } else if (anim_chunks
== 0) {
526 dmux
->bgcolor_
= ReadLE32(mem
);
527 dmux
->loop_count_
= ReadLE16s(mem
);
528 Skip(mem
, chunk_size_padded
- ANIM_CHUNK_SIZE
);
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
);
540 #ifdef WEBP_EXPERIMENTAL_FEATURES
541 case MKFOURCC('F', 'R', 'G', 'M'): {
542 status
= ParseFragment(dmux
, chunk_size_padded
);
546 case MKFOURCC('I', 'C', 'C', 'P'): {
547 store_chunk
= !!(dmux
->feature_flags_
& ICCP_FLAG
);
550 case MKFOURCC('X', 'M', 'P', ' '): {
551 store_chunk
= !!(dmux
->feature_flags_
& XMP_FLAG
);
554 case MKFOURCC('E', 'X', 'I', 'F'): {
555 store_chunk
= !!(dmux
->feature_flags_
& EXIF_FLAG
);
560 if (chunk_size_padded
<= MemDataSize(mem
)) {
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
)) {
569 Skip(mem
, chunk_size_padded
);
571 status
= PARSE_NEED_MORE_DATA
;
576 if (mem
->start_
== mem
->riff_end_
) {
578 } else if (MemDataSize(mem
) < CHUNK_HEADER_SIZE
) {
579 status
= PARSE_NEED_MORE_DATA
;
581 } while (status
== PARSE_OK
);
586 // -----------------------------------------------------------------------------
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;
593 if (dmux
->canvas_width_
<= 0 || dmux
->canvas_height_
<= 0) return 0;
594 if (dmux
->state_
== WEBP_DEMUX_DONE
&& frame
== NULL
) return 0;
596 if (frame
->width_
<= 0 || frame
->height_
<= 0) return 0;
600 // If 'exact' is true, check that the image resolution matches the canvas.
601 // If 'exact' is false, check that the x/y offsets do not exceed the canvas.
602 static int CheckFrameBounds(const Frame
* const frame
, int exact
,
603 int canvas_width
, int canvas_height
) {
605 if (frame
->x_offset_
!= 0 || frame
->y_offset_
!= 0) {
608 if (frame
->width_
!= canvas_width
|| frame
->height_
!= canvas_height
) {
612 if (frame
->x_offset_
< 0 || frame
->y_offset_
< 0) return 0;
613 if (frame
->width_
+ frame
->x_offset_
> canvas_width
) return 0;
614 if (frame
->height_
+ frame
->y_offset_
> canvas_height
) return 0;
619 static int IsValidExtendedFormat(const WebPDemuxer
* const dmux
) {
620 const int has_fragments
= !!(dmux
->feature_flags_
& FRAGMENTS_FLAG
);
621 const int has_frames
= !!(dmux
->feature_flags_
& ANIMATION_FLAG
);
624 if (dmux
->state_
== WEBP_DEMUX_PARSING_HEADER
) return 1;
626 if (dmux
->canvas_width_
<= 0 || dmux
->canvas_height_
<= 0) return 0;
627 if (dmux
->loop_count_
< 0) return 0;
628 if (dmux
->state_
== WEBP_DEMUX_DONE
&& dmux
->frames_
== NULL
) return 0;
630 for (f
= dmux
->frames_
; f
!= NULL
; f
= f
->next_
) {
631 const int cur_frame_set
= f
->frame_num_
;
632 int frame_count
= 0, fragment_count
= 0;
634 // Check frame properties and if the image is composed of fragments that
635 // each fragment came from a fragment.
636 for (; f
!= NULL
&& f
->frame_num_
== cur_frame_set
; f
= f
->next_
) {
637 const ChunkData
* const image
= f
->img_components_
;
638 const ChunkData
* const alpha
= f
->img_components_
+ 1;
640 if (!has_fragments
&& f
->is_fragment_
) return 0;
641 if (!has_frames
&& f
->frame_num_
> 1) return 0;
643 if (alpha
->size_
== 0 && image
->size_
== 0) return 0;
644 // Ensure alpha precedes image bitstream.
645 if (alpha
->size_
> 0 && alpha
->offset_
> image
->offset_
) {
649 if (f
->width_
<= 0 || f
->height_
<= 0) return 0;
651 // There shouldn't be a partial frame in a complete file.
652 if (dmux
->state_
== WEBP_DEMUX_DONE
) return 0;
654 // Ensure alpha precedes image bitstream.
655 if (alpha
->size_
> 0 && image
->size_
> 0 &&
656 alpha
->offset_
> image
->offset_
) {
659 // There shouldn't be any frames after an incomplete one.
660 if (f
->next_
!= NULL
) return 0;
663 if (f
->width_
> 0 && f
->height_
> 0 &&
664 !CheckFrameBounds(f
, !(has_frames
|| has_fragments
),
665 dmux
->canvas_width_
, dmux
->canvas_height_
)) {
669 fragment_count
+= f
->is_fragment_
;
672 if (!has_fragments
&& frame_count
> 1) return 0;
673 if (fragment_count
> 0 && frame_count
!= fragment_count
) return 0;
674 if (f
== NULL
) break;
679 // -----------------------------------------------------------------------------
680 // WebPDemuxer object
682 static void InitDemux(WebPDemuxer
* const dmux
, const MemBuffer
* const mem
) {
683 dmux
->state_
= WEBP_DEMUX_PARSING_HEADER
;
684 dmux
->loop_count_
= 1;
685 dmux
->bgcolor_
= 0xFFFFFFFF; // White background by default.
686 dmux
->canvas_width_
= -1;
687 dmux
->canvas_height_
= -1;
688 dmux
->frames_tail_
= &dmux
->frames_
;
692 WebPDemuxer
* WebPDemuxInternal(const WebPData
* data
, int allow_partial
,
693 WebPDemuxState
* state
, int version
) {
694 const ChunkParser
* parser
;
696 ParseStatus status
= PARSE_ERROR
;
700 if (WEBP_ABI_IS_INCOMPATIBLE(version
, WEBP_DEMUX_ABI_VERSION
)) return NULL
;
701 if (data
== NULL
|| data
->bytes
== NULL
|| data
->size
== 0) return NULL
;
703 if (!InitMemBuffer(&mem
, data
->bytes
, data
->size
)) return NULL
;
704 if (!ReadHeader(&mem
)) return NULL
;
706 partial
= (mem
.buf_size_
< mem
.riff_end_
);
707 if (!allow_partial
&& partial
) return NULL
;
709 dmux
= (WebPDemuxer
*)calloc(1, sizeof(*dmux
));
710 if (dmux
== NULL
) return NULL
;
711 InitDemux(dmux
, &mem
);
713 for (parser
= kMasterChunks
; parser
->parse
!= NULL
; ++parser
) {
714 if (!memcmp(parser
->id
, GetBuffer(&dmux
->mem_
), TAG_SIZE
)) {
715 status
= parser
->parse(dmux
);
716 if (status
== PARSE_OK
) dmux
->state_
= WEBP_DEMUX_DONE
;
717 if (status
== PARSE_NEED_MORE_DATA
&& !partial
) status
= PARSE_ERROR
;
718 if (status
!= PARSE_ERROR
&& !parser
->valid(dmux
)) status
= PARSE_ERROR
;
722 if (state
) *state
= dmux
->state_
;
724 if (status
== PARSE_ERROR
) {
725 WebPDemuxDelete(dmux
);
731 void WebPDemuxDelete(WebPDemuxer
* dmux
) {
734 if (dmux
== NULL
) return;
736 for (f
= dmux
->frames_
; f
!= NULL
;) {
737 Frame
* const cur_frame
= f
;
741 for (c
= dmux
->chunks_
; c
!= NULL
;) {
742 Chunk
* const cur_chunk
= c
;
749 // -----------------------------------------------------------------------------
751 uint32_t WebPDemuxGetI(const WebPDemuxer
* dmux
, WebPFormatFeature feature
) {
752 if (dmux
== NULL
) return 0;
755 case WEBP_FF_FORMAT_FLAGS
: return dmux
->feature_flags_
;
756 case WEBP_FF_CANVAS_WIDTH
: return (uint32_t)dmux
->canvas_width_
;
757 case WEBP_FF_CANVAS_HEIGHT
: return (uint32_t)dmux
->canvas_height_
;
758 case WEBP_FF_LOOP_COUNT
: return (uint32_t)dmux
->loop_count_
;
759 case WEBP_FF_BACKGROUND_COLOR
: return dmux
->bgcolor_
;
760 case WEBP_FF_FRAME_COUNT
: return (uint32_t)dmux
->num_frames_
;
765 // -----------------------------------------------------------------------------
768 // Find the first 'frame_num' frame. There may be multiple such frames in a
770 static const Frame
* GetFrame(const WebPDemuxer
* const dmux
, int frame_num
) {
772 for (f
= dmux
->frames_
; f
!= NULL
; f
= f
->next_
) {
773 if (frame_num
== f
->frame_num_
) break;
778 // Returns fragment 'fragment_num' and the total count.
779 static const Frame
* GetFragment(
780 const Frame
* const frame_set
, int fragment_num
, int* const count
) {
781 const int this_frame
= frame_set
->frame_num_
;
782 const Frame
* f
= frame_set
;
783 const Frame
* fragment
= NULL
;
786 for (total
= 0; f
!= NULL
&& f
->frame_num_
== this_frame
; f
= f
->next_
) {
787 if (++total
== fragment_num
) fragment
= f
;
793 static const uint8_t* GetFramePayload(const uint8_t* const mem_buf
,
794 const Frame
* const frame
,
795 size_t* const data_size
) {
798 const ChunkData
* const image
= frame
->img_components_
;
799 const ChunkData
* const alpha
= frame
->img_components_
+ 1;
800 size_t start_offset
= image
->offset_
;
801 *data_size
= image
->size_
;
803 // if alpha exists it precedes image, update the size allowing for
804 // intervening chunks.
805 if (alpha
->size_
> 0) {
806 const size_t inter_size
= (image
->offset_
> 0)
807 ? image
->offset_
- (alpha
->offset_
+ alpha
->size_
)
809 start_offset
= alpha
->offset_
;
810 *data_size
+= alpha
->size_
+ inter_size
;
812 return mem_buf
+ start_offset
;
817 // Create a whole 'frame' from VP8 (+ alpha) or lossless.
818 static int SynthesizeFrame(const WebPDemuxer
* const dmux
,
819 const Frame
* const first_frame
,
820 int fragment_num
, WebPIterator
* const iter
) {
821 const uint8_t* const mem_buf
= dmux
->mem_
.buf_
;
823 size_t payload_size
= 0;
824 const Frame
* const fragment
=
825 GetFragment(first_frame
, fragment_num
, &num_fragments
);
826 const uint8_t* const payload
=
827 GetFramePayload(mem_buf
, fragment
, &payload_size
);
828 if (payload
== NULL
) return 0;
829 assert(first_frame
!= NULL
);
831 iter
->frame_num
= first_frame
->frame_num_
;
832 iter
->num_frames
= dmux
->num_frames_
;
833 iter
->fragment_num
= fragment_num
;
834 iter
->num_fragments
= num_fragments
;
835 iter
->x_offset
= fragment
->x_offset_
;
836 iter
->y_offset
= fragment
->y_offset_
;
837 iter
->width
= fragment
->width_
;
838 iter
->height
= fragment
->height_
;
839 iter
->has_alpha
= fragment
->has_alpha_
;
840 iter
->duration
= fragment
->duration_
;
841 iter
->dispose_method
= fragment
->dispose_method_
;
842 iter
->blend_method
= fragment
->blend_method_
;
843 iter
->complete
= fragment
->complete_
;
844 iter
->fragment
.bytes
= payload
;
845 iter
->fragment
.size
= payload_size
;
846 // TODO(jzern): adjust offsets for 'FRGM's embedded in 'ANMF's
850 static int SetFrame(int frame_num
, WebPIterator
* const iter
) {
852 const WebPDemuxer
* const dmux
= (WebPDemuxer
*)iter
->private_
;
853 if (dmux
== NULL
|| frame_num
< 0) return 0;
854 if (frame_num
> dmux
->num_frames_
) return 0;
855 if (frame_num
== 0) frame_num
= dmux
->num_frames_
;
857 frame
= GetFrame(dmux
, frame_num
);
858 if (frame
== NULL
) return 0;
860 return SynthesizeFrame(dmux
, frame
, 1, iter
);
863 int WebPDemuxGetFrame(const WebPDemuxer
* dmux
, int frame
, WebPIterator
* iter
) {
864 if (iter
== NULL
) return 0;
866 memset(iter
, 0, sizeof(*iter
));
867 iter
->private_
= (void*)dmux
;
868 return SetFrame(frame
, iter
);
871 int WebPDemuxNextFrame(WebPIterator
* iter
) {
872 if (iter
== NULL
) return 0;
873 return SetFrame(iter
->frame_num
+ 1, iter
);
876 int WebPDemuxPrevFrame(WebPIterator
* iter
) {
877 if (iter
== NULL
) return 0;
878 if (iter
->frame_num
<= 1) return 0;
879 return SetFrame(iter
->frame_num
- 1, iter
);
882 int WebPDemuxSelectFragment(WebPIterator
* iter
, int fragment_num
) {
883 if (iter
!= NULL
&& iter
->private_
!= NULL
&& fragment_num
> 0) {
884 const WebPDemuxer
* const dmux
= (WebPDemuxer
*)iter
->private_
;
885 const Frame
* const frame
= GetFrame(dmux
, iter
->frame_num
);
886 if (frame
== NULL
) return 0;
888 return SynthesizeFrame(dmux
, frame
, fragment_num
, iter
);
893 void WebPDemuxReleaseIterator(WebPIterator
* iter
) {
897 // -----------------------------------------------------------------------------
900 static int ChunkCount(const WebPDemuxer
* const dmux
, const char fourcc
[4]) {
901 const uint8_t* const mem_buf
= dmux
->mem_
.buf_
;
904 for (c
= dmux
->chunks_
; c
!= NULL
; c
= c
->next_
) {
905 const uint8_t* const header
= mem_buf
+ c
->data_
.offset_
;
906 if (!memcmp(header
, fourcc
, TAG_SIZE
)) ++count
;
911 static const Chunk
* GetChunk(const WebPDemuxer
* const dmux
,
912 const char fourcc
[4], int chunk_num
) {
913 const uint8_t* const mem_buf
= dmux
->mem_
.buf_
;
916 for (c
= dmux
->chunks_
; c
!= NULL
; c
= c
->next_
) {
917 const uint8_t* const header
= mem_buf
+ c
->data_
.offset_
;
918 if (!memcmp(header
, fourcc
, TAG_SIZE
)) ++count
;
919 if (count
== chunk_num
) break;
924 static int SetChunk(const char fourcc
[4], int chunk_num
,
925 WebPChunkIterator
* const iter
) {
926 const WebPDemuxer
* const dmux
= (WebPDemuxer
*)iter
->private_
;
929 if (dmux
== NULL
|| fourcc
== NULL
|| chunk_num
< 0) return 0;
930 count
= ChunkCount(dmux
, fourcc
);
931 if (count
== 0) return 0;
932 if (chunk_num
== 0) chunk_num
= count
;
934 if (chunk_num
<= count
) {
935 const uint8_t* const mem_buf
= dmux
->mem_
.buf_
;
936 const Chunk
* const chunk
= GetChunk(dmux
, fourcc
, chunk_num
);
937 iter
->chunk
.bytes
= mem_buf
+ chunk
->data_
.offset_
+ CHUNK_HEADER_SIZE
;
938 iter
->chunk
.size
= chunk
->data_
.size_
- CHUNK_HEADER_SIZE
;
939 iter
->num_chunks
= count
;
940 iter
->chunk_num
= chunk_num
;
946 int WebPDemuxGetChunk(const WebPDemuxer
* dmux
,
947 const char fourcc
[4], int chunk_num
,
948 WebPChunkIterator
* iter
) {
949 if (iter
== NULL
) return 0;
951 memset(iter
, 0, sizeof(*iter
));
952 iter
->private_
= (void*)dmux
;
953 return SetChunk(fourcc
, chunk_num
, iter
);
956 int WebPDemuxNextChunk(WebPChunkIterator
* iter
) {
958 const char* const fourcc
=
959 (const char*)iter
->chunk
.bytes
- CHUNK_HEADER_SIZE
;
960 return SetChunk(fourcc
, iter
->chunk_num
+ 1, iter
);
965 int WebPDemuxPrevChunk(WebPChunkIterator
* iter
) {
966 if (iter
!= NULL
&& iter
->chunk_num
> 1) {
967 const char* const fourcc
=
968 (const char*)iter
->chunk
.bytes
- CHUNK_HEADER_SIZE
;
969 return SetChunk(fourcc
, iter
->chunk_num
- 1, iter
);
974 void WebPDemuxReleaseChunkIterator(WebPChunkIterator
* iter
) {
978 #if defined(__cplusplus) || defined(c_plusplus)