mirror of
https://github.com/webmproject/libwebp.git
synced 2026-04-09 14:22:31 +02:00
Compare commits
7 Commits
0c9546f7ef
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
080044c7f2 | ||
|
|
c95ed44524 | ||
|
|
6a9eb44282 | ||
|
|
7ab12ced1e | ||
|
|
f51e813bf4 | ||
|
|
3307a349ed | ||
|
|
c696aadf69 |
@@ -34,9 +34,15 @@ static int AdditionWillOverflow(int a, int b) {
|
||||
}
|
||||
|
||||
static int FramesAreEqual(const uint8_t* const rgba1,
|
||||
const uint8_t* const rgba2, int width, int height) {
|
||||
const int stride = width * 4; // Always true for 'DecodedFrame.rgba'.
|
||||
return !memcmp(rgba1, rgba2, stride * height);
|
||||
const uint8_t* const rgba2, uint32_t width,
|
||||
uint32_t height) {
|
||||
// Always * 4 for 'DecodedFrame.rgba'.
|
||||
const uint32_t stride = width * 4;
|
||||
size_t size;
|
||||
if (!CheckMultiplicationOverflow(stride, height, &size)) {
|
||||
return 0;
|
||||
}
|
||||
return !memcmp(rgba1, rgba2, size);
|
||||
}
|
||||
|
||||
static WEBP_INLINE int PixelsAreSimilar(uint32_t src, uint32_t dst,
|
||||
@@ -64,8 +70,10 @@ static int FramesAreSimilar(const uint8_t* const rgba1,
|
||||
for (j = 0; j < height; ++j) {
|
||||
for (i = 0; i < width; ++i) {
|
||||
const int stride = width * 4;
|
||||
const size_t offset = j * stride + i;
|
||||
if (!PixelsAreSimilar(rgba1[offset], rgba2[offset], max_allowed_diff)) {
|
||||
size_t offset_row, offset;
|
||||
if (!CheckMultiplicationOverflow(j, stride, &offset_row) ||
|
||||
!CheckAdditionOverflow(offset_row, i, &offset) ||
|
||||
!PixelsAreSimilar(rgba1[offset], rgba2[offset], max_allowed_diff)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,6 +101,7 @@ int main(int argc, const char* argv[]) {
|
||||
for (i = 0; !error && i < image.num_frames; ++i) {
|
||||
W_CHAR out_file[1024];
|
||||
WebPDecBuffer buffer;
|
||||
size_t size;
|
||||
if (!WebPInitDecBuffer(&buffer)) {
|
||||
fprintf(stderr, "Cannot init dec buffer\n");
|
||||
error = 1;
|
||||
@@ -112,7 +113,15 @@ int main(int argc, const char* argv[]) {
|
||||
buffer.height = image.canvas_height;
|
||||
buffer.u.RGBA.rgba = image.frames[i].rgba;
|
||||
buffer.u.RGBA.stride = buffer.width * sizeof(uint32_t);
|
||||
buffer.u.RGBA.size = buffer.u.RGBA.stride * buffer.height;
|
||||
if (!CheckMultiplicationOverflow(buffer.u.RGBA.stride, buffer.height,
|
||||
&size)) {
|
||||
fprintf(stderr, "Invalid canvas size: %d x %d\n", buffer.width,
|
||||
buffer.height);
|
||||
error = 1;
|
||||
WebPFreeDecBuffer(&buffer);
|
||||
continue;
|
||||
}
|
||||
buffer.u.RGBA.size = size;
|
||||
WSNPRINTF(out_file, sizeof(out_file), "%s/%s%.4d.%s", dump_folder,
|
||||
prefix, i, suffix);
|
||||
if (!WebPSaveImage(&buffer, format, (const char*)out_file)) {
|
||||
|
||||
@@ -92,40 +92,96 @@ void ClearAnimatedImage(AnimatedImage* const image) {
|
||||
}
|
||||
}
|
||||
|
||||
WEBP_NODISCARD
|
||||
int CheckMultiplicationOverflow(uint32_t val1, uint32_t val2, size_t* product) {
|
||||
const uint64_t size = (uint64_t)val1 * val2;
|
||||
if (CheckSizeForOverflow(size)) {
|
||||
*product = (size_t)size;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
WEBP_NODISCARD
|
||||
int CheckAdditionOverflow(size_t val1, uint32_t val2, size_t* addition) {
|
||||
const uint64_t size = (uint64_t)val1 + val2;
|
||||
if (CheckSizeForOverflow(size)) {
|
||||
*addition = (size_t)size;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(WEBP_HAVE_GIF)
|
||||
|
||||
// For the GIF functions below, the width, height, x_offset, y_offset fit on 16
|
||||
// bits (but can fill the 16 bits) as per the GIF specification.
|
||||
// Multiplications that can overflow are cast to 64 bits.
|
||||
|
||||
const uint32_t kGifDimMax = (1 << 16) - 1;
|
||||
|
||||
// Clear the canvas to transparent.
|
||||
static void ZeroFillCanvas(uint8_t* rgba, uint32_t canvas_width,
|
||||
uint32_t canvas_height) {
|
||||
memset(rgba, 0, canvas_width * kNumChannels * canvas_height);
|
||||
WEBP_NODISCARD
|
||||
static int ZeroFillCanvas(uint8_t* rgba, uint32_t canvas_width,
|
||||
uint32_t canvas_height) {
|
||||
size_t size;
|
||||
assert(canvas_width <= kGifDimMax && canvas_height <= kGifDimMax);
|
||||
if (!CheckMultiplicationOverflow(canvas_width * kNumChannels, canvas_height,
|
||||
&size)) {
|
||||
return 0;
|
||||
}
|
||||
memset(rgba, 0, size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Clear given frame rectangle to transparent.
|
||||
static void ZeroFillFrameRect(uint8_t* rgba, int rgba_stride, int x_offset,
|
||||
int y_offset, int width, int height) {
|
||||
int j;
|
||||
WEBP_NODISCARD
|
||||
static int ZeroFillFrameRect(uint8_t* rgba, uint32_t rgba_stride,
|
||||
uint32_t x_offset, uint32_t y_offset,
|
||||
uint32_t width, uint32_t height) {
|
||||
uint32_t j;
|
||||
size_t size, offset;
|
||||
assert(width <= kGifDimMax && x_offset <= kGifDimMax);
|
||||
assert(width * kNumChannels <= rgba_stride);
|
||||
rgba += y_offset * rgba_stride + x_offset * kNumChannels;
|
||||
if (!CheckMultiplicationOverflow(y_offset, rgba_stride, &size) ||
|
||||
!CheckAdditionOverflow(size, x_offset * kNumChannels, &offset)) {
|
||||
return 0;
|
||||
}
|
||||
rgba += offset;
|
||||
for (j = 0; j < height; ++j) {
|
||||
memset(rgba, 0, width * kNumChannels);
|
||||
rgba += rgba_stride;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Copy width * height pixels from 'src' to 'dst'.
|
||||
static void CopyCanvas(const uint8_t* src, uint8_t* dst, uint32_t width,
|
||||
uint32_t height) {
|
||||
WEBP_NODISCARD
|
||||
static int CopyCanvas(const uint8_t* src, uint8_t* dst, uint32_t width,
|
||||
uint32_t height) {
|
||||
size_t size;
|
||||
assert(width <= kGifDimMax && height <= kGifDimMax);
|
||||
if (!CheckMultiplicationOverflow(width * kNumChannels, height, &size)) {
|
||||
return 0;
|
||||
}
|
||||
assert(src != NULL && dst != NULL);
|
||||
memcpy(dst, src, width * kNumChannels * height);
|
||||
memcpy(dst, src, size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Copy pixels in the given rectangle from 'src' to 'dst' honoring the 'stride'.
|
||||
static void CopyFrameRectangle(const uint8_t* src, uint8_t* dst, int stride,
|
||||
int x_offset, int y_offset, int width,
|
||||
int height) {
|
||||
int j;
|
||||
const int width_in_bytes = width * kNumChannels;
|
||||
const size_t offset = y_offset * stride + x_offset * kNumChannels;
|
||||
static int CopyFrameRectangle(const uint8_t* src, uint8_t* dst, uint32_t stride,
|
||||
uint32_t x_offset, uint32_t y_offset,
|
||||
uint32_t width, uint32_t height) {
|
||||
uint32_t j;
|
||||
const uint32_t width_in_bytes = width * kNumChannels;
|
||||
size_t offset, size;
|
||||
assert(width <= kGifDimMax && x_offset <= kGifDimMax);
|
||||
assert(width_in_bytes <= stride);
|
||||
if (!CheckMultiplicationOverflow(y_offset, stride, &size) ||
|
||||
!CheckAdditionOverflow(size, x_offset * kNumChannels, &offset)) {
|
||||
return 0;
|
||||
}
|
||||
src += offset;
|
||||
dst += offset;
|
||||
for (j = 0; j < height; ++j) {
|
||||
@@ -133,6 +189,7 @@ static void CopyFrameRectangle(const uint8_t* src, uint8_t* dst, int stride,
|
||||
src += stride;
|
||||
dst += stride;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
#endif // WEBP_HAVE_GIF
|
||||
|
||||
@@ -252,6 +309,7 @@ static int ReadAnimatedWebP(const char filename[],
|
||||
uint8_t* curr_rgba;
|
||||
uint8_t* frame_rgba;
|
||||
int timestamp;
|
||||
size_t size;
|
||||
|
||||
if (!WebPAnimDecoderGetNext(dec, &frame_rgba, ×tamp)) {
|
||||
fprintf(stderr, "Error decoding frame #%u\n", frame_index);
|
||||
@@ -262,8 +320,11 @@ static int ReadAnimatedWebP(const char filename[],
|
||||
curr_rgba = curr_frame->rgba;
|
||||
curr_frame->duration = timestamp - prev_frame_timestamp;
|
||||
curr_frame->is_key_frame = 0; // Unused.
|
||||
memcpy(curr_rgba, frame_rgba,
|
||||
image->canvas_width * kNumChannels * image->canvas_height);
|
||||
if (!CheckMultiplicationOverflow(image->canvas_width * kNumChannels,
|
||||
image->canvas_height, &size)) {
|
||||
goto End;
|
||||
}
|
||||
memcpy(curr_rgba, frame_rgba, size);
|
||||
|
||||
// Needed only because we may want to compare with GIF later.
|
||||
CleanupTransparentPixels((uint32_t*)curr_rgba, image->canvas_width,
|
||||
@@ -478,30 +539,39 @@ static int CoversFrameGIF(const GifImageDesc* const target,
|
||||
covered->Top + covered->Height <= target->Top + target->Height;
|
||||
}
|
||||
|
||||
static void RemapPixelsGIF(const uint8_t* const src,
|
||||
const ColorMapObject* const cmap,
|
||||
int transparent_color, int len, uint8_t* dst) {
|
||||
WEBP_NODISCARD
|
||||
static int RemapPixelsGIF(const uint8_t* const src,
|
||||
const ColorMapObject* const cmap,
|
||||
int transparent_color, int len, uint8_t* dst) {
|
||||
int i;
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (src[i] != transparent_color) {
|
||||
// If a pixel in the current frame is transparent, we don't modify it, so
|
||||
// that we can see-through the corresponding pixel from an earlier frame.
|
||||
const GifColorType c = cmap->Colors[src[i]];
|
||||
GifColorType c;
|
||||
if (src[i] >= cmap->ColorCount) {
|
||||
fprintf(stderr, "Invalid color index: %d\n", src[i]);
|
||||
return 0;
|
||||
}
|
||||
c = cmap->Colors[src[i]];
|
||||
dst[4 * i + 0] = c.Red;
|
||||
dst[4 * i + 1] = c.Green;
|
||||
dst[4 * i + 2] = c.Blue;
|
||||
dst[4 * i + 3] = 0xff;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
WEBP_NODISCARD
|
||||
static int ReadFrameGIF(const SavedImage* const gif_image,
|
||||
const ColorMapObject* cmap, int transparent_color,
|
||||
int out_stride, uint8_t* const dst) {
|
||||
uint32_t out_stride, uint8_t* const dst) {
|
||||
const GifImageDesc* image_desc = &gif_image->ImageDesc;
|
||||
const uint8_t* in;
|
||||
uint8_t* out;
|
||||
int j;
|
||||
size_t size, offset;
|
||||
|
||||
if (image_desc->ColorMap) cmap = image_desc->ColorMap;
|
||||
|
||||
@@ -511,10 +581,17 @@ static int ReadFrameGIF(const SavedImage* const gif_image,
|
||||
}
|
||||
|
||||
in = (const uint8_t*)gif_image->RasterBits;
|
||||
out = dst + image_desc->Top * out_stride + image_desc->Left * kNumChannels;
|
||||
if (!CheckMultiplicationOverflow(image_desc->Top, out_stride, &size) ||
|
||||
!CheckAdditionOverflow(size, image_desc->Left * kNumChannels, &offset)) {
|
||||
fprintf(stderr, "Invalid image description.\n");
|
||||
return 0;
|
||||
}
|
||||
out = dst + offset;
|
||||
|
||||
for (j = 0; j < image_desc->Height; ++j) {
|
||||
RemapPixelsGIF(in, cmap, transparent_color, image_desc->Width, out);
|
||||
if (!RemapPixelsGIF(in, cmap, transparent_color, image_desc->Width, out)) {
|
||||
return 0;
|
||||
}
|
||||
in += image_desc->Width;
|
||||
out += out_stride;
|
||||
}
|
||||
@@ -592,11 +669,23 @@ static int ReadAnimatedGIF(const char filename[],
|
||||
|
||||
// Decode and reconstruct frames.
|
||||
for (i = 0; i < frame_count; ++i) {
|
||||
const int canvas_width_in_bytes = canvas_width * kNumChannels;
|
||||
const uint32_t canvas_width_in_bytes = canvas_width * kNumChannels;
|
||||
const SavedImage* const curr_gif_image = &gif->SavedImages[i];
|
||||
GraphicsControlBlock curr_gcb;
|
||||
DecodedFrame* curr_frame;
|
||||
uint8_t* curr_rgba;
|
||||
const int left = curr_gif_image->ImageDesc.Left;
|
||||
const int top = curr_gif_image->ImageDesc.Top;
|
||||
const int width = curr_gif_image->ImageDesc.Width;
|
||||
const int height = curr_gif_image->ImageDesc.Height;
|
||||
|
||||
if (left < 0 || top < 0 || width <= 0 || height <= 0 ||
|
||||
(uint32_t)(left + width) > canvas_width ||
|
||||
(uint32_t)(top + height) > canvas_height) {
|
||||
DGifCloseFile(gif, NULL);
|
||||
return 0;
|
||||
}
|
||||
assert((uint32_t)width <= kGifDimMax && (uint32_t)height <= kGifDimMax);
|
||||
|
||||
memset(&curr_gcb, 0, sizeof(curr_gcb));
|
||||
DGifSavedExtensionToGCB(gif, i, &curr_gcb);
|
||||
@@ -610,7 +699,10 @@ static int ReadAnimatedGIF(const char filename[],
|
||||
|
||||
if (i == 0) { // Initialize as transparent.
|
||||
curr_frame->is_key_frame = 1;
|
||||
ZeroFillCanvas(curr_rgba, canvas_width, canvas_height);
|
||||
if (!ZeroFillCanvas(curr_rgba, canvas_width, canvas_height)) {
|
||||
DGifCloseFile(gif, NULL);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
DecodedFrame* const prev_frame = &image->frames[i - 1];
|
||||
const GifImageDesc* const prev_desc = &gif->SavedImages[i - 1].ImageDesc;
|
||||
@@ -623,13 +715,19 @@ static int ReadAnimatedGIF(const char filename[],
|
||||
canvas_width, canvas_height);
|
||||
|
||||
if (curr_frame->is_key_frame) { // Initialize as transparent.
|
||||
ZeroFillCanvas(curr_rgba, canvas_width, canvas_height);
|
||||
if (!ZeroFillCanvas(curr_rgba, canvas_width, canvas_height)) {
|
||||
DGifCloseFile(gif, NULL);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
int prev_frame_disposed, curr_frame_opaque;
|
||||
int prev_frame_completely_covered;
|
||||
// Initialize with previous canvas.
|
||||
uint8_t* const prev_rgba = image->frames[i - 1].rgba;
|
||||
CopyCanvas(prev_rgba, curr_rgba, canvas_width, canvas_height);
|
||||
if (!CopyCanvas(prev_rgba, curr_rgba, canvas_width, canvas_height)) {
|
||||
DGifCloseFile(gif, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Dispose previous frame rectangle.
|
||||
prev_frame_disposed = (prev_gcb.DisposalMode == DISPOSE_BACKGROUND ||
|
||||
@@ -642,9 +740,12 @@ static int ReadAnimatedGIF(const char filename[],
|
||||
if (prev_frame_disposed && !prev_frame_completely_covered) {
|
||||
switch (prev_gcb.DisposalMode) {
|
||||
case DISPOSE_BACKGROUND: {
|
||||
ZeroFillFrameRect(curr_rgba, canvas_width_in_bytes,
|
||||
prev_desc->Left, prev_desc->Top,
|
||||
prev_desc->Width, prev_desc->Height);
|
||||
if (!ZeroFillFrameRect(curr_rgba, canvas_width_in_bytes,
|
||||
prev_desc->Left, prev_desc->Top,
|
||||
prev_desc->Width, prev_desc->Height)) {
|
||||
DGifCloseFile(gif, NULL);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DISPOSE_PREVIOUS: {
|
||||
@@ -661,16 +762,22 @@ static int ReadAnimatedGIF(const char filename[],
|
||||
// corresponding pixels in source canvas.
|
||||
uint8_t* const src_frame_rgba =
|
||||
image->frames[src_frame_num].rgba;
|
||||
CopyFrameRectangle(src_frame_rgba, curr_rgba,
|
||||
canvas_width_in_bytes, prev_desc->Left,
|
||||
prev_desc->Top, prev_desc->Width,
|
||||
prev_desc->Height);
|
||||
if (!CopyFrameRectangle(src_frame_rgba, curr_rgba,
|
||||
canvas_width_in_bytes, prev_desc->Left,
|
||||
prev_desc->Top, prev_desc->Width,
|
||||
prev_desc->Height)) {
|
||||
DGifCloseFile(gif, NULL);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
// Source canvas doesn't exist. So clear previous frame
|
||||
// rectangle to background.
|
||||
ZeroFillFrameRect(curr_rgba, canvas_width_in_bytes,
|
||||
prev_desc->Left, prev_desc->Top,
|
||||
prev_desc->Width, prev_desc->Height);
|
||||
if (!ZeroFillFrameRect(curr_rgba, canvas_width_in_bytes,
|
||||
prev_desc->Left, prev_desc->Top,
|
||||
prev_desc->Width, prev_desc->Height)) {
|
||||
DGifCloseFile(gif, NULL);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -70,6 +70,12 @@ void GetDiffAndPSNR(const uint8_t rgba1[], const uint8_t rgba2[],
|
||||
void GetAnimatedImageVersions(int* const decoder_version,
|
||||
int* const demux_version);
|
||||
|
||||
// Check whether val1 * val2 fits in a size_t. Returns 1 on success.
|
||||
int CheckMultiplicationOverflow(uint32_t val1, uint32_t val2, size_t* product);
|
||||
|
||||
// Check whether val1 + val2 fits in a size_t. Returns 1 on success.
|
||||
int CheckAdditionOverflow(size_t val1, uint32_t val2, size_t* addition);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
@@ -493,12 +493,18 @@ static int ParseFrameArgs(const char* args, WebPMuxFrameInfo* const info) {
|
||||
&plus_minus, &blend_method, &unused);
|
||||
switch (num_args) {
|
||||
case 1:
|
||||
info->x_offset = info->y_offset = 0; // fall through
|
||||
info->x_offset = info->y_offset = 0;
|
||||
WEBP_FALLTHROUGH;
|
||||
// fall through
|
||||
case 3:
|
||||
dispose_method = 0; // fall through
|
||||
dispose_method = 0;
|
||||
WEBP_FALLTHROUGH;
|
||||
// fall through
|
||||
case 4:
|
||||
plus_minus = '+';
|
||||
blend_method = 'b'; // fall through
|
||||
blend_method = 'b';
|
||||
WEBP_FALLTHROUGH;
|
||||
// fall through
|
||||
case 6:
|
||||
break;
|
||||
case 2:
|
||||
|
||||
@@ -58,7 +58,6 @@ WEBP_NODISCARD static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data,
|
||||
uint8_t* output) {
|
||||
int ok = 0;
|
||||
const uint8_t* const alpha_data = data + ALPHA_HEADER_LEN;
|
||||
const size_t alpha_data_size = data_size - ALPHA_HEADER_LEN;
|
||||
int rsrv;
|
||||
VP8Io* const io = &dec->io;
|
||||
|
||||
@@ -101,16 +100,19 @@ WEBP_NODISCARD static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data,
|
||||
io->crop_bottom = src_io->crop_bottom;
|
||||
// No need to copy the scaling parameters.
|
||||
|
||||
if (dec->method == ALPHA_NO_COMPRESSION) {
|
||||
const size_t alpha_decoded_size = dec->width * dec->height;
|
||||
ok = (alpha_data_size >= alpha_decoded_size);
|
||||
} else {
|
||||
assert(dec->method == ALPHA_LOSSLESS_COMPRESSION);
|
||||
{
|
||||
const uint8_t* WEBP_BIDI_INDEXABLE const bounded_alpha_data =
|
||||
WEBP_UNSAFE_FORGE_BIDI_INDEXABLE(const uint8_t*, alpha_data,
|
||||
alpha_data_size);
|
||||
ok = VP8LDecodeAlphaHeader(dec, bounded_alpha_data, alpha_data_size);
|
||||
{
|
||||
const size_t alpha_data_size = data_size - ALPHA_HEADER_LEN;
|
||||
if (dec->method == ALPHA_NO_COMPRESSION) {
|
||||
const size_t alpha_decoded_size = dec->width * dec->height;
|
||||
ok = (alpha_data_size >= alpha_decoded_size);
|
||||
} else {
|
||||
assert(dec->method == ALPHA_LOSSLESS_COMPRESSION);
|
||||
{
|
||||
const uint8_t* WEBP_BIDI_INDEXABLE const bounded_alpha_data =
|
||||
WEBP_UNSAFE_FORGE_BIDI_INDEXABLE(const uint8_t*, alpha_data,
|
||||
alpha_data_size);
|
||||
ok = VP8LDecodeAlphaHeader(dec, bounded_alpha_data, alpha_data_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1339,7 +1339,7 @@ End:
|
||||
// instead of a subframe.
|
||||
static int64_t KeyFramePenalty(const EncodedFrame* const encoded_frame) {
|
||||
return ((int64_t)encoded_frame->key_frame.bitstream.size -
|
||||
encoded_frame->sub_frame.bitstream.size);
|
||||
(int64_t)encoded_frame->sub_frame.bitstream.size);
|
||||
}
|
||||
|
||||
static int CacheFrame(WebPAnimEncoder* const enc,
|
||||
|
||||
@@ -500,6 +500,14 @@ WebPMuxError MuxValidate(const WebPMux* const mux) {
|
||||
err = ValidateChunk(mux, IDX_XMP, XMP_FLAG, flags, 1, &num_xmp);
|
||||
if (err != WEBP_MUX_OK) return err;
|
||||
|
||||
// Verify either VP8X chunk is present OR there is only one elem in
|
||||
// mux->images.
|
||||
err = ValidateChunk(mux, IDX_VP8X, NO_FLAG, flags, 1, &num_vp8x);
|
||||
if (err != WEBP_MUX_OK) return err;
|
||||
err = ValidateChunk(mux, IDX_VP8, NO_FLAG, flags, -1, &num_images);
|
||||
if (err != WEBP_MUX_OK) return err;
|
||||
if (num_vp8x == 0 && num_images != 1) return WEBP_MUX_INVALID_ARGUMENT;
|
||||
|
||||
// Animation: ANIMATION_FLAG, ANIM chunk and ANMF chunk(s) are consistent.
|
||||
// At most one ANIM chunk.
|
||||
err = ValidateChunk(mux, IDX_ANIM, NO_FLAG, flags, 1, &num_anim);
|
||||
@@ -509,7 +517,10 @@ WebPMuxError MuxValidate(const WebPMux* const mux) {
|
||||
|
||||
{
|
||||
const int has_animation = !!(flags & ANIMATION_FLAG);
|
||||
if (has_animation && (num_anim == 0 || num_frames == 0)) {
|
||||
// An ANIM chunk must be present when has_animation is true, and all images
|
||||
// must be contained in ANMF chunks.
|
||||
if (has_animation &&
|
||||
(num_anim == 0 || num_frames == 0 || num_frames != num_images)) {
|
||||
return WEBP_MUX_INVALID_ARGUMENT;
|
||||
}
|
||||
if (!has_animation && (num_anim == 1 || num_frames > 0)) {
|
||||
@@ -531,14 +542,6 @@ WebPMuxError MuxValidate(const WebPMux* const mux) {
|
||||
}
|
||||
}
|
||||
|
||||
// Verify either VP8X chunk is present OR there is only one elem in
|
||||
// mux->images.
|
||||
err = ValidateChunk(mux, IDX_VP8X, NO_FLAG, flags, 1, &num_vp8x);
|
||||
if (err != WEBP_MUX_OK) return err;
|
||||
err = ValidateChunk(mux, IDX_VP8, NO_FLAG, flags, -1, &num_images);
|
||||
if (err != WEBP_MUX_OK) return err;
|
||||
if (num_vp8x == 0 && num_images != 1) return WEBP_MUX_INVALID_ARGUMENT;
|
||||
|
||||
// ALPHA_FLAG & alpha chunk(s) are consistent.
|
||||
// Note: ALPHA_FLAG can be set when there is actually no Alpha data present.
|
||||
if (MuxHasAlpha(mux->images)) {
|
||||
|
||||
@@ -71,6 +71,15 @@ typedef long long int int64_t;
|
||||
#endif /* defined(_WIN32) && defined(WEBP_DLL) */
|
||||
#endif /* WEBP_EXTERN */
|
||||
|
||||
#ifndef WEBP_FALLTHROUGH
|
||||
#if (defined(__cplusplus) && __cplusplus >= 201703L) || \
|
||||
(defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L)
|
||||
#define WEBP_FALLTHROUGH [[fallthrough]]
|
||||
#else
|
||||
#define WEBP_FALLTHROUGH
|
||||
#endif
|
||||
#endif /* WEBP_FALLTHROUGH */
|
||||
|
||||
// Macro to check ABI compatibility (same major revision number)
|
||||
#define WEBP_ABI_IS_INCOMPATIBLE(a, b) (((a) >> 8) != ((b) >> 8))
|
||||
|
||||
|
||||
@@ -18,9 +18,11 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "./fuzz_utils.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "src/dec/webpi_dec.h"
|
||||
#include "src/utils/rescaler_utils.h"
|
||||
#include "webp/decode.h"
|
||||
@@ -162,3 +164,25 @@ FUZZ_TEST(AdvancedApi, AdvancedApiTest)
|
||||
#endif
|
||||
/*incremental=*/fuzztest::Arbitrary<bool>(),
|
||||
fuzz_utils::ArbitraryValidWebPDecoderOptions());
|
||||
|
||||
TEST(AdvancedApi, Buganizer498966235) {
|
||||
AdvancedApiTest(
|
||||
std::string(
|
||||
"RIFF\014|"
|
||||
"\000\000WEBPVP8X\n\000\000\000\020\000\000D\002\000\000\017\000\000A"
|
||||
"LPH5\000\000\000\004\327\000\000\000\000\000\000c8\345S\000\243\000"
|
||||
"\253c\311\000\027\000\000\000\200\000\000\000\000\240\"AE\001\000"
|
||||
"\000\0008<"
|
||||
"ALP\010\000s\002\000\000\000\000\000\000\000\000\000ALPH\000\000\000"
|
||||
"\000VP8 "
|
||||
"(\000\000\000\224\001\000\235\001*\003\000\020\000\003,\000~"
|
||||
"\342\000\000se\002ionR\265Vq\302M}\"webp\"r\010\003\000\020#"
|
||||
"\366\356\002\323\220\000 "
|
||||
"\212N@\000\026\327A\367\266\201\201\"IFF@\"RIFF\"&\226!"
|
||||
"VP\n8Rg\000\0001\"\335\"I\"XEBP\"\002\002\"\367\\x0\203\203\203\341"
|
||||
"\341l,\203\\sectiqncJUN=\"sectistre\\x9D\\x01\\x2A\"JUKQ\"",
|
||||
257),
|
||||
68, 3, true,
|
||||
fuzz_utils::WebPDecoderOptionsCpp{
|
||||
0, 0, 1, 5, 10, 5, 9, 0, 1, 3, 0, 72, 0, 83, {0, 0, 0, 0, 0}});
|
||||
}
|
||||
|
||||
@@ -16,9 +16,11 @@
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "./fuzz_utils.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "webp/decode.h"
|
||||
#include "webp/demux.h"
|
||||
#include "webp/mux_types.h"
|
||||
@@ -88,3 +90,15 @@ FUZZ_TEST(AnimationApi, AnimationApiTest)
|
||||
// Animations only support 4 (out of 12) modes.
|
||||
fuzztest::ElementOf<WEBP_CSP_MODE>({MODE_RGBA, MODE_BGRA,
|
||||
MODE_rgbA, MODE_bgrA}));
|
||||
|
||||
TEST(AnimationApi, Buganizer498965803) {
|
||||
AnimationApiTest(
|
||||
std::string("ALPH\000\000\000\000\000\000\000\000\021\000\000\000\t\305"
|
||||
"\006d\301\013\177\000\000webp\034\205\000#@VP8 "
|
||||
"!\000\000\000v\003\000\235\001*\007\200\"\000\0020("
|
||||
"\000\377\377\377\003\000\000\000\311\311\311\311\311\311\311"
|
||||
"\311\311\311\311\311\311\311\311\311\311\311\311\311\311\211"
|
||||
"\311\311\311\311\311\030\030\030\030\030\030\311\311",
|
||||
98),
|
||||
false, static_cast<WEBP_CSP_MODE>(1));
|
||||
}
|
||||
|
||||
@@ -16,10 +16,12 @@
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "./fuzz_utils.h"
|
||||
#include "./nalloc.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "imageio/imageio_util.h"
|
||||
#include "webp/decode.h"
|
||||
#include "webp/demux.h"
|
||||
@@ -79,3 +81,12 @@ End:
|
||||
FUZZ_TEST(AnimDecoder, AnimDecoderTest)
|
||||
.WithDomains(fuzztest::String().WithMaxSize(fuzz_utils::kMaxWebPFileSize +
|
||||
1));
|
||||
|
||||
TEST(AnimDecoder, Buganizer498967090) {
|
||||
AnimDecoderTest(std::string(
|
||||
"ALPH\000\000\000\000\000\000\000\000\003\000\000\000\014EBPVP8 "
|
||||
"\030\000\000\0000\001\000\235\001*\002\000\001\000\003\0004%"
|
||||
"\244\000\003~\000*\316\373\224\"AFM\"<0\334\"\231J\002`"
|
||||
"\256\233\233\233\233\272\000\000",
|
||||
72));
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "./fuzz_utils.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "src/dsp/cpu.h"
|
||||
#include "webp/encode.h"
|
||||
#include "webp/mux.h"
|
||||
@@ -208,3 +209,38 @@ FUZZ_TEST(AnimArbitraryEncoder, AnimEncoderTest)
|
||||
.WithMaxSize(15),
|
||||
/*optimization_index=*/
|
||||
fuzztest::InRange<uint32_t>(0, fuzz_utils::kMaxOptimizationIndex));
|
||||
|
||||
TEST(AnimIndexEncoder, Buganizer498967191) {
|
||||
auto GetWebPPicture = [](int index,
|
||||
bool use_argb) -> fuzz_utils::WebPPictureCpp {
|
||||
WebPPicture pic = fuzz_utils::GetSourcePicture(index, use_argb);
|
||||
return fuzz_utils::WebPPictureCpp(
|
||||
use_argb, pic.colorspace, pic.width, pic.height, pic.y, pic.u, pic.v,
|
||||
pic.y_stride, pic.uv_stride, pic.a, pic.a_stride, pic.argb,
|
||||
pic.argb_stride, pic.memory_, pic.memory_argb_);
|
||||
};
|
||||
AnimEncoderTest(
|
||||
false, {0, 1}, true,
|
||||
{FrameConfig{1, 0, WebPConfig{0, 0.f, 6, static_cast<WebPImageHint>(3),
|
||||
0, 0.f, 4, 0,
|
||||
38, 7, 1, 0,
|
||||
0, 0, 1, 10,
|
||||
1, 1, 1, 10,
|
||||
0, 0, 0, 20,
|
||||
1, 0, 0, 0,
|
||||
100},
|
||||
fuzz_utils::CropOrScaleParams{true, true, 6, 1, 2, 1},
|
||||
GetWebPPicture(0, true)},
|
||||
FrameConfig{0, 7248,
|
||||
WebPConfig{1, 0.f, 1, static_cast<WebPImageHint>(3),
|
||||
0, 0.f, 1, 0,
|
||||
100, 0, 1, 0,
|
||||
1, 0, 0, 10,
|
||||
1, 1, 0, 10,
|
||||
0, 1, 0, 0,
|
||||
1, 0, 0, 0,
|
||||
100},
|
||||
fuzz_utils::CropOrScaleParams{true, true, 6, 8, 2, 1},
|
||||
GetWebPPicture(0, true)}},
|
||||
1);
|
||||
}
|
||||
|
||||
@@ -17,9 +17,11 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "./fuzz_utils.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "webp/decode.h"
|
||||
#include "webp/types.h"
|
||||
|
||||
@@ -101,3 +103,15 @@ void SimpleApiTest(std::string_view data_in) {
|
||||
FUZZ_TEST(SimpleApi, SimpleApiTest)
|
||||
.WithDomains(fuzztest::String().WithMaxSize(fuzz_utils::kMaxWebPFileSize +
|
||||
1));
|
||||
|
||||
TEST(SimpleApi, Buganizer498966511) {
|
||||
SimpleApiTest(
|
||||
std::string("ALPH\004\000\000\000A\377\377\377\377LP\010\000\000\000\000"
|
||||
"\000\000\311H\006\000\000\000\"E\356PW\"ALPH\000\000\000\000"
|
||||
"ALpH\004\000\000\000\004\010\000\200VP8 "
|
||||
"T\000\000\000\266\003\000\235\001*"
|
||||
"\001\000\002\000y\336n\366\001O\363\374\243\000\003LPS\"\002"
|
||||
"iF\000FjRsa\232vP\"EO\"K\217OM;rOect\275n\"Wsection_JUNQ="
|
||||
"\"JUNQ\"\250YO,_I\362\021\"ANIM\"",
|
||||
150));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user