OLD | NEW |
1 // Copyright 2012 Google Inc. All Rights Reserved. | 1 // Copyright 2012 Google Inc. All Rights Reserved. |
2 // | 2 // |
3 // Use of this source code is governed by a BSD-style license | 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 | 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 | 5 // tree. An additional intellectual property rights grant can be found |
6 // in the file PATENTS. All contributing project authors may | 6 // in the file PATENTS. All contributing project authors may |
7 // be found in the AUTHORS file in the root of the source tree. | 7 // be found in the AUTHORS file in the root of the source tree. |
8 // ----------------------------------------------------------------------------- | 8 // ----------------------------------------------------------------------------- |
9 // | 9 // |
10 // WebP container demux. | 10 // WebP container demux. |
11 // | 11 // |
12 | 12 |
13 #ifdef HAVE_CONFIG_H | 13 #ifdef HAVE_CONFIG_H |
14 #include "config.h" | 14 #include "config.h" |
15 #endif | 15 #endif |
16 | 16 |
17 #include <assert.h> | 17 #include <assert.h> |
18 #include <stdlib.h> | 18 #include <stdlib.h> |
19 #include <string.h> | 19 #include <string.h> |
20 | 20 |
21 #include "../utils/utils.h" | 21 #include "../utils/utils.h" |
22 #include "../webp/decode.h" // WebPGetFeatures | 22 #include "../webp/decode.h" // WebPGetFeatures |
23 #include "../webp/demux.h" | 23 #include "../webp/demux.h" |
24 #include "../webp/format_constants.h" | 24 #include "../webp/format_constants.h" |
25 | 25 |
26 #if defined(__cplusplus) || defined(c_plusplus) | |
27 extern "C" { | |
28 #endif | |
29 | |
30 #define DMUX_MAJ_VERSION 0 | 26 #define DMUX_MAJ_VERSION 0 |
31 #define DMUX_MIN_VERSION 1 | 27 #define DMUX_MIN_VERSION 2 |
32 #define DMUX_REV_VERSION 1 | 28 #define DMUX_REV_VERSION 0 |
33 | 29 |
34 typedef struct { | 30 typedef struct { |
35 size_t start_; // start location of the data | 31 size_t start_; // start location of the data |
36 size_t end_; // end location | 32 size_t end_; // end location |
37 size_t riff_end_; // riff chunk end location, can be > end_. | 33 size_t riff_end_; // riff chunk end location, can be > end_. |
38 size_t buf_size_; // size of the buffer | 34 size_t buf_size_; // size of the buffer |
39 const uint8_t* buf_; | 35 const uint8_t* buf_; |
40 } MemBuffer; | 36 } MemBuffer; |
41 | 37 |
42 typedef struct { | 38 typedef struct { |
(...skipping 25 matching lines...) Expand all Loading... |
68 WebPDemuxState state_; | 64 WebPDemuxState state_; |
69 int is_ext_format_; | 65 int is_ext_format_; |
70 uint32_t feature_flags_; | 66 uint32_t feature_flags_; |
71 int canvas_width_, canvas_height_; | 67 int canvas_width_, canvas_height_; |
72 int loop_count_; | 68 int loop_count_; |
73 uint32_t bgcolor_; | 69 uint32_t bgcolor_; |
74 int num_frames_; | 70 int num_frames_; |
75 Frame* frames_; | 71 Frame* frames_; |
76 Frame** frames_tail_; | 72 Frame** frames_tail_; |
77 Chunk* chunks_; // non-image chunks | 73 Chunk* chunks_; // non-image chunks |
| 74 Chunk** chunks_tail_; |
78 }; | 75 }; |
79 | 76 |
80 typedef enum { | 77 typedef enum { |
81 PARSE_OK, | 78 PARSE_OK, |
82 PARSE_NEED_MORE_DATA, | 79 PARSE_NEED_MORE_DATA, |
83 PARSE_ERROR | 80 PARSE_ERROR |
84 } ParseStatus; | 81 } ParseStatus; |
85 | 82 |
86 typedef struct ChunkParser { | 83 typedef struct ChunkParser { |
87 uint8_t id[4]; | 84 uint8_t id[4]; |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
172 const uint8_t* const data = mem->buf_ + mem->start_; | 169 const uint8_t* const data = mem->buf_ + mem->start_; |
173 const uint32_t val = GetLE32(data); | 170 const uint32_t val = GetLE32(data); |
174 Skip(mem, 4); | 171 Skip(mem, 4); |
175 return val; | 172 return val; |
176 } | 173 } |
177 | 174 |
178 // ----------------------------------------------------------------------------- | 175 // ----------------------------------------------------------------------------- |
179 // Secondary chunk parsing | 176 // Secondary chunk parsing |
180 | 177 |
181 static void AddChunk(WebPDemuxer* const dmux, Chunk* const chunk) { | 178 static void AddChunk(WebPDemuxer* const dmux, Chunk* const chunk) { |
182 Chunk** c = &dmux->chunks_; | 179 *dmux->chunks_tail_ = chunk; |
183 while (*c != NULL) c = &(*c)->next_; | |
184 *c = chunk; | |
185 chunk->next_ = NULL; | 180 chunk->next_ = NULL; |
| 181 dmux->chunks_tail_ = &chunk->next_; |
186 } | 182 } |
187 | 183 |
188 // Add a frame to the end of the list, ensuring the last frame is complete. | 184 // Add a frame to the end of the list, ensuring the last frame is complete. |
189 // Returns true on success, false otherwise. | 185 // Returns true on success, false otherwise. |
190 static int AddFrame(WebPDemuxer* const dmux, Frame* const frame) { | 186 static int AddFrame(WebPDemuxer* const dmux, Frame* const frame) { |
191 const Frame* const last_frame = *dmux->frames_tail_; | 187 const Frame* const last_frame = *dmux->frames_tail_; |
192 if (last_frame != NULL && !last_frame->complete_) return 0; | 188 if (last_frame != NULL && !last_frame->complete_) return 0; |
193 | 189 |
194 *dmux->frames_tail_ = frame; | 190 *dmux->frames_tail_ = frame; |
195 frame->next_ = NULL; | 191 frame->next_ = NULL; |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
294 if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA; | 290 if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA; |
295 | 291 |
296 *frame = (Frame*)calloc(1, sizeof(**frame)); | 292 *frame = (Frame*)calloc(1, sizeof(**frame)); |
297 return (*frame == NULL) ? PARSE_ERROR : PARSE_OK; | 293 return (*frame == NULL) ? PARSE_ERROR : PARSE_OK; |
298 } | 294 } |
299 | 295 |
300 // Parse a 'ANMF' chunk and any image bearing chunks that immediately follow. | 296 // Parse a 'ANMF' chunk and any image bearing chunks that immediately follow. |
301 // 'frame_chunk_size' is the previously validated, padded chunk size. | 297 // 'frame_chunk_size' is the previously validated, padded chunk size. |
302 static ParseStatus ParseAnimationFrame( | 298 static ParseStatus ParseAnimationFrame( |
303 WebPDemuxer* const dmux, uint32_t frame_chunk_size) { | 299 WebPDemuxer* const dmux, uint32_t frame_chunk_size) { |
304 const int has_frames = !!(dmux->feature_flags_ & ANIMATION_FLAG); | 300 const int is_animation = !!(dmux->feature_flags_ & ANIMATION_FLAG); |
305 const uint32_t anmf_payload_size = frame_chunk_size - ANMF_CHUNK_SIZE; | 301 const uint32_t anmf_payload_size = frame_chunk_size - ANMF_CHUNK_SIZE; |
306 int added_frame = 0; | 302 int added_frame = 0; |
307 int bits; | 303 int bits; |
308 MemBuffer* const mem = &dmux->mem_; | 304 MemBuffer* const mem = &dmux->mem_; |
309 Frame* frame; | 305 Frame* frame; |
310 ParseStatus status = | 306 ParseStatus status = |
311 NewFrame(mem, ANMF_CHUNK_SIZE, frame_chunk_size, &frame); | 307 NewFrame(mem, ANMF_CHUNK_SIZE, frame_chunk_size, &frame); |
312 if (status != PARSE_OK) return status; | 308 if (status != PARSE_OK) return status; |
313 | 309 |
314 frame->x_offset_ = 2 * ReadLE24s(mem); | 310 frame->x_offset_ = 2 * ReadLE24s(mem); |
315 frame->y_offset_ = 2 * ReadLE24s(mem); | 311 frame->y_offset_ = 2 * ReadLE24s(mem); |
316 frame->width_ = 1 + ReadLE24s(mem); | 312 frame->width_ = 1 + ReadLE24s(mem); |
317 frame->height_ = 1 + ReadLE24s(mem); | 313 frame->height_ = 1 + ReadLE24s(mem); |
318 frame->duration_ = ReadLE24s(mem); | 314 frame->duration_ = ReadLE24s(mem); |
319 bits = ReadByte(mem); | 315 bits = ReadByte(mem); |
320 frame->dispose_method_ = | 316 frame->dispose_method_ = |
321 (bits & 1) ? WEBP_MUX_DISPOSE_BACKGROUND : WEBP_MUX_DISPOSE_NONE; | 317 (bits & 1) ? WEBP_MUX_DISPOSE_BACKGROUND : WEBP_MUX_DISPOSE_NONE; |
322 frame->blend_method_ = (bits & 2) ? WEBP_MUX_NO_BLEND : WEBP_MUX_BLEND; | 318 frame->blend_method_ = (bits & 2) ? WEBP_MUX_NO_BLEND : WEBP_MUX_BLEND; |
323 if (frame->width_ * (uint64_t)frame->height_ >= MAX_IMAGE_AREA) { | 319 if (frame->width_ * (uint64_t)frame->height_ >= MAX_IMAGE_AREA) { |
324 free(frame); | 320 free(frame); |
325 return PARSE_ERROR; | 321 return PARSE_ERROR; |
326 } | 322 } |
327 | 323 |
328 // Store a frame only if the animation flag is set there is some data for | 324 // Store a frame only if the animation flag is set there is some data for |
329 // this frame is available. | 325 // this frame is available. |
330 status = StoreFrame(dmux->num_frames_ + 1, anmf_payload_size, mem, frame); | 326 status = StoreFrame(dmux->num_frames_ + 1, anmf_payload_size, mem, frame); |
331 if (status != PARSE_ERROR && has_frames && frame->frame_num_ > 0) { | 327 if (status != PARSE_ERROR && is_animation && frame->frame_num_ > 0) { |
332 added_frame = AddFrame(dmux, frame); | 328 added_frame = AddFrame(dmux, frame); |
333 if (added_frame) { | 329 if (added_frame) { |
334 ++dmux->num_frames_; | 330 ++dmux->num_frames_; |
335 } else { | 331 } else { |
336 status = PARSE_ERROR; | 332 status = PARSE_ERROR; |
337 } | 333 } |
338 } | 334 } |
339 | 335 |
340 if (!added_frame) free(frame); | 336 if (!added_frame) free(frame); |
341 return status; | 337 return status; |
342 } | 338 } |
343 | 339 |
344 #ifdef WEBP_EXPERIMENTAL_FEATURES | 340 #ifdef WEBP_EXPERIMENTAL_FEATURES |
345 // Parse a 'FRGM' chunk and any image bearing chunks that immediately follow. | 341 // Parse a 'FRGM' chunk and any image bearing chunks that immediately follow. |
346 // 'fragment_chunk_size' is the previously validated, padded chunk size. | 342 // 'fragment_chunk_size' is the previously validated, padded chunk size. |
347 static ParseStatus ParseFragment(WebPDemuxer* const dmux, | 343 static ParseStatus ParseFragment(WebPDemuxer* const dmux, |
348 uint32_t fragment_chunk_size) { | 344 uint32_t fragment_chunk_size) { |
349 const int frame_num = 1; // All fragments belong to the 1st (and only) frame. | 345 const int frame_num = 1; // All fragments belong to the 1st (and only) frame. |
350 const int has_fragments = !!(dmux->feature_flags_ & FRAGMENTS_FLAG); | 346 const int is_fragmented = !!(dmux->feature_flags_ & FRAGMENTS_FLAG); |
351 const uint32_t frgm_payload_size = fragment_chunk_size - FRGM_CHUNK_SIZE; | 347 const uint32_t frgm_payload_size = fragment_chunk_size - FRGM_CHUNK_SIZE; |
352 int added_fragment = 0; | 348 int added_fragment = 0; |
353 MemBuffer* const mem = &dmux->mem_; | 349 MemBuffer* const mem = &dmux->mem_; |
354 Frame* frame; | 350 Frame* frame; |
355 ParseStatus status = | 351 ParseStatus status = |
356 NewFrame(mem, FRGM_CHUNK_SIZE, fragment_chunk_size, &frame); | 352 NewFrame(mem, FRGM_CHUNK_SIZE, fragment_chunk_size, &frame); |
357 if (status != PARSE_OK) return status; | 353 if (status != PARSE_OK) return status; |
358 | 354 |
359 frame->is_fragment_ = 1; | 355 frame->is_fragment_ = 1; |
360 frame->x_offset_ = 2 * ReadLE24s(mem); | 356 frame->x_offset_ = 2 * ReadLE24s(mem); |
361 frame->y_offset_ = 2 * ReadLE24s(mem); | 357 frame->y_offset_ = 2 * ReadLE24s(mem); |
362 | 358 |
363 // Store a fragment only if the fragments flag is set there is some data for | 359 // Store a fragment only if the 'fragments' flag is set and there is some |
364 // this fragment is available. | 360 // data available. |
365 status = StoreFrame(frame_num, frgm_payload_size, mem, frame); | 361 status = StoreFrame(frame_num, frgm_payload_size, mem, frame); |
366 if (status != PARSE_ERROR && has_fragments && frame->frame_num_ > 0) { | 362 if (status != PARSE_ERROR && is_fragmented && frame->frame_num_ > 0) { |
367 added_fragment = AddFrame(dmux, frame); | 363 added_fragment = AddFrame(dmux, frame); |
368 if (!added_fragment) { | 364 if (!added_fragment) { |
369 status = PARSE_ERROR; | 365 status = PARSE_ERROR; |
370 } else { | 366 } else { |
371 dmux->num_frames_ = 1; | 367 dmux->num_frames_ = 1; |
372 } | 368 } |
373 } | 369 } |
374 | 370 |
375 if (!added_fragment) free(frame); | 371 if (!added_fragment) free(frame); |
376 return status; | 372 return status; |
(...skipping 11 matching lines...) Expand all Loading... |
388 | 384 |
389 chunk->data_.offset_ = start_offset; | 385 chunk->data_.offset_ = start_offset; |
390 chunk->data_.size_ = size; | 386 chunk->data_.size_ = size; |
391 AddChunk(dmux, chunk); | 387 AddChunk(dmux, chunk); |
392 return 1; | 388 return 1; |
393 } | 389 } |
394 | 390 |
395 // ----------------------------------------------------------------------------- | 391 // ----------------------------------------------------------------------------- |
396 // Primary chunk parsing | 392 // Primary chunk parsing |
397 | 393 |
398 static int ReadHeader(MemBuffer* const mem) { | 394 static ParseStatus ReadHeader(MemBuffer* const mem) { |
399 const size_t min_size = RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE; | 395 const size_t min_size = RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE; |
400 uint32_t riff_size; | 396 uint32_t riff_size; |
401 | 397 |
402 // Basic file level validation. | 398 // Basic file level validation. |
403 if (MemDataSize(mem) < min_size) return 0; | 399 if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA; |
404 if (memcmp(GetBuffer(mem), "RIFF", CHUNK_SIZE_BYTES) || | 400 if (memcmp(GetBuffer(mem), "RIFF", CHUNK_SIZE_BYTES) || |
405 memcmp(GetBuffer(mem) + CHUNK_HEADER_SIZE, "WEBP", CHUNK_SIZE_BYTES)) { | 401 memcmp(GetBuffer(mem) + CHUNK_HEADER_SIZE, "WEBP", CHUNK_SIZE_BYTES)) { |
406 return 0; | 402 return PARSE_ERROR; |
407 } | 403 } |
408 | 404 |
409 riff_size = GetLE32(GetBuffer(mem) + TAG_SIZE); | 405 riff_size = GetLE32(GetBuffer(mem) + TAG_SIZE); |
410 if (riff_size < CHUNK_HEADER_SIZE) return 0; | 406 if (riff_size < CHUNK_HEADER_SIZE) return PARSE_ERROR; |
411 if (riff_size > MAX_CHUNK_PAYLOAD) return 0; | 407 if (riff_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR; |
412 | 408 |
413 // There's no point in reading past the end of the RIFF chunk | 409 // There's no point in reading past the end of the RIFF chunk |
414 mem->riff_end_ = riff_size + CHUNK_HEADER_SIZE; | 410 mem->riff_end_ = riff_size + CHUNK_HEADER_SIZE; |
415 if (mem->buf_size_ > mem->riff_end_) { | 411 if (mem->buf_size_ > mem->riff_end_) { |
416 mem->buf_size_ = mem->end_ = mem->riff_end_; | 412 mem->buf_size_ = mem->end_ = mem->riff_end_; |
417 } | 413 } |
418 | 414 |
419 Skip(mem, RIFF_HEADER_SIZE); | 415 Skip(mem, RIFF_HEADER_SIZE); |
420 return 1; | 416 return PARSE_OK; |
421 } | 417 } |
422 | 418 |
423 static ParseStatus ParseSingleImage(WebPDemuxer* const dmux) { | 419 static ParseStatus ParseSingleImage(WebPDemuxer* const dmux) { |
424 const size_t min_size = CHUNK_HEADER_SIZE; | 420 const size_t min_size = CHUNK_HEADER_SIZE; |
425 MemBuffer* const mem = &dmux->mem_; | 421 MemBuffer* const mem = &dmux->mem_; |
426 Frame* frame; | 422 Frame* frame; |
427 ParseStatus status; | 423 ParseStatus status; |
| 424 int image_added = 0; |
428 | 425 |
429 if (dmux->frames_ != NULL) return PARSE_ERROR; | 426 if (dmux->frames_ != NULL) return PARSE_ERROR; |
430 if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR; | 427 if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR; |
431 if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA; | 428 if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA; |
432 | 429 |
433 frame = (Frame*)calloc(1, sizeof(*frame)); | 430 frame = (Frame*)calloc(1, sizeof(*frame)); |
434 if (frame == NULL) return PARSE_ERROR; | 431 if (frame == NULL) return PARSE_ERROR; |
435 | 432 |
436 // For the single image case we allow parsing of a partial frame, but we need | 433 // For the single image case we allow parsing of a partial frame, but we need |
437 // at least CHUNK_HEADER_SIZE for parsing. | 434 // at least CHUNK_HEADER_SIZE for parsing. |
438 status = StoreFrame(1, CHUNK_HEADER_SIZE, &dmux->mem_, frame); | 435 status = StoreFrame(1, CHUNK_HEADER_SIZE, &dmux->mem_, frame); |
439 if (status != PARSE_ERROR) { | 436 if (status != PARSE_ERROR) { |
440 const int has_alpha = !!(dmux->feature_flags_ & ALPHA_FLAG); | 437 const int has_alpha = !!(dmux->feature_flags_ & ALPHA_FLAG); |
441 // Clear any alpha when the alpha flag is missing. | 438 // Clear any alpha when the alpha flag is missing. |
442 if (!has_alpha && frame->img_components_[1].size_ > 0) { | 439 if (!has_alpha && frame->img_components_[1].size_ > 0) { |
443 frame->img_components_[1].offset_ = 0; | 440 frame->img_components_[1].offset_ = 0; |
444 frame->img_components_[1].size_ = 0; | 441 frame->img_components_[1].size_ = 0; |
445 frame->has_alpha_ = 0; | 442 frame->has_alpha_ = 0; |
446 } | 443 } |
447 | 444 |
448 // Use the frame width/height as the canvas values for non-vp8x files. | 445 // 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. | 446 // Also, set ALPHA_FLAG if this is a lossless image with alpha. |
450 if (!dmux->is_ext_format_ && frame->width_ > 0 && frame->height_ > 0) { | 447 if (!dmux->is_ext_format_ && frame->width_ > 0 && frame->height_ > 0) { |
451 dmux->state_ = WEBP_DEMUX_PARSED_HEADER; | 448 dmux->state_ = WEBP_DEMUX_PARSED_HEADER; |
452 dmux->canvas_width_ = frame->width_; | 449 dmux->canvas_width_ = frame->width_; |
453 dmux->canvas_height_ = frame->height_; | 450 dmux->canvas_height_ = frame->height_; |
454 dmux->feature_flags_ |= frame->has_alpha_ ? ALPHA_FLAG : 0; | 451 dmux->feature_flags_ |= frame->has_alpha_ ? ALPHA_FLAG : 0; |
455 } | 452 } |
456 AddFrame(dmux, frame); | 453 if (!AddFrame(dmux, frame)) { |
457 dmux->num_frames_ = 1; | 454 status = PARSE_ERROR; // last frame was left incomplete |
458 } else { | 455 } else { |
459 free(frame); | 456 image_added = 1; |
| 457 dmux->num_frames_ = 1; |
| 458 } |
460 } | 459 } |
461 | 460 |
| 461 if (!image_added) free(frame); |
462 return status; | 462 return status; |
463 } | 463 } |
464 | 464 |
465 static ParseStatus ParseVP8X(WebPDemuxer* const dmux) { | 465 static ParseStatus ParseVP8XChunks(WebPDemuxer* const dmux) { |
| 466 const int is_animation = !!(dmux->feature_flags_ & ANIMATION_FLAG); |
466 MemBuffer* const mem = &dmux->mem_; | 467 MemBuffer* const mem = &dmux->mem_; |
467 int anim_chunks = 0; | 468 int anim_chunks = 0; |
468 uint32_t vp8x_size; | |
469 ParseStatus status = PARSE_OK; | 469 ParseStatus status = PARSE_OK; |
470 | 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 { | 471 do { |
496 int store_chunk = 1; | 472 int store_chunk = 1; |
497 const size_t chunk_start_offset = mem->start_; | 473 const size_t chunk_start_offset = mem->start_; |
498 const uint32_t fourcc = ReadLE32(mem); | 474 const uint32_t fourcc = ReadLE32(mem); |
499 const uint32_t chunk_size = ReadLE32(mem); | 475 const uint32_t chunk_size = ReadLE32(mem); |
500 const uint32_t chunk_size_padded = chunk_size + (chunk_size & 1); | 476 const uint32_t chunk_size_padded = chunk_size + (chunk_size & 1); |
501 | 477 |
502 if (chunk_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR; | 478 if (chunk_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR; |
503 if (SizeIsInvalid(mem, chunk_size_padded)) return PARSE_ERROR; | 479 if (SizeIsInvalid(mem, chunk_size_padded)) return PARSE_ERROR; |
504 | 480 |
505 switch (fourcc) { | 481 switch (fourcc) { |
506 case MKFOURCC('V', 'P', '8', 'X'): { | 482 case MKFOURCC('V', 'P', '8', 'X'): { |
507 return PARSE_ERROR; | 483 return PARSE_ERROR; |
508 } | 484 } |
509 case MKFOURCC('A', 'L', 'P', 'H'): | 485 case MKFOURCC('A', 'L', 'P', 'H'): |
510 case MKFOURCC('V', 'P', '8', ' '): | 486 case MKFOURCC('V', 'P', '8', ' '): |
511 case MKFOURCC('V', 'P', '8', 'L'): { | 487 case MKFOURCC('V', 'P', '8', 'L'): { |
512 const int has_frames = !!(dmux->feature_flags_ & ANIMATION_FLAG); | |
513 // check that this isn't an animation (all frames should be in an ANMF). | 488 // check that this isn't an animation (all frames should be in an ANMF). |
514 if (anim_chunks > 0 || has_frames) return PARSE_ERROR; | 489 if (anim_chunks > 0 || is_animation) return PARSE_ERROR; |
515 | 490 |
516 Rewind(mem, CHUNK_HEADER_SIZE); | 491 Rewind(mem, CHUNK_HEADER_SIZE); |
517 status = ParseSingleImage(dmux); | 492 status = ParseSingleImage(dmux); |
518 break; | 493 break; |
519 } | 494 } |
520 case MKFOURCC('A', 'N', 'I', 'M'): { | 495 case MKFOURCC('A', 'N', 'I', 'M'): { |
521 if (chunk_size_padded < ANIM_CHUNK_SIZE) return PARSE_ERROR; | 496 if (chunk_size_padded < ANIM_CHUNK_SIZE) return PARSE_ERROR; |
522 | 497 |
523 if (MemDataSize(mem) < chunk_size_padded) { | 498 if (MemDataSize(mem) < chunk_size_padded) { |
524 status = PARSE_NEED_MORE_DATA; | 499 status = PARSE_NEED_MORE_DATA; |
(...skipping 16 matching lines...) Expand all Loading... |
541 #ifdef WEBP_EXPERIMENTAL_FEATURES | 516 #ifdef WEBP_EXPERIMENTAL_FEATURES |
542 case MKFOURCC('F', 'R', 'G', 'M'): { | 517 case MKFOURCC('F', 'R', 'G', 'M'): { |
543 status = ParseFragment(dmux, chunk_size_padded); | 518 status = ParseFragment(dmux, chunk_size_padded); |
544 break; | 519 break; |
545 } | 520 } |
546 #endif | 521 #endif |
547 case MKFOURCC('I', 'C', 'C', 'P'): { | 522 case MKFOURCC('I', 'C', 'C', 'P'): { |
548 store_chunk = !!(dmux->feature_flags_ & ICCP_FLAG); | 523 store_chunk = !!(dmux->feature_flags_ & ICCP_FLAG); |
549 goto Skip; | 524 goto Skip; |
550 } | 525 } |
| 526 case MKFOURCC('E', 'X', 'I', 'F'): { |
| 527 store_chunk = !!(dmux->feature_flags_ & EXIF_FLAG); |
| 528 goto Skip; |
| 529 } |
551 case MKFOURCC('X', 'M', 'P', ' '): { | 530 case MKFOURCC('X', 'M', 'P', ' '): { |
552 store_chunk = !!(dmux->feature_flags_ & XMP_FLAG); | 531 store_chunk = !!(dmux->feature_flags_ & XMP_FLAG); |
553 goto Skip; | 532 goto Skip; |
554 } | 533 } |
555 case MKFOURCC('E', 'X', 'I', 'F'): { | |
556 store_chunk = !!(dmux->feature_flags_ & EXIF_FLAG); | |
557 goto Skip; | |
558 } | |
559 Skip: | 534 Skip: |
560 default: { | 535 default: { |
561 if (chunk_size_padded <= MemDataSize(mem)) { | 536 if (chunk_size_padded <= MemDataSize(mem)) { |
562 if (store_chunk) { | 537 if (store_chunk) { |
563 // Store only the chunk header and unpadded size as only the payload | 538 // Store only the chunk header and unpadded size as only the payload |
564 // will be returned to the user. | 539 // will be returned to the user. |
565 if (!StoreChunk(dmux, chunk_start_offset, | 540 if (!StoreChunk(dmux, chunk_start_offset, |
566 CHUNK_HEADER_SIZE + chunk_size)) { | 541 CHUNK_HEADER_SIZE + chunk_size)) { |
567 return PARSE_ERROR; | 542 return PARSE_ERROR; |
568 } | 543 } |
569 } | 544 } |
570 Skip(mem, chunk_size_padded); | 545 Skip(mem, chunk_size_padded); |
571 } else { | 546 } else { |
572 status = PARSE_NEED_MORE_DATA; | 547 status = PARSE_NEED_MORE_DATA; |
573 } | 548 } |
574 } | 549 } |
575 } | 550 } |
576 | 551 |
577 if (mem->start_ == mem->riff_end_) { | 552 if (mem->start_ == mem->riff_end_) { |
578 break; | 553 break; |
579 } else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) { | 554 } else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) { |
580 status = PARSE_NEED_MORE_DATA; | 555 status = PARSE_NEED_MORE_DATA; |
581 } | 556 } |
582 } while (status == PARSE_OK); | 557 } while (status == PARSE_OK); |
583 | 558 |
584 return status; | 559 return status; |
585 } | 560 } |
586 | 561 |
| 562 static ParseStatus ParseVP8X(WebPDemuxer* const dmux) { |
| 563 MemBuffer* const mem = &dmux->mem_; |
| 564 uint32_t vp8x_size; |
| 565 |
| 566 if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA; |
| 567 |
| 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; |
| 576 |
| 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 |
| 583 } |
| 584 Skip(mem, vp8x_size - VP8X_CHUNK_SIZE); // skip any trailing data. |
| 585 dmux->state_ = WEBP_DEMUX_PARSED_HEADER; |
| 586 |
| 587 if (SizeIsInvalid(mem, CHUNK_HEADER_SIZE)) return PARSE_ERROR; |
| 588 if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA; |
| 589 |
| 590 return ParseVP8XChunks(dmux); |
| 591 } |
| 592 |
587 // ----------------------------------------------------------------------------- | 593 // ----------------------------------------------------------------------------- |
588 // Format validation | 594 // Format validation |
589 | 595 |
590 static int IsValidSimpleFormat(const WebPDemuxer* const dmux) { | 596 static int IsValidSimpleFormat(const WebPDemuxer* const dmux) { |
591 const Frame* const frame = dmux->frames_; | 597 const Frame* const frame = dmux->frames_; |
592 if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1; | 598 if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1; |
593 | 599 |
594 if (dmux->canvas_width_ <= 0 || dmux->canvas_height_ <= 0) return 0; | 600 if (dmux->canvas_width_ <= 0 || dmux->canvas_height_ <= 0) return 0; |
595 if (dmux->state_ == WEBP_DEMUX_DONE && frame == NULL) return 0; | 601 if (dmux->state_ == WEBP_DEMUX_DONE && frame == NULL) return 0; |
596 | 602 |
(...skipping 16 matching lines...) Expand all Loading... |
613 } | 619 } |
614 } else { | 620 } else { |
615 if (frame->x_offset_ < 0 || frame->y_offset_ < 0) return 0; | 621 if (frame->x_offset_ < 0 || frame->y_offset_ < 0) return 0; |
616 if (frame->width_ + frame->x_offset_ > canvas_width) return 0; | 622 if (frame->width_ + frame->x_offset_ > canvas_width) return 0; |
617 if (frame->height_ + frame->y_offset_ > canvas_height) return 0; | 623 if (frame->height_ + frame->y_offset_ > canvas_height) return 0; |
618 } | 624 } |
619 return 1; | 625 return 1; |
620 } | 626 } |
621 | 627 |
622 static int IsValidExtendedFormat(const WebPDemuxer* const dmux) { | 628 static int IsValidExtendedFormat(const WebPDemuxer* const dmux) { |
623 const int has_fragments = !!(dmux->feature_flags_ & FRAGMENTS_FLAG); | 629 const int is_animation = !!(dmux->feature_flags_ & ANIMATION_FLAG); |
624 const int has_frames = !!(dmux->feature_flags_ & ANIMATION_FLAG); | 630 const int is_fragmented = !!(dmux->feature_flags_ & FRAGMENTS_FLAG); |
625 const Frame* f = dmux->frames_; | 631 const Frame* f = dmux->frames_; |
626 | 632 |
627 if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1; | 633 if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1; |
628 | 634 |
629 if (dmux->canvas_width_ <= 0 || dmux->canvas_height_ <= 0) return 0; | 635 if (dmux->canvas_width_ <= 0 || dmux->canvas_height_ <= 0) return 0; |
630 if (dmux->loop_count_ < 0) return 0; | 636 if (dmux->loop_count_ < 0) return 0; |
631 if (dmux->state_ == WEBP_DEMUX_DONE && dmux->frames_ == NULL) return 0; | 637 if (dmux->state_ == WEBP_DEMUX_DONE && dmux->frames_ == NULL) return 0; |
632 #ifndef WEBP_EXPERIMENTAL_FEATURES | 638 #ifndef WEBP_EXPERIMENTAL_FEATURES |
633 if (has_fragments) return 0; | 639 if (is_fragmented) return 0; |
634 #endif | 640 #endif |
635 | 641 |
636 while (f != NULL) { | 642 while (f != NULL) { |
637 const int cur_frame_set = f->frame_num_; | 643 const int cur_frame_set = f->frame_num_; |
638 int frame_count = 0, fragment_count = 0; | 644 int frame_count = 0, fragment_count = 0; |
639 | 645 |
640 // Check frame properties and if the image is composed of fragments that | 646 // Check frame properties and if the image is composed of fragments that |
641 // each fragment came from a fragment. | 647 // each fragment came from a fragment. |
642 for (; f != NULL && f->frame_num_ == cur_frame_set; f = f->next_) { | 648 for (; f != NULL && f->frame_num_ == cur_frame_set; f = f->next_) { |
643 const ChunkData* const image = f->img_components_; | 649 const ChunkData* const image = f->img_components_; |
644 const ChunkData* const alpha = f->img_components_ + 1; | 650 const ChunkData* const alpha = f->img_components_ + 1; |
645 | 651 |
646 if (has_fragments && !f->is_fragment_) return 0; | 652 if (is_fragmented && !f->is_fragment_) return 0; |
647 if (!has_fragments && f->is_fragment_) return 0; | 653 if (!is_fragmented && f->is_fragment_) return 0; |
648 if (!has_frames && f->frame_num_ > 1) return 0; | 654 if (!is_animation && f->frame_num_ > 1) return 0; |
649 | 655 |
650 if (f->complete_) { | 656 if (f->complete_) { |
651 if (alpha->size_ == 0 && image->size_ == 0) return 0; | 657 if (alpha->size_ == 0 && image->size_ == 0) return 0; |
652 // Ensure alpha precedes image bitstream. | 658 // Ensure alpha precedes image bitstream. |
653 if (alpha->size_ > 0 && alpha->offset_ > image->offset_) { | 659 if (alpha->size_ > 0 && alpha->offset_ > image->offset_) { |
654 return 0; | 660 return 0; |
655 } | 661 } |
656 | 662 |
657 if (f->width_ <= 0 || f->height_ <= 0) return 0; | 663 if (f->width_ <= 0 || f->height_ <= 0) return 0; |
658 } else { | 664 } else { |
659 // There shouldn't be a partial frame in a complete file. | 665 // There shouldn't be a partial frame in a complete file. |
660 if (dmux->state_ == WEBP_DEMUX_DONE) return 0; | 666 if (dmux->state_ == WEBP_DEMUX_DONE) return 0; |
661 | 667 |
662 // Ensure alpha precedes image bitstream. | 668 // Ensure alpha precedes image bitstream. |
663 if (alpha->size_ > 0 && image->size_ > 0 && | 669 if (alpha->size_ > 0 && image->size_ > 0 && |
664 alpha->offset_ > image->offset_) { | 670 alpha->offset_ > image->offset_) { |
665 return 0; | 671 return 0; |
666 } | 672 } |
667 // There shouldn't be any frames after an incomplete one. | 673 // There shouldn't be any frames after an incomplete one. |
668 if (f->next_ != NULL) return 0; | 674 if (f->next_ != NULL) return 0; |
669 } | 675 } |
670 | 676 |
671 if (f->width_ > 0 && f->height_ > 0 && | 677 if (f->width_ > 0 && f->height_ > 0 && |
672 !CheckFrameBounds(f, !(has_frames || has_fragments), | 678 !CheckFrameBounds(f, !(is_animation || is_fragmented), |
673 dmux->canvas_width_, dmux->canvas_height_)) { | 679 dmux->canvas_width_, dmux->canvas_height_)) { |
674 return 0; | 680 return 0; |
675 } | 681 } |
676 | 682 |
677 fragment_count += f->is_fragment_; | 683 fragment_count += f->is_fragment_; |
678 ++frame_count; | 684 ++frame_count; |
679 } | 685 } |
680 if (!has_fragments && frame_count > 1) return 0; | 686 if (!is_fragmented && frame_count > 1) return 0; |
681 if (fragment_count > 0 && frame_count != fragment_count) return 0; | 687 if (fragment_count > 0 && frame_count != fragment_count) return 0; |
682 if (f == NULL) break; | |
683 } | 688 } |
684 return 1; | 689 return 1; |
685 } | 690 } |
686 | 691 |
687 // ----------------------------------------------------------------------------- | 692 // ----------------------------------------------------------------------------- |
688 // WebPDemuxer object | 693 // WebPDemuxer object |
689 | 694 |
690 static void InitDemux(WebPDemuxer* const dmux, const MemBuffer* const mem) { | 695 static void InitDemux(WebPDemuxer* const dmux, const MemBuffer* const mem) { |
691 dmux->state_ = WEBP_DEMUX_PARSING_HEADER; | 696 dmux->state_ = WEBP_DEMUX_PARSING_HEADER; |
692 dmux->loop_count_ = 1; | 697 dmux->loop_count_ = 1; |
693 dmux->bgcolor_ = 0xFFFFFFFF; // White background by default. | 698 dmux->bgcolor_ = 0xFFFFFFFF; // White background by default. |
694 dmux->canvas_width_ = -1; | 699 dmux->canvas_width_ = -1; |
695 dmux->canvas_height_ = -1; | 700 dmux->canvas_height_ = -1; |
696 dmux->frames_tail_ = &dmux->frames_; | 701 dmux->frames_tail_ = &dmux->frames_; |
| 702 dmux->chunks_tail_ = &dmux->chunks_; |
697 dmux->mem_ = *mem; | 703 dmux->mem_ = *mem; |
698 } | 704 } |
699 | 705 |
700 WebPDemuxer* WebPDemuxInternal(const WebPData* data, int allow_partial, | 706 WebPDemuxer* WebPDemuxInternal(const WebPData* data, int allow_partial, |
701 WebPDemuxState* state, int version) { | 707 WebPDemuxState* state, int version) { |
702 const ChunkParser* parser; | 708 const ChunkParser* parser; |
703 int partial; | 709 int partial; |
704 ParseStatus status = PARSE_ERROR; | 710 ParseStatus status = PARSE_ERROR; |
705 MemBuffer mem; | 711 MemBuffer mem; |
706 WebPDemuxer* dmux; | 712 WebPDemuxer* dmux; |
707 | 713 |
| 714 if (state != NULL) *state = WEBP_DEMUX_PARSE_ERROR; |
| 715 |
708 if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DEMUX_ABI_VERSION)) return NULL; | 716 if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DEMUX_ABI_VERSION)) return NULL; |
709 if (data == NULL || data->bytes == NULL || data->size == 0) return NULL; | 717 if (data == NULL || data->bytes == NULL || data->size == 0) return NULL; |
710 | 718 |
711 if (!InitMemBuffer(&mem, data->bytes, data->size)) return NULL; | 719 if (!InitMemBuffer(&mem, data->bytes, data->size)) return NULL; |
712 if (!ReadHeader(&mem)) return NULL; | 720 status = ReadHeader(&mem); |
| 721 if (status != PARSE_OK) { |
| 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 } |
713 | 728 |
714 partial = (mem.buf_size_ < mem.riff_end_); | 729 partial = (mem.buf_size_ < mem.riff_end_); |
715 if (!allow_partial && partial) return NULL; | 730 if (!allow_partial && partial) return NULL; |
716 | 731 |
717 dmux = (WebPDemuxer*)calloc(1, sizeof(*dmux)); | 732 dmux = (WebPDemuxer*)calloc(1, sizeof(*dmux)); |
718 if (dmux == NULL) return NULL; | 733 if (dmux == NULL) return NULL; |
719 InitDemux(dmux, &mem); | 734 InitDemux(dmux, &mem); |
720 | 735 |
| 736 status = PARSE_ERROR; |
721 for (parser = kMasterChunks; parser->parse != NULL; ++parser) { | 737 for (parser = kMasterChunks; parser->parse != NULL; ++parser) { |
722 if (!memcmp(parser->id, GetBuffer(&dmux->mem_), TAG_SIZE)) { | 738 if (!memcmp(parser->id, GetBuffer(&dmux->mem_), TAG_SIZE)) { |
723 status = parser->parse(dmux); | 739 status = parser->parse(dmux); |
724 if (status == PARSE_OK) dmux->state_ = WEBP_DEMUX_DONE; | 740 if (status == PARSE_OK) dmux->state_ = WEBP_DEMUX_DONE; |
725 if (status == PARSE_NEED_MORE_DATA && !partial) status = PARSE_ERROR; | 741 if (status == PARSE_NEED_MORE_DATA && !partial) status = PARSE_ERROR; |
726 if (status != PARSE_ERROR && !parser->valid(dmux)) 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; |
727 break; | 744 break; |
728 } | 745 } |
729 } | 746 } |
730 if (state) *state = dmux->state_; | 747 if (state != NULL) *state = dmux->state_; |
731 | 748 |
732 if (status == PARSE_ERROR) { | 749 if (status == PARSE_ERROR) { |
733 WebPDemuxDelete(dmux); | 750 WebPDemuxDelete(dmux); |
734 return NULL; | 751 return NULL; |
735 } | 752 } |
736 return dmux; | 753 return dmux; |
737 } | 754 } |
738 | 755 |
739 void WebPDemuxDelete(WebPDemuxer* dmux) { | 756 void WebPDemuxDelete(WebPDemuxer* dmux) { |
740 Chunk* c; | 757 Chunk* c; |
(...skipping 235 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
976 (const char*)iter->chunk.bytes - CHUNK_HEADER_SIZE; | 993 (const char*)iter->chunk.bytes - CHUNK_HEADER_SIZE; |
977 return SetChunk(fourcc, iter->chunk_num - 1, iter); | 994 return SetChunk(fourcc, iter->chunk_num - 1, iter); |
978 } | 995 } |
979 return 0; | 996 return 0; |
980 } | 997 } |
981 | 998 |
982 void WebPDemuxReleaseChunkIterator(WebPChunkIterator* iter) { | 999 void WebPDemuxReleaseChunkIterator(WebPChunkIterator* iter) { |
983 (void)iter; | 1000 (void)iter; |
984 } | 1001 } |
985 | 1002 |
986 #if defined(__cplusplus) || defined(c_plusplus) | |
987 } // extern "C" | |
988 #endif | |
OLD | NEW |