// Get the canvas width, height and flags after validating that VP8X/VP8/VP8L // chunk and canvas size are valid. static WebPMuxError MuxGetCanvasInfo(const WebPMux* const mux, int* width, int* height, uint32_t* flags) { int w, h; uint32_t f = 0; WebPData data; assert(mux != NULL); // Check if VP8X chunk is present. if (MuxGet(mux, IDX_VP8X, 1, &data) == WEBP_MUX_OK) { if (data.size < VP8X_CHUNK_SIZE) return WEBP_MUX_BAD_DATA; f = GetLE32(data.bytes + 0); w = GetLE24(data.bytes + 4) + 1; h = GetLE24(data.bytes + 7) + 1; } else { const WebPMuxImage* const wpi = mux->images_; // Grab user-forced canvas size as default. w = mux->canvas_width_; h = mux->canvas_height_; if (w == 0 && h == 0 && ValidateForSingleImage(mux) == WEBP_MUX_OK) { // single image and not forced canvas size => use dimension of first frame assert(wpi != NULL); w = wpi->width_; h = wpi->height_; } if (wpi != NULL) { if (wpi->has_alpha_) f |= ALPHA_FLAG; } } if (w * (uint64_t)h >= MAX_IMAGE_AREA) return WEBP_MUX_BAD_DATA; if (width != NULL) *width = w; if (height != NULL) *height = h; if (flags != NULL) *flags = f; return WEBP_MUX_OK; }
// Validates the RIFF container (if detected) and skips over it. // If a RIFF container is detected, returns: // VP8_STATUS_BITSTREAM_ERROR for invalid header, // VP8_STATUS_NOT_ENOUGH_DATA for truncated data if have_all_data is true, // and VP8_STATUS_OK otherwise. // In case there are not enough bytes (partial RIFF container), return 0 for // *riff_size. Else return the RIFF size extracted from the header. static VP8StatusCode ParseRIFF(const uint8_t** const data, size_t* const data_size, int have_all_data, size_t* const riff_size) { assert(data != NULL); assert(data_size != NULL); assert(riff_size != NULL); *riff_size = 0; // Default: no RIFF present. if (*data_size >= RIFF_HEADER_SIZE && !memcmp(*data, "RIFF", TAG_SIZE)) { if (memcmp(*data + 8, "WEBP", TAG_SIZE)) { return VP8_STATUS_BITSTREAM_ERROR; // Wrong image file signature. } else { const uint32_t size = GetLE32(*data + TAG_SIZE); // Check that we have at least one chunk (i.e "WEBP" + "VP8?nnnn"). if (size < TAG_SIZE + CHUNK_HEADER_SIZE) { return VP8_STATUS_BITSTREAM_ERROR; } if (size > MAX_CHUNK_PAYLOAD) { return VP8_STATUS_BITSTREAM_ERROR; } if (have_all_data && (size > *data_size - CHUNK_HEADER_SIZE)) { return VP8_STATUS_NOT_ENOUGH_DATA; // Truncated bitstream. } // We have a RIFF container. Skip it. *riff_size = size; *data += RIFF_HEADER_SIZE; *data_size -= RIFF_HEADER_SIZE; } } return VP8_STATUS_OK; }
// Get the canvas width, height and flags after validating that VP8X/VP8/VP8L // chunk and canvas size are valid. static WebPMuxError MuxGetCanvasInfo(const WebPMux* const mux, int* width, int* height, uint32_t* flags) { int w, h; uint32_t f = 0; WebPData data; assert(mux != NULL); // Check if VP8X chunk is present. if (MuxGet(mux, IDX_VP8X, 1, &data) == WEBP_MUX_OK) { if (data.size < VP8X_CHUNK_SIZE) return WEBP_MUX_BAD_DATA; f = GetLE32(data.bytes + 0); w = GetLE24(data.bytes + 4) + 1; h = GetLE24(data.bytes + 7) + 1; } else { // Single image case. const WebPMuxImage* const wpi = mux->images_; WebPMuxError err = ValidateForSingleImage(mux); if (err != WEBP_MUX_OK) return err; assert(wpi != NULL); w = wpi->width_; h = wpi->height_; if (wpi->has_alpha_) f |= ALPHA_FLAG; } if (w * (uint64_t)h >= MAX_IMAGE_AREA) return WEBP_MUX_BAD_DATA; if (width != NULL) *width = w; if (height != NULL) *height = h; if (flags != NULL) *flags = f; return WEBP_MUX_OK; }
// Validates the VP8X header and skips over it. // Returns VP8_STATUS_BITSTREAM_ERROR for invalid VP8X header, // VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and // VP8_STATUS_OK otherwise. // If a VP8X chunk is found, found_vp8x is set to true and *width_ptr, // *height_ptr and *flags_ptr are set to the corresponding values extracted // from the VP8X chunk. static VP8StatusCode ParseVP8X(const uint8_t** const data, size_t* const data_size, int* const found_vp8x, int* const width_ptr, int* const height_ptr, uint32_t* const flags_ptr) { const uint32_t vp8x_size = CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE; assert(data != NULL); assert(data_size != NULL); assert(found_vp8x != NULL); *found_vp8x = 0; if (*data_size < CHUNK_HEADER_SIZE) { return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data. } if (!memcmp(*data, "VP8X", TAG_SIZE)) { int width, height; uint32_t flags; const uint32_t chunk_size = GetLE32(*data + TAG_SIZE); if (chunk_size != VP8X_CHUNK_SIZE) { return VP8_STATUS_BITSTREAM_ERROR; // Wrong chunk size. } // Verify if enough data is available to validate the VP8X chunk. if (*data_size < vp8x_size) { return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data. } flags = GetLE32(*data + 8); width = 1 + GetLE24(*data + 12); height = 1 + GetLE24(*data + 15); if (width * (uint64_t)height >= MAX_IMAGE_AREA) { return VP8_STATUS_BITSTREAM_ERROR; // image is too large } if (flags_ptr != NULL) *flags_ptr = flags; if (width_ptr != NULL) *width_ptr = width; if (height_ptr != NULL) *height_ptr = height; // Skip over VP8X header bytes. *data += vp8x_size; *data_size -= vp8x_size; *found_vp8x = 1; } return VP8_STATUS_OK; }
// Fill the chunk with the given data (includes chunk header bytes), after some // verifications. static WebPMuxError ChunkVerifyAndAssign(WebPChunk* chunk, const uint8_t* data, size_t data_size, size_t riff_size, int copy_data) { uint32_t chunk_size; WebPData chunk_data; // Sanity checks. if (data_size < CHUNK_HEADER_SIZE) return WEBP_MUX_NOT_ENOUGH_DATA; chunk_size = GetLE32(data + TAG_SIZE); { const size_t chunk_disk_size = SizeWithPadding(chunk_size); if (chunk_disk_size > riff_size) return WEBP_MUX_BAD_DATA; if (chunk_disk_size > data_size) return WEBP_MUX_NOT_ENOUGH_DATA; } // Data assignment. chunk_data.bytes = data + CHUNK_HEADER_SIZE; chunk_data.size = chunk_size; return ChunkAssignData(chunk, &chunk_data, copy_data, GetLE32(data + 0)); }
WebPMuxError WebPMuxGetAnimationParams(const WebPMux* mux, WebPMuxAnimParams* params) { WebPData anim; WebPMuxError err; if (mux == NULL || params == NULL) return WEBP_MUX_INVALID_ARGUMENT; err = MuxGet(mux, IDX_ANIM, 1, &anim); if (err != WEBP_MUX_OK) return err; if (anim.size < kChunks[WEBP_CHUNK_ANIM].size) return WEBP_MUX_BAD_DATA; params->bgcolor = GetLE32(anim.bytes); params->loop_count = GetLE16(anim.bytes + 4); return WEBP_MUX_OK; }
// Validates the VP8/VP8L Header ("VP8 nnnn" or "VP8L nnnn") and skips over it. // Returns VP8_STATUS_BITSTREAM_ERROR for invalid (chunk larger than // riff_size) VP8/VP8L header, // VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and // VP8_STATUS_OK otherwise. // If a VP8/VP8L chunk is found, *chunk_size is set to the total number of bytes // extracted from the VP8/VP8L chunk header. // The flag '*is_lossless' is set to 1 in case of VP8L chunk / raw VP8L data. static VP8StatusCode ParseVP8Header(const uint8_t** const data_ptr, size_t* const data_size, int have_all_data, size_t riff_size, size_t* const chunk_size, int* const is_lossless) { const uint8_t* const data = *data_ptr; const int is_vp8 = !memcmp(data, "VP8 ", TAG_SIZE); const int is_vp8l = !memcmp(data, "VP8L", TAG_SIZE); const uint32_t minimal_size = TAG_SIZE + CHUNK_HEADER_SIZE; // "WEBP" + "VP8 nnnn" OR // "WEBP" + "VP8Lnnnn" assert(data != NULL); assert(data_size != NULL); assert(chunk_size != NULL); assert(is_lossless != NULL); if (*data_size < CHUNK_HEADER_SIZE) { return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data. } if (is_vp8 || is_vp8l) { // Bitstream contains VP8/VP8L header. const uint32_t size = GetLE32(data + TAG_SIZE); if ((riff_size >= minimal_size) && (size > riff_size - minimal_size)) { return VP8_STATUS_BITSTREAM_ERROR; // Inconsistent size information. } if (have_all_data && (size > *data_size - CHUNK_HEADER_SIZE)) { return VP8_STATUS_NOT_ENOUGH_DATA; // Truncated bitstream. } // Skip over CHUNK_HEADER_SIZE bytes from VP8/VP8L Header. *chunk_size = size; *data_ptr += CHUNK_HEADER_SIZE; *data_size -= CHUNK_HEADER_SIZE; *is_lossless = is_vp8l; } else { // Raw VP8/VP8L bitstream (no header). *is_lossless = VP8LCheckSignature(data, *data_size); *chunk_size = *data_size; } return VP8_STATUS_OK; }
static WebPMuxError MuxSet(WebPMux* const mux, CHUNK_INDEX idx, uint32_t nth, const WebPData* const data, int copy_data) { WebPChunk chunk; WebPMuxError err = WEBP_MUX_NOT_FOUND; assert(mux != NULL); assert(!IsWPI(kChunks[idx].id)); ChunkInit(&chunk); SWITCH_ID_LIST(IDX_VP8X, &mux->vp8x_); SWITCH_ID_LIST(IDX_ICCP, &mux->iccp_); SWITCH_ID_LIST(IDX_LOOP, &mux->loop_); SWITCH_ID_LIST(IDX_META, &mux->meta_); if (idx == IDX_UNKNOWN && data->size_ > TAG_SIZE) { // For raw-data unknown chunk, the first four bytes should be the tag to be // used for the chunk. const WebPData tmp = { data->bytes_ + TAG_SIZE, data->size_ - TAG_SIZE }; err = ChunkAssignData(&chunk, &tmp, copy_data, GetLE32(data->bytes_ + 0)); if (err == WEBP_MUX_OK) err = ChunkSetNth(&chunk, &mux->unknown_, nth); } return err; }
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 PARSE_NEED_MORE_DATA; if (memcmp(GetBuffer(mem), "RIFF", CHUNK_SIZE_BYTES) || memcmp(GetBuffer(mem) + CHUNK_HEADER_SIZE, "WEBP", CHUNK_SIZE_BYTES)) { return PARSE_ERROR; } riff_size = GetLE32(GetBuffer(mem) + TAG_SIZE); 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; if (mem->buf_size_ > mem->riff_end_) { mem->buf_size_ = mem->end_ = mem->riff_end_; } Skip(mem, RIFF_HEADER_SIZE); return PARSE_OK; }
static WEBP_INLINE uint32_t ReadLE32(MemBuffer* const mem) { const uint8_t* const data = mem->buf_ + mem->start_; const uint32_t val = GetLE32(data); Skip(mem, 4); return val; }
// Skips to the next VP8/VP8L chunk header in the data given the size of the // RIFF chunk 'riff_size'. // Returns VP8_STATUS_BITSTREAM_ERROR if any invalid chunk size is encountered, // VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and // VP8_STATUS_OK otherwise. // If an alpha chunk is found, *alpha_data and *alpha_size are set // appropriately. static VP8StatusCode ParseOptionalChunks(const uint8_t** const data, size_t* const data_size, size_t const riff_size, const uint8_t** const alpha_data, size_t* const alpha_size) { const uint8_t* buf; size_t buf_size; uint32_t total_size = TAG_SIZE + // "WEBP". CHUNK_HEADER_SIZE + // "VP8Xnnnn". VP8X_CHUNK_SIZE; // data. assert(data != NULL); assert(data_size != NULL); buf = *data; buf_size = *data_size; assert(alpha_data != NULL); assert(alpha_size != NULL); *alpha_data = NULL; *alpha_size = 0; while (1) { uint32_t chunk_size; uint32_t disk_chunk_size; // chunk_size with padding *data = buf; *data_size = buf_size; if (buf_size < CHUNK_HEADER_SIZE) { // Insufficient data. return VP8_STATUS_NOT_ENOUGH_DATA; } chunk_size = GetLE32(buf + TAG_SIZE); if (chunk_size > MAX_CHUNK_PAYLOAD) { return VP8_STATUS_BITSTREAM_ERROR; // Not a valid chunk size. } // For odd-sized chunk-payload, there's one byte padding at the end. disk_chunk_size = (CHUNK_HEADER_SIZE + chunk_size + 1) & ~1; total_size += disk_chunk_size; // Check that total bytes skipped so far does not exceed riff_size. if (riff_size > 0 && (total_size > riff_size)) { return VP8_STATUS_BITSTREAM_ERROR; // Not a valid chunk size. } // Start of a (possibly incomplete) VP8/VP8L chunk implies that we have // parsed all the optional chunks. // Note: This check must occur before the check 'buf_size < disk_chunk_size' // below to allow incomplete VP8/VP8L chunks. if (!memcmp(buf, "VP8 ", TAG_SIZE) || !memcmp(buf, "VP8L", TAG_SIZE)) { return VP8_STATUS_OK; } if (buf_size < disk_chunk_size) { // Insufficient data. return VP8_STATUS_NOT_ENOUGH_DATA; } if (!memcmp(buf, "ALPH", TAG_SIZE)) { // A valid ALPH header. *alpha_data = buf + CHUNK_HEADER_SIZE; *alpha_size = chunk_size; } // We have a full and valid chunk; skip it. buf += disk_chunk_size; buf_size -= disk_chunk_size; } }
WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data, int version) { size_t riff_size; uint32_t tag; const uint8_t* end; WebPMux* mux = NULL; WebPMuxImage* wpi = NULL; const uint8_t* data; size_t size; WebPChunk chunk; ChunkInit(&chunk); // Sanity checks. if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_MUX_ABI_VERSION)) { return NULL; // version mismatch } if (bitstream == NULL) return NULL; data = bitstream->bytes; size = bitstream->size; if (data == NULL) return NULL; if (size < RIFF_HEADER_SIZE) return NULL; if (GetLE32(data + 0) != MKFOURCC('R', 'I', 'F', 'F') || GetLE32(data + CHUNK_HEADER_SIZE) != MKFOURCC('W', 'E', 'B', 'P')) { return NULL; } mux = WebPMuxNew(); if (mux == NULL) return NULL; if (size < RIFF_HEADER_SIZE + TAG_SIZE) goto Err; tag = GetLE32(data + RIFF_HEADER_SIZE); if (tag != kChunks[IDX_VP8].tag && tag != kChunks[IDX_VP8L].tag && tag != kChunks[IDX_VP8X].tag) { goto Err; // First chunk should be VP8, VP8L or VP8X. } riff_size = SizeWithPadding(GetLE32(data + TAG_SIZE)); if (riff_size > MAX_CHUNK_PAYLOAD || riff_size > size) { goto Err; } else { if (riff_size < size) { // Redundant data after last chunk. size = riff_size; // To make sure we don't read any data beyond mux_size. } } end = data + size; data += RIFF_HEADER_SIZE; size -= RIFF_HEADER_SIZE; wpi = (WebPMuxImage*)WebPSafeMalloc(1ULL, sizeof(*wpi)); if (wpi == NULL) goto Err; MuxImageInit(wpi); // Loop over chunks. while (data != end) { size_t data_size; WebPChunkId id; WebPChunk** chunk_list; if (ChunkVerifyAndAssign(&chunk, data, size, riff_size, copy_data) != WEBP_MUX_OK) { goto Err; } data_size = ChunkDiskSize(&chunk); id = ChunkGetIdFromTag(chunk.tag_); switch (id) { case WEBP_CHUNK_ALPHA: if (wpi->alpha_ != NULL) goto Err; // Consecutive ALPH chunks. if (ChunkSetNth(&chunk, &wpi->alpha_, 1) != WEBP_MUX_OK) goto Err; wpi->is_partial_ = 1; // Waiting for a VP8 chunk. break; case WEBP_CHUNK_IMAGE: if (ChunkSetNth(&chunk, &wpi->img_, 1) != WEBP_MUX_OK) goto Err; if (!MuxImageFinalize(wpi)) goto Err; wpi->is_partial_ = 0; // wpi is completely filled. PushImage: // Add this to mux->images_ list. if (MuxImagePush(wpi, &mux->images_) != WEBP_MUX_OK) goto Err; MuxImageInit(wpi); // Reset for reading next image. break; case WEBP_CHUNK_ANMF: if (wpi->is_partial_) goto Err; // Previous wpi is still incomplete. if (!MuxImageParse(&chunk, copy_data, wpi)) goto Err; ChunkRelease(&chunk); goto PushImage; break; default: // A non-image chunk. if (wpi->is_partial_) goto Err; // Encountered a non-image chunk before // getting all chunks of an image. chunk_list = MuxGetChunkListFromId(mux, id); // List to add this chunk. if (ChunkSetNth(&chunk, chunk_list, 0) != WEBP_MUX_OK) goto Err; if (id == WEBP_CHUNK_VP8X) { // grab global specs mux->canvas_width_ = GetLE24(data + 12) + 1; mux->canvas_height_ = GetLE24(data + 15) + 1; } break; } data += data_size; size -= data_size; ChunkInit(&chunk); } // Validate mux if complete. if (MuxValidate(mux) != WEBP_MUX_OK) goto Err; MuxImageDelete(wpi); return mux; // All OK; Err: // Something bad happened. ChunkRelease(&chunk); MuxImageDelete(wpi); WebPMuxDelete(mux); return NULL; }
static ParseStatus ParseVP8X(WebPDemuxer* const dmux) { MemBuffer* const mem = &dmux->mem_; int loop_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 = GetLE32(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_ = GetByte(mem); Skip(mem, 3); // Reserved. dmux->canvas_width_ = 1 + GetLE24s(mem); dmux->canvas_height_ = 1 + GetLE24s(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_; const uint32_t fourcc = GetLE32(mem); const uint32_t chunk_size = GetLE32(mem); const uint32_t chunk_size_padded = chunk_size + (chunk_size & 1); if (chunk_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR; if (SizeIsInvalid(mem, chunk_size_padded)) return PARSE_ERROR; switch (fourcc) { case MKFOURCC('V', 'P', '8', 'X'): { return PARSE_ERROR; } case MKFOURCC('A', 'L', 'P', 'H'): case MKFOURCC('V', 'P', '8', ' '): case MKFOURCC('V', 'P', '8', 'L'): { Rewind(mem, CHUNK_HEADER_SIZE); status = ParseSingleImage(dmux); break; } case MKFOURCC('L', 'O', 'O', 'P'): { if (chunk_size_padded < LOOP_CHUNK_SIZE) return PARSE_ERROR; if (MemDataSize(mem) < chunk_size_padded) { status = PARSE_NEED_MORE_DATA; } else if (loop_chunks == 0) { ++loop_chunks; dmux->loop_count_ = GetLE16s(mem); Skip(mem, chunk_size_padded - LOOP_CHUNK_SIZE); } else { store_chunk = 0; goto Skip; } break; } case MKFOURCC('F', 'R', 'M', ' '): { status = ParseFrame(dmux, chunk_size_padded); break; } case MKFOURCC('T', 'I', 'L', 'E'): { if (dmux->num_frames_ == 0) dmux->num_frames_ = 1; status = ParseTile(dmux, chunk_size_padded); break; } case MKFOURCC('I', 'C', 'C', 'P'): { store_chunk = !!(dmux->feature_flags_ & ICCP_FLAG); goto Skip; } case MKFOURCC('M', 'E', 'T', 'A'): { store_chunk = !!(dmux->feature_flags_ & META_FLAG); goto Skip; } Skip: default: { if (chunk_size_padded <= MemDataSize(mem)) { if (store_chunk) { // Store only the chunk header and unpadded size as only the payload // will be returned to the user. if (!StoreChunk(dmux, chunk_start_offset, CHUNK_HEADER_SIZE + chunk_size)) { return PARSE_ERROR; } } Skip(mem, chunk_size_padded); } else { status = PARSE_NEED_MORE_DATA; } } } if (mem->start_ == mem->riff_end_) { break; } else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) { status = PARSE_NEED_MORE_DATA; } } while (status == PARSE_OK); return status; }
// Store image bearing chunks to 'frame'. static ParseStatus StoreFrame(int frame_num, MemBuffer* const mem, Frame* const frame) { int alpha_chunks = 0; int image_chunks = 0; int done = (MemDataSize(mem) < CHUNK_HEADER_SIZE); ParseStatus status = PARSE_OK; if (done) return PARSE_NEED_MORE_DATA; do { const size_t chunk_start_offset = mem->start_; const uint32_t fourcc = GetLE32(mem); const uint32_t payload_size = GetLE32(mem); const uint32_t payload_size_padded = payload_size + (payload_size & 1); const size_t payload_available = (payload_size_padded > MemDataSize(mem)) ? MemDataSize(mem) : payload_size_padded; const size_t chunk_size = CHUNK_HEADER_SIZE + payload_available; if (payload_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR; if (SizeIsInvalid(mem, payload_size_padded)) return PARSE_ERROR; if (payload_size_padded > MemDataSize(mem)) status = PARSE_NEED_MORE_DATA; switch (fourcc) { case MKFOURCC('A', 'L', 'P', 'H'): if (alpha_chunks == 0) { ++alpha_chunks; frame->img_components_[1].offset_ = chunk_start_offset; frame->img_components_[1].size_ = chunk_size; frame->frame_num_ = frame_num; Skip(mem, payload_available); } else { goto Done; } break; case MKFOURCC('V', 'P', '8', ' '): case MKFOURCC('V', 'P', '8', 'L'): if (image_chunks == 0) { int width = 0, height = 0; ++image_chunks; frame->img_components_[0].offset_ = chunk_start_offset; frame->img_components_[0].size_ = chunk_size; // Extract the width and height from the bitstream, tolerating // failures when the data is incomplete. if (!WebPGetInfo(mem->buf_ + frame->img_components_[0].offset_, frame->img_components_[0].size_, &width, &height) && status != PARSE_NEED_MORE_DATA) { return PARSE_ERROR; } frame->width_ = width; frame->height_ = height; frame->frame_num_ = frame_num; frame->complete_ = (status == PARSE_OK); Skip(mem, payload_available); } else { goto Done; } break; Done: default: // Restore fourcc/size when moving up one level in parsing. Rewind(mem, CHUNK_HEADER_SIZE); done = 1; break; } if (mem->start_ == mem->riff_end_) { done = 1; } else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) { status = PARSE_NEED_MORE_DATA; } } while (!done && status == PARSE_OK); return status; }