From 857650c8fcc4804c7a7b873e70f9ac0f23ef5383 Mon Sep 17 00:00:00 2001 From: Urvang Joshi Date: Fri, 6 Jul 2012 14:29:35 +0530 Subject: [PATCH 1/5] Mux: Add WebPDataInit() and remove WebPImageInfo Change-Id: If661f7d198e284a103a53a451e9f74805119fcf9 --- src/mux/muxedit.c | 53 ++++++++++++++----------------------------- src/mux/muxinternal.c | 10 ++++++-- src/mux/muxread.c | 2 +- src/webp/mux.h | 3 +++ 4 files changed, 29 insertions(+), 39 deletions(-) diff --git a/src/mux/muxedit.c b/src/mux/muxedit.c index 8e163a97..882ae708 100644 --- a/src/mux/muxedit.c +++ b/src/mux/muxedit.c @@ -17,15 +17,6 @@ extern "C" { #endif -// Object to store metadata about images. -typedef struct { - int x_offset_; - int y_offset_; - int duration_; - int width_; - int height_; -} WebPImageInfo; - //------------------------------------------------------------------------------ // Life of a mux object. @@ -155,7 +146,7 @@ static WebPMuxError CreateFrameTileData(const WebPData* const image, static WebPMuxError GetImageData(const WebPData* const bitstream, WebPData* const image, WebPData* const alpha, int* const is_lossless) { - memset(alpha, 0, sizeof(*alpha)); // Default: no alpha. + WebPDataInit(alpha); // Default: no alpha. if (bitstream->size_ < TAG_SIZE || memcmp(bitstream->bytes_, "RIFF", TAG_SIZE)) { // It is NOT webp file data. Return input data as is. @@ -359,7 +350,7 @@ static WebPMuxError MuxPushFrameTileInternal( if (err != WEBP_MUX_OK) return err; image_tag = is_lossless ? kChunks[IDX_VP8L].tag : kChunks[IDX_VP8].tag; - memset(&frame_tile, 0, sizeof(frame_tile)); + WebPDataInit(&frame_tile); ChunkInit(&chunk); MuxImageInit(&wpi); @@ -506,30 +497,19 @@ WebPMuxError MuxGetImageWidthHeight(const WebPChunk* const image_chunk, } static WebPMuxError GetImageInfo(const WebPMuxImage* const wpi, - WebPImageInfo* const image_info) { + int* const x_offset, int* const y_offset, + int* const duration, + int* const width, int* const height) { const WebPChunk* const image_chunk = wpi->img_; const WebPChunk* const frame_tile_chunk = wpi->header_; - WebPMuxError err; - int x_offset, y_offset, duration; - int width, height; - - memset(image_info, 0, sizeof(*image_info)); // Get offsets and duration from FRM/TILE chunk. - err = GetFrameTileInfo(frame_tile_chunk, &x_offset, &y_offset, &duration); + const WebPMuxError err = + GetFrameTileInfo(frame_tile_chunk, x_offset, y_offset, duration); if (err != WEBP_MUX_OK) return err; // Get width and height from VP8/VP8L chunk. - err = MuxGetImageWidthHeight(image_chunk, &width, &height); - if (err != WEBP_MUX_OK) return err; - - // All OK: fill up image_info. - image_info->x_offset_ = x_offset; - image_info->y_offset_ = y_offset; - image_info->duration_ = duration; - image_info->width_ = width; - image_info->height_ = height; - return WEBP_MUX_OK; + return MuxGetImageWidthHeight(image_chunk, width, height); } static WebPMuxError GetImageCanvasWidthHeight( @@ -537,7 +517,7 @@ static WebPMuxError GetImageCanvasWidthHeight( int* const width, int* const height) { WebPMuxImage* wpi = NULL; assert(mux != NULL); - assert(width && height); + assert(width != NULL && height != NULL); wpi = mux->images_; assert(wpi != NULL); @@ -549,17 +529,18 @@ static WebPMuxError GetImageCanvasWidthHeight( 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 int max_x_pos = image_info.x_offset_ + image_info.width_; - const int max_y_pos = image_info.y_offset_ + image_info.height_; + int x_offset, y_offset, duration, w, h; + const WebPMuxError err = GetImageInfo(wpi, &x_offset, &y_offset, + &duration, &w, &h); + const int max_x_pos = x_offset + w; + const int max_y_pos = y_offset + h; if (err != WEBP_MUX_OK) return err; - assert(image_info.x_offset_ < MAX_POSITION_OFFSET); - assert(image_info.y_offset_ < MAX_POSITION_OFFSET); + assert(x_offset < MAX_POSITION_OFFSET); + assert(y_offset < MAX_POSITION_OFFSET); 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_); + image_area += w * h; } *width = max_x; *height = max_y; diff --git a/src/mux/muxinternal.c b/src/mux/muxinternal.c index ce3cb6f1..a2e60ca7 100644 --- a/src/mux/muxinternal.c +++ b/src/mux/muxinternal.c @@ -212,17 +212,23 @@ uint8_t* ChunkListEmit(const WebPChunk* chunk_list, uint8_t* dst) { //------------------------------------------------------------------------------ // Manipulation of a WebPData object. +void WebPDataInit(WebPData* const webp_data) { + if (webp_data != NULL) { + memset(webp_data, 0, sizeof(*webp_data)); + } +} + void WebPDataClear(WebPData* const webp_data) { if (webp_data != NULL) { free((void*)webp_data->bytes_); - memset(webp_data, 0, sizeof(*webp_data)); + WebPDataInit(webp_data); } } int WebPDataCopy(const WebPData* const src, WebPData* const dst) { if (src == NULL || dst == NULL) return 0; - memset(dst, 0, sizeof(*dst)); + WebPDataInit(dst); if (src->bytes_ != NULL && src->size_ != 0) { dst->bytes_ = (uint8_t*)malloc(src->size_); if (dst->bytes_ == NULL) return 0; diff --git a/src/mux/muxread.c b/src/mux/muxread.c index 4562bc9e..b0e86a6b 100644 --- a/src/mux/muxread.c +++ b/src/mux/muxread.c @@ -37,7 +37,7 @@ static WebPMuxError MuxGet(const WebPMux* const mux, CHUNK_INDEX idx, uint32_t nth, WebPData* const data) { assert(mux != NULL); assert(!IsWPI(kChunks[idx].id)); - memset(data, 0, sizeof(*data)); + WebPDataInit(data); SWITCH_ID_LIST(IDX_VP8X, mux->vp8x_); SWITCH_ID_LIST(IDX_ICCP, mux->iccp_); diff --git a/src/webp/mux.h b/src/webp/mux.h index c028f019..40a86756 100644 --- a/src/webp/mux.h +++ b/src/webp/mux.h @@ -100,6 +100,9 @@ typedef struct { //------------------------------------------------------------------------------ // Manipulation of a WebPData object. +// Initializes the contents of the 'webp_data' object with default values. +WEBP_EXTERN(void) WebPDataInit(WebPData* const webp_data); + // Clears the contents of the 'webp_data' object by calling free(). Does not // deallocate the object itself. WEBP_EXTERN(void) WebPDataClear(WebPData* const webp_data); From a4b9b1c604ca872ea6fe67d8ad9522f79012bf13 Mon Sep 17 00:00:00 2001 From: Urvang Joshi Date: Fri, 6 Jul 2012 17:33:59 +0530 Subject: [PATCH 2/5] Remove some unused enum values. - WEBP_MUX_INVALID_PARAMETER: was used only at one place, and that too should actually be an assert(). - WEBP_MUX_ERROR: was never used. Change-Id: I8883cb4dfae7a7918507501f21fced0c04dda36a --- examples/webpmux.c | 7 +++---- src/mux/muxedit.c | 3 +-- src/webp/mux.h | 12 +++++------- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/examples/webpmux.c b/examples/webpmux.c index a33a86db..51333b06 100644 --- a/examples/webpmux.c +++ b/examples/webpmux.c @@ -117,13 +117,12 @@ static int CountOccurrences(const char* arglist[], int list_length, } static const char* const kErrorMessages[] = { - "WEBP_MUX_ERROR", "WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", - "WEBP_MUX_INVALID_PARAMETER", "WEBP_MUX_BAD_DATA", "WEBP_MUX_MEMORY_ERROR", - "WEBP_MUX_NOT_ENOUGH_DATA" + "WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", "WEBP_MUX_BAD_DATA", + "WEBP_MUX_MEMORY_ERROR", "WEBP_MUX_NOT_ENOUGH_DATA" }; static const char* ErrorString(WebPMuxError err) { - assert(err <= WEBP_MUX_ERROR && err >= WEBP_MUX_NOT_ENOUGH_DATA); + assert(err <= WEBP_MUX_NOT_FOUND && err >= WEBP_MUX_NOT_ENOUGH_DATA); return kErrorMessages[-err]; } diff --git a/src/mux/muxedit.c b/src/mux/muxedit.c index 882ae708..86069022 100644 --- a/src/mux/muxedit.c +++ b/src/mux/muxedit.c @@ -99,8 +99,7 @@ static WebPMuxError MuxAddChunk(WebPMux* const mux, uint32_t nth, uint32_t tag, const WebPData chunk_data = { data, size }; assert(mux != NULL); assert(size <= MAX_CHUNK_PAYLOAD); - - if (idx == IDX_NIL) return WEBP_MUX_INVALID_PARAMETER; + assert(idx != IDX_NIL); return MuxSet(mux, idx, nth, &chunk_data, copy_data); } diff --git a/src/webp/mux.h b/src/webp/mux.h index 40a86756..4e0310c0 100644 --- a/src/webp/mux.h +++ b/src/webp/mux.h @@ -56,13 +56,11 @@ extern "C" { // Error codes typedef enum { WEBP_MUX_OK = 1, - WEBP_MUX_ERROR = 0, - WEBP_MUX_NOT_FOUND = -1, - WEBP_MUX_INVALID_ARGUMENT = -2, - WEBP_MUX_INVALID_PARAMETER = -3, - WEBP_MUX_BAD_DATA = -4, - WEBP_MUX_MEMORY_ERROR = -5, - WEBP_MUX_NOT_ENOUGH_DATA = -6 + WEBP_MUX_NOT_FOUND = 0, + WEBP_MUX_INVALID_ARGUMENT = -1, + WEBP_MUX_BAD_DATA = -2, + WEBP_MUX_MEMORY_ERROR = -3, + WEBP_MUX_NOT_ENOUGH_DATA = -4 } WebPMuxError; // Flag values for different features used in VP8X chunk. From e45a446ad5ec17afaac8204a854e05d6d99f5187 Mon Sep 17 00:00:00 2001 From: Pascal Massimino Date: Mon, 23 Jul 2012 14:21:30 -0700 Subject: [PATCH 3/5] replace x*155/100 by x*101581>>16 Don't expect a visible speed diff. it's just cool. (and, that's one less TODO in the code). Change-Id: Iaeb2f1c930debb51501e170ee806f2f945fb1a8d --- src/dec/quant.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/dec/quant.c b/src/dec/quant.c index 95514114..d54097af 100644 --- a/src/dec/quant.c +++ b/src/dec/quant.c @@ -94,8 +94,10 @@ void VP8ParseQuant(VP8Decoder* const dec) { m->y1_mat_[1] = kAcTable[clip(q + 0, 127)]; m->y2_mat_[0] = kDcTable[clip(q + dqy2_dc, 127)] * 2; - // TODO(skal): make it another table? - m->y2_mat_[1] = kAcTable[clip(q + dqy2_ac, 127)] * 155 / 100; + // For all x in [0..284], x*155/100 is bitwise equal to (x*101581) >> 16. + // The smallest precision for that is '(x*6349) >> 12' but 16 is a good + // word size. + m->y2_mat_[1] = (kAcTable[clip(q + dqy2_ac, 127)] * 101581) >> 16; if (m->y2_mat_[1] < 8) m->y2_mat_[1] = 8; m->uv_mat_[0] = kDcTable[clip(q + dquv_dc, 117)]; From 7d732f905bbf0d2af1a30fa772a7f967e5d0d084 Mon Sep 17 00:00:00 2001 From: Pascal Massimino Date: Mon, 23 Jul 2012 14:26:56 -0700 Subject: [PATCH 4/5] make QuantizeLevels() store the sum of squared error (instead of MSE). Useful for directly storing the alpha-PSNR (in another patch) Change-Id: I4072864f9c53eb4f38366e8025a2816eb14f504e --- src/utils/quant_levels.c | 27 +++++++++------------------ src/utils/quant_levels.h | 8 ++++---- 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/src/utils/quant_levels.c b/src/utils/quant_levels.c index ba779a0b..f6884392 100644 --- a/src/utils/quant_levels.c +++ b/src/utils/quant_levels.c @@ -11,7 +11,6 @@ // Author: Skal (pascal.massimino@gmail.com) #include -#include // for sqrt() #include "./quant_levels.h" @@ -27,8 +26,8 @@ extern "C" { // ----------------------------------------------------------------------------- // Quantize levels. -int QuantizeLevels(uint8_t* data, int width, int height, - int num_levels, float* mse) { +int QuantizeLevels(uint8_t* const data, int width, int height, + int num_levels, uint64_t* const sse) { int freq[NUM_SYMBOLS] = { 0 }; int q_level[NUM_SYMBOLS] = { 0 }; double inv_q_level[NUM_SYMBOLS] = { 0 }; @@ -36,6 +35,7 @@ int QuantizeLevels(uint8_t* data, int width, int height, const size_t data_size = height * width; int i, num_levels_in, iter; double last_err = 1.e38, err = 0.; + const double err_threshold = ERROR_THRESHOLD * data_size; if (data == NULL) { return 0; @@ -60,10 +60,7 @@ int QuantizeLevels(uint8_t* data, int width, int height, } } - if (num_levels_in <= num_levels) { - if (mse) *mse = 0.; - return 1; // nothing to do ! - } + if (num_levels_in <= num_levels) goto End; // nothing to do! // Start with uniformly spread centroids. for (i = 0; i < num_levels; ++i) { @@ -78,7 +75,6 @@ int QuantizeLevels(uint8_t* data, int width, int height, // k-Means iterations. for (iter = 0; iter < MAX_ITER; ++iter) { - double err_count; double q_sum[NUM_SYMBOLS] = { 0 }; double q_count[NUM_SYMBOLS] = { 0 }; int s, slot = 0; @@ -109,17 +105,14 @@ int QuantizeLevels(uint8_t* data, int width, int height, // Compute convergence error. err = 0.; - err_count = 0.; for (s = min_s; s <= max_s; ++s) { const double error = s - inv_q_level[q_level[s]]; err += freq[s] * error * error; - err_count += freq[s]; } - if (err_count > 0.) err /= err_count; // Check for convergence: we stop as soon as the error is no // longer improving. - if (last_err - err < ERROR_THRESHOLD) break; + if (last_err - err < err_threshold) break; last_err = err; } @@ -140,16 +133,14 @@ int QuantizeLevels(uint8_t* data, int width, int height, data[n] = map[data[n]]; } } - - // Compute final mean squared error if needed. - if (mse != NULL) { - *mse = (float)sqrt(err); - } + End: + // Store sum of squared error if needed. + if (sse != NULL) *sse = (uint64_t)err; return 1; } -int DequantizeLevels(uint8_t* data, int width, int height) { +int DequantizeLevels(uint8_t* const data, int width, int height) { if (data == NULL || width <= 0 || height <= 0) return 0; // TODO(skal): implement gradient smoothing. (void)data; diff --git a/src/utils/quant_levels.h b/src/utils/quant_levels.h index d1075e62..89ccafe4 100644 --- a/src/utils/quant_levels.h +++ b/src/utils/quant_levels.h @@ -21,16 +21,16 @@ extern "C" { #endif // Replace the input 'data' of size 'width'x'height' with 'num-levels' -// quantized values. If not NULL, 'mse' will contain the mean-squared error. +// quantized values. If not NULL, 'sse' will contain the sum of squared error. // Valid range for 'num_levels' is [2, 256]. // Returns false in case of error (data is NULL, or parameters are invalid). -int QuantizeLevels(uint8_t* data, int width, int height, int num_levels, - float* mse); +int QuantizeLevels(uint8_t* const data, int width, int height, int num_levels, + uint64_t* const sse); // Apply post-processing to input 'data' of size 'width'x'height' assuming // that the source was quantized to a reduced number of levels. // Returns false in case of error (data is NULL, invalid parameters, ...). -int DequantizeLevels(uint8_t* data, int width, int height); +int DequantizeLevels(uint8_t* const data, int width, int height); #if defined(__cplusplus) || defined(c_plusplus) } // extern "C" From 80cc7303abce069415b039746c23a7750b4d6790 Mon Sep 17 00:00:00 2001 From: Pascal Massimino Date: Tue, 31 Jul 2012 16:56:39 -0700 Subject: [PATCH 5/5] WebPCheckMalloc() and WebPCheckCalloc(): safe size-checking versions of malloc() and calloc() Change-Id: Iffa3138c48b9b254b3d7eaad913e1f852d9dafba --- Android.mk | 1 + Makefile.vc | 1 + makefile.unix | 1 + src/utils/Makefile.am | 2 ++ src/utils/utils.c | 44 +++++++++++++++++++++++++++++++++++++++++++ src/utils/utils.h | 44 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 93 insertions(+) create mode 100644 src/utils/utils.c create mode 100644 src/utils/utils.h diff --git a/Android.mk b/Android.mk index 6931f9d5..8daa508d 100644 --- a/Android.mk +++ b/Android.mk @@ -47,6 +47,7 @@ LOCAL_SRC_FILES := \ src/utils/quant_levels.c \ src/utils/rescaler.c \ src/utils/thread.c \ + src/utils/utils.c \ LOCAL_CFLAGS := -Wall -DANDROID -DHAVE_MALLOC_H -DHAVE_PTHREAD \ -DNOT_HAVE_LOG2 -DWEBP_USE_THREAD \ diff --git a/Makefile.vc b/Makefile.vc index a0a495c4..c69b62dc 100644 --- a/Makefile.vc +++ b/Makefile.vc @@ -199,6 +199,7 @@ UTILS_OBJS = \ $(DIROBJ)\utils\quant_levels.obj \ $(DIROBJ)\utils\rescaler.obj \ $(DIROBJ)\utils\thread.obj \ + $(DIROBJ)\utils\utils.obj \ LIBWEBP_OBJS = $(DEC_OBJS) $(DSP_OBJS) $(ENC_OBJS) $(UTILS_OBJS) $(LIBWEBP_OBJS) LIBWEBPMUX_OBJS = $(MUX_OBJS) $(LIBWEBPMUX_OBJS) diff --git a/makefile.unix b/makefile.unix index 473b90af..85b21073 100644 --- a/makefile.unix +++ b/makefile.unix @@ -130,6 +130,7 @@ UTILS_OBJS = \ src/utils/quant_levels.o \ src/utils/rescaler.o \ src/utils/thread.o \ + src/utils/utils.o \ LIBWEBP_OBJS = $(DEC_OBJS) $(DSP_OBJS) $(ENC_OBJS) $(UTILS_OBJS) LIBWEBPMUX_OBJS = $(MUX_OBJS) diff --git a/src/utils/Makefile.am b/src/utils/Makefile.am index 96b2bd45..65054c03 100644 --- a/src/utils/Makefile.am +++ b/src/utils/Makefile.am @@ -20,6 +20,8 @@ libwebputils_la_SOURCES += rescaler.c libwebputils_la_SOURCES += rescaler.h libwebputils_la_SOURCES += thread.c libwebputils_la_SOURCES += thread.h +libwebputils_la_SOURCES += utils.c +libwebputils_la_SOURCES += utils.h libwebputilsinclude_HEADERS = ../webp/types.h libwebputilsincludedir = $(includedir)/webp diff --git a/src/utils/utils.c b/src/utils/utils.c new file mode 100644 index 00000000..673b7e28 --- /dev/null +++ b/src/utils/utils.c @@ -0,0 +1,44 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// This code is licensed under the same terms as WebM: +// Software License Agreement: http://www.webmproject.org/license/software/ +// Additional IP Rights Grant: http://www.webmproject.org/license/additional/ +// ----------------------------------------------------------------------------- +// +// Misc. common utility functions +// +// Author: Skal (pascal.massimino@gmail.com) + +#include +#include "./utils.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +//------------------------------------------------------------------------------ +// Checked memory allocation + +static int CheckSizeArguments(uint64_t nmemb, size_t size) { + const uint64_t total_size = nmemb * size; + if (nmemb == 0) return 1; + if ((uint64_t)size > WEBP_MAX_ALLOCABLE_MEMORY / nmemb) return 0; + if (total_size != (size_t)total_size) return 0; + return 1; +} + +void* WebPSafeMalloc(uint64_t nmemb, size_t size) { + if (!CheckSizeArguments(nmemb, size)) return NULL; + return malloc((size_t)(nmemb * size)); +} + +void* WebPSafeCalloc(uint64_t nmemb, size_t size) { + if (!CheckSizeArguments(nmemb, size)) return NULL; + return calloc((size_t)nmemb, size); +} + +//------------------------------------------------------------------------------ + +#if defined(__cplusplus) || defined(c_plusplus) +} // extern "C" +#endif diff --git a/src/utils/utils.h b/src/utils/utils.h new file mode 100644 index 00000000..a0347625 --- /dev/null +++ b/src/utils/utils.h @@ -0,0 +1,44 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// This code is licensed under the same terms as WebM: +// Software License Agreement: http://www.webmproject.org/license/software/ +// Additional IP Rights Grant: http://www.webmproject.org/license/additional/ +// ----------------------------------------------------------------------------- +// +// Misc. common utility functions +// +// Author: Skal (pascal.massimino@gmail.com) + +#ifndef WEBP_UTILS_UTILS_H_ +#define WEBP_UTILS_UTILS_H_ + +#include "../webp/types.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +//------------------------------------------------------------------------------ +// Memory allocation + +// This is the maximum memory amount that libwebp will ever try to allocate. +#define WEBP_MAX_ALLOCABLE_MEMORY (1ULL << 40) + +// size-checking safe malloc/calloc: verify that the requested size is not too +// large, or return NULL. You don't need to call these for constructs like +// malloc(sizeof(foo)), but only if there's picture-dependent size involved +// somewhere (like: malloc(num_pixels * sizeof(*something))). That's why this +// safe malloc() borrows the signature from calloc(), pointing at the dangerous +// underlying multiply involved. +void* WebPSafeMalloc(uint64_t nmemb, size_t size); +// Note that WebPSafeCalloc() expects the second argument type to be 'size_t' +// in order to favor the "calloc(num_foo, sizeof(foo))" pattern. +void* WebPSafeCalloc(uint64_t nmemb, size_t size); + +//------------------------------------------------------------------------------ + +#if defined(__cplusplus) || defined(c_plusplus) +} // extern "C" +#endif + +#endif /* WEBP_UTILS_UTILS_H_ */