Index: third_party/libwebp/demux/demux.c |
diff --git a/third_party/libwebp/demux/demux.c b/third_party/libwebp/demux/demux.c |
index 55a7918fbf3362cb998a70ef9eb0bcf69b47ebd6..0d2989f6f49d5c841a9993ad297ac66d38ea5db3 100644 |
--- a/third_party/libwebp/demux/demux.c |
+++ b/third_party/libwebp/demux/demux.c |
@@ -24,8 +24,8 @@ |
#include "../webp/format_constants.h" |
#define DMUX_MAJ_VERSION 0 |
-#define DMUX_MIN_VERSION 2 |
-#define DMUX_REV_VERSION 2 |
+#define DMUX_MIN_VERSION 3 |
+#define DMUX_REV_VERSION 0 |
typedef struct { |
size_t start_; // start location of the data |
@@ -47,8 +47,7 @@ typedef struct Frame { |
int duration_; |
WebPMuxAnimDispose dispose_method_; |
WebPMuxAnimBlend blend_method_; |
- int is_fragment_; // this is a frame fragment (and not a full frame). |
- int frame_num_; // the referent frame number for use in assembling fragments. |
+ int frame_num_; |
int complete_; // img_components_ contains a full image. |
ChunkData img_components_[2]; // 0=VP8{,L} 1=ALPH |
struct Frame* next_; |
@@ -193,6 +192,19 @@ static int AddFrame(WebPDemuxer* const dmux, Frame* const frame) { |
return 1; |
} |
+static void SetFrameInfo(size_t start_offset, size_t size, |
+ int frame_num, int complete, |
+ const WebPBitstreamFeatures* const features, |
+ Frame* const frame) { |
+ frame->img_components_[0].offset_ = start_offset; |
+ frame->img_components_[0].size_ = size; |
+ frame->width_ = features->width; |
+ frame->height_ = features->height; |
+ frame->has_alpha_ |= features->has_alpha; |
+ frame->frame_num_ = frame_num; |
+ frame->complete_ = complete; |
+} |
+ |
// Store image bearing chunks to 'frame'. |
static ParseStatus StoreFrame(int frame_num, uint32_t min_size, |
MemBuffer* const mem, Frame* const frame) { |
@@ -248,13 +260,8 @@ static ParseStatus StoreFrame(int frame_num, uint32_t min_size, |
return PARSE_ERROR; |
} |
++image_chunks; |
- frame->img_components_[0].offset_ = chunk_start_offset; |
- frame->img_components_[0].size_ = chunk_size; |
- frame->width_ = features.width; |
- frame->height_ = features.height; |
- frame->has_alpha_ |= features.has_alpha; |
- frame->frame_num_ = frame_num; |
- frame->complete_ = (status == PARSE_OK); |
+ SetFrameInfo(chunk_start_offset, chunk_size, frame_num, |
+ status == PARSE_OK, &features, frame); |
Skip(mem, payload_available); |
} else { |
goto Done; |
@@ -337,42 +344,6 @@ static ParseStatus ParseAnimationFrame( |
return status; |
} |
-#ifdef WEBP_EXPERIMENTAL_FEATURES |
-// Parse a 'FRGM' chunk and any image bearing chunks that immediately follow. |
-// 'fragment_chunk_size' is the previously validated, padded chunk size. |
-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 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_; |
- Frame* frame; |
- ParseStatus status = |
- NewFrame(mem, FRGM_CHUNK_SIZE, fragment_chunk_size, &frame); |
- if (status != PARSE_OK) return status; |
- |
- frame->is_fragment_ = 1; |
- frame->x_offset_ = 2 * ReadLE24s(mem); |
- frame->y_offset_ = 2 * ReadLE24s(mem); |
- |
- // 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 && is_fragmented && frame->frame_num_ > 0) { |
- added_fragment = AddFrame(dmux, frame); |
- if (!added_fragment) { |
- status = PARSE_ERROR; |
- } else { |
- dmux->num_frames_ = 1; |
- } |
- } |
- |
- if (!added_fragment) WebPSafeFree(frame); |
- return status; |
-} |
-#endif // WEBP_EXPERIMENTAL_FEATURES |
- |
// General chunk storage, starting with the header at 'start_offset', allowing |
// the user to request the payload via a fourcc string. 'size' includes the |
// header and the unpadded payload size. |
@@ -513,12 +484,6 @@ static ParseStatus ParseVP8XChunks(WebPDemuxer* const dmux) { |
status = ParseAnimationFrame(dmux, chunk_size_padded); |
break; |
} |
-#ifdef WEBP_EXPERIMENTAL_FEATURES |
- case MKFOURCC('F', 'R', 'G', 'M'): { |
- status = ParseFragment(dmux, chunk_size_padded); |
- break; |
- } |
-#endif |
case MKFOURCC('I', 'C', 'C', 'P'): { |
store_chunk = !!(dmux->feature_flags_ & ICCP_FLAG); |
goto Skip; |
@@ -606,8 +571,6 @@ static int IsValidSimpleFormat(const WebPDemuxer* const dmux) { |
// If 'exact' is true, check that the image resolution matches the canvas. |
// If 'exact' is false, check that the x/y offsets do not exceed the canvas. |
-// TODO(jzern): this is insufficient in the fragmented image case if the |
-// expectation is that the fragments completely cover the canvas. |
static int CheckFrameBounds(const Frame* const frame, int exact, |
int canvas_width, int canvas_height) { |
if (exact) { |
@@ -635,22 +598,17 @@ static int IsValidExtendedFormat(const WebPDemuxer* const dmux) { |
if (dmux->canvas_width_ <= 0 || dmux->canvas_height_ <= 0) return 0; |
if (dmux->loop_count_ < 0) return 0; |
if (dmux->state_ == WEBP_DEMUX_DONE && dmux->frames_ == NULL) return 0; |
-#ifndef WEBP_EXPERIMENTAL_FEATURES |
if (is_fragmented) return 0; |
-#endif |
while (f != NULL) { |
const int cur_frame_set = f->frame_num_; |
- int frame_count = 0, fragment_count = 0; |
+ int frame_count = 0; |
- // Check frame properties and if the image is composed of fragments that |
- // each fragment came from a fragment. |
+ // Check frame properties. |
for (; f != NULL && f->frame_num_ == cur_frame_set; f = f->next_) { |
const ChunkData* const image = f->img_components_; |
const ChunkData* const alpha = f->img_components_ + 1; |
- 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_) { |
@@ -675,16 +633,13 @@ static int IsValidExtendedFormat(const WebPDemuxer* const dmux) { |
} |
if (f->width_ > 0 && f->height_ > 0 && |
- !CheckFrameBounds(f, !(is_animation || is_fragmented), |
+ !CheckFrameBounds(f, !is_animation, |
dmux->canvas_width_, dmux->canvas_height_)) { |
return 0; |
} |
- fragment_count += f->is_fragment_; |
++frame_count; |
} |
- if (!is_fragmented && frame_count > 1) return 0; |
- if (fragment_count > 0 && frame_count != fragment_count) return 0; |
} |
return 1; |
} |
@@ -703,6 +658,41 @@ static void InitDemux(WebPDemuxer* const dmux, const MemBuffer* const mem) { |
dmux->mem_ = *mem; |
} |
+static ParseStatus CreateRawImageDemuxer(MemBuffer* const mem, |
+ WebPDemuxer** demuxer) { |
+ WebPBitstreamFeatures features; |
+ const VP8StatusCode status = |
+ WebPGetFeatures(mem->buf_, mem->buf_size_, &features); |
+ *demuxer = NULL; |
+ if (status != VP8_STATUS_OK) { |
+ return (status == VP8_STATUS_NOT_ENOUGH_DATA) ? PARSE_NEED_MORE_DATA |
+ : PARSE_ERROR; |
+ } |
+ |
+ { |
+ WebPDemuxer* const dmux = (WebPDemuxer*)WebPSafeCalloc(1ULL, sizeof(*dmux)); |
+ Frame* const frame = (Frame*)WebPSafeCalloc(1ULL, sizeof(*frame)); |
+ if (dmux == NULL || frame == NULL) goto Error; |
+ InitDemux(dmux, mem); |
+ SetFrameInfo(0, mem->buf_size_, 1 /*frame_num*/, 1 /*complete*/, &features, |
+ frame); |
+ if (!AddFrame(dmux, frame)) goto Error; |
+ dmux->state_ = WEBP_DEMUX_DONE; |
+ dmux->canvas_width_ = frame->width_; |
+ dmux->canvas_height_ = frame->height_; |
+ dmux->feature_flags_ |= frame->has_alpha_ ? ALPHA_FLAG : 0; |
+ dmux->num_frames_ = 1; |
+ assert(IsValidSimpleFormat(dmux)); |
+ *demuxer = dmux; |
+ return PARSE_OK; |
+ |
+ Error: |
+ WebPSafeFree(dmux); |
+ WebPSafeFree(frame); |
+ return PARSE_ERROR; |
+ } |
+} |
+ |
WebPDemuxer* WebPDemuxInternal(const WebPData* data, int allow_partial, |
WebPDemuxState* state, int version) { |
const ChunkParser* parser; |
@@ -719,6 +709,15 @@ WebPDemuxer* WebPDemuxInternal(const WebPData* data, int allow_partial, |
if (!InitMemBuffer(&mem, data->bytes, data->size)) return NULL; |
status = ReadHeader(&mem); |
if (status != PARSE_OK) { |
+ // If parsing of the webp file header fails attempt to handle a raw |
+ // VP8/VP8L frame. Note 'allow_partial' is ignored in this case. |
+ if (status == PARSE_ERROR) { |
+ status = CreateRawImageDemuxer(&mem, &dmux); |
+ if (status == PARSE_OK) { |
+ if (state != NULL) *state = WEBP_DEMUX_DONE; |
+ return dmux; |
+ } |
+ } |
if (state != NULL) { |
*state = (status == PARSE_NEED_MORE_DATA) ? WEBP_DEMUX_PARSING_HEADER |
: WEBP_DEMUX_PARSE_ERROR; |
@@ -790,8 +789,6 @@ uint32_t WebPDemuxGetI(const WebPDemuxer* dmux, WebPFormatFeature feature) { |
// ----------------------------------------------------------------------------- |
// Frame iteration |
-// Find the first 'frame_num' frame. There may be multiple such frames in a |
-// fragmented frame. |
static const Frame* GetFrame(const WebPDemuxer* const dmux, int frame_num) { |
const Frame* f; |
for (f = dmux->frames_; f != NULL; f = f->next_) { |
@@ -800,21 +797,6 @@ static const Frame* GetFrame(const WebPDemuxer* const dmux, int frame_num) { |
return f; |
} |
-// Returns fragment 'fragment_num' and the total count. |
-static const Frame* GetFragment( |
- const Frame* const frame_set, int fragment_num, int* const count) { |
- const int this_frame = frame_set->frame_num_; |
- const Frame* f = frame_set; |
- const Frame* fragment = NULL; |
- int total; |
- |
- for (total = 0; f != NULL && f->frame_num_ == this_frame; f = f->next_) { |
- if (++total == fragment_num) fragment = f; |
- } |
- *count = total; |
- return fragment; |
-} |
- |
static const uint8_t* GetFramePayload(const uint8_t* const mem_buf, |
const Frame* const frame, |
size_t* const data_size) { |
@@ -841,34 +823,27 @@ static const uint8_t* GetFramePayload(const uint8_t* const mem_buf, |
// Create a whole 'frame' from VP8 (+ alpha) or lossless. |
static int SynthesizeFrame(const WebPDemuxer* const dmux, |
- const Frame* const first_frame, |
- int fragment_num, WebPIterator* const iter) { |
+ const Frame* const frame, |
+ WebPIterator* const iter) { |
const uint8_t* const mem_buf = dmux->mem_.buf_; |
- int num_fragments; |
size_t payload_size = 0; |
- const Frame* const fragment = |
- GetFragment(first_frame, fragment_num, &num_fragments); |
- const uint8_t* const payload = |
- GetFramePayload(mem_buf, fragment, &payload_size); |
+ const uint8_t* const payload = GetFramePayload(mem_buf, frame, &payload_size); |
if (payload == NULL) return 0; |
- assert(first_frame != NULL); |
+ assert(frame != NULL); |
- iter->frame_num = first_frame->frame_num_; |
+ iter->frame_num = frame->frame_num_; |
iter->num_frames = dmux->num_frames_; |
- iter->fragment_num = fragment_num; |
- iter->num_fragments = num_fragments; |
- iter->x_offset = fragment->x_offset_; |
- iter->y_offset = fragment->y_offset_; |
- iter->width = fragment->width_; |
- iter->height = fragment->height_; |
- iter->has_alpha = fragment->has_alpha_; |
- iter->duration = fragment->duration_; |
- iter->dispose_method = fragment->dispose_method_; |
- iter->blend_method = fragment->blend_method_; |
- iter->complete = fragment->complete_; |
+ iter->x_offset = frame->x_offset_; |
+ iter->y_offset = frame->y_offset_; |
+ iter->width = frame->width_; |
+ iter->height = frame->height_; |
+ iter->has_alpha = frame->has_alpha_; |
+ iter->duration = frame->duration_; |
+ iter->dispose_method = frame->dispose_method_; |
+ iter->blend_method = frame->blend_method_; |
+ iter->complete = frame->complete_; |
iter->fragment.bytes = payload; |
iter->fragment.size = payload_size; |
- // TODO(jzern): adjust offsets for 'FRGM's embedded in 'ANMF's |
return 1; |
} |
@@ -882,7 +857,7 @@ static int SetFrame(int frame_num, WebPIterator* const iter) { |
frame = GetFrame(dmux, frame_num); |
if (frame == NULL) return 0; |
- return SynthesizeFrame(dmux, frame, 1, iter); |
+ return SynthesizeFrame(dmux, frame, iter); |
} |
int WebPDemuxGetFrame(const WebPDemuxer* dmux, int frame, WebPIterator* iter) { |
@@ -904,17 +879,6 @@ int WebPDemuxPrevFrame(WebPIterator* iter) { |
return SetFrame(iter->frame_num - 1, iter); |
} |
-int WebPDemuxSelectFragment(WebPIterator* iter, int fragment_num) { |
- if (iter != NULL && iter->private_ != NULL && fragment_num > 0) { |
- const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_; |
- const Frame* const frame = GetFrame(dmux, iter->frame_num); |
- if (frame == NULL) return 0; |
- |
- return SynthesizeFrame(dmux, frame, fragment_num, iter); |
- } |
- return 0; |
-} |
- |
void WebPDemuxReleaseIterator(WebPIterator* iter) { |
(void)iter; |
} |