diff options
Diffstat (limited to 'src/3rdparty/libwebp/src/mux')
-rw-r--r-- | src/3rdparty/libwebp/src/mux/anim_encode.c | 56 | ||||
-rw-r--r-- | src/3rdparty/libwebp/src/mux/animi.h | 4 | ||||
-rw-r--r-- | src/3rdparty/libwebp/src/mux/muxedit.c | 22 | ||||
-rw-r--r-- | src/3rdparty/libwebp/src/mux/muxi.h | 28 | ||||
-rw-r--r-- | src/3rdparty/libwebp/src/mux/muxinternal.c | 74 | ||||
-rw-r--r-- | src/3rdparty/libwebp/src/mux/muxread.c | 70 |
6 files changed, 146 insertions, 108 deletions
diff --git a/src/3rdparty/libwebp/src/mux/anim_encode.c b/src/3rdparty/libwebp/src/mux/anim_encode.c index 6066388..7be9906 100644 --- a/src/3rdparty/libwebp/src/mux/anim_encode.c +++ b/src/3rdparty/libwebp/src/mux/anim_encode.c @@ -16,12 +16,12 @@ #include <stdio.h> #include <stdlib.h> // for abs() -#include "../mux/animi.h" -#include "../utils/utils.h" -#include "../webp/decode.h" -#include "../webp/encode.h" -#include "../webp/format_constants.h" -#include "../webp/mux.h" +#include "src/mux/animi.h" +#include "src/utils/utils.h" +#include "src/webp/decode.h" +#include "src/webp/encode.h" +#include "src/webp/format_constants.h" +#include "src/webp/mux.h" #if defined(_MSC_VER) && _MSC_VER < 1900 #define snprintf _snprintf @@ -35,7 +35,7 @@ // Stores frame rectangle dimensions. typedef struct { int x_offset_, y_offset_, width_, height_; -} FrameRect; +} FrameRectangle; // Used to store two candidates of encoded data for an animation frame. One of // the two will be chosen later. @@ -50,7 +50,7 @@ struct WebPAnimEncoder { const int canvas_height_; // Canvas height. const WebPAnimEncoderOptions options_; // Global encoding options. - FrameRect prev_rect_; // Previous WebP frame rectangle. + FrameRectangle prev_rect_; // Previous WebP frame rectangle. WebPConfig last_config_; // Cached in case a re-encode is needed. WebPConfig last_config_reversed_; // If 'last_config_' uses lossless, then // this config uses lossy and vice versa; @@ -206,7 +206,7 @@ static void ClearRectangle(WebPPicture* const picture, } static void WebPUtilClearPic(WebPPicture* const picture, - const FrameRect* const rect) { + const FrameRectangle* const rect) { if (rect != NULL) { ClearRectangle(picture, rect->x_offset_, rect->y_offset_, rect->width_, rect->height_); @@ -400,7 +400,7 @@ static WEBP_INLINE int ComparePixelsLossy(const uint32_t* src, int src_step, return 1; } -static int IsEmptyRect(const FrameRect* const rect) { +static int IsEmptyRect(const FrameRectangle* const rect) { return (rect->width_ == 0) || (rect->height_ == 0); } @@ -413,7 +413,7 @@ static int QualityToMaxDiff(float quality) { // Assumes that an initial valid guess of change rectangle 'rect' is passed. static void MinimizeChangeRectangle(const WebPPicture* const src, const WebPPicture* const dst, - FrameRect* const rect, + FrameRectangle* const rect, int is_lossless, float quality) { int i, j; const ComparePixelsFunc compare_pixels = @@ -498,7 +498,7 @@ static void MinimizeChangeRectangle(const WebPPicture* const src, } // Snap rectangle to even offsets (and adjust dimensions if needed). -static WEBP_INLINE void SnapToEvenOffsets(FrameRect* const rect) { +static WEBP_INLINE void SnapToEvenOffsets(FrameRectangle* const rect) { rect->width_ += (rect->x_offset_ & 1); rect->height_ += (rect->y_offset_ & 1); rect->x_offset_ &= ~1; @@ -508,9 +508,9 @@ static WEBP_INLINE void SnapToEvenOffsets(FrameRect* const rect) { typedef struct { int should_try_; // Should try this set of parameters. int empty_rect_allowed_; // Frame with empty rectangle can be skipped. - FrameRect rect_ll_; // Frame rectangle for lossless compression. + FrameRectangle rect_ll_; // Frame rectangle for lossless compression. WebPPicture sub_frame_ll_; // Sub-frame pic for lossless compression. - FrameRect rect_lossy_; // Frame rectangle for lossy compression. + FrameRectangle rect_lossy_; // Frame rectangle for lossy compression. // Could be smaller than rect_ll_ as pixels // with small diffs can be ignored. WebPPicture sub_frame_lossy_; // Sub-frame pic for lossless compression. @@ -538,7 +538,8 @@ static void SubFrameParamsFree(SubFrameParams* const params) { static int GetSubRect(const WebPPicture* const prev_canvas, const WebPPicture* const curr_canvas, int is_key_frame, int is_first_frame, int empty_rect_allowed, - int is_lossless, float quality, FrameRect* const rect, + int is_lossless, float quality, + FrameRectangle* const rect, WebPPicture* const sub_frame) { if (!is_key_frame || is_first_frame) { // Optimize frame rectangle. // Note: This behaves as expected for first frame, as 'prev_canvas' is @@ -594,7 +595,7 @@ int WebPAnimEncoderRefineRect( const WebPPicture* const prev_canvas, const WebPPicture* const curr_canvas, int is_lossless, float quality, int* const x_offset, int* const y_offset, int* const width, int* const height) { - FrameRect rect; + FrameRectangle rect; const int right = clip(*x_offset + *width, 0, curr_canvas->width); const int left = clip(*x_offset, 0, curr_canvas->width - 1); const int bottom = clip(*y_offset + *height, 0, curr_canvas->height); @@ -620,7 +621,7 @@ int WebPAnimEncoderRefineRect( } static void DisposeFrameRectangle(int dispose_method, - const FrameRect* const rect, + const FrameRectangle* const rect, WebPPicture* const curr_canvas) { assert(rect != NULL); if (dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) { @@ -628,13 +629,13 @@ static void DisposeFrameRectangle(int dispose_method, } } -static uint32_t RectArea(const FrameRect* const rect) { +static uint32_t RectArea(const FrameRectangle* const rect) { return (uint32_t)rect->width_ * rect->height_; } static int IsLosslessBlendingPossible(const WebPPicture* const src, const WebPPicture* const dst, - const FrameRect* const rect) { + const FrameRectangle* const rect) { int i, j; assert(src->width == dst->width && src->height == dst->height); assert(rect->x_offset_ + rect->width_ <= dst->width); @@ -656,7 +657,7 @@ static int IsLosslessBlendingPossible(const WebPPicture* const src, static int IsLossyBlendingPossible(const WebPPicture* const src, const WebPPicture* const dst, - const FrameRect* const rect, + const FrameRectangle* const rect, float quality) { const int max_allowed_diff_lossy = QualityToMaxDiff(quality); int i, j; @@ -683,7 +684,7 @@ static int IsLossyBlendingPossible(const WebPPicture* const src, // transparent pixels. // Returns true if at least one pixel gets modified. static int IncreaseTransparency(const WebPPicture* const src, - const FrameRect* const rect, + const FrameRectangle* const rect, WebPPicture* const dst) { int i, j; int modified = 0; @@ -709,7 +710,7 @@ static int IncreaseTransparency(const WebPPicture* const src, // Assumes lossy compression is being used. // Returns true if at least one pixel gets modified. static int FlattenSimilarBlocks(const WebPPicture* const src, - const FrameRect* const rect, + const FrameRectangle* const rect, WebPPicture* const dst, float quality) { const int max_allowed_diff_lossy = QualityToMaxDiff(quality); int i, j; @@ -778,13 +779,13 @@ static int EncodeFrame(const WebPConfig* const config, WebPPicture* const pic, typedef struct { WebPMemoryWriter mem_; WebPMuxFrameInfo info_; - FrameRect rect_; + FrameRectangle rect_; int evaluate_; // True if this candidate should be evaluated. } Candidate; // Generates a candidate encoded frame given a picture and metadata. static WebPEncodingError EncodeCandidate(WebPPicture* const sub_frame, - const FrameRect* const rect, + const FrameRectangle* const rect, const WebPConfig* const encoder_config, int use_blending, Candidate* const candidate) { @@ -958,7 +959,7 @@ static int IncreasePreviousDuration(WebPAnimEncoder* const enc, int duration) { if (new_duration >= MAX_DURATION) { // Special case. // Separate out previous frame from earlier merged frames to avoid overflow. // We add a 1x1 transparent frame for the previous frame, with blending on. - const FrameRect rect = { 0, 0, 1, 1 }; + const FrameRectangle rect = { 0, 0, 1, 1 }; const uint8_t lossless_1x1_bytes[] = { 0x52, 0x49, 0x46, 0x46, 0x14, 0x00, 0x00, 0x00, 0x57, 0x45, 0x42, 0x50, 0x56, 0x50, 0x38, 0x4c, 0x08, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, @@ -1223,7 +1224,7 @@ static int CacheFrame(WebPAnimEncoder* const enc, enc->prev_candidate_undecided_ = 0; } else { int64_t curr_delta; - FrameRect prev_rect_key, prev_rect_sub; + FrameRectangle prev_rect_key, prev_rect_sub; // Add this as a frame rectangle to enc. error_code = SetFrame(enc, config, 0, encoded_frame, &frame_skipped); @@ -1535,7 +1536,8 @@ int WebPAnimEncoderAssemble(WebPAnimEncoder* enc, WebPData* webp_data) { if (!enc->got_null_frame_ && enc->in_frame_count_ > 1 && enc->count_ > 0) { // set duration of the last frame to be avg of durations of previous frames. - const double delta_time = enc->prev_timestamp_ - enc->first_timestamp_; + const double delta_time = + (uint32_t)enc->prev_timestamp_ - enc->first_timestamp_; const int average_duration = (int)(delta_time / (enc->in_frame_count_ - 1)); if (!IncreasePreviousDuration(enc, average_duration)) { return 0; diff --git a/src/3rdparty/libwebp/src/mux/animi.h b/src/3rdparty/libwebp/src/mux/animi.h index cecaf1f..34c45ba 100644 --- a/src/3rdparty/libwebp/src/mux/animi.h +++ b/src/3rdparty/libwebp/src/mux/animi.h @@ -14,7 +14,7 @@ #ifndef WEBP_MUX_ANIMI_H_ #define WEBP_MUX_ANIMI_H_ -#include "../webp/mux.h" +#include "src/webp/mux.h" #ifdef __cplusplus extern "C" { @@ -40,4 +40,4 @@ int WebPAnimEncoderRefineRect( } // extern "C" #endif -#endif /* WEBP_MUX_ANIMI_H_ */ +#endif // WEBP_MUX_ANIMI_H_ diff --git a/src/3rdparty/libwebp/src/mux/muxedit.c b/src/3rdparty/libwebp/src/mux/muxedit.c index d2c5305..ccf14b2 100644 --- a/src/3rdparty/libwebp/src/mux/muxedit.c +++ b/src/3rdparty/libwebp/src/mux/muxedit.c @@ -13,8 +13,8 @@ // Vikas (vikasa@google.com) #include <assert.h> -#include "./muxi.h" -#include "../utils/utils.h" +#include "src/mux/muxi.h" +#include "src/utils/utils.h" //------------------------------------------------------------------------------ // Life of a mux object. @@ -69,12 +69,12 @@ void WebPMuxDelete(WebPMux* mux) { if (idx == (INDEX)) { \ err = ChunkAssignData(&chunk, data, copy_data, tag); \ if (err == WEBP_MUX_OK) { \ - err = ChunkSetNth(&chunk, (LIST), nth); \ + err = ChunkSetHead(&chunk, (LIST)); \ } \ return err; \ } -static WebPMuxError MuxSet(WebPMux* const mux, uint32_t tag, uint32_t nth, +static WebPMuxError MuxSet(WebPMux* const mux, uint32_t tag, const WebPData* const data, int copy_data) { WebPChunk chunk; WebPMuxError err = WEBP_MUX_NOT_FOUND; @@ -190,7 +190,7 @@ WebPMuxError WebPMuxSetChunk(WebPMux* mux, const char fourcc[4], if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err; // Add the given chunk. - return MuxSet(mux, tag, 1, chunk_data, copy_data); + return MuxSet(mux, tag, chunk_data, copy_data); } // Creates a chunk from given 'data' and sets it as 1st chunk in 'chunk_list'. @@ -202,7 +202,7 @@ static WebPMuxError AddDataToChunkList( ChunkInit(&chunk); err = ChunkAssignData(&chunk, data, copy_data, tag); if (err != WEBP_MUX_OK) goto Err; - err = ChunkSetNth(&chunk, chunk_list, 1); + err = ChunkSetHead(&chunk, chunk_list); if (err != WEBP_MUX_OK) goto Err; return WEBP_MUX_OK; Err: @@ -266,14 +266,14 @@ WebPMuxError WebPMuxPushFrame(WebPMux* mux, const WebPMuxFrameInfo* info, int copy_data) { WebPMuxImage wpi; WebPMuxError err; - const WebPData* const bitstream = &info->bitstream; // Sanity checks. if (mux == NULL || info == NULL) return WEBP_MUX_INVALID_ARGUMENT; if (info->id != WEBP_CHUNK_ANMF) return WEBP_MUX_INVALID_ARGUMENT; - if (bitstream->bytes == NULL || bitstream->size > MAX_CHUNK_PAYLOAD) { + if (info->bitstream.bytes == NULL || + info->bitstream.size > MAX_CHUNK_PAYLOAD) { return WEBP_MUX_INVALID_ARGUMENT; } @@ -287,7 +287,7 @@ WebPMuxError WebPMuxPushFrame(WebPMux* mux, const WebPMuxFrameInfo* info, } MuxImageInit(&wpi); - err = SetAlphaAndImageChunks(bitstream, copy_data, &wpi); + err = SetAlphaAndImageChunks(&info->bitstream, copy_data, &wpi); if (err != WEBP_MUX_OK) goto Err; assert(wpi.img_ != NULL); // As SetAlphaAndImageChunks() was successful. @@ -342,7 +342,7 @@ WebPMuxError WebPMuxSetAnimationParams(WebPMux* mux, // Set the animation parameters. PutLE32(data, params->bgcolor); PutLE16(data + 4, params->loop_count); - return MuxSet(mux, kChunks[IDX_ANIM].tag, 1, &anim, 1); + return MuxSet(mux, kChunks[IDX_ANIM].tag, &anim, 1); } WebPMuxError WebPMuxSetCanvasSize(WebPMux* mux, @@ -540,7 +540,7 @@ static WebPMuxError CreateVP8XChunk(WebPMux* const mux) { PutLE24(data + 4, width - 1); // canvas width. PutLE24(data + 7, height - 1); // canvas height. - return MuxSet(mux, kChunks[IDX_VP8X].tag, 1, &vp8x, 1); + return MuxSet(mux, kChunks[IDX_VP8X].tag, &vp8x, 1); } // Cleans up 'mux' by removing any unnecessary chunks. diff --git a/src/3rdparty/libwebp/src/mux/muxi.h b/src/3rdparty/libwebp/src/mux/muxi.h index e6606aa..7bc0b07 100644 --- a/src/3rdparty/libwebp/src/mux/muxi.h +++ b/src/3rdparty/libwebp/src/mux/muxi.h @@ -14,10 +14,11 @@ #ifndef WEBP_MUX_MUXI_H_ #define WEBP_MUX_MUXI_H_ +#include <assert.h> #include <stdlib.h> -#include "../dec/vp8i_dec.h" -#include "../dec/vp8li_dec.h" -#include "../webp/mux.h" +#include "src/dec/vp8i_dec.h" +#include "src/dec/vp8li_dec.h" +#include "src/webp/mux.h" #ifdef __cplusplus extern "C" { @@ -26,9 +27,9 @@ extern "C" { //------------------------------------------------------------------------------ // Defines and constants. -#define MUX_MAJ_VERSION 0 -#define MUX_MIN_VERSION 4 -#define MUX_REV_VERSION 0 +#define MUX_MAJ_VERSION 1 +#define MUX_MIN_VERSION 0 +#define MUX_REV_VERSION 3 // Chunk object. typedef struct WebPChunk WebPChunk; @@ -126,11 +127,14 @@ WebPChunk* ChunkSearchList(WebPChunk* first, uint32_t nth, uint32_t tag); WebPMuxError ChunkAssignData(WebPChunk* chunk, const WebPData* const data, int copy_data, uint32_t tag); -// Sets 'chunk' at nth position in the 'chunk_list'. -// nth = 0 has the special meaning "last of the list". +// Sets 'chunk' as the only element in 'chunk_list' if it is empty. // On success ownership is transferred from 'chunk' to the 'chunk_list'. -WebPMuxError ChunkSetNth(WebPChunk* chunk, WebPChunk** chunk_list, - uint32_t nth); +WebPMuxError ChunkSetHead(WebPChunk* const chunk, WebPChunk** const chunk_list); +// Sets 'chunk' at last position in the 'chunk_list'. +// On success ownership is transferred from 'chunk' to the 'chunk_list'. +// *chunk_list also points towards the last valid element of the initial +// *chunk_list. +WebPMuxError ChunkAppend(WebPChunk* const chunk, WebPChunk*** const chunk_list); // Releases chunk and returns chunk->next_. WebPChunk* ChunkRelease(WebPChunk* const chunk); @@ -143,13 +147,13 @@ void ChunkListDelete(WebPChunk** const chunk_list); // Returns size of the chunk including chunk header and padding byte (if any). static WEBP_INLINE size_t SizeWithPadding(size_t chunk_size) { + assert(chunk_size <= MAX_CHUNK_PAYLOAD); return CHUNK_HEADER_SIZE + ((chunk_size + 1) & ~1U); } // Size of a chunk including header and padding. static WEBP_INLINE size_t ChunkDiskSize(const WebPChunk* chunk) { const size_t data_size = chunk->data_.size; - assert(data_size < MAX_CHUNK_PAYLOAD); return SizeWithPadding(data_size); } @@ -227,4 +231,4 @@ WebPMuxError MuxValidate(const WebPMux* const mux); } // extern "C" #endif -#endif /* WEBP_MUX_MUXI_H_ */ +#endif // WEBP_MUX_MUXI_H_ diff --git a/src/3rdparty/libwebp/src/mux/muxinternal.c b/src/3rdparty/libwebp/src/mux/muxinternal.c index 387b57e..b9ee671 100644 --- a/src/3rdparty/libwebp/src/mux/muxinternal.c +++ b/src/3rdparty/libwebp/src/mux/muxinternal.c @@ -13,8 +13,8 @@ // Vikas (vikasa@google.com) #include <assert.h> -#include "./muxi.h" -#include "../utils/utils.h" +#include "src/mux/muxi.h" +#include "src/utils/utils.h" #define UNDEFINED_CHUNK_SIZE ((uint32_t)(-1)) @@ -111,27 +111,6 @@ WebPChunk* ChunkSearchList(WebPChunk* first, uint32_t nth, uint32_t tag) { return ((nth > 0) && (iter > 0)) ? NULL : first; } -// Outputs a pointer to 'prev_chunk->next_', -// where 'prev_chunk' is the pointer to the chunk at position (nth - 1). -// Returns true if nth chunk was found. -static int ChunkSearchListToSet(WebPChunk** chunk_list, uint32_t nth, - WebPChunk*** const location) { - uint32_t count = 0; - assert(chunk_list != NULL); - *location = chunk_list; - - while (*chunk_list != NULL) { - WebPChunk* const cur_chunk = *chunk_list; - ++count; - if (count == nth) return 1; // Found. - chunk_list = &cur_chunk->next_; - *location = chunk_list; - } - - // *chunk_list is ok to be NULL if adding at last location. - return (nth == 0 || (count == nth - 1)) ? 1 : 0; -} - //------------------------------------------------------------------------------ // Chunk writer methods. @@ -156,11 +135,12 @@ WebPMuxError ChunkAssignData(WebPChunk* chunk, const WebPData* const data, return WEBP_MUX_OK; } -WebPMuxError ChunkSetNth(WebPChunk* chunk, WebPChunk** chunk_list, - uint32_t nth) { +WebPMuxError ChunkSetHead(WebPChunk* const chunk, + WebPChunk** const chunk_list) { WebPChunk* new_chunk; - if (!ChunkSearchListToSet(chunk_list, nth, &chunk_list)) { + assert(chunk_list != NULL); + if (*chunk_list != NULL) { return WEBP_MUX_NOT_FOUND; } @@ -168,11 +148,26 @@ WebPMuxError ChunkSetNth(WebPChunk* chunk, WebPChunk** chunk_list, if (new_chunk == NULL) return WEBP_MUX_MEMORY_ERROR; *new_chunk = *chunk; chunk->owner_ = 0; - new_chunk->next_ = *chunk_list; + new_chunk->next_ = NULL; *chunk_list = new_chunk; return WEBP_MUX_OK; } +WebPMuxError ChunkAppend(WebPChunk* const chunk, + WebPChunk*** const chunk_list) { + assert(chunk_list != NULL && *chunk_list != NULL); + + if (**chunk_list == NULL) { + ChunkSetHead(chunk, *chunk_list); + } else { + WebPChunk* last_chunk = **chunk_list; + while (last_chunk->next_ != NULL) last_chunk = last_chunk->next_; + ChunkSetHead(chunk, &last_chunk->next_); + *chunk_list = &last_chunk->next_; + } + return WEBP_MUX_OK; +} + //------------------------------------------------------------------------------ // Chunk deletion method(s). @@ -232,9 +227,11 @@ void MuxImageInit(WebPMuxImage* const wpi) { WebPMuxImage* MuxImageRelease(WebPMuxImage* const wpi) { WebPMuxImage* next; if (wpi == NULL) return NULL; - ChunkDelete(wpi->header_); - ChunkDelete(wpi->alpha_); - ChunkDelete(wpi->img_); + // There should be at most one chunk of header_, alpha_, img_ but we call + // ChunkListDelete to be safe + ChunkListDelete(&wpi->header_); + ChunkListDelete(&wpi->alpha_); + ChunkListDelete(&wpi->img_); ChunkListDelete(&wpi->unknown_); next = wpi->next_; @@ -504,6 +501,20 @@ WebPMuxError MuxValidate(const WebPMux* const mux) { if (!has_animation && (num_anim == 1 || num_frames > 0)) { return WEBP_MUX_INVALID_ARGUMENT; } + if (!has_animation) { + const WebPMuxImage* images = mux->images_; + // There can be only one image. + if (images == NULL || images->next_ != NULL) { + return WEBP_MUX_INVALID_ARGUMENT; + } + // Size must match. + if (mux->canvas_width_ > 0) { + if (images->width_ != mux->canvas_width_ || + images->height_ != mux->canvas_height_) { + return WEBP_MUX_INVALID_ARGUMENT; + } + } + } } // Verify either VP8X chunk is present OR there is only one elem in @@ -515,6 +526,7 @@ WebPMuxError MuxValidate(const WebPMux* const mux) { 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_)) { if (num_vp8x > 0) { // VP8X chunk is present, so it should contain ALPHA_FLAG. @@ -525,8 +537,6 @@ WebPMuxError MuxValidate(const WebPMux* const mux) { if (err != WEBP_MUX_OK) return err; if (num_alpha > 0) return WEBP_MUX_INVALID_ARGUMENT; } - } else { // Mux doesn't need alpha. So, ALPHA_FLAG should NOT be present. - if (flags & ALPHA_FLAG) return WEBP_MUX_INVALID_ARGUMENT; } return WEBP_MUX_OK; diff --git a/src/3rdparty/libwebp/src/mux/muxread.c b/src/3rdparty/libwebp/src/mux/muxread.c index 410acd9..268f6ac 100644 --- a/src/3rdparty/libwebp/src/mux/muxread.c +++ b/src/3rdparty/libwebp/src/mux/muxread.c @@ -13,8 +13,8 @@ // Vikas (vikasa@google.com) #include <assert.h> -#include "./muxi.h" -#include "../utils/utils.h" +#include "src/mux/muxi.h" +#include "src/utils/utils.h" //------------------------------------------------------------------------------ // Helper method(s). @@ -43,7 +43,7 @@ static WebPMuxError MuxGet(const WebPMux* const mux, CHUNK_INDEX idx, SWITCH_ID_LIST(IDX_ANIM, mux->anim_); SWITCH_ID_LIST(IDX_EXIF, mux->exif_); SWITCH_ID_LIST(IDX_XMP, mux->xmp_); - SWITCH_ID_LIST(IDX_UNKNOWN, mux->unknown_); + assert(idx != IDX_UNKNOWN); return WEBP_MUX_NOT_FOUND; } #undef SWITCH_ID_LIST @@ -59,6 +59,7 @@ static WebPMuxError ChunkVerifyAndAssign(WebPChunk* chunk, // Sanity checks. if (data_size < CHUNK_HEADER_SIZE) return WEBP_MUX_NOT_ENOUGH_DATA; chunk_size = GetLE32(data + TAG_SIZE); + if (chunk_size > MAX_CHUNK_PAYLOAD) return WEBP_MUX_BAD_DATA; { const size_t chunk_disk_size = SizeWithPadding(chunk_size); @@ -102,6 +103,7 @@ static int MuxImageParse(const WebPChunk* const chunk, int copy_data, const uint8_t* const last = bytes + size; WebPChunk subchunk; size_t subchunk_size; + WebPChunk** unknown_chunk_list = &wpi->unknown_; ChunkInit(&subchunk); assert(chunk->tag_ == kChunks[IDX_ANMF].tag); @@ -116,7 +118,7 @@ static int MuxImageParse(const WebPChunk* const chunk, int copy_data, if (size < hdr_size) goto Fail; ChunkAssignData(&subchunk, &temp, copy_data, chunk->tag_); } - ChunkSetNth(&subchunk, &wpi->header_, 1); + ChunkSetHead(&subchunk, &wpi->header_); wpi->is_partial_ = 1; // Waiting for ALPH and/or VP8/VP8L chunks. // Rest of the chunks. @@ -133,18 +135,23 @@ static int MuxImageParse(const WebPChunk* const chunk, int copy_data, switch (ChunkGetIdFromTag(subchunk.tag_)) { case WEBP_CHUNK_ALPHA: if (wpi->alpha_ != NULL) goto Fail; // Consecutive ALPH chunks. - if (ChunkSetNth(&subchunk, &wpi->alpha_, 1) != WEBP_MUX_OK) goto Fail; + if (ChunkSetHead(&subchunk, &wpi->alpha_) != WEBP_MUX_OK) goto Fail; wpi->is_partial_ = 1; // Waiting for a VP8 chunk. break; case WEBP_CHUNK_IMAGE: - if (ChunkSetNth(&subchunk, &wpi->img_, 1) != WEBP_MUX_OK) goto Fail; + if (wpi->img_ != NULL) goto Fail; // Only 1 image chunk allowed. + if (ChunkSetHead(&subchunk, &wpi->img_) != WEBP_MUX_OK) goto Fail; if (!MuxImageFinalize(wpi)) goto Fail; wpi->is_partial_ = 0; // wpi is completely filled. break; case WEBP_CHUNK_UNKNOWN: - if (wpi->is_partial_) goto Fail; // Encountered an unknown chunk - // before some image chunks. - if (ChunkSetNth(&subchunk, &wpi->unknown_, 0) != WEBP_MUX_OK) goto Fail; + if (wpi->is_partial_) { + goto Fail; // Encountered an unknown chunk + // before some image chunks. + } + if (ChunkAppend(&subchunk, &unknown_chunk_list) != WEBP_MUX_OK) { + goto Fail; + } break; default: goto Fail; @@ -175,6 +182,9 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data, const uint8_t* data; size_t size; WebPChunk chunk; + // Stores the end of the chunk lists so that it is faster to append data to + // their ends. + WebPChunk** chunk_list_ends[WEBP_CHUNK_NIL + 1] = { NULL }; ChunkInit(&chunk); // Sanity checks. @@ -187,7 +197,7 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data, size = bitstream->size; if (data == NULL) return NULL; - if (size < RIFF_HEADER_SIZE) return NULL; + if (size < RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE) return NULL; if (GetLE32(data + 0) != MKFOURCC('R', 'I', 'F', 'F') || GetLE32(data + CHUNK_HEADER_SIZE) != MKFOURCC('W', 'E', 'B', 'P')) { return NULL; @@ -196,8 +206,6 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data, mux = WebPMuxNew(); if (mux == NULL) return NULL; - if (size < RIFF_HEADER_SIZE + TAG_SIZE) goto Err; - tag = GetLE32(data + RIFF_HEADER_SIZE); if (tag != kChunks[IDX_VP8].tag && tag != kChunks[IDX_VP8L].tag && @@ -205,13 +213,17 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data, goto Err; // First chunk should be VP8, VP8L or VP8X. } - riff_size = SizeWithPadding(GetLE32(data + TAG_SIZE)); - if (riff_size > MAX_CHUNK_PAYLOAD || riff_size > size) { - goto Err; - } else { - if (riff_size < size) { // Redundant data after last chunk. - size = riff_size; // To make sure we don't read any data beyond mux_size. - } + riff_size = GetLE32(data + TAG_SIZE); + if (riff_size > MAX_CHUNK_PAYLOAD) goto Err; + + // Note this padding is historical and differs from demux.c which does not + // pad the file size. + riff_size = SizeWithPadding(riff_size); + if (riff_size < CHUNK_HEADER_SIZE) goto Err; + if (riff_size > size) goto Err; + // There's no point in reading past the end of the RIFF chunk. + if (size > riff_size + CHUNK_HEADER_SIZE) { + size = riff_size + CHUNK_HEADER_SIZE; } end = data + size; @@ -226,7 +238,6 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data, while (data != end) { size_t data_size; WebPChunkId id; - WebPChunk** chunk_list; if (ChunkVerifyAndAssign(&chunk, data, size, riff_size, copy_data) != WEBP_MUX_OK) { goto Err; @@ -236,11 +247,11 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data, switch (id) { case WEBP_CHUNK_ALPHA: if (wpi->alpha_ != NULL) goto Err; // Consecutive ALPH chunks. - if (ChunkSetNth(&chunk, &wpi->alpha_, 1) != WEBP_MUX_OK) goto Err; + if (ChunkSetHead(&chunk, &wpi->alpha_) != WEBP_MUX_OK) goto Err; wpi->is_partial_ = 1; // Waiting for a VP8 chunk. break; case WEBP_CHUNK_IMAGE: - if (ChunkSetNth(&chunk, &wpi->img_, 1) != WEBP_MUX_OK) goto Err; + if (ChunkSetHead(&chunk, &wpi->img_) != WEBP_MUX_OK) goto Err; if (!MuxImageFinalize(wpi)) goto Err; wpi->is_partial_ = 0; // wpi is completely filled. PushImage: @@ -257,9 +268,13 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data, default: // A non-image chunk. if (wpi->is_partial_) goto Err; // Encountered a non-image chunk before // getting all chunks of an image. - chunk_list = MuxGetChunkListFromId(mux, id); // List to add this chunk. - if (ChunkSetNth(&chunk, chunk_list, 0) != WEBP_MUX_OK) goto Err; + if (chunk_list_ends[id] == NULL) { + chunk_list_ends[id] = + MuxGetChunkListFromId(mux, id); // List to add this chunk. + } + if (ChunkAppend(&chunk, &chunk_list_ends[id]) != WEBP_MUX_OK) goto Err; if (id == WEBP_CHUNK_VP8X) { // grab global specs + if (data_size < CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE) goto Err; mux->canvas_width_ = GetLE24(data + 12) + 1; mux->canvas_height_ = GetLE24(data + 15) + 1; } @@ -270,6 +285,9 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data, ChunkInit(&chunk); } + // Incomplete image. + if (wpi->is_partial_) goto Err; + // Validate mux if complete. if (MuxValidate(mux) != WEBP_MUX_OK) goto Err; @@ -382,6 +400,10 @@ static WebPMuxError SynthesizeBitstream(const WebPMuxImage* const wpi, uint8_t* const data = (uint8_t*)WebPSafeMalloc(1ULL, size); if (data == NULL) return WEBP_MUX_MEMORY_ERROR; + // There should be at most one alpha_ chunk and exactly one img_ chunk. + assert(wpi->alpha_ == NULL || wpi->alpha_->next_ == NULL); + assert(wpi->img_ != NULL && wpi->img_->next_ == NULL); + // Main RIFF header. dst = MuxEmitRiffHeader(data, size); |