Path: blob/master/thirdparty/libwebp/src/demux/demux.c
21689 views
// Copyright 2012 Google Inc. All Rights Reserved.1//2// Use of this source code is governed by a BSD-style license3// that can be found in the COPYING file in the root of the source4// tree. An additional intellectual property rights grant can be found5// in the file PATENTS. All contributing project authors may6// be found in the AUTHORS file in the root of the source tree.7// -----------------------------------------------------------------------------8//9// WebP container demux.10//1112#ifdef HAVE_CONFIG_H13#include "src/webp/config.h"14#endif1516#include <assert.h>17#include <stdlib.h>18#include <string.h>1920#include "src/utils/utils.h"21#include "src/webp/decode.h" // WebPGetFeatures22#include "src/webp/demux.h"23#include "src/webp/format_constants.h"24#include "src/webp/mux.h"25#include "src/webp/mux_types.h"26#include "src/webp/types.h"2728#define DMUX_MAJ_VERSION 129#define DMUX_MIN_VERSION 630#define DMUX_REV_VERSION 03132typedef struct {33size_t start; // start location of the data34size_t end; // end location35size_t riff_end; // riff chunk end location, can be > end.36size_t buf_size; // size of the buffer37const uint8_t* buf;38} MemBuffer;3940typedef struct {41size_t offset;42size_t size;43} ChunkData;4445typedef struct Frame {46int x_offset, y_offset;47int width, height;48int has_alpha;49int duration;50WebPMuxAnimDispose dispose_method;51WebPMuxAnimBlend blend_method;52int frame_num;53int complete; // img_components contains a full image.54ChunkData img_components[2]; // 0=VP8{,L} 1=ALPH55struct Frame* next;56} Frame;5758typedef struct Chunk {59ChunkData data;60struct Chunk* next;61} Chunk;6263struct WebPDemuxer {64MemBuffer mem;65WebPDemuxState state;66int is_ext_format;67uint32_t feature_flags;68int canvas_width, canvas_height;69int loop_count;70uint32_t bgcolor;71int num_frames;72Frame* frames;73Frame** frames_tail;74Chunk* chunks; // non-image chunks75Chunk** chunks_tail;76};7778typedef enum {79PARSE_OK,80PARSE_NEED_MORE_DATA,81PARSE_ERROR82} ParseStatus;8384typedef struct ChunkParser {85uint8_t id[4];86ParseStatus (*parse)(WebPDemuxer* const dmux);87int (*valid)(const WebPDemuxer* const dmux);88} ChunkParser;8990static ParseStatus ParseSingleImage(WebPDemuxer* const dmux);91static ParseStatus ParseVP8X(WebPDemuxer* const dmux);92static int IsValidSimpleFormat(const WebPDemuxer* const dmux);93static int IsValidExtendedFormat(const WebPDemuxer* const dmux);9495static const ChunkParser kMasterChunks[] = {96{ { 'V', 'P', '8', ' ' }, ParseSingleImage, IsValidSimpleFormat },97{ { 'V', 'P', '8', 'L' }, ParseSingleImage, IsValidSimpleFormat },98{ { 'V', 'P', '8', 'X' }, ParseVP8X, IsValidExtendedFormat },99{ { '0', '0', '0', '0' }, NULL, NULL },100};101102//------------------------------------------------------------------------------103104int WebPGetDemuxVersion(void) {105return (DMUX_MAJ_VERSION << 16) | (DMUX_MIN_VERSION << 8) | DMUX_REV_VERSION;106}107108// -----------------------------------------------------------------------------109// MemBuffer110111static int RemapMemBuffer(MemBuffer* const mem,112const uint8_t* data, size_t size) {113if (size < mem->buf_size) return 0; // can't remap to a shorter buffer!114115mem->buf = data;116mem->end = mem->buf_size = size;117return 1;118}119120static int InitMemBuffer(MemBuffer* const mem,121const uint8_t* data, size_t size) {122memset(mem, 0, sizeof(*mem));123return RemapMemBuffer(mem, data, size);124}125126// Return the remaining data size available in 'mem'.127static WEBP_INLINE size_t MemDataSize(const MemBuffer* const mem) {128return (mem->end - mem->start);129}130131// Return true if 'size' exceeds the end of the RIFF chunk.132static WEBP_INLINE int SizeIsInvalid(const MemBuffer* const mem, size_t size) {133return (size > mem->riff_end - mem->start);134}135136static WEBP_INLINE void Skip(MemBuffer* const mem, size_t size) {137mem->start += size;138}139140static WEBP_INLINE void Rewind(MemBuffer* const mem, size_t size) {141mem->start -= size;142}143144static WEBP_INLINE const uint8_t* GetBuffer(MemBuffer* const mem) {145return mem->buf + mem->start;146}147148// Read from 'mem' and skip the read bytes.149static WEBP_INLINE uint8_t ReadByte(MemBuffer* const mem) {150const uint8_t byte = mem->buf[mem->start];151Skip(mem, 1);152return byte;153}154155static WEBP_INLINE int ReadLE16s(MemBuffer* const mem) {156const uint8_t* const data = mem->buf + mem->start;157const int val = GetLE16(data);158Skip(mem, 2);159return val;160}161162static WEBP_INLINE int ReadLE24s(MemBuffer* const mem) {163const uint8_t* const data = mem->buf + mem->start;164const int val = GetLE24(data);165Skip(mem, 3);166return val;167}168169static WEBP_INLINE uint32_t ReadLE32(MemBuffer* const mem) {170const uint8_t* const data = mem->buf + mem->start;171const uint32_t val = GetLE32(data);172Skip(mem, 4);173return val;174}175176// -----------------------------------------------------------------------------177// Secondary chunk parsing178179static void AddChunk(WebPDemuxer* const dmux, Chunk* const chunk) {180*dmux->chunks_tail = chunk;181chunk->next = NULL;182dmux->chunks_tail = &chunk->next;183}184185// Add a frame to the end of the list, ensuring the last frame is complete.186// Returns true on success, false otherwise.187static int AddFrame(WebPDemuxer* const dmux, Frame* const frame) {188const Frame* const last_frame = *dmux->frames_tail;189if (last_frame != NULL && !last_frame->complete) return 0;190191*dmux->frames_tail = frame;192frame->next = NULL;193dmux->frames_tail = &frame->next;194return 1;195}196197static void SetFrameInfo(size_t start_offset, size_t size,198int frame_num, int complete,199const WebPBitstreamFeatures* const features,200Frame* const frame) {201frame->img_components[0].offset = start_offset;202frame->img_components[0].size = size;203frame->width = features->width;204frame->height = features->height;205frame->has_alpha |= features->has_alpha;206frame->frame_num = frame_num;207frame->complete = complete;208}209210// Store image bearing chunks to 'frame'. 'min_size' is an optional size211// requirement, it may be zero.212static ParseStatus StoreFrame(int frame_num, uint32_t min_size,213MemBuffer* const mem, Frame* const frame) {214int alpha_chunks = 0;215int image_chunks = 0;216int done = (MemDataSize(mem) < CHUNK_HEADER_SIZE ||217MemDataSize(mem) < min_size);218ParseStatus status = PARSE_OK;219220if (done) return PARSE_NEED_MORE_DATA;221222do {223const size_t chunk_start_offset = mem->start;224const uint32_t fourcc = ReadLE32(mem);225const uint32_t payload_size = ReadLE32(mem);226uint32_t payload_size_padded;227size_t payload_available;228size_t chunk_size;229230if (payload_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;231232payload_size_padded = payload_size + (payload_size & 1);233payload_available = (payload_size_padded > MemDataSize(mem))234? MemDataSize(mem) : payload_size_padded;235chunk_size = CHUNK_HEADER_SIZE + payload_available;236if (SizeIsInvalid(mem, payload_size_padded)) return PARSE_ERROR;237if (payload_size_padded > MemDataSize(mem)) status = PARSE_NEED_MORE_DATA;238239switch (fourcc) {240case MKFOURCC('A', 'L', 'P', 'H'):241if (alpha_chunks == 0) {242++alpha_chunks;243frame->img_components[1].offset = chunk_start_offset;244frame->img_components[1].size = chunk_size;245frame->has_alpha = 1;246frame->frame_num = frame_num;247Skip(mem, payload_available);248} else {249goto Done;250}251break;252case MKFOURCC('V', 'P', '8', 'L'):253if (alpha_chunks > 0) return PARSE_ERROR; // VP8L has its own alpha254// fall through255case MKFOURCC('V', 'P', '8', ' '):256if (image_chunks == 0) {257// Extract the bitstream features, tolerating failures when the data258// is incomplete.259WebPBitstreamFeatures features;260const VP8StatusCode vp8_status =261WebPGetFeatures(mem->buf + chunk_start_offset, chunk_size,262&features);263if (status == PARSE_NEED_MORE_DATA &&264vp8_status == VP8_STATUS_NOT_ENOUGH_DATA) {265return PARSE_NEED_MORE_DATA;266} else if (vp8_status != VP8_STATUS_OK) {267// We have enough data, and yet WebPGetFeatures() failed.268return PARSE_ERROR;269}270++image_chunks;271SetFrameInfo(chunk_start_offset, chunk_size, frame_num,272status == PARSE_OK, &features, frame);273Skip(mem, payload_available);274} else {275goto Done;276}277break;278Done:279default:280// Restore fourcc/size when moving up one level in parsing.281Rewind(mem, CHUNK_HEADER_SIZE);282done = 1;283break;284}285286if (mem->start == mem->riff_end) {287done = 1;288} else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) {289status = PARSE_NEED_MORE_DATA;290}291} while (!done && status == PARSE_OK);292293return status;294}295296// Creates a new Frame if 'actual_size' is within bounds and 'mem' contains297// enough data ('min_size') to parse the payload.298// Returns PARSE_OK on success with *frame pointing to the new Frame.299// Returns PARSE_NEED_MORE_DATA with insufficient data, PARSE_ERROR otherwise.300static ParseStatus NewFrame(const MemBuffer* const mem,301uint32_t min_size, uint32_t actual_size,302Frame** frame) {303if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR;304if (actual_size < min_size) return PARSE_ERROR;305if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA;306307*frame = (Frame*)WebPSafeCalloc(1ULL, sizeof(**frame));308return (*frame == NULL) ? PARSE_ERROR : PARSE_OK;309}310311// Parse a 'ANMF' chunk and any image bearing chunks that immediately follow.312// 'frame_chunk_size' is the previously validated, padded chunk size.313static ParseStatus ParseAnimationFrame(314WebPDemuxer* const dmux, uint32_t frame_chunk_size) {315const int is_animation = !!(dmux->feature_flags & ANIMATION_FLAG);316const uint32_t anmf_payload_size = frame_chunk_size - ANMF_CHUNK_SIZE;317int added_frame = 0;318int bits;319MemBuffer* const mem = &dmux->mem;320Frame* frame;321size_t start_offset;322ParseStatus status =323NewFrame(mem, ANMF_CHUNK_SIZE, frame_chunk_size, &frame);324if (status != PARSE_OK) return status;325326frame->x_offset = 2 * ReadLE24s(mem);327frame->y_offset = 2 * ReadLE24s(mem);328frame->width = 1 + ReadLE24s(mem);329frame->height = 1 + ReadLE24s(mem);330frame->duration = ReadLE24s(mem);331bits = ReadByte(mem);332frame->dispose_method =333(bits & 1) ? WEBP_MUX_DISPOSE_BACKGROUND : WEBP_MUX_DISPOSE_NONE;334frame->blend_method = (bits & 2) ? WEBP_MUX_NO_BLEND : WEBP_MUX_BLEND;335if (frame->width * (uint64_t)frame->height >= MAX_IMAGE_AREA) {336WebPSafeFree(frame);337return PARSE_ERROR;338}339340// Store a frame only if the animation flag is set there is some data for341// this frame is available.342start_offset = mem->start;343status = StoreFrame(dmux->num_frames + 1, anmf_payload_size, mem, frame);344if (status != PARSE_ERROR && mem->start - start_offset > anmf_payload_size) {345status = PARSE_ERROR;346}347if (status != PARSE_ERROR && is_animation && frame->frame_num > 0) {348added_frame = AddFrame(dmux, frame);349if (added_frame) {350++dmux->num_frames;351} else {352status = PARSE_ERROR;353}354}355356if (!added_frame) WebPSafeFree(frame);357return status;358}359360// General chunk storage, starting with the header at 'start_offset', allowing361// the user to request the payload via a fourcc string. 'size' includes the362// header and the unpadded payload size.363// Returns true on success, false otherwise.364static int StoreChunk(WebPDemuxer* const dmux,365size_t start_offset, uint32_t size) {366Chunk* const chunk = (Chunk*)WebPSafeCalloc(1ULL, sizeof(*chunk));367if (chunk == NULL) return 0;368369chunk->data.offset = start_offset;370chunk->data.size = size;371AddChunk(dmux, chunk);372return 1;373}374375// -----------------------------------------------------------------------------376// Primary chunk parsing377378static ParseStatus ReadHeader(MemBuffer* const mem) {379const size_t min_size = RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE;380uint32_t riff_size;381382// Basic file level validation.383if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA;384if (memcmp(GetBuffer(mem), "RIFF", CHUNK_SIZE_BYTES) ||385memcmp(GetBuffer(mem) + CHUNK_HEADER_SIZE, "WEBP", CHUNK_SIZE_BYTES)) {386return PARSE_ERROR;387}388389riff_size = GetLE32(GetBuffer(mem) + TAG_SIZE);390if (riff_size < CHUNK_HEADER_SIZE) return PARSE_ERROR;391if (riff_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;392393// There's no point in reading past the end of the RIFF chunk394mem->riff_end = riff_size + CHUNK_HEADER_SIZE;395if (mem->buf_size > mem->riff_end) {396mem->buf_size = mem->end = mem->riff_end;397}398399Skip(mem, RIFF_HEADER_SIZE);400return PARSE_OK;401}402403static ParseStatus ParseSingleImage(WebPDemuxer* const dmux) {404const size_t min_size = CHUNK_HEADER_SIZE;405MemBuffer* const mem = &dmux->mem;406Frame* frame;407ParseStatus status;408int image_added = 0;409410if (dmux->frames != NULL) return PARSE_ERROR;411if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR;412if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA;413414frame = (Frame*)WebPSafeCalloc(1ULL, sizeof(*frame));415if (frame == NULL) return PARSE_ERROR;416417// For the single image case we allow parsing of a partial frame, so no418// minimum size is imposed here.419status = StoreFrame(1, 0, &dmux->mem, frame);420if (status != PARSE_ERROR) {421const int has_alpha = !!(dmux->feature_flags & ALPHA_FLAG);422// Clear any alpha when the alpha flag is missing.423if (!has_alpha && frame->img_components[1].size > 0) {424frame->img_components[1].offset = 0;425frame->img_components[1].size = 0;426frame->has_alpha = 0;427}428429// Use the frame width/height as the canvas values for non-vp8x files.430// Also, set ALPHA_FLAG if this is a lossless image with alpha.431if (!dmux->is_ext_format && frame->width > 0 && frame->height > 0) {432dmux->state = WEBP_DEMUX_PARSED_HEADER;433dmux->canvas_width = frame->width;434dmux->canvas_height = frame->height;435dmux->feature_flags |= frame->has_alpha ? ALPHA_FLAG : 0;436}437if (!AddFrame(dmux, frame)) {438status = PARSE_ERROR; // last frame was left incomplete439} else {440image_added = 1;441dmux->num_frames = 1;442}443}444445if (!image_added) WebPSafeFree(frame);446return status;447}448449static ParseStatus ParseVP8XChunks(WebPDemuxer* const dmux) {450const int is_animation = !!(dmux->feature_flags & ANIMATION_FLAG);451MemBuffer* const mem = &dmux->mem;452int anim_chunks = 0;453ParseStatus status = PARSE_OK;454455do {456int store_chunk = 1;457const size_t chunk_start_offset = mem->start;458const uint32_t fourcc = ReadLE32(mem);459const uint32_t chunk_size = ReadLE32(mem);460uint32_t chunk_size_padded;461462if (chunk_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;463464chunk_size_padded = chunk_size + (chunk_size & 1);465if (SizeIsInvalid(mem, chunk_size_padded)) return PARSE_ERROR;466467switch (fourcc) {468case MKFOURCC('V', 'P', '8', 'X'): {469return PARSE_ERROR;470}471case MKFOURCC('A', 'L', 'P', 'H'):472case MKFOURCC('V', 'P', '8', ' '):473case MKFOURCC('V', 'P', '8', 'L'): {474// check that this isn't an animation (all frames should be in an ANMF).475if (anim_chunks > 0 || is_animation) return PARSE_ERROR;476477Rewind(mem, CHUNK_HEADER_SIZE);478status = ParseSingleImage(dmux);479break;480}481case MKFOURCC('A', 'N', 'I', 'M'): {482if (chunk_size_padded < ANIM_CHUNK_SIZE) return PARSE_ERROR;483484if (MemDataSize(mem) < chunk_size_padded) {485status = PARSE_NEED_MORE_DATA;486} else if (anim_chunks == 0) {487++anim_chunks;488dmux->bgcolor = ReadLE32(mem);489dmux->loop_count = ReadLE16s(mem);490Skip(mem, chunk_size_padded - ANIM_CHUNK_SIZE);491} else {492store_chunk = 0;493goto Skip;494}495break;496}497case MKFOURCC('A', 'N', 'M', 'F'): {498if (anim_chunks == 0) return PARSE_ERROR; // 'ANIM' precedes frames.499status = ParseAnimationFrame(dmux, chunk_size_padded);500break;501}502case MKFOURCC('I', 'C', 'C', 'P'): {503store_chunk = !!(dmux->feature_flags & ICCP_FLAG);504goto Skip;505}506case MKFOURCC('E', 'X', 'I', 'F'): {507store_chunk = !!(dmux->feature_flags & EXIF_FLAG);508goto Skip;509}510case MKFOURCC('X', 'M', 'P', ' '): {511store_chunk = !!(dmux->feature_flags & XMP_FLAG);512goto Skip;513}514Skip:515default: {516if (chunk_size_padded <= MemDataSize(mem)) {517if (store_chunk) {518// Store only the chunk header and unpadded size as only the payload519// will be returned to the user.520if (!StoreChunk(dmux, chunk_start_offset,521CHUNK_HEADER_SIZE + chunk_size)) {522return PARSE_ERROR;523}524}525Skip(mem, chunk_size_padded);526} else {527status = PARSE_NEED_MORE_DATA;528}529}530}531532if (mem->start == mem->riff_end) {533break;534} else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) {535status = PARSE_NEED_MORE_DATA;536}537} while (status == PARSE_OK);538539return status;540}541542static ParseStatus ParseVP8X(WebPDemuxer* const dmux) {543MemBuffer* const mem = &dmux->mem;544uint32_t vp8x_size;545546if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA;547548dmux->is_ext_format = 1;549Skip(mem, TAG_SIZE); // VP8X550vp8x_size = ReadLE32(mem);551if (vp8x_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;552if (vp8x_size < VP8X_CHUNK_SIZE) return PARSE_ERROR;553vp8x_size += vp8x_size & 1;554if (SizeIsInvalid(mem, vp8x_size)) return PARSE_ERROR;555if (MemDataSize(mem) < vp8x_size) return PARSE_NEED_MORE_DATA;556557dmux->feature_flags = ReadByte(mem);558Skip(mem, 3); // Reserved.559dmux->canvas_width = 1 + ReadLE24s(mem);560dmux->canvas_height = 1 + ReadLE24s(mem);561if (dmux->canvas_width * (uint64_t)dmux->canvas_height >= MAX_IMAGE_AREA) {562return PARSE_ERROR; // image final dimension is too large563}564Skip(mem, vp8x_size - VP8X_CHUNK_SIZE); // skip any trailing data.565dmux->state = WEBP_DEMUX_PARSED_HEADER;566567if (SizeIsInvalid(mem, CHUNK_HEADER_SIZE)) return PARSE_ERROR;568if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA;569570return ParseVP8XChunks(dmux);571}572573// -----------------------------------------------------------------------------574// Format validation575576static int IsValidSimpleFormat(const WebPDemuxer* const dmux) {577const Frame* const frame = dmux->frames;578if (dmux->state == WEBP_DEMUX_PARSING_HEADER) return 1;579580if (dmux->canvas_width <= 0 || dmux->canvas_height <= 0) return 0;581if (dmux->state == WEBP_DEMUX_DONE && frame == NULL) return 0;582583if (frame->width <= 0 || frame->height <= 0) return 0;584return 1;585}586587// If 'exact' is true, check that the image resolution matches the canvas.588// If 'exact' is false, check that the x/y offsets do not exceed the canvas.589static int CheckFrameBounds(const Frame* const frame, int exact,590int canvas_width, int canvas_height) {591if (exact) {592if (frame->x_offset != 0 || frame->y_offset != 0) {593return 0;594}595if (frame->width != canvas_width || frame->height != canvas_height) {596return 0;597}598} else {599if (frame->x_offset < 0 || frame->y_offset < 0) return 0;600if (frame->width + frame->x_offset > canvas_width) return 0;601if (frame->height + frame->y_offset > canvas_height) return 0;602}603return 1;604}605606static int IsValidExtendedFormat(const WebPDemuxer* const dmux) {607const int is_animation = !!(dmux->feature_flags & ANIMATION_FLAG);608const Frame* f = dmux->frames;609610if (dmux->state == WEBP_DEMUX_PARSING_HEADER) return 1;611612if (dmux->canvas_width <= 0 || dmux->canvas_height <= 0) return 0;613if (dmux->loop_count < 0) return 0;614if (dmux->state == WEBP_DEMUX_DONE && dmux->frames == NULL) return 0;615if (dmux->feature_flags & ~ALL_VALID_FLAGS) return 0; // invalid bitstream616617while (f != NULL) {618const int cur_frame_set = f->frame_num;619620// Check frame properties.621for (; f != NULL && f->frame_num == cur_frame_set; f = f->next) {622const ChunkData* const image = f->img_components;623const ChunkData* const alpha = f->img_components + 1;624625if (!is_animation && f->frame_num > 1) return 0;626627if (f->complete) {628if (alpha->size == 0 && image->size == 0) return 0;629// Ensure alpha precedes image bitstream.630if (alpha->size > 0 && alpha->offset > image->offset) {631return 0;632}633634if (f->width <= 0 || f->height <= 0) return 0;635} else {636// There shouldn't be a partial frame in a complete file.637if (dmux->state == WEBP_DEMUX_DONE) return 0;638639// Ensure alpha precedes image bitstream.640if (alpha->size > 0 && image->size > 0 &&641alpha->offset > image->offset) {642return 0;643}644// There shouldn't be any frames after an incomplete one.645if (f->next != NULL) return 0;646}647648if (f->width > 0 && f->height > 0 &&649!CheckFrameBounds(f, !is_animation,650dmux->canvas_width, dmux->canvas_height)) {651return 0;652}653}654}655return 1;656}657658// -----------------------------------------------------------------------------659// WebPDemuxer object660661static void InitDemux(WebPDemuxer* const dmux, const MemBuffer* const mem) {662dmux->state = WEBP_DEMUX_PARSING_HEADER;663dmux->loop_count = 1;664dmux->bgcolor = 0xFFFFFFFF; // White background by default.665dmux->canvas_width = -1;666dmux->canvas_height = -1;667dmux->frames_tail = &dmux->frames;668dmux->chunks_tail = &dmux->chunks;669dmux->mem = *mem;670}671672static ParseStatus CreateRawImageDemuxer(MemBuffer* const mem,673WebPDemuxer** demuxer) {674WebPBitstreamFeatures features;675const VP8StatusCode status =676WebPGetFeatures(mem->buf, mem->buf_size, &features);677*demuxer = NULL;678if (status != VP8_STATUS_OK) {679return (status == VP8_STATUS_NOT_ENOUGH_DATA) ? PARSE_NEED_MORE_DATA680: PARSE_ERROR;681}682683{684WebPDemuxer* const dmux = (WebPDemuxer*)WebPSafeCalloc(1ULL, sizeof(*dmux));685Frame* const frame = (Frame*)WebPSafeCalloc(1ULL, sizeof(*frame));686if (dmux == NULL || frame == NULL) goto Error;687InitDemux(dmux, mem);688SetFrameInfo(0, mem->buf_size, 1 /*frame_num*/, 1 /*complete*/, &features,689frame);690if (!AddFrame(dmux, frame)) goto Error;691dmux->state = WEBP_DEMUX_DONE;692dmux->canvas_width = frame->width;693dmux->canvas_height = frame->height;694dmux->feature_flags |= frame->has_alpha ? ALPHA_FLAG : 0;695dmux->num_frames = 1;696assert(IsValidSimpleFormat(dmux));697*demuxer = dmux;698return PARSE_OK;699700Error:701WebPSafeFree(dmux);702WebPSafeFree(frame);703return PARSE_ERROR;704}705}706707WebPDemuxer* WebPDemuxInternal(const WebPData* data, int allow_partial,708WebPDemuxState* state, int version) {709const ChunkParser* parser;710int partial;711ParseStatus status = PARSE_ERROR;712MemBuffer mem;713WebPDemuxer* dmux;714715if (state != NULL) *state = WEBP_DEMUX_PARSE_ERROR;716717if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DEMUX_ABI_VERSION)) return NULL;718if (data == NULL || data->bytes == NULL || data->size == 0) return NULL;719720if (!InitMemBuffer(&mem, data->bytes, data->size)) return NULL;721status = ReadHeader(&mem);722if (status != PARSE_OK) {723// If parsing of the webp file header fails attempt to handle a raw724// VP8/VP8L frame. Note 'allow_partial' is ignored in this case.725if (status == PARSE_ERROR) {726status = CreateRawImageDemuxer(&mem, &dmux);727if (status == PARSE_OK) {728if (state != NULL) *state = WEBP_DEMUX_DONE;729return dmux;730}731}732if (state != NULL) {733*state = (status == PARSE_NEED_MORE_DATA) ? WEBP_DEMUX_PARSING_HEADER734: WEBP_DEMUX_PARSE_ERROR;735}736return NULL;737}738739partial = (mem.buf_size < mem.riff_end);740if (!allow_partial && partial) return NULL;741742dmux = (WebPDemuxer*)WebPSafeCalloc(1ULL, sizeof(*dmux));743if (dmux == NULL) return NULL;744InitDemux(dmux, &mem);745746status = PARSE_ERROR;747for (parser = kMasterChunks; parser->parse != NULL; ++parser) {748if (!memcmp(parser->id, GetBuffer(&dmux->mem), TAG_SIZE)) {749status = parser->parse(dmux);750if (status == PARSE_OK) dmux->state = WEBP_DEMUX_DONE;751if (status == PARSE_NEED_MORE_DATA && !partial) status = PARSE_ERROR;752if (status != PARSE_ERROR && !parser->valid(dmux)) status = PARSE_ERROR;753if (status == PARSE_ERROR) dmux->state = WEBP_DEMUX_PARSE_ERROR;754break;755}756}757if (state != NULL) *state = dmux->state;758759if (status == PARSE_ERROR) {760WebPDemuxDelete(dmux);761return NULL;762}763return dmux;764}765766void WebPDemuxDelete(WebPDemuxer* dmux) {767Chunk* c;768Frame* f;769if (dmux == NULL) return;770771for (f = dmux->frames; f != NULL;) {772Frame* const cur_frame = f;773f = f->next;774WebPSafeFree(cur_frame);775}776for (c = dmux->chunks; c != NULL;) {777Chunk* const cur_chunk = c;778c = c->next;779WebPSafeFree(cur_chunk);780}781WebPSafeFree(dmux);782}783784// -----------------------------------------------------------------------------785786uint32_t WebPDemuxGetI(const WebPDemuxer* dmux, WebPFormatFeature feature) {787if (dmux == NULL) return 0;788789switch (feature) {790case WEBP_FF_FORMAT_FLAGS: return dmux->feature_flags;791case WEBP_FF_CANVAS_WIDTH: return (uint32_t)dmux->canvas_width;792case WEBP_FF_CANVAS_HEIGHT: return (uint32_t)dmux->canvas_height;793case WEBP_FF_LOOP_COUNT: return (uint32_t)dmux->loop_count;794case WEBP_FF_BACKGROUND_COLOR: return dmux->bgcolor;795case WEBP_FF_FRAME_COUNT: return (uint32_t)dmux->num_frames;796}797return 0;798}799800// -----------------------------------------------------------------------------801// Frame iteration802803static const Frame* GetFrame(const WebPDemuxer* const dmux, int frame_num) {804const Frame* f;805for (f = dmux->frames; f != NULL; f = f->next) {806if (frame_num == f->frame_num) break;807}808return f;809}810811static const uint8_t* GetFramePayload(const uint8_t* const mem_buf,812const Frame* const frame,813size_t* const data_size) {814*data_size = 0;815if (frame != NULL) {816const ChunkData* const image = frame->img_components;817const ChunkData* const alpha = frame->img_components + 1;818size_t start_offset = image->offset;819*data_size = image->size;820821// if alpha exists it precedes image, update the size allowing for822// intervening chunks.823if (alpha->size > 0) {824const size_t inter_size = (image->offset > 0)825? image->offset - (alpha->offset + alpha->size)826: 0;827start_offset = alpha->offset;828*data_size += alpha->size + inter_size;829}830return mem_buf + start_offset;831}832return NULL;833}834835// Create a whole 'frame' from VP8 (+ alpha) or lossless.836static int SynthesizeFrame(const WebPDemuxer* const dmux,837const Frame* const frame,838WebPIterator* const iter) {839const uint8_t* const mem_buf = dmux->mem.buf;840size_t payload_size = 0;841const uint8_t* const payload = GetFramePayload(mem_buf, frame, &payload_size);842if (payload == NULL) return 0;843assert(frame != NULL);844845iter->frame_num = frame->frame_num;846iter->num_frames = dmux->num_frames;847iter->x_offset = frame->x_offset;848iter->y_offset = frame->y_offset;849iter->width = frame->width;850iter->height = frame->height;851iter->has_alpha = frame->has_alpha;852iter->duration = frame->duration;853iter->dispose_method = frame->dispose_method;854iter->blend_method = frame->blend_method;855iter->complete = frame->complete;856iter->fragment.bytes = payload;857iter->fragment.size = payload_size;858return 1;859}860861static int SetFrame(int frame_num, WebPIterator* const iter) {862const Frame* frame;863const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_;864if (dmux == NULL || frame_num < 0) return 0;865if (frame_num > dmux->num_frames) return 0;866if (frame_num == 0) frame_num = dmux->num_frames;867868frame = GetFrame(dmux, frame_num);869if (frame == NULL) return 0;870871return SynthesizeFrame(dmux, frame, iter);872}873874int WebPDemuxGetFrame(const WebPDemuxer* dmux, int frame, WebPIterator* iter) {875if (iter == NULL) return 0;876877memset(iter, 0, sizeof(*iter));878iter->private_ = (void*)dmux;879return SetFrame(frame, iter);880}881882int WebPDemuxNextFrame(WebPIterator* iter) {883if (iter == NULL) return 0;884return SetFrame(iter->frame_num + 1, iter);885}886887int WebPDemuxPrevFrame(WebPIterator* iter) {888if (iter == NULL) return 0;889if (iter->frame_num <= 1) return 0;890return SetFrame(iter->frame_num - 1, iter);891}892893void WebPDemuxReleaseIterator(WebPIterator* iter) {894(void)iter;895}896897// -----------------------------------------------------------------------------898// Chunk iteration899900static int ChunkCount(const WebPDemuxer* const dmux, const char fourcc[4]) {901const uint8_t* const mem_buf = dmux->mem.buf;902const Chunk* c;903int count = 0;904for (c = dmux->chunks; c != NULL; c = c->next) {905const uint8_t* const header = mem_buf + c->data.offset;906if (!memcmp(header, fourcc, TAG_SIZE)) ++count;907}908return count;909}910911static const Chunk* GetChunk(const WebPDemuxer* const dmux,912const char fourcc[4], int chunk_num) {913const uint8_t* const mem_buf = dmux->mem.buf;914const Chunk* c;915int count = 0;916for (c = dmux->chunks; c != NULL; c = c->next) {917const uint8_t* const header = mem_buf + c->data.offset;918if (!memcmp(header, fourcc, TAG_SIZE)) ++count;919if (count == chunk_num) break;920}921return c;922}923924static int SetChunk(const char fourcc[4], int chunk_num,925WebPChunkIterator* const iter) {926const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_;927int count;928929if (dmux == NULL || fourcc == NULL || chunk_num < 0) return 0;930count = ChunkCount(dmux, fourcc);931if (count == 0) return 0;932if (chunk_num == 0) chunk_num = count;933934if (chunk_num <= count) {935const uint8_t* const mem_buf = dmux->mem.buf;936const Chunk* const chunk = GetChunk(dmux, fourcc, chunk_num);937iter->chunk.bytes = mem_buf + chunk->data.offset + CHUNK_HEADER_SIZE;938iter->chunk.size = chunk->data.size - CHUNK_HEADER_SIZE;939iter->num_chunks = count;940iter->chunk_num = chunk_num;941return 1;942}943return 0;944}945946int WebPDemuxGetChunk(const WebPDemuxer* dmux,947const char fourcc[4], int chunk_num,948WebPChunkIterator* iter) {949if (iter == NULL) return 0;950951memset(iter, 0, sizeof(*iter));952iter->private_ = (void*)dmux;953return SetChunk(fourcc, chunk_num, iter);954}955956int WebPDemuxNextChunk(WebPChunkIterator* iter) {957if (iter != NULL) {958const char* const fourcc =959(const char*)iter->chunk.bytes - CHUNK_HEADER_SIZE;960return SetChunk(fourcc, iter->chunk_num + 1, iter);961}962return 0;963}964965int WebPDemuxPrevChunk(WebPChunkIterator* iter) {966if (iter != NULL && iter->chunk_num > 1) {967const char* const fourcc =968(const char*)iter->chunk.bytes - CHUNK_HEADER_SIZE;969return SetChunk(fourcc, iter->chunk_num - 1, iter);970}971return 0;972}973974void WebPDemuxReleaseChunkIterator(WebPChunkIterator* iter) {975(void)iter;976}977978979