mirror of
https://github.com/webmproject/libwebp.git
synced 2024-12-26 13:48:21 +01:00
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:
parent
1bd7dd5097
commit
6808e69db8
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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,23 +544,19 @@ 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;
|
||||
image_area += (image_info.width_ * image_info.height_);
|
||||
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
@ -77,8 +77,8 @@ 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_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.
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user