diff --git a/examples/vwebp.c b/examples/vwebp.c index 8b562a5e..e0a64fb2 100644 --- a/examples/vwebp.c +++ b/examples/vwebp.c @@ -47,7 +47,7 @@ static struct { int print_info; uint32_t flags; - uint32_t loop_count; + int loop_count; int frame_num; int frame_max; @@ -155,10 +155,10 @@ static void StartDisplay(const WebPDecBuffer* const pic) { //------------------------------------------------------------------------------ // File decoding -static int Decode(const int frame_number, uint32_t* const duration) { +static int Decode(const int frame_number, int* const duration) { WebPDecoderConfig* const config = kParams.config; WebPData *data, image_data; - uint32_t x_off = 0, y_off = 0; + int x_off = 0, y_off = 0; WebPDecBuffer* const output_buffer = &config->output; int ok = 0; @@ -192,7 +192,7 @@ static int Decode(const int frame_number, uint32_t* const duration) { static void decode_callback(int what) { if (what == 0 && !kParams.done) { - uint32_t duration = 0; + int duration = 0; if (kParams.mux != NULL) { if (!Decode(kParams.frame_num, &duration)) { kParams.decoding_error = 1; @@ -317,7 +317,7 @@ int main(int argc, char *argv[]) { // Decode first frame { - uint32_t duration; + int duration; if (!Decode(1, &duration)) goto Error; } diff --git a/examples/webpmux.c b/examples/webpmux.c index 0f3d21ae..a33a86db 100644 --- a/examples/webpmux.c +++ b/examples/webpmux.c @@ -186,7 +186,7 @@ static WebPMuxError DisplayInfo(const WebPMux* mux) { if (flag & ANIMATION_FLAG) { int nFrames; - uint32_t loop_count; + int loop_count; err = WebPMuxGetLoopCount(mux, &loop_count); RETURN_IF_ERROR("Failed to retrieve loop count\n"); printf("Loop Count : %d\n", loop_count); @@ -200,7 +200,7 @@ static WebPMuxError DisplayInfo(const WebPMux* mux) { printf("No.: x_offset y_offset duration image_size"); printf("\n"); for (i = 1; i <= nFrames; i++) { - uint32_t x_offset, y_offset, duration; + int x_offset, y_offset, duration; WebPData bitstream; err = WebPMuxGetFrame(mux, i, &bitstream, &x_offset, &y_offset, &duration); @@ -223,7 +223,7 @@ static WebPMuxError DisplayInfo(const WebPMux* mux) { printf("No.: x_offset y_offset image_size"); printf("\n"); for (i = 1; i <= nTiles; i++) { - uint32_t x_offset, y_offset; + int x_offset, y_offset; WebPData bitstream; err = WebPMuxGetTile(mux, i, &bitstream, &x_offset, &y_offset); RETURN_IF_ERROR2("Failed to retrieve tile#%d\n", i); @@ -356,13 +356,13 @@ static int WriteWebP(WebPMux* const mux, const char* filename) { return ok; } -static int ParseFrameArgs(const char* args, uint32_t* x_offset, - uint32_t* y_offset, uint32_t* duration) { +static int ParseFrameArgs(const char* args, int* const x_offset, + int* const y_offset, int* const duration) { return (sscanf(args, "+%d+%d+%d", x_offset, y_offset, duration) == 3); } -static int ParseTileArgs(const char* args, uint32_t* x_offset, - uint32_t* y_offset) { +static int ParseTileArgs(const char* args, + int* const x_offset, int* const y_offset) { return (sscanf(args, "+%d+%d", x_offset, y_offset) == 2); } @@ -690,9 +690,9 @@ static int InitializeConfig(int argc, const char* argv[], static int GetFrameTile(const WebPMux* mux, const WebPMuxConfig* config, int isFrame) { WebPData bitstream; - uint32_t x_offset = 0; - uint32_t y_offset = 0; - uint32_t duration = 0; + int x_offset = 0; + int y_offset = 0; + int duration = 0; WebPMuxError err = WEBP_MUX_OK; WebPMux* mux_single = NULL; long num = 0; @@ -741,8 +741,8 @@ static int Process(const WebPMuxConfig* config) { WebPMux* mux = NULL; WebPData webpdata; WebPData metadata, color_profile; - uint32_t x_offset = 0; - uint32_t y_offset = 0; + int x_offset = 0; + int y_offset = 0; WebPMuxError err = WEBP_MUX_OK; int index = 0; int ok = 1; @@ -804,7 +804,7 @@ static int Process(const WebPMuxConfig* config) { ErrorString(err), Err2); } } else if (feature->args_[index].subtype_ == SUBTYPE_FRM) { - uint32_t duration; + int duration; ok = ReadFileToWebPData(feature->args_[index].filename_, &webpdata); if (!ok) goto Err2; diff --git a/src/mux/demux.c b/src/mux/demux.c index a2313723..b98bccfc 100644 --- a/src/mux/demux.c +++ b/src/mux/demux.c @@ -133,17 +133,30 @@ static WEBP_INLINE uint8_t GetByte(MemBuffer* const mem) { return byte; } +// Read 16, 24 or 32 bits stored in little-endian order. +static WEBP_INLINE int ReadLE16s(const uint8_t* const data) { + return (int)(data[0] << 0) | (data[1] << 8); +} + static WEBP_INLINE int ReadLE24s(const uint8_t* const data) { - return (int)(data[0] << 0) | (data[1] << 8) | (data[2] << 16); + return ReadLE16s(data) | (data[2] << 16); } static WEBP_INLINE uint32_t ReadLE32(const uint8_t* const data) { return (uint32_t)ReadLE24s(data) | (data[3] << 24); } +// In addition to reading, skip the read bytes. +static WEBP_INLINE int GetLE16s(MemBuffer* const mem) { + const uint8_t* const data = mem->buf_ + mem->start_; + const int val = ReadLE16s(data); + Skip(mem, 2); + return val; +} + static WEBP_INLINE int GetLE24s(MemBuffer* const mem) { const uint8_t* const data = mem->buf_ + mem->start_; - const uint32_t val = ReadLE24s(data); + const int val = ReadLE24s(data); Skip(mem, 3); return val; } @@ -155,10 +168,6 @@ static WEBP_INLINE uint32_t GetLE32(MemBuffer* const mem) { return val; } -static WEBP_INLINE int GetLE32s(MemBuffer* const mem) { - return (int)GetLE32(mem); -} - // ----------------------------------------------------------------------------- // Secondary chunk parsing @@ -289,12 +298,15 @@ static ParseStatus ParseFrame( NewFrame(mem, min_size, FRAME_CHUNK_SIZE, frame_chunk_size, &frame); if (status != PARSE_OK) return status; - frame->x_offset_ = GetLE32s(mem); - frame->y_offset_ = GetLE32s(mem); - frame->width_ = GetLE32s(mem); - frame->height_ = GetLE32s(mem); - frame->duration_ = GetLE32s(mem); + frame->x_offset_ = 2 * GetLE24s(mem); + frame->y_offset_ = 2 * GetLE24s(mem); + frame->width_ = 1 + GetLE24s(mem); + frame->height_ = 1 + GetLE24s(mem); + frame->duration_ = 1 + GetLE24s(mem); Skip(mem, frame_chunk_size - FRAME_CHUNK_SIZE); // skip any trailing data. + if (frame->width_ * (uint64_t)frame->height_ >= MAX_IMAGE_AREA) { + return PARSE_ERROR; + } // Store a (potentially partial) frame only if the animation flag is set // and there is some data in 'frame'. @@ -326,8 +338,8 @@ static ParseStatus ParseTile(WebPDemuxer* const dmux, if (status != PARSE_OK) return status; frame->is_tile_ = 1; - frame->x_offset_ = GetLE32s(mem); - frame->y_offset_ = GetLE32s(mem); + frame->x_offset_ = 2 * GetLE24s(mem); + frame->y_offset_ = 2 * GetLE24s(mem); Skip(mem, tile_chunk_size - TILE_CHUNK_SIZE); // skip any trailing data. // Store a (potentially partial) tile only if the tile flag is set @@ -481,7 +493,7 @@ static ParseStatus ParseVP8X(WebPDemuxer* const dmux) { status = PARSE_NEED_MORE_DATA; } else if (loop_chunks == 0) { ++loop_chunks; - dmux->loop_count_ = GetLE32s(mem); + dmux->loop_count_ = GetLE16s(mem); Skip(mem, chunk_size_padded - LOOP_CHUNK_SIZE); } else { store_chunk = 0; diff --git a/src/mux/muxedit.c b/src/mux/muxedit.c index 0c169c9d..8e163a97 100644 --- a/src/mux/muxedit.c +++ b/src/mux/muxedit.c @@ -19,11 +19,11 @@ extern "C" { // Object to store metadata about images. typedef struct { - uint32_t x_offset_; - uint32_t y_offset_; - uint32_t duration_; - uint32_t width_; - uint32_t height_; + int x_offset_; + int y_offset_; + int duration_; + int width_; + int height_; } WebPImageInfo; //------------------------------------------------------------------------------ @@ -115,8 +115,8 @@ static WebPMuxError MuxAddChunk(WebPMux* const mux, uint32_t nth, uint32_t tag, // Create data for frame/tile given image data, offsets and duration. static WebPMuxError CreateFrameTileData(const WebPData* const image, - uint32_t x_offset, uint32_t y_offset, - uint32_t duration, int is_lossless, + int x_offset, int y_offset, + int duration, int is_lossless, int is_frame, WebPData* const frame_tile) { int width; @@ -129,16 +129,19 @@ static WebPMuxError CreateFrameTileData(const WebPData* const image, VP8GetInfo(image->bytes_, image->size_, image->size_, &width, &height); if (!ok) return WEBP_MUX_INVALID_ARGUMENT; + assert(width > 0 && height > 0 && duration > 0); + // Note: assertion on upper bounds is done in PutLE24(). + frame_tile_bytes = (uint8_t*)malloc(frame_tile_size); if (frame_tile_bytes == NULL) return WEBP_MUX_MEMORY_ERROR; - PutLE32(frame_tile_bytes + 0, x_offset); - PutLE32(frame_tile_bytes + 4, y_offset); + PutLE24(frame_tile_bytes + 0, x_offset / 2); + PutLE24(frame_tile_bytes + 3, y_offset / 2); if (is_frame) { - PutLE32(frame_tile_bytes + 8, (uint32_t)width); - PutLE32(frame_tile_bytes + 12, (uint32_t)height); - PutLE32(frame_tile_bytes + 16, duration); + PutLE24(frame_tile_bytes + 6, width - 1); + PutLE24(frame_tile_bytes + 9, height - 1); + PutLE24(frame_tile_bytes + 12, duration - 1); } frame_tile->bytes_ = frame_tile_bytes; @@ -300,11 +303,12 @@ WebPMuxError WebPMuxSetColorProfile(WebPMux* const mux, return MuxSet(mux, IDX_ICCP, 1, color_profile, copy_data); } -WebPMuxError WebPMuxSetLoopCount(WebPMux* const mux, uint32_t loop_count) { +WebPMuxError WebPMuxSetLoopCount(WebPMux* const mux, int loop_count) { WebPMuxError err; uint8_t* data = NULL; if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT; + if (loop_count >= MAX_LOOP_COUNT) return WEBP_MUX_INVALID_ARGUMENT; // Delete the existing LOOP chunk(s). err = DeleteLoopCount(mux); @@ -314,7 +318,7 @@ WebPMuxError WebPMuxSetLoopCount(WebPMux* const mux, uint32_t loop_count) { data = (uint8_t*)malloc(kChunks[IDX_LOOP].size); if (data == NULL) return WEBP_MUX_MEMORY_ERROR; - PutLE32(data, loop_count); + PutLE16(data, loop_count); err = MuxAddChunk(mux, 1, kChunks[IDX_LOOP].tag, data, kChunks[IDX_LOOP].size, 1); free(data); @@ -322,8 +326,8 @@ WebPMuxError WebPMuxSetLoopCount(WebPMux* const mux, uint32_t loop_count) { } static WebPMuxError MuxPushFrameTileInternal( - WebPMux* const mux, const WebPData* const bitstream, uint32_t x_offset, - uint32_t y_offset, uint32_t duration, int copy_data, uint32_t tag) { + WebPMux* const mux, const WebPData* const bitstream, int x_offset, + int y_offset, int duration, int copy_data, uint32_t tag) { WebPChunk chunk; WebPData image; WebPData alpha; @@ -334,10 +338,20 @@ static WebPMuxError MuxPushFrameTileInternal( int is_lossless; int image_tag; + // Sanity checks. if (mux == NULL || bitstream == NULL || bitstream->bytes_ == NULL || bitstream->size_ > MAX_CHUNK_PAYLOAD) { return WEBP_MUX_INVALID_ARGUMENT; } + if (x_offset < 0 || x_offset >= MAX_POSITION_OFFSET || + y_offset < 0 || y_offset >= MAX_POSITION_OFFSET || + duration <= 0 || duration > MAX_DURATION) { + return WEBP_MUX_INVALID_ARGUMENT; + } + + // Snap offsets to even positions. + x_offset &= ~1; + y_offset &= ~1; // If given data is for a whole webp file, // extract only the VP8/VP8L data from it. @@ -394,15 +408,15 @@ static WebPMuxError MuxPushFrameTileInternal( WebPMuxError WebPMuxPushFrame(WebPMux* const mux, const WebPData* const bitstream, - uint32_t x_offset, uint32_t y_offset, - uint32_t duration, int copy_data) { + int x_offset, int y_offset, + int duration, int copy_data) { return MuxPushFrameTileInternal(mux, bitstream, x_offset, y_offset, duration, copy_data, kChunks[IDX_FRAME].tag); } WebPMuxError WebPMuxPushTile(WebPMux* const mux, const WebPData* const bitstream, - uint32_t x_offset, uint32_t y_offset, + int x_offset, int y_offset, int copy_data) { return MuxPushFrameTileInternal(mux, bitstream, x_offset, y_offset, 1 /* unused duration */, copy_data, @@ -454,9 +468,8 @@ WebPMuxError WebPMuxDeleteTile(WebPMux* const mux, uint32_t nth) { // Assembly of the WebP RIFF file. static WebPMuxError GetFrameTileInfo(const WebPChunk* const frame_tile_chunk, - uint32_t* const x_offset, - uint32_t* const y_offset, - uint32_t* const duration) { + int* const x_offset, int* const y_offset, + int* const duration) { const uint32_t tag = frame_tile_chunk->tag_; const int is_frame = (tag == kChunks[IDX_FRAME].tag); const WebPData* const data = &frame_tile_chunk->data_; @@ -466,9 +479,9 @@ static WebPMuxError GetFrameTileInfo(const WebPChunk* const frame_tile_chunk, assert(tag == kChunks[IDX_FRAME].tag || tag == kChunks[IDX_TILE].tag); if (data->size_ != expected_data_size) return WEBP_MUX_INVALID_ARGUMENT; - *x_offset = GetLE32(data->bytes_ + 0); - *y_offset = GetLE32(data->bytes_ + 4); - if (is_frame) *duration = GetLE32(data->bytes_ + 16); + *x_offset = 2 * GetLE24(data->bytes_ + 0); + *y_offset = 2 * GetLE24(data->bytes_ + 3); + if (is_frame) *duration = 1 + GetLE24(data->bytes_ + 12); return WEBP_MUX_OK; } @@ -497,7 +510,7 @@ static WebPMuxError GetImageInfo(const WebPMuxImage* const wpi, const WebPChunk* const image_chunk = wpi->img_; const WebPChunk* const frame_tile_chunk = wpi->header_; WebPMuxError err; - uint32_t x_offset, y_offset, duration; + int x_offset, y_offset, duration; int width, height; memset(image_info, 0, sizeof(*image_info)); @@ -520,8 +533,8 @@ static WebPMuxError GetImageInfo(const WebPMuxImage* const wpi, } static WebPMuxError GetImageCanvasWidthHeight( - const WebPMux* const mux, - uint32_t flags, uint32_t* width, uint32_t* height) { + const WebPMux* const mux, uint32_t flags, + int* const width, int* const height) { WebPMuxImage* wpi = NULL; assert(mux != NULL); assert(width && height); @@ -531,25 +544,21 @@ static WebPMuxError GetImageCanvasWidthHeight( assert(wpi->img_ != NULL); if (wpi->next_) { - uint32_t max_x = 0; - uint32_t max_y = 0; - uint64_t image_area = 0; + int max_x = 0; + int max_y = 0; + int64_t image_area = 0; // Aggregate the bounding box for animation frames & tiled images. for (; wpi != NULL; wpi = wpi->next_) { WebPImageInfo image_info; const WebPMuxError err = GetImageInfo(wpi, &image_info); - const uint32_t max_x_pos = image_info.x_offset_ + image_info.width_; - const uint32_t max_y_pos = image_info.y_offset_ + image_info.height_; + const int max_x_pos = image_info.x_offset_ + image_info.width_; + const int max_y_pos = image_info.y_offset_ + image_info.height_; if (err != WEBP_MUX_OK) return err; + assert(image_info.x_offset_ < MAX_POSITION_OFFSET); + assert(image_info.y_offset_ < MAX_POSITION_OFFSET); - if (max_x_pos < image_info.x_offset_) { // Overflow occurred. - return WEBP_MUX_INVALID_ARGUMENT; - } - if (max_y_pos < image_info.y_offset_) { // Overflow occurred. - return WEBP_MUX_INVALID_ARGUMENT; - } - if (max_x_pos > max_x) max_x = max_x_pos; - if (max_y_pos > max_y) max_y = max_y_pos; + if (max_x_pos > max_x) max_x = max_x_pos; + if (max_y_pos > max_y) max_y = max_y_pos; image_area += (image_info.width_ * image_info.height_); } *width = max_x; @@ -583,8 +592,8 @@ static WebPMuxError GetImageCanvasWidthHeight( static WebPMuxError CreateVP8XChunk(WebPMux* const mux) { WebPMuxError err = WEBP_MUX_OK; uint32_t flags = 0; - uint32_t width = 0; - uint32_t height = 0; + int width = 0; + int height = 0; uint8_t data[VP8X_CHUNK_SIZE]; const size_t data_size = VP8X_CHUNK_SIZE; const WebPMuxImage* images = NULL; @@ -632,6 +641,13 @@ static WebPMuxError CreateVP8XChunk(WebPMux* const mux) { err = GetImageCanvasWidthHeight(mux, flags, &width, &height); if (err != WEBP_MUX_OK) return err; + if (width <= 0 || height <= 0) { + return WEBP_MUX_INVALID_ARGUMENT; + } + if (width > MAX_CANVAS_SIZE || height > MAX_CANVAS_SIZE) { + return WEBP_MUX_INVALID_ARGUMENT; + } + if (MuxHasLosslessImages(images)) { // We have a file with a VP8X chunk having some lossless images. // As lossless images implicitly contain alpha, force ALPHA_FLAG to be true. diff --git a/src/mux/muxi.h b/src/mux/muxi.h index 96b166e3..61a46312 100644 --- a/src/mux/muxi.h +++ b/src/mux/muxi.h @@ -80,8 +80,7 @@ typedef enum { #define NIL_TAG 0x00000000u // To signal void chunk. -#define mktag(c1, c2, c3, c4) \ - ((uint32_t)c1 | (c2 << 8) | (c3 << 16) | (c4 << 24)) +#define MKFOURCC(a, b, c, d) ((uint32_t)(a) | (b) << 8 | (c) << 16 | (d) << 24) typedef struct { uint32_t tag; @@ -94,23 +93,35 @@ extern const ChunkInfo kChunks[IDX_LAST_CHUNK]; //------------------------------------------------------------------------------ // Helper functions. +// Read 16, 24 or 32 bits stored in little-endian order. +static WEBP_INLINE int GetLE16(const uint8_t* const data) { + return (int)(data[0] << 0) | (data[1] << 8); +} + +static WEBP_INLINE int GetLE24(const uint8_t* const data) { + return GetLE16(data) | (data[2] << 16); +} + static WEBP_INLINE uint32_t GetLE32(const uint8_t* const data) { - return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); + return (uint32_t)GetLE16(data) | (GetLE16(data + 2) << 16); } -static WEBP_INLINE void PutLE16(uint8_t* const data, uint32_t val) { - data[0] = (val >> 0) & 0xff; - data[1] = (val >> 8) & 0xff; +// Store 16, 24 or 32 bits in little-endian order. +static WEBP_INLINE void PutLE16(uint8_t* const data, int val) { + assert(val < (1 << 16)); + data[0] = (val >> 0); + data[1] = (val >> 8); } -static WEBP_INLINE void PutLE24(uint8_t* const data, uint32_t val) { - PutLE16(data, val); +static WEBP_INLINE void PutLE24(uint8_t* const data, int val) { + assert(val < (1 << 24)); + PutLE16(data, val & 0xffff); data[2] = (val >> 16); } static WEBP_INLINE void PutLE32(uint8_t* const data, uint32_t val) { - PutLE16(data, val); - PutLE16(data + 2, val >> 16); + PutLE16(data, (int)(val & 0xffff)); + PutLE16(data + 2, (int)(val >> 16)); } static WEBP_INLINE size_t SizeWithPadding(size_t chunk_size) { diff --git a/src/mux/muxinternal.c b/src/mux/muxinternal.c index 46c4f8b1..e8f763f7 100644 --- a/src/mux/muxinternal.c +++ b/src/mux/muxinternal.c @@ -20,16 +20,16 @@ extern "C" { #define UNDEFINED_CHUNK_SIZE (-1) const ChunkInfo kChunks[] = { - { mktag('V', 'P', '8', 'X'), WEBP_CHUNK_VP8X, VP8X_CHUNK_SIZE }, - { mktag('I', 'C', 'C', 'P'), WEBP_CHUNK_ICCP, UNDEFINED_CHUNK_SIZE }, - { mktag('L', 'O', 'O', 'P'), WEBP_CHUNK_LOOP, LOOP_CHUNK_SIZE }, - { mktag('F', 'R', 'M', ' '), WEBP_CHUNK_FRAME, FRAME_CHUNK_SIZE }, - { mktag('T', 'I', 'L', 'E'), WEBP_CHUNK_TILE, TILE_CHUNK_SIZE }, - { mktag('A', 'L', 'P', 'H'), WEBP_CHUNK_ALPHA, UNDEFINED_CHUNK_SIZE }, - { mktag('V', 'P', '8', ' '), WEBP_CHUNK_IMAGE, UNDEFINED_CHUNK_SIZE }, - { mktag('V', 'P', '8', 'L'), WEBP_CHUNK_IMAGE, UNDEFINED_CHUNK_SIZE }, - { mktag('M', 'E', 'T', 'A'), WEBP_CHUNK_META, UNDEFINED_CHUNK_SIZE }, - { mktag('U', 'N', 'K', 'N'), WEBP_CHUNK_UNKNOWN, UNDEFINED_CHUNK_SIZE }, + { MKFOURCC('V', 'P', '8', 'X'), WEBP_CHUNK_VP8X, VP8X_CHUNK_SIZE }, + { MKFOURCC('I', 'C', 'C', 'P'), WEBP_CHUNK_ICCP, UNDEFINED_CHUNK_SIZE }, + { MKFOURCC('L', 'O', 'O', 'P'), WEBP_CHUNK_LOOP, LOOP_CHUNK_SIZE }, + { MKFOURCC('F', 'R', 'M', ' '), WEBP_CHUNK_FRAME, FRAME_CHUNK_SIZE }, + { MKFOURCC('T', 'I', 'L', 'E'), WEBP_CHUNK_TILE, TILE_CHUNK_SIZE }, + { MKFOURCC('A', 'L', 'P', 'H'), WEBP_CHUNK_ALPHA, UNDEFINED_CHUNK_SIZE }, + { MKFOURCC('V', 'P', '8', ' '), WEBP_CHUNK_IMAGE, UNDEFINED_CHUNK_SIZE }, + { MKFOURCC('V', 'P', '8', 'L'), WEBP_CHUNK_IMAGE, UNDEFINED_CHUNK_SIZE }, + { MKFOURCC('M', 'E', 'T', 'A'), WEBP_CHUNK_META, UNDEFINED_CHUNK_SIZE }, + { MKFOURCC('U', 'N', 'K', 'N'), WEBP_CHUNK_UNKNOWN, UNDEFINED_CHUNK_SIZE }, { NIL_TAG, WEBP_CHUNK_NIL, UNDEFINED_CHUNK_SIZE } }; @@ -426,10 +426,10 @@ int MuxHasLosslessImages(const WebPMuxImage* images) { } uint8_t* MuxEmitRiffHeader(uint8_t* const data, size_t size) { - PutLE32(data + 0, mktag('R', 'I', 'F', 'F')); + PutLE32(data + 0, MKFOURCC('R', 'I', 'F', 'F')); PutLE32(data + TAG_SIZE, (uint32_t)size - CHUNK_HEADER_SIZE); assert(size == (uint32_t)size); - PutLE32(data + TAG_SIZE + CHUNK_SIZE_BYTES, mktag('W', 'E', 'B', 'P')); + PutLE32(data + TAG_SIZE + CHUNK_SIZE_BYTES, MKFOURCC('W', 'E', 'B', 'P')); return data + RIFF_HEADER_SIZE; } diff --git a/src/mux/muxread.c b/src/mux/muxread.c index dd94fab4..7f8ba5e6 100644 --- a/src/mux/muxread.c +++ b/src/mux/muxread.c @@ -97,8 +97,8 @@ WebPMux* WebPMuxCreateInternal(const WebPData* const bitstream, int copy_data, if (data == NULL) return NULL; if (size < RIFF_HEADER_SIZE) return NULL; - if (GetLE32(data + 0) != mktag('R', 'I', 'F', 'F') || - GetLE32(data + CHUNK_HEADER_SIZE) != mktag('W', 'E', 'B', 'P')) { + if (GetLE32(data + 0) != MKFOURCC('R', 'I', 'F', 'F') || + GetLE32(data + CHUNK_HEADER_SIZE) != MKFOURCC('W', 'E', 'B', 'P')) { return NULL; } @@ -213,13 +213,13 @@ WebPMuxError WebPMuxGetFeatures(const WebPMux* const mux, uint32_t* flags) { return WEBP_MUX_OK; } -static uint8_t* EmitVP8XChunk(uint8_t* const dst, uint32_t width, - uint32_t height, uint32_t flags) { +static uint8_t* EmitVP8XChunk(uint8_t* const dst, int width, + int height, uint32_t flags) { const size_t vp8x_size = CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE; assert(width >= 1 && height >= 1); assert(width <= MAX_CANVAS_SIZE && height <= MAX_CANVAS_SIZE); assert(width * (uint64_t)height < MAX_IMAGE_AREA); - PutLE32(dst, mktag('V', 'P', '8', 'X')); + PutLE32(dst, MKFOURCC('V', 'P', '8', 'X')); PutLE32(dst + TAG_SIZE, VP8X_CHUNK_SIZE); PutLE32(dst + CHUNK_HEADER_SIZE, flags); PutLE24(dst + CHUNK_HEADER_SIZE + 4, width - 1); @@ -301,7 +301,7 @@ WebPMuxError WebPMuxGetColorProfile(const WebPMux* const mux, } WebPMuxError WebPMuxGetLoopCount(const WebPMux* const mux, - uint32_t* loop_count) { + int* const loop_count) { WebPData image; WebPMuxError err; @@ -310,14 +310,15 @@ WebPMuxError WebPMuxGetLoopCount(const WebPMux* const mux, err = MuxGet(mux, IDX_LOOP, 1, &image); if (err != WEBP_MUX_OK) return err; if (image.size_ < kChunks[WEBP_CHUNK_LOOP].size) return WEBP_MUX_BAD_DATA; - *loop_count = GetLE32(image.bytes_); + *loop_count = GetLE16(image.bytes_); return WEBP_MUX_OK; } static WebPMuxError MuxGetFrameTileInternal( const WebPMux* const mux, uint32_t nth, WebPData* const bitstream, - uint32_t* x_offset, uint32_t* y_offset, uint32_t* duration, uint32_t tag) { + int* const x_offset, int* const y_offset, int* const duration, + uint32_t tag) { const WebPData* frame_tile_data; WebPMuxError err; WebPMuxImage* wpi; @@ -340,27 +341,24 @@ static WebPMuxError MuxGetFrameTileInternal( frame_tile_data = &wpi->header_->data_; if (frame_tile_data->size_ < kChunks[idx].size) return WEBP_MUX_BAD_DATA; - *x_offset = GetLE32(frame_tile_data->bytes_ + 0); - *y_offset = GetLE32(frame_tile_data->bytes_ + 4); - if (is_frame) *duration = GetLE32(frame_tile_data->bytes_ + 16); + *x_offset = 2 * GetLE24(frame_tile_data->bytes_ + 0); + *y_offset = 2 * GetLE24(frame_tile_data->bytes_ + 3); + if (is_frame) *duration = 1 + GetLE24(frame_tile_data->bytes_ + 12); return SynthesizeBitstream(wpi, bitstream); } WebPMuxError WebPMuxGetFrame(const WebPMux* const mux, uint32_t nth, - WebPData* const bitstream, - uint32_t* x_offset, uint32_t* y_offset, - uint32_t* duration) { - return MuxGetFrameTileInternal(mux, nth, bitstream, - x_offset, y_offset, duration, - kChunks[IDX_FRAME].tag); + WebPData* const bitstream, int* const x_offset, + int* const y_offset, int* const duration) { + return MuxGetFrameTileInternal(mux, nth, bitstream, x_offset, y_offset, + duration, kChunks[IDX_FRAME].tag); } WebPMuxError WebPMuxGetTile(const WebPMux* const mux, uint32_t nth, WebPData* const bitstream, - uint32_t* x_offset, uint32_t* y_offset) { - return MuxGetFrameTileInternal(mux, nth, bitstream, - x_offset, y_offset, NULL, + int* const x_offset, int* const y_offset) { + return MuxGetFrameTileInternal(mux, nth, bitstream, x_offset, y_offset, NULL, kChunks[IDX_TILE].tag); } diff --git a/src/webp/format_constants.h b/src/webp/format_constants.h index 02281509..5eb0feec 100644 --- a/src/webp/format_constants.h +++ b/src/webp/format_constants.h @@ -62,9 +62,9 @@ typedef enum { #define CHUNK_SIZE_BYTES 4 // Size needed to store chunk's size. #define CHUNK_HEADER_SIZE 8 // Size of a chunk header. #define RIFF_HEADER_SIZE 12 // Size of the RIFF header ("RIFFnnnnWEBP"). -#define FRAME_CHUNK_SIZE 20 // Size of a FRM chunk. -#define LOOP_CHUNK_SIZE 4 // Size of a LOOP chunk. -#define TILE_CHUNK_SIZE 8 // Size of a TILE chunk. +#define FRAME_CHUNK_SIZE 15 // Size of a FRM chunk. +#define LOOP_CHUNK_SIZE 2 // Size of a LOOP chunk. +#define TILE_CHUNK_SIZE 6 // Size of a TILE chunk. #define VP8X_CHUNK_SIZE 10 // Size of a VP8X chunk. #define TILING_FLAG_BIT 0x01 // Set if tiles are possibly used. @@ -76,9 +76,9 @@ typedef enum { #define MAX_CANVAS_SIZE (1 << 24) // 24-bit max for VP8X width/height. #define MAX_IMAGE_AREA (1ULL << 32) // 32-bit max for width x height. -#define MAX_LOOP_COUNT (1 << 16) // maximum value for loop-count -#define MAX_DURATION (1U << 24) // maximum duration -#define MAX_POSITION_OFFSET (1U << 24) // maximum frame/tile x/y offset +#define MAX_LOOP_COUNT (1 << 16) // maximum value for loop-count +#define MAX_DURATION (1 << 24) // maximum duration +#define MAX_POSITION_OFFSET (1 << 24) // maximum frame/tile x/y offset // Maximum chunk payload is such that adding the header and padding won't // overflow a uint32_t. diff --git a/src/webp/mux.h b/src/webp/mux.h index 25ad3113..c028f019 100644 --- a/src/webp/mux.h +++ b/src/webp/mux.h @@ -268,6 +268,8 @@ WEBP_EXTERN(WebPMuxError) WebPMuxDeleteColorProfile(WebPMux* const mux); // Animation. // Adds an animation frame at the end of the mux object. +// Note: as WebP only supports even offsets, any odd offset will be snapped to +// an even location using: offset &= ~1 // Parameters: // mux - (in/out) object to which an animation frame is to be added // bitstream - (in) the image data corresponding to the frame. It can either @@ -284,15 +286,13 @@ WEBP_EXTERN(WebPMuxError) WebPMuxDeleteColorProfile(WebPMux* const mux); // WEBP_MUX_OK - on success. WEBP_EXTERN(WebPMuxError) WebPMuxPushFrame( WebPMux* const mux, const WebPData* const bitstream, - uint32_t x_offset, uint32_t y_offset, uint32_t duration, - int copy_data); + int x_offset, int y_offset, int duration, int copy_data); // TODO(urvang): Create a struct as follows to reduce argument list size: // typedef struct { -// WebPData image; -// WebPData alpha; -// uint32_t x_offset, y_offset; -// uint32_t duration; +// WebPData bitstream; +// int x_offset, y_offset; +// int duration; // } FrameInfo; // Gets the nth animation frame from the mux object. @@ -315,7 +315,7 @@ WEBP_EXTERN(WebPMuxError) WebPMuxPushFrame( // WEBP_MUX_OK - on success. WEBP_EXTERN(WebPMuxError) WebPMuxGetFrame( const WebPMux* const mux, uint32_t nth, WebPData* const bitstream, - uint32_t* x_offset, uint32_t* y_offset, uint32_t* duration); + int* const x_offset, int* const y_offset, int* const duration); // Deletes an animation frame from the mux object. // nth=0 has a special meaning - last position. @@ -340,7 +340,7 @@ WEBP_EXTERN(WebPMuxError) WebPMuxDeleteFrame(WebPMux* const mux, uint32_t nth); // WEBP_MUX_MEMORY_ERROR - on memory allocation error. // WEBP_MUX_OK - on success. WEBP_EXTERN(WebPMuxError) WebPMuxSetLoopCount(WebPMux* const mux, - uint32_t loop_count); + int loop_count); // Gets the animation loop count from the mux object. // Parameters: @@ -351,12 +351,14 @@ WEBP_EXTERN(WebPMuxError) WebPMuxSetLoopCount(WebPMux* const mux, // WEBP_MUX_NOT_FOUND - if loop chunk is not present in mux object. // WEBP_MUX_OK - on success. WEBP_EXTERN(WebPMuxError) WebPMuxGetLoopCount(const WebPMux* const mux, - uint32_t* loop_count); + int* const loop_count); //------------------------------------------------------------------------------ // Tiling. // Adds a tile at the end of the mux object. +// Note: as WebP only supports even offsets, any odd offset will be snapped to +// an even location using: offset &= ~1 // Parameters: // mux - (in/out) object to which a tile is to be added. // bitstream - (in) the image data corresponding to the frame. It can either @@ -372,7 +374,7 @@ WEBP_EXTERN(WebPMuxError) WebPMuxGetLoopCount(const WebPMux* const mux, // WEBP_MUX_OK - on success. WEBP_EXTERN(WebPMuxError) WebPMuxPushTile( WebPMux* const mux, const WebPData* const bitstream, - uint32_t x_offset, uint32_t y_offset, int copy_data); + int x_offset, int y_offset, int copy_data); // Gets the nth tile from the mux object. // The content of 'bitstream' is allocated using malloc(), and NOT @@ -393,7 +395,7 @@ WEBP_EXTERN(WebPMuxError) WebPMuxPushTile( // WEBP_MUX_OK - on success. WEBP_EXTERN(WebPMuxError) WebPMuxGetTile( const WebPMux* const mux, uint32_t nth, WebPData* const bitstream, - uint32_t* x_offset, uint32_t* y_offset); + int* const x_offset, int* const y_offset); // Deletes a tile from the mux object. // nth=0 has a special meaning - last position