More spec/code matching in mux:

- Match offsets, duration, width/height for frames/tiles and enforce
some constraints.
- Note that this also means using 'int's instead of 'uint32_t's for
16-bit and 24-bit fields.

Change-Id: If0b229ad9fce296372d961104aa36731a3b1304b
This commit is contained in:
Urvang Joshi 2012-07-06 11:35:36 +05:30
parent 1bd7dd5097
commit 6808e69db8
9 changed files with 173 additions and 134 deletions

View File

@ -47,7 +47,7 @@ static struct {
int print_info; int print_info;
uint32_t flags; uint32_t flags;
uint32_t loop_count; int loop_count;
int frame_num; int frame_num;
int frame_max; int frame_max;
@ -155,10 +155,10 @@ static void StartDisplay(const WebPDecBuffer* const pic) {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// File decoding // 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; WebPDecoderConfig* const config = kParams.config;
WebPData *data, image_data; 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; WebPDecBuffer* const output_buffer = &config->output;
int ok = 0; int ok = 0;
@ -192,7 +192,7 @@ static int Decode(const int frame_number, uint32_t* const duration) {
static void decode_callback(int what) { static void decode_callback(int what) {
if (what == 0 && !kParams.done) { if (what == 0 && !kParams.done) {
uint32_t duration = 0; int duration = 0;
if (kParams.mux != NULL) { if (kParams.mux != NULL) {
if (!Decode(kParams.frame_num, &duration)) { if (!Decode(kParams.frame_num, &duration)) {
kParams.decoding_error = 1; kParams.decoding_error = 1;
@ -317,7 +317,7 @@ int main(int argc, char *argv[]) {
// Decode first frame // Decode first frame
{ {
uint32_t duration; int duration;
if (!Decode(1, &duration)) goto Error; if (!Decode(1, &duration)) goto Error;
} }

View File

@ -186,7 +186,7 @@ static WebPMuxError DisplayInfo(const WebPMux* mux) {
if (flag & ANIMATION_FLAG) { if (flag & ANIMATION_FLAG) {
int nFrames; int nFrames;
uint32_t loop_count; int loop_count;
err = WebPMuxGetLoopCount(mux, &loop_count); err = WebPMuxGetLoopCount(mux, &loop_count);
RETURN_IF_ERROR("Failed to retrieve loop count\n"); RETURN_IF_ERROR("Failed to retrieve loop count\n");
printf("Loop Count : %d\n", loop_count); 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("No.: x_offset y_offset duration image_size");
printf("\n"); printf("\n");
for (i = 1; i <= nFrames; i++) { for (i = 1; i <= nFrames; i++) {
uint32_t x_offset, y_offset, duration; int x_offset, y_offset, duration;
WebPData bitstream; WebPData bitstream;
err = WebPMuxGetFrame(mux, i, &bitstream, err = WebPMuxGetFrame(mux, i, &bitstream,
&x_offset, &y_offset, &duration); &x_offset, &y_offset, &duration);
@ -223,7 +223,7 @@ static WebPMuxError DisplayInfo(const WebPMux* mux) {
printf("No.: x_offset y_offset image_size"); printf("No.: x_offset y_offset image_size");
printf("\n"); printf("\n");
for (i = 1; i <= nTiles; i++) { for (i = 1; i <= nTiles; i++) {
uint32_t x_offset, y_offset; int x_offset, y_offset;
WebPData bitstream; WebPData bitstream;
err = WebPMuxGetTile(mux, i, &bitstream, &x_offset, &y_offset); err = WebPMuxGetTile(mux, i, &bitstream, &x_offset, &y_offset);
RETURN_IF_ERROR2("Failed to retrieve tile#%d\n", i); 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; return ok;
} }
static int ParseFrameArgs(const char* args, uint32_t* x_offset, static int ParseFrameArgs(const char* args, int* const x_offset,
uint32_t* y_offset, uint32_t* duration) { int* const y_offset, int* const duration) {
return (sscanf(args, "+%d+%d+%d", x_offset, y_offset, duration) == 3); return (sscanf(args, "+%d+%d+%d", x_offset, y_offset, duration) == 3);
} }
static int ParseTileArgs(const char* args, uint32_t* x_offset, static int ParseTileArgs(const char* args,
uint32_t* y_offset) { int* const x_offset, int* const y_offset) {
return (sscanf(args, "+%d+%d", x_offset, y_offset) == 2); 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, static int GetFrameTile(const WebPMux* mux,
const WebPMuxConfig* config, int isFrame) { const WebPMuxConfig* config, int isFrame) {
WebPData bitstream; WebPData bitstream;
uint32_t x_offset = 0; int x_offset = 0;
uint32_t y_offset = 0; int y_offset = 0;
uint32_t duration = 0; int duration = 0;
WebPMuxError err = WEBP_MUX_OK; WebPMuxError err = WEBP_MUX_OK;
WebPMux* mux_single = NULL; WebPMux* mux_single = NULL;
long num = 0; long num = 0;
@ -741,8 +741,8 @@ static int Process(const WebPMuxConfig* config) {
WebPMux* mux = NULL; WebPMux* mux = NULL;
WebPData webpdata; WebPData webpdata;
WebPData metadata, color_profile; WebPData metadata, color_profile;
uint32_t x_offset = 0; int x_offset = 0;
uint32_t y_offset = 0; int y_offset = 0;
WebPMuxError err = WEBP_MUX_OK; WebPMuxError err = WEBP_MUX_OK;
int index = 0; int index = 0;
int ok = 1; int ok = 1;
@ -804,7 +804,7 @@ static int Process(const WebPMuxConfig* config) {
ErrorString(err), Err2); ErrorString(err), Err2);
} }
} else if (feature->args_[index].subtype_ == SUBTYPE_FRM) { } else if (feature->args_[index].subtype_ == SUBTYPE_FRM) {
uint32_t duration; int duration;
ok = ReadFileToWebPData(feature->args_[index].filename_, ok = ReadFileToWebPData(feature->args_[index].filename_,
&webpdata); &webpdata);
if (!ok) goto Err2; if (!ok) goto Err2;

View File

@ -133,17 +133,30 @@ static WEBP_INLINE uint8_t GetByte(MemBuffer* const mem) {
return byte; 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) { 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) { static WEBP_INLINE uint32_t ReadLE32(const uint8_t* const data) {
return (uint32_t)ReadLE24s(data) | (data[3] << 24); 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) { static WEBP_INLINE int GetLE24s(MemBuffer* const mem) {
const uint8_t* const data = mem->buf_ + mem->start_; const uint8_t* const data = mem->buf_ + mem->start_;
const uint32_t val = ReadLE24s(data); const int val = ReadLE24s(data);
Skip(mem, 3); Skip(mem, 3);
return val; return val;
} }
@ -155,10 +168,6 @@ static WEBP_INLINE uint32_t GetLE32(MemBuffer* const mem) {
return val; return val;
} }
static WEBP_INLINE int GetLE32s(MemBuffer* const mem) {
return (int)GetLE32(mem);
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Secondary chunk parsing // Secondary chunk parsing
@ -289,12 +298,15 @@ static ParseStatus ParseFrame(
NewFrame(mem, min_size, FRAME_CHUNK_SIZE, frame_chunk_size, &frame); NewFrame(mem, min_size, FRAME_CHUNK_SIZE, frame_chunk_size, &frame);
if (status != PARSE_OK) return status; if (status != PARSE_OK) return status;
frame->x_offset_ = GetLE32s(mem); frame->x_offset_ = 2 * GetLE24s(mem);
frame->y_offset_ = GetLE32s(mem); frame->y_offset_ = 2 * GetLE24s(mem);
frame->width_ = GetLE32s(mem); frame->width_ = 1 + GetLE24s(mem);
frame->height_ = GetLE32s(mem); frame->height_ = 1 + GetLE24s(mem);
frame->duration_ = GetLE32s(mem); frame->duration_ = 1 + GetLE24s(mem);
Skip(mem, frame_chunk_size - FRAME_CHUNK_SIZE); // skip any trailing data. 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 // Store a (potentially partial) frame only if the animation flag is set
// and there is some data in 'frame'. // and there is some data in 'frame'.
@ -326,8 +338,8 @@ static ParseStatus ParseTile(WebPDemuxer* const dmux,
if (status != PARSE_OK) return status; if (status != PARSE_OK) return status;
frame->is_tile_ = 1; frame->is_tile_ = 1;
frame->x_offset_ = GetLE32s(mem); frame->x_offset_ = 2 * GetLE24s(mem);
frame->y_offset_ = GetLE32s(mem); frame->y_offset_ = 2 * GetLE24s(mem);
Skip(mem, tile_chunk_size - TILE_CHUNK_SIZE); // skip any trailing data. Skip(mem, tile_chunk_size - TILE_CHUNK_SIZE); // skip any trailing data.
// Store a (potentially partial) tile only if the tile flag is set // 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; status = PARSE_NEED_MORE_DATA;
} else if (loop_chunks == 0) { } else if (loop_chunks == 0) {
++loop_chunks; ++loop_chunks;
dmux->loop_count_ = GetLE32s(mem); dmux->loop_count_ = GetLE16s(mem);
Skip(mem, chunk_size_padded - LOOP_CHUNK_SIZE); Skip(mem, chunk_size_padded - LOOP_CHUNK_SIZE);
} else { } else {
store_chunk = 0; store_chunk = 0;

View File

@ -19,11 +19,11 @@ extern "C" {
// Object to store metadata about images. // Object to store metadata about images.
typedef struct { typedef struct {
uint32_t x_offset_; int x_offset_;
uint32_t y_offset_; int y_offset_;
uint32_t duration_; int duration_;
uint32_t width_; int width_;
uint32_t height_; int height_;
} WebPImageInfo; } 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. // Create data for frame/tile given image data, offsets and duration.
static WebPMuxError CreateFrameTileData(const WebPData* const image, static WebPMuxError CreateFrameTileData(const WebPData* const image,
uint32_t x_offset, uint32_t y_offset, int x_offset, int y_offset,
uint32_t duration, int is_lossless, int duration, int is_lossless,
int is_frame, int is_frame,
WebPData* const frame_tile) { WebPData* const frame_tile) {
int width; int width;
@ -129,16 +129,19 @@ static WebPMuxError CreateFrameTileData(const WebPData* const image,
VP8GetInfo(image->bytes_, image->size_, image->size_, &width, &height); VP8GetInfo(image->bytes_, image->size_, image->size_, &width, &height);
if (!ok) return WEBP_MUX_INVALID_ARGUMENT; 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); frame_tile_bytes = (uint8_t*)malloc(frame_tile_size);
if (frame_tile_bytes == NULL) return WEBP_MUX_MEMORY_ERROR; if (frame_tile_bytes == NULL) return WEBP_MUX_MEMORY_ERROR;
PutLE32(frame_tile_bytes + 0, x_offset); PutLE24(frame_tile_bytes + 0, x_offset / 2);
PutLE32(frame_tile_bytes + 4, y_offset); PutLE24(frame_tile_bytes + 3, y_offset / 2);
if (is_frame) { if (is_frame) {
PutLE32(frame_tile_bytes + 8, (uint32_t)width); PutLE24(frame_tile_bytes + 6, width - 1);
PutLE32(frame_tile_bytes + 12, (uint32_t)height); PutLE24(frame_tile_bytes + 9, height - 1);
PutLE32(frame_tile_bytes + 16, duration); PutLE24(frame_tile_bytes + 12, duration - 1);
} }
frame_tile->bytes_ = frame_tile_bytes; 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); 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; WebPMuxError err;
uint8_t* data = NULL; uint8_t* data = NULL;
if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT; 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). // Delete the existing LOOP chunk(s).
err = DeleteLoopCount(mux); err = DeleteLoopCount(mux);
@ -314,7 +318,7 @@ WebPMuxError WebPMuxSetLoopCount(WebPMux* const mux, uint32_t loop_count) {
data = (uint8_t*)malloc(kChunks[IDX_LOOP].size); data = (uint8_t*)malloc(kChunks[IDX_LOOP].size);
if (data == NULL) return WEBP_MUX_MEMORY_ERROR; 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, err = MuxAddChunk(mux, 1, kChunks[IDX_LOOP].tag, data,
kChunks[IDX_LOOP].size, 1); kChunks[IDX_LOOP].size, 1);
free(data); free(data);
@ -322,8 +326,8 @@ WebPMuxError WebPMuxSetLoopCount(WebPMux* const mux, uint32_t loop_count) {
} }
static WebPMuxError MuxPushFrameTileInternal( static WebPMuxError MuxPushFrameTileInternal(
WebPMux* const mux, const WebPData* const bitstream, uint32_t x_offset, WebPMux* const mux, const WebPData* const bitstream, int x_offset,
uint32_t y_offset, uint32_t duration, int copy_data, uint32_t tag) { int y_offset, int duration, int copy_data, uint32_t tag) {
WebPChunk chunk; WebPChunk chunk;
WebPData image; WebPData image;
WebPData alpha; WebPData alpha;
@ -334,10 +338,20 @@ static WebPMuxError MuxPushFrameTileInternal(
int is_lossless; int is_lossless;
int image_tag; int image_tag;
// Sanity checks.
if (mux == NULL || bitstream == NULL || bitstream->bytes_ == NULL || if (mux == NULL || bitstream == NULL || bitstream->bytes_ == NULL ||
bitstream->size_ > MAX_CHUNK_PAYLOAD) { bitstream->size_ > MAX_CHUNK_PAYLOAD) {
return WEBP_MUX_INVALID_ARGUMENT; 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, // If given data is for a whole webp file,
// extract only the VP8/VP8L data from it. // extract only the VP8/VP8L data from it.
@ -394,15 +408,15 @@ static WebPMuxError MuxPushFrameTileInternal(
WebPMuxError WebPMuxPushFrame(WebPMux* const mux, WebPMuxError WebPMuxPushFrame(WebPMux* const mux,
const WebPData* const bitstream, const WebPData* const bitstream,
uint32_t x_offset, uint32_t y_offset, int x_offset, int y_offset,
uint32_t duration, int copy_data) { int duration, int copy_data) {
return MuxPushFrameTileInternal(mux, bitstream, x_offset, y_offset, return MuxPushFrameTileInternal(mux, bitstream, x_offset, y_offset,
duration, copy_data, kChunks[IDX_FRAME].tag); duration, copy_data, kChunks[IDX_FRAME].tag);
} }
WebPMuxError WebPMuxPushTile(WebPMux* const mux, WebPMuxError WebPMuxPushTile(WebPMux* const mux,
const WebPData* const bitstream, const WebPData* const bitstream,
uint32_t x_offset, uint32_t y_offset, int x_offset, int y_offset,
int copy_data) { int copy_data) {
return MuxPushFrameTileInternal(mux, bitstream, x_offset, y_offset, return MuxPushFrameTileInternal(mux, bitstream, x_offset, y_offset,
1 /* unused duration */, copy_data, 1 /* unused duration */, copy_data,
@ -454,9 +468,8 @@ WebPMuxError WebPMuxDeleteTile(WebPMux* const mux, uint32_t nth) {
// Assembly of the WebP RIFF file. // Assembly of the WebP RIFF file.
static WebPMuxError GetFrameTileInfo(const WebPChunk* const frame_tile_chunk, static WebPMuxError GetFrameTileInfo(const WebPChunk* const frame_tile_chunk,
uint32_t* const x_offset, int* const x_offset, int* const y_offset,
uint32_t* const y_offset, int* const duration) {
uint32_t* const duration) {
const uint32_t tag = frame_tile_chunk->tag_; const uint32_t tag = frame_tile_chunk->tag_;
const int is_frame = (tag == kChunks[IDX_FRAME].tag); const int is_frame = (tag == kChunks[IDX_FRAME].tag);
const WebPData* const data = &frame_tile_chunk->data_; 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); assert(tag == kChunks[IDX_FRAME].tag || tag == kChunks[IDX_TILE].tag);
if (data->size_ != expected_data_size) return WEBP_MUX_INVALID_ARGUMENT; if (data->size_ != expected_data_size) return WEBP_MUX_INVALID_ARGUMENT;
*x_offset = GetLE32(data->bytes_ + 0); *x_offset = 2 * GetLE24(data->bytes_ + 0);
*y_offset = GetLE32(data->bytes_ + 4); *y_offset = 2 * GetLE24(data->bytes_ + 3);
if (is_frame) *duration = GetLE32(data->bytes_ + 16); if (is_frame) *duration = 1 + GetLE24(data->bytes_ + 12);
return WEBP_MUX_OK; 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 image_chunk = wpi->img_;
const WebPChunk* const frame_tile_chunk = wpi->header_; const WebPChunk* const frame_tile_chunk = wpi->header_;
WebPMuxError err; WebPMuxError err;
uint32_t x_offset, y_offset, duration; int x_offset, y_offset, duration;
int width, height; int width, height;
memset(image_info, 0, sizeof(*image_info)); memset(image_info, 0, sizeof(*image_info));
@ -520,8 +533,8 @@ static WebPMuxError GetImageInfo(const WebPMuxImage* const wpi,
} }
static WebPMuxError GetImageCanvasWidthHeight( static WebPMuxError GetImageCanvasWidthHeight(
const WebPMux* const mux, const WebPMux* const mux, uint32_t flags,
uint32_t flags, uint32_t* width, uint32_t* height) { int* const width, int* const height) {
WebPMuxImage* wpi = NULL; WebPMuxImage* wpi = NULL;
assert(mux != NULL); assert(mux != NULL);
assert(width && height); assert(width && height);
@ -531,23 +544,19 @@ static WebPMuxError GetImageCanvasWidthHeight(
assert(wpi->img_ != NULL); assert(wpi->img_ != NULL);
if (wpi->next_) { if (wpi->next_) {
uint32_t max_x = 0; int max_x = 0;
uint32_t max_y = 0; int max_y = 0;
uint64_t image_area = 0; int64_t image_area = 0;
// Aggregate the bounding box for animation frames & tiled images. // Aggregate the bounding box for animation frames & tiled images.
for (; wpi != NULL; wpi = wpi->next_) { for (; wpi != NULL; wpi = wpi->next_) {
WebPImageInfo image_info; WebPImageInfo image_info;
const WebPMuxError err = GetImageInfo(wpi, &image_info); const WebPMuxError err = GetImageInfo(wpi, &image_info);
const uint32_t max_x_pos = image_info.x_offset_ + image_info.width_; const int 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_y_pos = image_info.y_offset_ + image_info.height_;
if (err != WEBP_MUX_OK) return err; 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_x_pos > max_x) max_x = max_x_pos;
if (max_y_pos > max_y) max_y = max_y_pos; if (max_y_pos > max_y) max_y = max_y_pos;
image_area += (image_info.width_ * image_info.height_); image_area += (image_info.width_ * image_info.height_);
@ -583,8 +592,8 @@ static WebPMuxError GetImageCanvasWidthHeight(
static WebPMuxError CreateVP8XChunk(WebPMux* const mux) { static WebPMuxError CreateVP8XChunk(WebPMux* const mux) {
WebPMuxError err = WEBP_MUX_OK; WebPMuxError err = WEBP_MUX_OK;
uint32_t flags = 0; uint32_t flags = 0;
uint32_t width = 0; int width = 0;
uint32_t height = 0; int height = 0;
uint8_t data[VP8X_CHUNK_SIZE]; uint8_t data[VP8X_CHUNK_SIZE];
const size_t data_size = VP8X_CHUNK_SIZE; const size_t data_size = VP8X_CHUNK_SIZE;
const WebPMuxImage* images = NULL; const WebPMuxImage* images = NULL;
@ -632,6 +641,13 @@ static WebPMuxError CreateVP8XChunk(WebPMux* const mux) {
err = GetImageCanvasWidthHeight(mux, flags, &width, &height); err = GetImageCanvasWidthHeight(mux, flags, &width, &height);
if (err != WEBP_MUX_OK) return err; 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)) { if (MuxHasLosslessImages(images)) {
// We have a file with a VP8X chunk having some lossless 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. // As lossless images implicitly contain alpha, force ALPHA_FLAG to be true.

View File

@ -80,8 +80,7 @@ typedef enum {
#define NIL_TAG 0x00000000u // To signal void chunk. #define NIL_TAG 0x00000000u // To signal void chunk.
#define mktag(c1, c2, c3, c4) \ #define MKFOURCC(a, b, c, d) ((uint32_t)(a) | (b) << 8 | (c) << 16 | (d) << 24)
((uint32_t)c1 | (c2 << 8) | (c3 << 16) | (c4 << 24))
typedef struct { typedef struct {
uint32_t tag; uint32_t tag;
@ -94,23 +93,35 @@ extern const ChunkInfo kChunks[IDX_LAST_CHUNK];
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Helper functions. // 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) { 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) { // Store 16, 24 or 32 bits in little-endian order.
data[0] = (val >> 0) & 0xff; static WEBP_INLINE void PutLE16(uint8_t* const data, int val) {
data[1] = (val >> 8) & 0xff; assert(val < (1 << 16));
data[0] = (val >> 0);
data[1] = (val >> 8);
} }
static WEBP_INLINE void PutLE24(uint8_t* const data, uint32_t val) { static WEBP_INLINE void PutLE24(uint8_t* const data, int val) {
PutLE16(data, val); assert(val < (1 << 24));
PutLE16(data, val & 0xffff);
data[2] = (val >> 16); data[2] = (val >> 16);
} }
static WEBP_INLINE void PutLE32(uint8_t* const data, uint32_t val) { static WEBP_INLINE void PutLE32(uint8_t* const data, uint32_t val) {
PutLE16(data, val); PutLE16(data, (int)(val & 0xffff));
PutLE16(data + 2, val >> 16); PutLE16(data + 2, (int)(val >> 16));
} }
static WEBP_INLINE size_t SizeWithPadding(size_t chunk_size) { static WEBP_INLINE size_t SizeWithPadding(size_t chunk_size) {

View File

@ -20,16 +20,16 @@ extern "C" {
#define UNDEFINED_CHUNK_SIZE (-1) #define UNDEFINED_CHUNK_SIZE (-1)
const ChunkInfo kChunks[] = { const ChunkInfo kChunks[] = {
{ mktag('V', 'P', '8', 'X'), WEBP_CHUNK_VP8X, VP8X_CHUNK_SIZE }, { MKFOURCC('V', 'P', '8', 'X'), WEBP_CHUNK_VP8X, VP8X_CHUNK_SIZE },
{ mktag('I', 'C', 'C', 'P'), WEBP_CHUNK_ICCP, UNDEFINED_CHUNK_SIZE }, { MKFOURCC('I', 'C', 'C', 'P'), WEBP_CHUNK_ICCP, UNDEFINED_CHUNK_SIZE },
{ mktag('L', 'O', 'O', 'P'), WEBP_CHUNK_LOOP, LOOP_CHUNK_SIZE }, { MKFOURCC('L', 'O', 'O', 'P'), WEBP_CHUNK_LOOP, LOOP_CHUNK_SIZE },
{ mktag('F', 'R', 'M', ' '), WEBP_CHUNK_FRAME, FRAME_CHUNK_SIZE }, { MKFOURCC('F', 'R', 'M', ' '), WEBP_CHUNK_FRAME, FRAME_CHUNK_SIZE },
{ mktag('T', 'I', 'L', 'E'), WEBP_CHUNK_TILE, TILE_CHUNK_SIZE }, { MKFOURCC('T', 'I', 'L', 'E'), WEBP_CHUNK_TILE, TILE_CHUNK_SIZE },
{ mktag('A', 'L', 'P', 'H'), WEBP_CHUNK_ALPHA, UNDEFINED_CHUNK_SIZE }, { MKFOURCC('A', 'L', 'P', 'H'), WEBP_CHUNK_ALPHA, UNDEFINED_CHUNK_SIZE },
{ mktag('V', 'P', '8', ' '), WEBP_CHUNK_IMAGE, UNDEFINED_CHUNK_SIZE }, { MKFOURCC('V', 'P', '8', ' '), WEBP_CHUNK_IMAGE, UNDEFINED_CHUNK_SIZE },
{ mktag('V', 'P', '8', 'L'), WEBP_CHUNK_IMAGE, UNDEFINED_CHUNK_SIZE }, { MKFOURCC('V', 'P', '8', 'L'), WEBP_CHUNK_IMAGE, UNDEFINED_CHUNK_SIZE },
{ mktag('M', 'E', 'T', 'A'), WEBP_CHUNK_META, UNDEFINED_CHUNK_SIZE }, { MKFOURCC('M', 'E', 'T', 'A'), WEBP_CHUNK_META, UNDEFINED_CHUNK_SIZE },
{ mktag('U', 'N', 'K', 'N'), WEBP_CHUNK_UNKNOWN, UNDEFINED_CHUNK_SIZE }, { MKFOURCC('U', 'N', 'K', 'N'), WEBP_CHUNK_UNKNOWN, UNDEFINED_CHUNK_SIZE },
{ NIL_TAG, WEBP_CHUNK_NIL, 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) { 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); PutLE32(data + TAG_SIZE, (uint32_t)size - CHUNK_HEADER_SIZE);
assert(size == (uint32_t)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; return data + RIFF_HEADER_SIZE;
} }

View File

@ -97,8 +97,8 @@ WebPMux* WebPMuxCreateInternal(const WebPData* const bitstream, int copy_data,
if (data == NULL) return NULL; if (data == NULL) return NULL;
if (size < RIFF_HEADER_SIZE) return NULL; if (size < RIFF_HEADER_SIZE) return NULL;
if (GetLE32(data + 0) != mktag('R', 'I', 'F', 'F') || if (GetLE32(data + 0) != MKFOURCC('R', 'I', 'F', 'F') ||
GetLE32(data + CHUNK_HEADER_SIZE) != mktag('W', 'E', 'B', 'P')) { GetLE32(data + CHUNK_HEADER_SIZE) != MKFOURCC('W', 'E', 'B', 'P')) {
return NULL; return NULL;
} }
@ -213,13 +213,13 @@ WebPMuxError WebPMuxGetFeatures(const WebPMux* const mux, uint32_t* flags) {
return WEBP_MUX_OK; return WEBP_MUX_OK;
} }
static uint8_t* EmitVP8XChunk(uint8_t* const dst, uint32_t width, static uint8_t* EmitVP8XChunk(uint8_t* const dst, int width,
uint32_t height, uint32_t flags) { int height, uint32_t flags) {
const size_t vp8x_size = CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE; const size_t vp8x_size = CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE;
assert(width >= 1 && height >= 1); assert(width >= 1 && height >= 1);
assert(width <= MAX_CANVAS_SIZE && height <= MAX_CANVAS_SIZE); assert(width <= MAX_CANVAS_SIZE && height <= MAX_CANVAS_SIZE);
assert(width * (uint64_t)height < MAX_IMAGE_AREA); 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 + TAG_SIZE, VP8X_CHUNK_SIZE);
PutLE32(dst + CHUNK_HEADER_SIZE, flags); PutLE32(dst + CHUNK_HEADER_SIZE, flags);
PutLE24(dst + CHUNK_HEADER_SIZE + 4, width - 1); PutLE24(dst + CHUNK_HEADER_SIZE + 4, width - 1);
@ -301,7 +301,7 @@ WebPMuxError WebPMuxGetColorProfile(const WebPMux* const mux,
} }
WebPMuxError WebPMuxGetLoopCount(const WebPMux* const mux, WebPMuxError WebPMuxGetLoopCount(const WebPMux* const mux,
uint32_t* loop_count) { int* const loop_count) {
WebPData image; WebPData image;
WebPMuxError err; WebPMuxError err;
@ -310,14 +310,15 @@ WebPMuxError WebPMuxGetLoopCount(const WebPMux* const mux,
err = MuxGet(mux, IDX_LOOP, 1, &image); err = MuxGet(mux, IDX_LOOP, 1, &image);
if (err != WEBP_MUX_OK) return err; if (err != WEBP_MUX_OK) return err;
if (image.size_ < kChunks[WEBP_CHUNK_LOOP].size) return WEBP_MUX_BAD_DATA; 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; return WEBP_MUX_OK;
} }
static WebPMuxError MuxGetFrameTileInternal( static WebPMuxError MuxGetFrameTileInternal(
const WebPMux* const mux, uint32_t nth, WebPData* const bitstream, 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; const WebPData* frame_tile_data;
WebPMuxError err; WebPMuxError err;
WebPMuxImage* wpi; WebPMuxImage* wpi;
@ -340,27 +341,24 @@ static WebPMuxError MuxGetFrameTileInternal(
frame_tile_data = &wpi->header_->data_; frame_tile_data = &wpi->header_->data_;
if (frame_tile_data->size_ < kChunks[idx].size) return WEBP_MUX_BAD_DATA; if (frame_tile_data->size_ < kChunks[idx].size) return WEBP_MUX_BAD_DATA;
*x_offset = GetLE32(frame_tile_data->bytes_ + 0); *x_offset = 2 * GetLE24(frame_tile_data->bytes_ + 0);
*y_offset = GetLE32(frame_tile_data->bytes_ + 4); *y_offset = 2 * GetLE24(frame_tile_data->bytes_ + 3);
if (is_frame) *duration = GetLE32(frame_tile_data->bytes_ + 16); if (is_frame) *duration = 1 + GetLE24(frame_tile_data->bytes_ + 12);
return SynthesizeBitstream(wpi, bitstream); return SynthesizeBitstream(wpi, bitstream);
} }
WebPMuxError WebPMuxGetFrame(const WebPMux* const mux, uint32_t nth, WebPMuxError WebPMuxGetFrame(const WebPMux* const mux, uint32_t nth,
WebPData* const bitstream, WebPData* const bitstream, int* const x_offset,
uint32_t* x_offset, uint32_t* y_offset, int* const y_offset, int* const duration) {
uint32_t* duration) { return MuxGetFrameTileInternal(mux, nth, bitstream, x_offset, y_offset,
return MuxGetFrameTileInternal(mux, nth, bitstream, duration, kChunks[IDX_FRAME].tag);
x_offset, y_offset, duration,
kChunks[IDX_FRAME].tag);
} }
WebPMuxError WebPMuxGetTile(const WebPMux* const mux, uint32_t nth, WebPMuxError WebPMuxGetTile(const WebPMux* const mux, uint32_t nth,
WebPData* const bitstream, WebPData* const bitstream,
uint32_t* x_offset, uint32_t* y_offset) { int* const x_offset, int* const y_offset) {
return MuxGetFrameTileInternal(mux, nth, bitstream, return MuxGetFrameTileInternal(mux, nth, bitstream, x_offset, y_offset, NULL,
x_offset, y_offset, NULL,
kChunks[IDX_TILE].tag); kChunks[IDX_TILE].tag);
} }

View File

@ -62,9 +62,9 @@ typedef enum {
#define CHUNK_SIZE_BYTES 4 // Size needed to store chunk's size. #define CHUNK_SIZE_BYTES 4 // Size needed to store chunk's size.
#define CHUNK_HEADER_SIZE 8 // Size of a chunk header. #define CHUNK_HEADER_SIZE 8 // Size of a chunk header.
#define RIFF_HEADER_SIZE 12 // Size of the RIFF header ("RIFFnnnnWEBP"). #define RIFF_HEADER_SIZE 12 // Size of the RIFF header ("RIFFnnnnWEBP").
#define FRAME_CHUNK_SIZE 20 // Size of a FRM chunk. #define FRAME_CHUNK_SIZE 15 // Size of a FRM chunk.
#define LOOP_CHUNK_SIZE 4 // Size of a LOOP chunk. #define LOOP_CHUNK_SIZE 2 // Size of a LOOP chunk.
#define TILE_CHUNK_SIZE 8 // Size of a TILE chunk. #define TILE_CHUNK_SIZE 6 // Size of a TILE chunk.
#define VP8X_CHUNK_SIZE 10 // Size of a VP8X chunk. #define VP8X_CHUNK_SIZE 10 // Size of a VP8X chunk.
#define TILING_FLAG_BIT 0x01 // Set if tiles are possibly used. #define TILING_FLAG_BIT 0x01 // Set if tiles are possibly used.
@ -77,8 +77,8 @@ typedef enum {
#define MAX_CANVAS_SIZE (1 << 24) // 24-bit max for VP8X width/height. #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_IMAGE_AREA (1ULL << 32) // 32-bit max for width x height.
#define MAX_LOOP_COUNT (1 << 16) // maximum value for loop-count #define MAX_LOOP_COUNT (1 << 16) // maximum value for loop-count
#define MAX_DURATION (1U << 24) // maximum duration #define MAX_DURATION (1 << 24) // maximum duration
#define MAX_POSITION_OFFSET (1U << 24) // maximum frame/tile x/y offset #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 // Maximum chunk payload is such that adding the header and padding won't
// overflow a uint32_t. // overflow a uint32_t.

View File

@ -268,6 +268,8 @@ WEBP_EXTERN(WebPMuxError) WebPMuxDeleteColorProfile(WebPMux* const mux);
// Animation. // Animation.
// Adds an animation frame at the end of the mux object. // 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: // Parameters:
// mux - (in/out) object to which an animation frame is to be added // 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 // 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_MUX_OK - on success.
WEBP_EXTERN(WebPMuxError) WebPMuxPushFrame( WEBP_EXTERN(WebPMuxError) WebPMuxPushFrame(
WebPMux* const mux, const WebPData* const bitstream, WebPMux* const mux, const WebPData* const bitstream,
uint32_t x_offset, uint32_t y_offset, uint32_t duration, int x_offset, int y_offset, int duration, int copy_data);
int copy_data);
// TODO(urvang): Create a struct as follows to reduce argument list size: // TODO(urvang): Create a struct as follows to reduce argument list size:
// typedef struct { // typedef struct {
// WebPData image; // WebPData bitstream;
// WebPData alpha; // int x_offset, y_offset;
// uint32_t x_offset, y_offset; // int duration;
// uint32_t duration;
// } FrameInfo; // } FrameInfo;
// Gets the nth animation frame from the mux object. // Gets the nth animation frame from the mux object.
@ -315,7 +315,7 @@ WEBP_EXTERN(WebPMuxError) WebPMuxPushFrame(
// WEBP_MUX_OK - on success. // WEBP_MUX_OK - on success.
WEBP_EXTERN(WebPMuxError) WebPMuxGetFrame( WEBP_EXTERN(WebPMuxError) WebPMuxGetFrame(
const WebPMux* const mux, uint32_t nth, WebPData* const bitstream, 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. // Deletes an animation frame from the mux object.
// nth=0 has a special meaning - last position. // 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_MEMORY_ERROR - on memory allocation error.
// WEBP_MUX_OK - on success. // WEBP_MUX_OK - on success.
WEBP_EXTERN(WebPMuxError) WebPMuxSetLoopCount(WebPMux* const mux, WEBP_EXTERN(WebPMuxError) WebPMuxSetLoopCount(WebPMux* const mux,
uint32_t loop_count); int loop_count);
// Gets the animation loop count from the mux object. // Gets the animation loop count from the mux object.
// Parameters: // 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_NOT_FOUND - if loop chunk is not present in mux object.
// WEBP_MUX_OK - on success. // WEBP_MUX_OK - on success.
WEBP_EXTERN(WebPMuxError) WebPMuxGetLoopCount(const WebPMux* const mux, WEBP_EXTERN(WebPMuxError) WebPMuxGetLoopCount(const WebPMux* const mux,
uint32_t* loop_count); int* const loop_count);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Tiling. // Tiling.
// Adds a tile at the end of the mux object. // 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: // Parameters:
// mux - (in/out) object to which a tile is to be added. // mux - (in/out) object to which a tile is to be added.
// bitstream - (in) the image data corresponding to the frame. It can either // 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_MUX_OK - on success.
WEBP_EXTERN(WebPMuxError) WebPMuxPushTile( WEBP_EXTERN(WebPMuxError) WebPMuxPushTile(
WebPMux* const mux, const WebPData* const bitstream, 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. // Gets the nth tile from the mux object.
// The content of 'bitstream' is allocated using malloc(), and NOT // The content of 'bitstream' is allocated using malloc(), and NOT
@ -393,7 +395,7 @@ WEBP_EXTERN(WebPMuxError) WebPMuxPushTile(
// WEBP_MUX_OK - on success. // WEBP_MUX_OK - on success.
WEBP_EXTERN(WebPMuxError) WebPMuxGetTile( WEBP_EXTERN(WebPMuxError) WebPMuxGetTile(
const WebPMux* const mux, uint32_t nth, WebPData* const bitstream, 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. // Deletes a tile from the mux object.
// nth=0 has a special meaning - last position // nth=0 has a special meaning - last position