Index: third_party/libwebp/demux/demux.c |
diff --git a/third_party/libwebp/demux/demux.c b/third_party/libwebp/demux/demux.c |
index c6e4acdcb866c1984a8accf6329f357ce6eed556..f66ac6d82bdf1ee9998726e58f39ca1876c259d6 100644 |
--- a/third_party/libwebp/demux/demux.c |
+++ b/third_party/libwebp/demux/demux.c |
@@ -23,13 +23,9 @@ |
#include "../webp/demux.h" |
#include "../webp/format_constants.h" |
-#if defined(__cplusplus) || defined(c_plusplus) |
-extern "C" { |
-#endif |
- |
#define DMUX_MAJ_VERSION 0 |
-#define DMUX_MIN_VERSION 1 |
-#define DMUX_REV_VERSION 1 |
+#define DMUX_MIN_VERSION 2 |
+#define DMUX_REV_VERSION 0 |
typedef struct { |
size_t start_; // start location of the data |
@@ -75,6 +71,7 @@ struct WebPDemuxer { |
Frame* frames_; |
Frame** frames_tail_; |
Chunk* chunks_; // non-image chunks |
+ Chunk** chunks_tail_; |
}; |
typedef enum { |
@@ -179,10 +176,9 @@ static WEBP_INLINE uint32_t ReadLE32(MemBuffer* const mem) { |
// Secondary chunk parsing |
static void AddChunk(WebPDemuxer* const dmux, Chunk* const chunk) { |
- Chunk** c = &dmux->chunks_; |
- while (*c != NULL) c = &(*c)->next_; |
- *c = chunk; |
+ *dmux->chunks_tail_ = chunk; |
chunk->next_ = NULL; |
+ dmux->chunks_tail_ = &chunk->next_; |
} |
// Add a frame to the end of the list, ensuring the last frame is complete. |
@@ -301,7 +297,7 @@ static ParseStatus NewFrame(const MemBuffer* const mem, |
// 'frame_chunk_size' is the previously validated, padded chunk size. |
static ParseStatus ParseAnimationFrame( |
WebPDemuxer* const dmux, uint32_t frame_chunk_size) { |
- const int has_frames = !!(dmux->feature_flags_ & ANIMATION_FLAG); |
+ const int is_animation = !!(dmux->feature_flags_ & ANIMATION_FLAG); |
const uint32_t anmf_payload_size = frame_chunk_size - ANMF_CHUNK_SIZE; |
int added_frame = 0; |
int bits; |
@@ -328,7 +324,7 @@ static ParseStatus ParseAnimationFrame( |
// Store a frame only if the animation flag is set there is some data for |
// this frame is available. |
status = StoreFrame(dmux->num_frames_ + 1, anmf_payload_size, mem, frame); |
- if (status != PARSE_ERROR && has_frames && frame->frame_num_ > 0) { |
+ if (status != PARSE_ERROR && is_animation && frame->frame_num_ > 0) { |
added_frame = AddFrame(dmux, frame); |
if (added_frame) { |
++dmux->num_frames_; |
@@ -347,7 +343,7 @@ static ParseStatus ParseAnimationFrame( |
static ParseStatus ParseFragment(WebPDemuxer* const dmux, |
uint32_t fragment_chunk_size) { |
const int frame_num = 1; // All fragments belong to the 1st (and only) frame. |
- const int has_fragments = !!(dmux->feature_flags_ & FRAGMENTS_FLAG); |
+ const int is_fragmented = !!(dmux->feature_flags_ & FRAGMENTS_FLAG); |
const uint32_t frgm_payload_size = fragment_chunk_size - FRGM_CHUNK_SIZE; |
int added_fragment = 0; |
MemBuffer* const mem = &dmux->mem_; |
@@ -360,10 +356,10 @@ static ParseStatus ParseFragment(WebPDemuxer* const dmux, |
frame->x_offset_ = 2 * ReadLE24s(mem); |
frame->y_offset_ = 2 * ReadLE24s(mem); |
- // Store a fragment only if the fragments flag is set there is some data for |
- // this fragment is available. |
+ // Store a fragment only if the 'fragments' flag is set and there is some |
+ // data available. |
status = StoreFrame(frame_num, frgm_payload_size, mem, frame); |
- if (status != PARSE_ERROR && has_fragments && frame->frame_num_ > 0) { |
+ if (status != PARSE_ERROR && is_fragmented && frame->frame_num_ > 0) { |
added_fragment = AddFrame(dmux, frame); |
if (!added_fragment) { |
status = PARSE_ERROR; |
@@ -395,20 +391,20 @@ static int StoreChunk(WebPDemuxer* const dmux, |
// ----------------------------------------------------------------------------- |
// Primary chunk parsing |
-static int ReadHeader(MemBuffer* const mem) { |
+static ParseStatus ReadHeader(MemBuffer* const mem) { |
const size_t min_size = RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE; |
uint32_t riff_size; |
// Basic file level validation. |
- if (MemDataSize(mem) < min_size) return 0; |
+ if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA; |
if (memcmp(GetBuffer(mem), "RIFF", CHUNK_SIZE_BYTES) || |
memcmp(GetBuffer(mem) + CHUNK_HEADER_SIZE, "WEBP", CHUNK_SIZE_BYTES)) { |
- return 0; |
+ return PARSE_ERROR; |
} |
riff_size = GetLE32(GetBuffer(mem) + TAG_SIZE); |
- if (riff_size < CHUNK_HEADER_SIZE) return 0; |
- if (riff_size > MAX_CHUNK_PAYLOAD) return 0; |
+ if (riff_size < CHUNK_HEADER_SIZE) return PARSE_ERROR; |
+ if (riff_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR; |
// There's no point in reading past the end of the RIFF chunk |
mem->riff_end_ = riff_size + CHUNK_HEADER_SIZE; |
@@ -417,7 +413,7 @@ static int ReadHeader(MemBuffer* const mem) { |
} |
Skip(mem, RIFF_HEADER_SIZE); |
- return 1; |
+ return PARSE_OK; |
} |
static ParseStatus ParseSingleImage(WebPDemuxer* const dmux) { |
@@ -425,6 +421,7 @@ static ParseStatus ParseSingleImage(WebPDemuxer* const dmux) { |
MemBuffer* const mem = &dmux->mem_; |
Frame* frame; |
ParseStatus status; |
+ int image_added = 0; |
if (dmux->frames_ != NULL) return PARSE_ERROR; |
if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR; |
@@ -453,45 +450,24 @@ static ParseStatus ParseSingleImage(WebPDemuxer* const dmux) { |
dmux->canvas_height_ = frame->height_; |
dmux->feature_flags_ |= frame->has_alpha_ ? ALPHA_FLAG : 0; |
} |
- AddFrame(dmux, frame); |
- dmux->num_frames_ = 1; |
- } else { |
- free(frame); |
+ if (!AddFrame(dmux, frame)) { |
+ status = PARSE_ERROR; // last frame was left incomplete |
+ } else { |
+ image_added = 1; |
+ dmux->num_frames_ = 1; |
+ } |
} |
+ if (!image_added) free(frame); |
return status; |
} |
-static ParseStatus ParseVP8X(WebPDemuxer* const dmux) { |
+static ParseStatus ParseVP8XChunks(WebPDemuxer* const dmux) { |
+ const int is_animation = !!(dmux->feature_flags_ & ANIMATION_FLAG); |
MemBuffer* const mem = &dmux->mem_; |
int anim_chunks = 0; |
- uint32_t vp8x_size; |
ParseStatus status = PARSE_OK; |
- if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA; |
- |
- dmux->is_ext_format_ = 1; |
- Skip(mem, TAG_SIZE); // VP8X |
- vp8x_size = ReadLE32(mem); |
- if (vp8x_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR; |
- if (vp8x_size < VP8X_CHUNK_SIZE) return PARSE_ERROR; |
- vp8x_size += vp8x_size & 1; |
- if (SizeIsInvalid(mem, vp8x_size)) return PARSE_ERROR; |
- if (MemDataSize(mem) < vp8x_size) return PARSE_NEED_MORE_DATA; |
- |
- dmux->feature_flags_ = ReadByte(mem); |
- Skip(mem, 3); // Reserved. |
- dmux->canvas_width_ = 1 + ReadLE24s(mem); |
- dmux->canvas_height_ = 1 + ReadLE24s(mem); |
- if (dmux->canvas_width_ * (uint64_t)dmux->canvas_height_ >= MAX_IMAGE_AREA) { |
- return PARSE_ERROR; // image final dimension is too large |
- } |
- Skip(mem, vp8x_size - VP8X_CHUNK_SIZE); // skip any trailing data. |
- dmux->state_ = WEBP_DEMUX_PARSED_HEADER; |
- |
- if (SizeIsInvalid(mem, CHUNK_HEADER_SIZE)) return PARSE_ERROR; |
- if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA; |
- |
do { |
int store_chunk = 1; |
const size_t chunk_start_offset = mem->start_; |
@@ -509,9 +485,8 @@ static ParseStatus ParseVP8X(WebPDemuxer* const dmux) { |
case MKFOURCC('A', 'L', 'P', 'H'): |
case MKFOURCC('V', 'P', '8', ' '): |
case MKFOURCC('V', 'P', '8', 'L'): { |
- const int has_frames = !!(dmux->feature_flags_ & ANIMATION_FLAG); |
// check that this isn't an animation (all frames should be in an ANMF). |
- if (anim_chunks > 0 || has_frames) return PARSE_ERROR; |
+ if (anim_chunks > 0 || is_animation) return PARSE_ERROR; |
Rewind(mem, CHUNK_HEADER_SIZE); |
status = ParseSingleImage(dmux); |
@@ -548,14 +523,14 @@ static ParseStatus ParseVP8X(WebPDemuxer* const dmux) { |
store_chunk = !!(dmux->feature_flags_ & ICCP_FLAG); |
goto Skip; |
} |
- case MKFOURCC('X', 'M', 'P', ' '): { |
- store_chunk = !!(dmux->feature_flags_ & XMP_FLAG); |
- goto Skip; |
- } |
case MKFOURCC('E', 'X', 'I', 'F'): { |
store_chunk = !!(dmux->feature_flags_ & EXIF_FLAG); |
goto Skip; |
} |
+ case MKFOURCC('X', 'M', 'P', ' '): { |
+ store_chunk = !!(dmux->feature_flags_ & XMP_FLAG); |
+ goto Skip; |
+ } |
Skip: |
default: { |
if (chunk_size_padded <= MemDataSize(mem)) { |
@@ -584,6 +559,37 @@ static ParseStatus ParseVP8X(WebPDemuxer* const dmux) { |
return status; |
} |
+static ParseStatus ParseVP8X(WebPDemuxer* const dmux) { |
+ MemBuffer* const mem = &dmux->mem_; |
+ uint32_t vp8x_size; |
+ |
+ if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA; |
+ |
+ dmux->is_ext_format_ = 1; |
+ Skip(mem, TAG_SIZE); // VP8X |
+ vp8x_size = ReadLE32(mem); |
+ if (vp8x_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR; |
+ if (vp8x_size < VP8X_CHUNK_SIZE) return PARSE_ERROR; |
+ vp8x_size += vp8x_size & 1; |
+ if (SizeIsInvalid(mem, vp8x_size)) return PARSE_ERROR; |
+ if (MemDataSize(mem) < vp8x_size) return PARSE_NEED_MORE_DATA; |
+ |
+ dmux->feature_flags_ = ReadByte(mem); |
+ Skip(mem, 3); // Reserved. |
+ dmux->canvas_width_ = 1 + ReadLE24s(mem); |
+ dmux->canvas_height_ = 1 + ReadLE24s(mem); |
+ if (dmux->canvas_width_ * (uint64_t)dmux->canvas_height_ >= MAX_IMAGE_AREA) { |
+ return PARSE_ERROR; // image final dimension is too large |
+ } |
+ Skip(mem, vp8x_size - VP8X_CHUNK_SIZE); // skip any trailing data. |
+ dmux->state_ = WEBP_DEMUX_PARSED_HEADER; |
+ |
+ if (SizeIsInvalid(mem, CHUNK_HEADER_SIZE)) return PARSE_ERROR; |
+ if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA; |
+ |
+ return ParseVP8XChunks(dmux); |
+} |
+ |
// ----------------------------------------------------------------------------- |
// Format validation |
@@ -620,8 +626,8 @@ static int CheckFrameBounds(const Frame* const frame, int exact, |
} |
static int IsValidExtendedFormat(const WebPDemuxer* const dmux) { |
- const int has_fragments = !!(dmux->feature_flags_ & FRAGMENTS_FLAG); |
- const int has_frames = !!(dmux->feature_flags_ & ANIMATION_FLAG); |
+ const int is_animation = !!(dmux->feature_flags_ & ANIMATION_FLAG); |
+ const int is_fragmented = !!(dmux->feature_flags_ & FRAGMENTS_FLAG); |
const Frame* f = dmux->frames_; |
if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1; |
@@ -630,7 +636,7 @@ static int IsValidExtendedFormat(const WebPDemuxer* const dmux) { |
if (dmux->loop_count_ < 0) return 0; |
if (dmux->state_ == WEBP_DEMUX_DONE && dmux->frames_ == NULL) return 0; |
#ifndef WEBP_EXPERIMENTAL_FEATURES |
- if (has_fragments) return 0; |
+ if (is_fragmented) return 0; |
#endif |
while (f != NULL) { |
@@ -643,9 +649,9 @@ static int IsValidExtendedFormat(const WebPDemuxer* const dmux) { |
const ChunkData* const image = f->img_components_; |
const ChunkData* const alpha = f->img_components_ + 1; |
- if (has_fragments && !f->is_fragment_) return 0; |
- if (!has_fragments && f->is_fragment_) return 0; |
- if (!has_frames && f->frame_num_ > 1) return 0; |
+ if (is_fragmented && !f->is_fragment_) return 0; |
+ if (!is_fragmented && f->is_fragment_) return 0; |
+ if (!is_animation && f->frame_num_ > 1) return 0; |
if (f->complete_) { |
if (alpha->size_ == 0 && image->size_ == 0) return 0; |
@@ -669,7 +675,7 @@ static int IsValidExtendedFormat(const WebPDemuxer* const dmux) { |
} |
if (f->width_ > 0 && f->height_ > 0 && |
- !CheckFrameBounds(f, !(has_frames || has_fragments), |
+ !CheckFrameBounds(f, !(is_animation || is_fragmented), |
dmux->canvas_width_, dmux->canvas_height_)) { |
return 0; |
} |
@@ -677,9 +683,8 @@ static int IsValidExtendedFormat(const WebPDemuxer* const dmux) { |
fragment_count += f->is_fragment_; |
++frame_count; |
} |
- if (!has_fragments && frame_count > 1) return 0; |
+ if (!is_fragmented && frame_count > 1) return 0; |
if (fragment_count > 0 && frame_count != fragment_count) return 0; |
- if (f == NULL) break; |
} |
return 1; |
} |
@@ -694,6 +699,7 @@ static void InitDemux(WebPDemuxer* const dmux, const MemBuffer* const mem) { |
dmux->canvas_width_ = -1; |
dmux->canvas_height_ = -1; |
dmux->frames_tail_ = &dmux->frames_; |
+ dmux->chunks_tail_ = &dmux->chunks_; |
dmux->mem_ = *mem; |
} |
@@ -705,11 +711,20 @@ WebPDemuxer* WebPDemuxInternal(const WebPData* data, int allow_partial, |
MemBuffer mem; |
WebPDemuxer* dmux; |
+ if (state != NULL) *state = WEBP_DEMUX_PARSE_ERROR; |
+ |
if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DEMUX_ABI_VERSION)) return NULL; |
if (data == NULL || data->bytes == NULL || data->size == 0) return NULL; |
if (!InitMemBuffer(&mem, data->bytes, data->size)) return NULL; |
- if (!ReadHeader(&mem)) return NULL; |
+ status = ReadHeader(&mem); |
+ if (status != PARSE_OK) { |
+ if (state != NULL) { |
+ *state = (status == PARSE_NEED_MORE_DATA) ? WEBP_DEMUX_PARSING_HEADER |
+ : WEBP_DEMUX_PARSE_ERROR; |
+ } |
+ return NULL; |
+ } |
partial = (mem.buf_size_ < mem.riff_end_); |
if (!allow_partial && partial) return NULL; |
@@ -718,16 +733,18 @@ WebPDemuxer* WebPDemuxInternal(const WebPData* data, int allow_partial, |
if (dmux == NULL) return NULL; |
InitDemux(dmux, &mem); |
+ status = PARSE_ERROR; |
for (parser = kMasterChunks; parser->parse != NULL; ++parser) { |
if (!memcmp(parser->id, GetBuffer(&dmux->mem_), TAG_SIZE)) { |
status = parser->parse(dmux); |
if (status == PARSE_OK) dmux->state_ = WEBP_DEMUX_DONE; |
if (status == PARSE_NEED_MORE_DATA && !partial) status = PARSE_ERROR; |
if (status != PARSE_ERROR && !parser->valid(dmux)) status = PARSE_ERROR; |
+ if (status == PARSE_ERROR) dmux->state_ = WEBP_DEMUX_PARSE_ERROR; |
break; |
} |
} |
- if (state) *state = dmux->state_; |
+ if (state != NULL) *state = dmux->state_; |
if (status == PARSE_ERROR) { |
WebPDemuxDelete(dmux); |
@@ -983,6 +1000,3 @@ void WebPDemuxReleaseChunkIterator(WebPChunkIterator* iter) { |
(void)iter; |
} |
-#if defined(__cplusplus) || defined(c_plusplus) |
-} // extern "C" |
-#endif |